From bda0951afd7d9d283cac7fc1055f2c427c6917bc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 15 Nov 2025 10:34:15 +0000 Subject: [PATCH 001/104] Bump js-yaml from 4.1.0 to 4.1.1 Bumps [js-yaml](https://github.com/nodeca/js-yaml) from 4.1.0 to 4.1.1. - [Changelog](https://github.com/nodeca/js-yaml/blob/master/CHANGELOG.md) - [Commits](https://github.com/nodeca/js-yaml/compare/4.1.0...4.1.1) --- updated-dependencies: - dependency-name: js-yaml dependency-version: 4.1.1 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- package-lock.json | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index b31f3b3d3..ecb3f5d9d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5580,10 +5580,11 @@ "dev": true }, "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "dev": true, + "license": "MIT", "dependencies": { "argparse": "^2.0.1" }, From fc4c37971c7eabe876939bac44f079e6af03ed91 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 16 Nov 2025 23:57:15 +0000 Subject: [PATCH 002/104] Bump min-document from 2.19.0 to 2.19.2 Bumps [min-document](https://github.com/Raynos/min-document) from 2.19.0 to 2.19.2. - [Commits](https://github.com/Raynos/min-document/compare/v2.19.0...v2.19.2) --- updated-dependencies: - dependency-name: min-document dependency-version: 2.19.2 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- package-lock.json | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index b31f3b3d3..cd859ea8d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6157,9 +6157,10 @@ } }, "node_modules/min-document": { - "version": "2.19.0", - "resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.0.tgz", - "integrity": "sha512-9Wy1B3m3f66bPPmU5hdA4DR4PB2OfDU/+GS3yAB7IQozE3tqXaVv2zOjgla7MEGSRv95+ILmOuvhLkOK6wJtCQ==", + "version": "2.19.2", + "resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.2.tgz", + "integrity": "sha512-8S5I8db/uZN8r9HSLFVWPdJCvYOejMcEC82VIzNUc6Zkklf/d1gg2psfE79/vyhWOj4+J8MtwmoOz3TmvaGu5A==", + "license": "MIT", "dependencies": { "dom-walk": "^0.1.0" } From f9d992faceae92c8be7f6bdd470b3d39adc005a8 Mon Sep 17 00:00:00 2001 From: Github Actions Date: Fri, 5 Dec 2025 22:12:12 +0000 Subject: [PATCH 003/104] Version bump --- box.json | 2 +- changelog.md | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/box.json b/box.json index d73ab4838..76d0e588f 100644 --- a/box.json +++ b/box.json @@ -1,6 +1,6 @@ { "name":"ContentBox Tester Site", - "version":"6.3.2", + "version":"6.4.0", "author":"Ortus Solutions ", "shortDescription":"A tester site for developing the ContentBox Modular CMS", "type":"cms", diff --git a/changelog.md b/changelog.md index 77535e5d6..469254ea0 100644 --- a/changelog.md +++ b/changelog.md @@ -9,9 +9,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [6.3.2] - 2025-12-05 + ### Fixed -* Fixed an issue where different engines were handing defaults +- Fixed an issue where different engines were handing defaults ## [6.3.1] - 2025-10-20 @@ -125,7 +127,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - [CONTENTBOX-1478](https://ortussolutions.atlassian.net/browse/CONTENTBOX-1478) Remove old tuckey urlrewrite.xml as this is now in the CommandBox Core - [CONTENTBOX-1493](https://ortussolutions.atlassian.net/browse/CONTENTBOX-1493) ContentBox Express removed since we now have CommandBox -[Unreleased]: https://github.com/Ortus-Solutions/ContentBox/compare/v6.3.1...HEAD +[Unreleased]: https://github.com/Ortus-Solutions/ContentBox/compare/v6.3.2...HEAD + +[6.3.2]: https://github.com/Ortus-Solutions/ContentBox/compare/v6.3.1...v6.3.2 [6.3.1]: https://github.com/Ortus-Solutions/ContentBox/compare/v6.3.0...v6.3.1 From 7a4257148c543f5f03c9514814231cd12e7c1482 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 31 Dec 2025 13:45:55 +0000 Subject: [PATCH 004/104] Bump qs from 6.11.2 to 6.14.1 Bumps [qs](https://github.com/ljharb/qs) from 6.11.2 to 6.14.1. - [Changelog](https://github.com/ljharb/qs/blob/main/CHANGELOG.md) - [Commits](https://github.com/ljharb/qs/compare/v6.11.2...v6.14.1) --- updated-dependencies: - dependency-name: qs dependency-version: 6.14.1 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- package-lock.json | 297 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 210 insertions(+), 87 deletions(-) diff --git a/package-lock.json b/package-lock.json index b31f3b3d3..d6efa9189 100644 --- a/package-lock.json +++ b/package-lock.json @@ -125,6 +125,7 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.3.tgz", "integrity": "sha512-Jg+msLuNuCJDyBvFv5+OKOUjWMZgd85bKjbICd3zWrKAo+bJ49HJufi7CQE0q0uR8NGyO6xkCACScNqyjHSZew==", "dev": true, + "peer": true, "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.22.13", @@ -2536,6 +2537,7 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz", "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==", "dev": true, + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -2579,6 +2581,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -2957,6 +2960,7 @@ "url": "https://github.com/sponsors/ai" } ], + "peer": true, "dependencies": { "caniuse-lite": "^1.0.30001541", "electron-to-chromium": "^1.4.535", @@ -3019,14 +3023,30 @@ "graceful-fs": "~1" } }, - "node_modules/call-bind": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz", - "integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==", + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", "dependencies": { - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.1", - "set-function-length": "^1.1.1" + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -3624,6 +3644,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", "dev": true, + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "json-schema-traverse": "^1.0.0", @@ -3915,19 +3936,6 @@ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, - "node_modules/define-data-property": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz", - "integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==", - "dependencies": { - "get-intrinsic": "^1.2.1", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, "node_modules/del": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/del/-/del-4.1.1.tgz", @@ -4071,6 +4079,20 @@ "url": "https://github.com/motdotla/dotenv?sponsor=1" } }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/duration": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/duration/-/duration-0.2.2.tgz", @@ -4189,12 +4211,42 @@ "is-arrayish": "^0.2.1" } }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/es-module-lexer": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.4.1.tgz", "integrity": "sha512-cXLGjP0c4T3flZJKQSuziYoq7MlT+rnvfZjfp7h+I7K9BNX54kP9nyWvdbwjQ4u1iWbOL4u96fgeZLToQlZC7w==", "dev": true }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/es5-ext": { "version": "0.9.2", "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.9.2.tgz", @@ -4265,6 +4317,7 @@ "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.53.0.tgz", "integrity": "sha512-N4VuiPjXDUa4xVeV/GC/RV3hQW9Nw+Y463lkWaKKXKYMvmRiRDAtfpuPFLN+E1/6ZhyR8J2ig+eVREnYgUsiag==", "dev": true, + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -4895,19 +4948,42 @@ } }, "node_modules/get-intrinsic": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz", - "integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/getsetdeep": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/getsetdeep/-/getsetdeep-2.0.0.tgz", @@ -5042,11 +5118,12 @@ } }, "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "dependencies": { - "get-intrinsic": "^1.1.3" + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -5093,32 +5170,11 @@ "node": ">=4" } }, - "node_modules/has-property-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz", - "integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==", - "dependencies": { - "get-intrinsic": "^1.2.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", - "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -5127,9 +5183,10 @@ } }, "node_modules/hasown": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", - "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", "dependencies": { "function-bind": "^1.1.2" }, @@ -5536,7 +5593,8 @@ "node_modules/jquery": { "version": "3.7.1", "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.7.1.tgz", - "integrity": "sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg==" + "integrity": "sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg==", + "peer": true }, "node_modules/jquery-contextmenu": { "version": "2.9.2", @@ -6076,6 +6134,15 @@ "node": ">= 18" } }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/mdn-data": { "version": "2.0.14", "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", @@ -6188,6 +6255,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", "dev": true, + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "json-schema-traverse": "^1.0.0", @@ -6473,9 +6541,13 @@ } }, "node_modules/object-inspect": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", - "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -6830,6 +6902,7 @@ "url": "https://github.com/sponsors/ai" } ], + "peer": true, "dependencies": { "nanoid": "^3.3.6", "picocolors": "^1.0.0", @@ -7604,11 +7677,12 @@ } }, "node_modules/qs": { - "version": "6.11.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.2.tgz", - "integrity": "sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA==", + "version": "6.14.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz", + "integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==", + "license": "BSD-3-Clause", "dependencies": { - "side-channel": "^1.0.4" + "side-channel": "^1.1.0" }, "engines": { "node": ">=0.6" @@ -7981,6 +8055,7 @@ "resolved": "https://registry.npmjs.org/sass/-/sass-1.69.5.tgz", "integrity": "sha512-qg2+UCJibLr2LCVOt3OlPhr/dqVHWOa9XtZf2OjbLs/T4VPSJ00udtgJxH3neXZm+QqX8B+3cU7RaLqp1iVfcQ==", "dev": true, + "peer": true, "dependencies": { "chokidar": ">=3.0.0 <4.0.0", "immutable": "^4.0.0", @@ -8068,20 +8143,6 @@ "randombytes": "^2.1.0" } }, - "node_modules/set-function-length": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz", - "integrity": "sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==", - "dependencies": { - "define-data-property": "^1.1.1", - "get-intrinsic": "^1.2.1", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, "node_modules/setimmediate": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", @@ -8122,13 +8183,72 @@ } }, "node_modules/side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", "dependencies": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -8602,6 +8722,7 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", "dev": true, + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -8895,6 +9016,7 @@ "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.94.0.tgz", "integrity": "sha512-KcsGn50VT+06JH/iunZJedYGUJS5FGjow8wb9c0v5n1Om8O1g4L6LjtfxwlXIATopoQu+vOXXa7gYisWxCoPyg==", "dev": true, + "peer": true, "dependencies": { "@types/estree": "^1.0.5", "@webassemblyjs/ast": "^1.12.1", @@ -9018,6 +9140,7 @@ "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.10.0.tgz", "integrity": "sha512-NLhDfH/h4O6UOy+0LSso42xvYypClINuMNBVVzX4vX98TmTaTUxwRbXdhucbFMd2qLaCTcLq/PdYrvi8onw90w==", "dev": true, + "peer": true, "dependencies": { "@discoveryjs/json-ext": "^0.5.0", "@webpack-cli/configtest": "^1.2.0", From f7f22be87671baf660654234a37dedb58baad414 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 21 Jan 2026 23:03:07 +0000 Subject: [PATCH 005/104] Bump lodash from 4.17.21 to 4.17.23 Bumps [lodash](https://github.com/lodash/lodash) from 4.17.21 to 4.17.23. - [Release notes](https://github.com/lodash/lodash/releases) - [Commits](https://github.com/lodash/lodash/compare/4.17.21...4.17.23) --- updated-dependencies: - dependency-name: lodash dependency-version: 4.17.23 dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- package-lock.json | 9 +++++---- package.json | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index b31f3b3d3..02cff4583 100644 --- a/package-lock.json +++ b/package-lock.json @@ -32,7 +32,7 @@ "jquery-nestable": "^0.8.0", "jquery-validation": "^1.20.0", "jquery.cookie": "^1.4.1", - "lodash": "^4.17.21", + "lodash": "^4.17.23", "luxon": "^3.4.3", "lz-string": "^1.5.0", "morris.js": "^0.5.0", @@ -5791,9 +5791,10 @@ } }, "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", + "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", + "license": "MIT" }, "node_modules/lodash.debounce": { "version": "4.0.8", diff --git a/package.json b/package.json index d50602fa6..57b47887d 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,7 @@ "jquery-nestable": "^0.8.0", "jquery-validation": "^1.20.0", "jquery.cookie": "^1.4.1", - "lodash": "^4.17.21", + "lodash": "^4.17.23", "luxon": "^3.4.3", "lz-string": "^1.5.0", "morris.js": "^0.5.0", From f827f0363908a3782fc4c9ec3981ca7cb65701f2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 7 Feb 2026 10:32:42 +0000 Subject: [PATCH 006/104] Bump webpack in /modules/contentbox/themes/default Bumps [webpack](https://github.com/webpack/webpack) from 5.97.1 to 5.105.0. - [Release notes](https://github.com/webpack/webpack/releases) - [Changelog](https://github.com/webpack/webpack/blob/main/CHANGELOG.md) - [Commits](https://github.com/webpack/webpack/compare/v5.97.1...v5.105.0) --- updated-dependencies: - dependency-name: webpack dependency-version: 5.105.0 dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- .../themes/default/package-lock.json | 585 ++++++++++++------ .../contentbox/themes/default/package.json | 2 +- 2 files changed, 380 insertions(+), 207 deletions(-) diff --git a/modules/contentbox/themes/default/package-lock.json b/modules/contentbox/themes/default/package-lock.json index aa6a85b24..3a7c2f762 100644 --- a/modules/contentbox/themes/default/package-lock.json +++ b/modules/contentbox/themes/default/package-lock.json @@ -13,7 +13,7 @@ "bootswatch": "^3.4.1", "coldbox-elixir": "^4.0.5", "font-awesome-sass": "^4.7.0", - "webpack": "^5.97.1", + "webpack": "^5.105.0", "webpack-cli": "^4.10.0" }, "devDependencies": { @@ -1549,36 +1549,32 @@ } }, "node_modules/@jridgewell/source-map": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.5.tgz", - "integrity": "sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==", + "version": "0.3.11", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz", + "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==", "dependencies": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" } }, "node_modules/@jridgewell/source-map/node_modules/@jridgewell/gen-mapping": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", - "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", "dependencies": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - }, - "engines": { - "node": ">=6.0.0" + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==" }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.22", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.22.tgz", - "integrity": "sha512-Wf963MzWtA2sjrNt+g18IAln9lKnlRp+K2eH4jjIoF1wYeq3aMREpG09xhlhdzS0EjwU7qmUJYangWa+151vZw==", + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" @@ -1611,9 +1607,9 @@ } }, "node_modules/@types/estree": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", - "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==" + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==" }, "node_modules/@types/glob": { "version": "7.2.0", @@ -1625,9 +1621,9 @@ } }, "node_modules/@types/json-schema": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", - "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==" + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==" }, "node_modules/@types/minimatch": { "version": "3.0.5", @@ -1814,9 +1810,9 @@ "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==" }, "node_modules/acorn": { - "version": "8.14.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", - "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "bin": { "acorn": "bin/acorn" }, @@ -1824,6 +1820,17 @@ "node": ">=0.4.0" } }, + "node_modules/acorn-import-phases": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/acorn-import-phases/-/acorn-import-phases-1.0.4.tgz", + "integrity": "sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==", + "engines": { + "node": ">=10.13.0" + }, + "peerDependencies": { + "acorn": "^8.14.0" + } + }, "node_modules/adjust-sourcemap-loader": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/adjust-sourcemap-loader/-/adjust-sourcemap-loader-4.0.0.tgz", @@ -1994,6 +2001,14 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, + "node_modules/baseline-browser-mapping": { + "version": "2.9.19", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.19.tgz", + "integrity": "sha512-ipDqC8FrAl/76p2SSWKSI+H9tFwm7vYqXQrItCuiVPt26Km0jS+NzSsBWAaBusvSbQcfJG+JitdMm+wZAgTYqg==", + "bin": { + "baseline-browser-mapping": "dist/cli.js" + } + }, "node_modules/big.js": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", @@ -2051,9 +2066,9 @@ } }, "node_modules/browserslist": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", - "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", + "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", "funding": [ { "type": "opencollective", @@ -2069,10 +2084,11 @@ } ], "dependencies": { - "caniuse-lite": "^1.0.30001688", - "electron-to-chromium": "^1.5.73", - "node-releases": "^2.0.19", - "update-browserslist-db": "^1.1.1" + "baseline-browser-mapping": "^2.9.0", + "caniuse-lite": "^1.0.30001759", + "electron-to-chromium": "^1.5.263", + "node-releases": "^2.0.27", + "update-browserslist-db": "^1.2.0" }, "bin": { "browserslist": "cli.js" @@ -2110,9 +2126,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001692", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001692.tgz", - "integrity": "sha512-A95VKan0kdtrsnMubMKxEKUKImOPSuCpYgxSQBo036P5YYgVIcOYJEgt/txJWqObiRQeISNCfef9nvlQ0vbV7A==", + "version": "1.0.30001769", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001769.tgz", + "integrity": "sha512-BCfFL1sHijQlBGWBMuJyhZUhzo7wer5sVj9hqekB/7xn0Ypy+pER/edCYQm4exbXj4WiySGp40P8UuTh6w1srg==", "funding": [ { "type": "opencollective", @@ -2691,9 +2707,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.80", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.80.tgz", - "integrity": "sha512-LTrKpW0AqIuHwmlVNV+cjFYTnXtM9K37OGhpe0ZI10ScPSxqVSryZHIY3WnCS5NSYbBODRTZyhRMS2h5FAEqAw==" + "version": "1.5.286", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.286.tgz", + "integrity": "sha512-9tfDXhJ4RKFNerfjdCcZfufu49vg620741MNs26a9+bhLThdB+plgMeou98CAaHu/WATj2iHOOHTp1hWtABj2A==" }, "node_modules/emojis-list": { "version": "3.0.0", @@ -2704,12 +2720,12 @@ } }, "node_modules/enhanced-resolve": { - "version": "5.18.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.0.tgz", - "integrity": "sha512-0/r0MySGYG8YqlayBZ6MuCfECmHFdJ5qyPh8s8wa5Hnm6SaFLSK1VYCbj+NKp090Nm1caZhD+QTnmxO7esYGyQ==", + "version": "5.19.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.19.0.tgz", + "integrity": "sha512-phv3E1Xl4tQOShqSte26C7Fl84EwUdZsyOuSSk9qtAGyyQs2s3jJzComh+Abf4g187lUUAvH+H26omrqia2aGg==", "dependencies": { "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" + "tapable": "^2.3.0" }, "engines": { "node": ">=10.13.0" @@ -2735,9 +2751,9 @@ } }, "node_modules/es-module-lexer": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.3.1.tgz", - "integrity": "sha512-JUFAyicQV9mXc3YRxPnDlrfBKpqt6hUYzz9/boprUJHs4e4KVr3XwOF70doO6gwXUor6EWZJAyWAfKki84t20Q==" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.0.0.tgz", + "integrity": "sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==" }, "node_modules/escalade": { "version": "3.2.0", @@ -2823,6 +2839,21 @@ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" }, + "node_modules/fast-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ] + }, "node_modules/fastest-levenshtein": { "version": "1.0.12", "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.12.tgz", @@ -3346,11 +3377,15 @@ } }, "node_modules/loader-runner": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", - "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.1.tgz", + "integrity": "sha512-IWqP2SCPhyVFTBtRcgMHdzlf9ul25NwaFx4wCEH/KjAXuuHY4yNjvPXsBokp8jCB936PyWRaPKUNh8NvylLp2Q==", "engines": { "node": ">=6.11.5" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" } }, "node_modules/loader-utils": { @@ -3552,9 +3587,9 @@ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" }, "node_modules/node-releases": { - "version": "2.0.19", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", - "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==" + "version": "2.0.27", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", + "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==" }, "node_modules/normalize-path": { "version": "3.0.0", @@ -4735,20 +4770,24 @@ } }, "node_modules/tapable": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", - "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz", + "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==", "engines": { "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" } }, "node_modules/terser": { - "version": "5.27.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.27.0.tgz", - "integrity": "sha512-bi1HRwVRskAjheeYl291n3JC4GgO/Ty4z1nVs5AAsmonJulGxpSektecnNedrwK9C7vpvVtcX3cw00VSLt7U2A==", + "version": "5.46.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.46.0.tgz", + "integrity": "sha512-jTwoImyr/QbOWFFso3YoU3ik0jBBDJ6JTOQiy/J2YxVJdZCc+5u7skhNwiOR3FQIygFqVUPHl7qbbxtjW2K3Qg==", "dependencies": { "@jridgewell/source-map": "^0.3.3", - "acorn": "^8.8.2", + "acorn": "^8.15.0", "commander": "^2.20.0", "source-map-support": "~0.5.20" }, @@ -4760,15 +4799,15 @@ } }, "node_modules/terser-webpack-plugin": { - "version": "5.3.10", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz", - "integrity": "sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==", + "version": "5.3.16", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.16.tgz", + "integrity": "sha512-h9oBFCWrq78NyWWVcSwZarJkZ01c2AyGrzs1crmHZO3QUg9D61Wu4NPjBy69n7JqylFF5y+CsUZYmYEIZ3mR+Q==", "dependencies": { - "@jridgewell/trace-mapping": "^0.3.20", + "@jridgewell/trace-mapping": "^0.3.25", "jest-worker": "^27.4.5", - "schema-utils": "^3.1.1", - "serialize-javascript": "^6.0.1", - "terser": "^5.26.0" + "schema-utils": "^4.3.0", + "serialize-javascript": "^6.0.2", + "terser": "^5.31.1" }, "engines": { "node": ">= 10.13.0" @@ -4792,14 +4831,46 @@ } } }, + "node_modules/terser-webpack-plugin/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/terser-webpack-plugin/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/terser-webpack-plugin/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, "node_modules/terser-webpack-plugin/node_modules/schema-utils": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", - "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz", + "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", "dependencies": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" }, "engines": { "node": ">= 10.13.0" @@ -4874,9 +4945,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.2.tgz", - "integrity": "sha512-PPypAm5qvlD7XMZC3BujecnaOxwhrtoFR+Dqkk5Aa/6DssiH0ibKoketaj9w8LP7Bont1rYeoV5plxD7RTEPRg==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", "funding": [ { "type": "opencollective", @@ -4959,9 +5030,9 @@ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, "node_modules/watchpack": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz", - "integrity": "sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.5.1.tgz", + "integrity": "sha512-Zn5uXdcFNIA1+1Ei5McRd+iRzfhENPCe7LeABkJtNulSxjma+l7ltNx55BWZkRlwRnpOgHqxnjyaDgJnNXnqzg==", "dependencies": { "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.1.2" @@ -4971,33 +5042,35 @@ } }, "node_modules/webpack": { - "version": "5.97.1", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.97.1.tgz", - "integrity": "sha512-EksG6gFY3L1eFMROS/7Wzgrii5mBAFe4rIr3r2BTfo7bcc+DWwFZ4OJ/miOuHJO/A85HwyI4eQ0F6IKXesO7Fg==", + "version": "5.105.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.105.0.tgz", + "integrity": "sha512-gX/dMkRQc7QOMzgTe6KsYFM7DxeIONQSui1s0n/0xht36HvrgbxtM1xBlgx596NbpHuQU8P7QpKwrZYwUX48nw==", "dependencies": { "@types/eslint-scope": "^3.7.7", - "@types/estree": "^1.0.6", + "@types/estree": "^1.0.8", + "@types/json-schema": "^7.0.15", "@webassemblyjs/ast": "^1.14.1", "@webassemblyjs/wasm-edit": "^1.14.1", "@webassemblyjs/wasm-parser": "^1.14.1", - "acorn": "^8.14.0", - "browserslist": "^4.24.0", + "acorn": "^8.15.0", + "acorn-import-phases": "^1.0.3", + "browserslist": "^4.28.1", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.17.1", - "es-module-lexer": "^1.2.1", + "enhanced-resolve": "^5.19.0", + "es-module-lexer": "^2.0.0", "eslint-scope": "5.1.1", "events": "^3.2.0", "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.2.11", "json-parse-even-better-errors": "^2.3.1", - "loader-runner": "^4.2.0", + "loader-runner": "^4.3.1", "mime-types": "^2.1.27", "neo-async": "^2.6.2", - "schema-utils": "^3.2.0", - "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.3.10", - "watchpack": "^2.4.1", - "webpack-sources": "^3.2.3" + "schema-utils": "^4.3.3", + "tapable": "^2.3.0", + "terser-webpack-plugin": "^5.3.16", + "watchpack": "^2.5.1", + "webpack-sources": "^3.3.3" }, "bin": { "webpack": "bin/webpack.js" @@ -5182,21 +5255,53 @@ } }, "node_modules/webpack-sources": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", - "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.3.3.tgz", + "integrity": "sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg==", "engines": { "node": ">=10.13.0" } }, + "node_modules/webpack/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/webpack/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/webpack/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, "node_modules/webpack/node_modules/schema-utils": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", - "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz", + "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", "dependencies": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" }, "engines": { "node": ">= 10.13.0" @@ -6268,35 +6373,34 @@ "integrity": "sha512-Ct5MqZkLGEXTVmQYbGtx9SVqD2fqwvdubdps5D3djjAkgkKwT918VNOz65pEHFaYTeWcukmJmH5SwsA9Tn2ObQ==" }, "@jridgewell/source-map": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.5.tgz", - "integrity": "sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==", + "version": "0.3.11", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz", + "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==", "requires": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" }, "dependencies": { "@jridgewell/gen-mapping": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", - "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", "requires": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" } } } }, "@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==" }, "@jridgewell/trace-mapping": { - "version": "0.3.22", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.22.tgz", - "integrity": "sha512-Wf963MzWtA2sjrNt+g18IAln9lKnlRp+K2eH4jjIoF1wYeq3aMREpG09xhlhdzS0EjwU7qmUJYangWa+151vZw==", + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", "requires": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" @@ -6326,9 +6430,9 @@ } }, "@types/estree": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", - "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==" + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==" }, "@types/glob": { "version": "7.2.0", @@ -6340,9 +6444,9 @@ } }, "@types/json-schema": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", - "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==" + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==" }, "@types/minimatch": { "version": "3.0.5", @@ -6516,9 +6620,15 @@ "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==" }, "acorn": { - "version": "8.14.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", - "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==" + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==" + }, + "acorn-import-phases": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/acorn-import-phases/-/acorn-import-phases-1.0.4.tgz", + "integrity": "sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==", + "requires": {} }, "adjust-sourcemap-loader": { "version": "4.0.0", @@ -6646,6 +6756,11 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, + "baseline-browser-mapping": { + "version": "2.9.19", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.19.tgz", + "integrity": "sha512-ipDqC8FrAl/76p2SSWKSI+H9tFwm7vYqXQrItCuiVPt26Km0jS+NzSsBWAaBusvSbQcfJG+JitdMm+wZAgTYqg==" + }, "big.js": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", @@ -6691,14 +6806,15 @@ } }, "browserslist": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", - "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", + "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", "requires": { - "caniuse-lite": "^1.0.30001688", - "electron-to-chromium": "^1.5.73", - "node-releases": "^2.0.19", - "update-browserslist-db": "^1.1.1" + "baseline-browser-mapping": "^2.9.0", + "caniuse-lite": "^1.0.30001759", + "electron-to-chromium": "^1.5.263", + "node-releases": "^2.0.27", + "update-browserslist-db": "^1.2.0" } }, "buffer-from": { @@ -6727,9 +6843,9 @@ } }, "caniuse-lite": { - "version": "1.0.30001692", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001692.tgz", - "integrity": "sha512-A95VKan0kdtrsnMubMKxEKUKImOPSuCpYgxSQBo036P5YYgVIcOYJEgt/txJWqObiRQeISNCfef9nvlQ0vbV7A==" + "version": "1.0.30001769", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001769.tgz", + "integrity": "sha512-BCfFL1sHijQlBGWBMuJyhZUhzo7wer5sVj9hqekB/7xn0Ypy+pER/edCYQm4exbXj4WiySGp40P8UuTh6w1srg==" }, "chokidar": { "version": "3.5.3", @@ -7117,9 +7233,9 @@ "integrity": "sha512-1K6hR6wtk2FviQ4kEiSjFiH5rpzEVi8WW0x96aztHVMhEspNpc4DVOUTEHtEva5VThQ8IaBX1Pe4gSzpVVUsKQ==" }, "electron-to-chromium": { - "version": "1.5.80", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.80.tgz", - "integrity": "sha512-LTrKpW0AqIuHwmlVNV+cjFYTnXtM9K37OGhpe0ZI10ScPSxqVSryZHIY3WnCS5NSYbBODRTZyhRMS2h5FAEqAw==" + "version": "1.5.286", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.286.tgz", + "integrity": "sha512-9tfDXhJ4RKFNerfjdCcZfufu49vg620741MNs26a9+bhLThdB+plgMeou98CAaHu/WATj2iHOOHTp1hWtABj2A==" }, "emojis-list": { "version": "3.0.0", @@ -7127,12 +7243,12 @@ "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==" }, "enhanced-resolve": { - "version": "5.18.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.0.tgz", - "integrity": "sha512-0/r0MySGYG8YqlayBZ6MuCfECmHFdJ5qyPh8s8wa5Hnm6SaFLSK1VYCbj+NKp090Nm1caZhD+QTnmxO7esYGyQ==", + "version": "5.19.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.19.0.tgz", + "integrity": "sha512-phv3E1Xl4tQOShqSte26C7Fl84EwUdZsyOuSSk9qtAGyyQs2s3jJzComh+Abf4g187lUUAvH+H26omrqia2aGg==", "requires": { "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" + "tapable": "^2.3.0" } }, "entities": { @@ -7146,9 +7262,9 @@ "integrity": "sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==" }, "es-module-lexer": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.3.1.tgz", - "integrity": "sha512-JUFAyicQV9mXc3YRxPnDlrfBKpqt6hUYzz9/boprUJHs4e4KVr3XwOF70doO6gwXUor6EWZJAyWAfKki84t20Q==" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.0.0.tgz", + "integrity": "sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==" }, "escalade": { "version": "3.2.0", @@ -7212,6 +7328,11 @@ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" }, + "fast-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==" + }, "fastest-levenshtein": { "version": "1.0.12", "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.12.tgz", @@ -7591,9 +7712,9 @@ "integrity": "sha512-xaYmXZtTHPAw5m+xLN8ab9C+3a8YmV3asNSPOATITbtwrfbwaLJj8h66H1WMIpALCkqsIzK3h7oQ+PdX+LQ9Eg==" }, "loader-runner": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", - "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==" + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.1.tgz", + "integrity": "sha512-IWqP2SCPhyVFTBtRcgMHdzlf9ul25NwaFx4wCEH/KjAXuuHY4yNjvPXsBokp8jCB936PyWRaPKUNh8NvylLp2Q==" }, "loader-utils": { "version": "2.0.4", @@ -7736,9 +7857,9 @@ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" }, "node-releases": { - "version": "2.0.19", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", - "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==" + "version": "2.0.27", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", + "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==" }, "normalize-path": { "version": "3.0.0", @@ -8511,17 +8632,17 @@ } }, "tapable": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", - "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==" + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz", + "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==" }, "terser": { - "version": "5.27.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.27.0.tgz", - "integrity": "sha512-bi1HRwVRskAjheeYl291n3JC4GgO/Ty4z1nVs5AAsmonJulGxpSektecnNedrwK9C7vpvVtcX3cw00VSLt7U2A==", + "version": "5.46.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.46.0.tgz", + "integrity": "sha512-jTwoImyr/QbOWFFso3YoU3ik0jBBDJ6JTOQiy/J2YxVJdZCc+5u7skhNwiOR3FQIygFqVUPHl7qbbxtjW2K3Qg==", "requires": { "@jridgewell/source-map": "^0.3.3", - "acorn": "^8.8.2", + "acorn": "^8.15.0", "commander": "^2.20.0", "source-map-support": "~0.5.20" }, @@ -8534,25 +8655,50 @@ } }, "terser-webpack-plugin": { - "version": "5.3.10", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz", - "integrity": "sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==", + "version": "5.3.16", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.16.tgz", + "integrity": "sha512-h9oBFCWrq78NyWWVcSwZarJkZ01c2AyGrzs1crmHZO3QUg9D61Wu4NPjBy69n7JqylFF5y+CsUZYmYEIZ3mR+Q==", "requires": { - "@jridgewell/trace-mapping": "^0.3.20", + "@jridgewell/trace-mapping": "^0.3.25", "jest-worker": "^27.4.5", - "schema-utils": "^3.1.1", - "serialize-javascript": "^6.0.1", - "terser": "^5.26.0" + "schema-utils": "^4.3.0", + "serialize-javascript": "^6.0.2", + "terser": "^5.31.1" }, "dependencies": { + "ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "requires": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + } + }, + "ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "requires": { + "fast-deep-equal": "^3.1.3" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, "schema-utils": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", - "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz", + "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", "requires": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" } } } @@ -8599,9 +8745,9 @@ "integrity": "sha512-5Zfuy9q/DFr4tfO7ZPeVXb1aPoeQSdeFMLpYuFebehDAhbuevLs5yxSZmIFN1tP5F9Wl4IpJrYojg85/zgyZHQ==" }, "update-browserslist-db": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.2.tgz", - "integrity": "sha512-PPypAm5qvlD7XMZC3BujecnaOxwhrtoFR+Dqkk5Aa/6DssiH0ibKoketaj9w8LP7Bont1rYeoV5plxD7RTEPRg==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", "requires": { "escalade": "^3.2.0", "picocolors": "^1.1.1" @@ -8643,52 +8789,79 @@ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, "watchpack": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz", - "integrity": "sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.5.1.tgz", + "integrity": "sha512-Zn5uXdcFNIA1+1Ei5McRd+iRzfhENPCe7LeABkJtNulSxjma+l7ltNx55BWZkRlwRnpOgHqxnjyaDgJnNXnqzg==", "requires": { "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.1.2" } }, "webpack": { - "version": "5.97.1", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.97.1.tgz", - "integrity": "sha512-EksG6gFY3L1eFMROS/7Wzgrii5mBAFe4rIr3r2BTfo7bcc+DWwFZ4OJ/miOuHJO/A85HwyI4eQ0F6IKXesO7Fg==", + "version": "5.105.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.105.0.tgz", + "integrity": "sha512-gX/dMkRQc7QOMzgTe6KsYFM7DxeIONQSui1s0n/0xht36HvrgbxtM1xBlgx596NbpHuQU8P7QpKwrZYwUX48nw==", "requires": { "@types/eslint-scope": "^3.7.7", - "@types/estree": "^1.0.6", + "@types/estree": "^1.0.8", + "@types/json-schema": "^7.0.15", "@webassemblyjs/ast": "^1.14.1", "@webassemblyjs/wasm-edit": "^1.14.1", "@webassemblyjs/wasm-parser": "^1.14.1", - "acorn": "^8.14.0", - "browserslist": "^4.24.0", + "acorn": "^8.15.0", + "acorn-import-phases": "^1.0.3", + "browserslist": "^4.28.1", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.17.1", - "es-module-lexer": "^1.2.1", + "enhanced-resolve": "^5.19.0", + "es-module-lexer": "^2.0.0", "eslint-scope": "5.1.1", "events": "^3.2.0", "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.2.11", "json-parse-even-better-errors": "^2.3.1", - "loader-runner": "^4.2.0", + "loader-runner": "^4.3.1", "mime-types": "^2.1.27", "neo-async": "^2.6.2", - "schema-utils": "^3.2.0", - "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.3.10", - "watchpack": "^2.4.1", - "webpack-sources": "^3.2.3" + "schema-utils": "^4.3.3", + "tapable": "^2.3.0", + "terser-webpack-plugin": "^5.3.16", + "watchpack": "^2.5.1", + "webpack-sources": "^3.3.3" }, "dependencies": { + "ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "requires": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + } + }, + "ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "requires": { + "fast-deep-equal": "^3.1.3" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, "schema-utils": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", - "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz", + "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", "requires": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" } } } @@ -8804,9 +8977,9 @@ } }, "webpack-sources": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", - "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==" + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.3.3.tgz", + "integrity": "sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg==" }, "which": { "version": "1.3.1", diff --git a/modules/contentbox/themes/default/package.json b/modules/contentbox/themes/default/package.json index 52c5ca6b0..5e57a1e10 100644 --- a/modules/contentbox/themes/default/package.json +++ b/modules/contentbox/themes/default/package.json @@ -15,7 +15,7 @@ "bootswatch": "^3.4.1", "coldbox-elixir": "^4.0.5", "font-awesome-sass": "^4.7.0", - "webpack": "^5.97.1", + "webpack": "^5.105.0", "webpack-cli": "^4.10.0" }, "devDependencies": { From dba34947b2b3f812aabcb96dad747fd952070bb8 Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Tue, 24 Mar 2026 18:53:25 +0100 Subject: [PATCH 007/104] CONTENTBOX-1555 #resolve Updated all github actions --- .github/copilot-instructions.md | 93 +++++++++++++++++++++++++++++++++ .github/dependabot.yml | 13 +++++ .github/workflows/lts.yml | 4 +- .github/workflows/pr.yml | 2 +- .github/workflows/release.yml | 24 ++++----- .github/workflows/snapshot.yml | 4 +- .github/workflows/tests.yml | 37 ++++++------- 7 files changed, 138 insertions(+), 39 deletions(-) create mode 100644 .github/copilot-instructions.md create mode 100644 .github/dependabot.yml diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 000000000..21b3fce34 --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,93 @@ +# ContentBox Workspace Instructions + +These instructions help AI coding agents work productively in this repository. + +## Scope And Priorities + +- Prefer small, focused changes and preserve existing module boundaries. +- Do not move features between modules unless explicitly requested. +- Link to canonical docs in this repo instead of duplicating long guidance. + +## Quick Start + +1. Install project dependencies: + - `box recipe workbench/setup.boxr` +2. Start local server (default is port `8589`): + - `box server start` +3. Build frontend assets after clone and after asset changes: + - `npm run build-dev` + +## Canonical Commands + +- Format code: + - `box run-script format` + - `box run-script format:check` + - `box run-script format:watch` +- Test runners: + - `http://127.0.0.1:8589/tests/runner.cfm` + - `http://127.0.0.1:8589/tests/runner-api.cfm` +- Migrations: + - `box run-script contentbox:migrate:create name=YourMigration` + - `box run-script contentbox:migrate:up` + - `box run-script contentbox:migrate:down` +- Frontend builds: + - `npm run dev` + - `npm run watch` + - `npm run prod` + - `npm run build-dev` + - `npm run build-prod` + +## Architecture Boundaries + +- Root app (`/`) is the host ColdBox application and local development shell. +- `modules/contentbox/` is the core ContentBox engine and should remain self-contained. +- `modules_app/contentbox-custom/` is the customization layer for user-owned content/themes/widgets/modules. +- `modules/contentbox-installer/` is installation workflow code. + +When implementing features/fixes: + +- Keep core logic in `modules/contentbox/` unless it is customization-specific. +- Keep user/theme customizations in `modules_app/contentbox-custom/`. +- Avoid editing generated asset output directories under `includes/`. + +## CFML And Style Conventions + +- Target Lucee 5+ and Adobe ColdFusion 2018+ compatibility. +- Follow project formatting in `/.cfformat.json`. +- Run formatter before finalizing changes. +- Maintain DocBox-style CFC documentation for new public functions/components. + +## Testing Expectations + +- Add or update tests with all behavioral changes. +- Prefer existing test patterns in: + - `tests/resources/BaseTest.cfc` + - `tests/resources/BaseApiTest.cfc` +- Keep tests scoped to impacted area (`tests/specs/contentbox-web/` or `tests/specs/contentbox-api/`). + +## Known Pitfalls + +- Asset outputs in `modules/contentbox/modules/contentbox-admin/includes/` and theme `includes/` are generated and can be overwritten by builds. +- After asset/source changes, rebuild assets instead of manually editing compiled output. +- If tests require DB-backed integration behavior, verify local datasource prerequisites from test docs. + +## PR And Branch Expectations + +- Target PRs to `development`, not `master`. +- Include tests for fixes/features. +- Follow the repository PR template. + +## Key References + +- Contributor workflow: `CONTRIBUTING.md` +- Project overview/setup: `readme.md` +- Test setup details: `tests/readme.md` +- UI/asset workflow: `workbench/Developer.md` +- PR template: `.github/PULL_REQUEST_TEMPLATE.md` + +## Agent Behavior In This Repo + +- Prefer `rg` for file/text search. +- Read nearby code before editing to preserve local conventions. +- Avoid broad refactors unless explicitly requested. +- If command/tooling assumptions are unclear, verify in `box.json` and `package.json` first. \ No newline at end of file diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000..089cc640d --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,13 @@ +version: 2 +updates: + # GitHub Actions - updates uses: statements in workflows + - package-ecosystem: "github-actions" + directory: "/" # Where your .github/workflows/ folder is + schedule: + interval: "quarterly" + + # NPM + - package-ecosystem: "npm" + directory: "/system/exceptions" # adjust if needed + schedule: + interval: "monthly" diff --git a/.github/workflows/lts.yml b/.github/workflows/lts.yml index 76399b413..9ee557c89 100644 --- a/.github/workflows/lts.yml +++ b/.github/workflows/lts.yml @@ -20,7 +20,7 @@ jobs: name: Code Auto-Formatting runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Auto-format uses: Ortus-Solutions/commandbox-action@v1.0.3 @@ -28,7 +28,7 @@ jobs: cmd: run-script format - name: Commit Format Changes - uses: stefanzweifel/git-auto-commit-action@v5 + uses: stefanzweifel/git-auto-commit-action@v7 with: commit_message: Apply cfformat changes push_options: --force diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index d84eac388..cf8a3350f 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -22,7 +22,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout Repository - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Setup CommandBox CLI uses: Ortus-Solutions/setup-commandbox@v2.0.1 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e9e6b2174..c512a9573 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -40,10 +40,10 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout Repository - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Setup Java - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: distribution: "temurin" java-version: "21" @@ -60,7 +60,7 @@ jobs: cd modules/contentbox && box install --production - name: Install Node 18 - uses: actions/setup-node@v4 + uses: actions/setup-node@v6 with: node-version: 18 @@ -101,7 +101,7 @@ jobs: printf "DB_CONNECTIONSTRING=jdbc:mysql://127.0.0.1:3306/contentbox?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&useLegacyDatetimeCode=true\n" >> .env - name: Download Test Artifacts - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v8 if: env.SNAPSHOT == 'true' with: path: tests/results @@ -123,7 +123,7 @@ jobs: box run-script build:allDocs - name: Update changelog [unreleased] with latest version - uses: thomaseizinger/keep-a-changelog-new-release@1.3.0 + uses: thomaseizinger/keep-a-changelog-new-release@3.1.0 if: env.SNAPSHOT == 'false' with: changelogPath: ./changelog.md @@ -139,7 +139,7 @@ jobs: working-directory: build/build-contentbox - name: Commit Changelog [unreleased] with latest version - uses: EndBug/add-and-commit@v9.1.4 + uses: EndBug/add-and-commit@v10 if: env.SNAPSHOT == 'false' with: author_name: Github Actions @@ -157,7 +157,7 @@ jobs: - name: Upload Build Artifacts if: success() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: contentbox-variants path: | @@ -195,7 +195,7 @@ jobs: cd $ROOT_DIR/build/build-contentbox/installer-module && box forgebox publish - name: Create Github Release - uses: taiki-e/create-gh-release-action@v1.8.0 + uses: taiki-e/create-gh-release-action@v1.9.3 continue-on-error: true if: env.SNAPSHOT == 'false' with: @@ -226,13 +226,13 @@ jobs: needs: [ build ] steps: - name: Checkout Development Repository - uses: actions/checkout@v4 + uses: actions/checkout@v6 if: env.LTS == 'false' with: ref: development - name: Checkout LTS Repository - uses: actions/checkout@v4 + uses: actions/checkout@v6 if: env.LTS == 'true' - name: Setup CommandBox @@ -241,7 +241,7 @@ jobs: forgeboxAPIKey: ${{ secrets.FORGEBOX_TOKEN }} - name: Download build artifacts - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v8 with: name: contentbox-variants path: .tmp @@ -261,7 +261,7 @@ jobs: git pull - name: Commit Version Bump - uses: EndBug/add-and-commit@v9.1.4 + uses: EndBug/add-and-commit@v10 with: author_name: Github Actions author_email: info@ortussolutions.com diff --git a/.github/workflows/snapshot.yml b/.github/workflows/snapshot.yml index 1e8546b0b..fe837959b 100644 --- a/.github/workflows/snapshot.yml +++ b/.github/workflows/snapshot.yml @@ -26,7 +26,7 @@ jobs: name: Code Auto-Formatting runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Setup CommandBox CLI uses: Ortus-Solutions/setup-commandbox@v2.0.1 @@ -38,7 +38,7 @@ jobs: run: box run-script format - name: Commit Format Changes - uses: stefanzweifel/git-auto-commit-action@v5 + uses: stefanzweifel/git-auto-commit-action@v7 with: commit_message: Apply cfformat changes push_options: --force diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 3c2f8d1ac..60bd39358 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -18,32 +18,28 @@ jobs: strategy: fail-fast: false matrix: - cfengine: [ "lucee@5", "adobe@2021", "adobe@2023" ] - coldboxVersion: [ "^7" ] + cfengine: [ "lucee@5", "adobe@2023", "adobe@2025" ] + coldboxVersion: [ "^8" ] jdkVersion: [ "21" ] experimental: [ false ] include: - # Lucee 6 has a regression - - cfengine: "boxlang@1" - coldboxVersion: "^7" + # BoxLang CFML + - cfengine: "boxlang-cfml@1" + coldboxVersion: "^8" jdkVersion: "21" experimental: true - - cfengine: "boxlang@1" - coldboxVersion: "be" + - cfengine: "boxlang-cfml@be" + coldboxVersion: "^8" jdkVersion: "21" experimental: true # Lucee 6 has a regression - cfengine: "lucee@6" - coldboxVersion: "^7" + coldboxVersion: "^8" jdkVersion: "21" experimental: true - # - cfengine: "adobe@2025" - # coldboxVersion: "^7" - # jdkVersion: "21" - # experimental: true # ColdBox BE on all allowed distros # This makes sure we don't have any issues with the BE of ColdBox - - cfengine: "lucee@5" + - cfengine: "boxlang-cfml@1" coldboxVersion: "be" jdkVersion: "21" experimental: true @@ -51,14 +47,14 @@ jobs: coldboxVersion: "be" jdkVersion: "21" experimental: true - - cfengine: "adobe@2023" + - cfengine: "adobe@2025" coldboxVersion: "be" jdkVersion: "21" experimental: true steps: - name: Checkout Repository - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Setup Database and Fixtures run: | @@ -67,7 +63,7 @@ jobs: mysql -u${{ env.DB_USER }} -p${{ env.DB_PASSWORD }} < tests/resources/sql/mysql/contentbox.sql - name: Setup Java - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: distribution: "temurin" java-version: ${{ matrix.jdkVersion }} @@ -130,7 +126,7 @@ jobs: - name: Upload Test Results Artifacts if: always() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: contentbox-test-results-${{ matrix.cfengine }}-coldbox${{ matrix.coldboxVersion }}-experimental-${{ matrix.experimental }} path: | @@ -151,9 +147,6 @@ jobs: - name: Failure Debugging Info if: ${{ failure() }} run: | - if [[ "{{ matrix.cfengine }}" == "adobe@2021" ]] ; then - cat .engines/acf2021/WEB-INF/cfusion/logs/hibernatesql.log - fi if [[ "{{ matrix.cfengine }}" == "adobe@2023" ]] ; then cat .engines/acf2023/WEB-INF/cfusion/logs/hibernatesql.log fi @@ -165,7 +158,7 @@ jobs: - name: Upload Debugging Info To Artifacts if: ${{ failure() && !startsWith( 'pull_request', github.event_name ) }} - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: Failure Debugging Info - ${{ matrix.cfengine }} jdk:${{ matrix.jdkVersion }} coldbox:${{ matrix.coldboxVersion }} path: | @@ -183,7 +176,7 @@ jobs: steps: # Download the test results - name: Download Artifacts - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v8 with: path: artifacts From 0757c95115f93118aead3e4b85365283e565a757 Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Tue, 24 Mar 2026 18:53:37 +0100 Subject: [PATCH 008/104] CONTENTBOX-1557 #resolve Standardize upgrade to ColdBox 8 --- box-site.json | 2 +- box.json | 2 +- changelog.md | 2 +- ...be@2021.json => server-boxlang-cfml@1.json | 21 +++++++++++-------- ...lang@1.json => server-boxlang-cfml@be.json | 4 ++-- 5 files changed, 17 insertions(+), 14 deletions(-) rename server-adobe@2021.json => server-boxlang-cfml@1.json (57%) rename server-boxlang@1.json => server-boxlang-cfml@be.json (92%) diff --git a/box-site.json b/box-site.json index 4a2671be8..32e614a7d 100644 --- a/box-site.json +++ b/box-site.json @@ -27,7 +27,7 @@ "Curt Gratz " ], "dependencies":{ - "coldbox":"^7.0.0+7", + "coldbox":"^8", "contentbox":"~@version.number@+@build.number@", "commandbox-boxlang":"*", "commandbox-migrations":"*" diff --git a/box.json b/box.json index 76d0e588f..41d40956e 100644 --- a/box.json +++ b/box.json @@ -24,7 +24,7 @@ "Curt Gratz " ], "dependencies":{ - "coldbox":"be", + "coldbox":"^8", "commandbox-migrations":"*" }, "devDependencies":{ diff --git a/changelog.md b/changelog.md index 469254ea0..4cde9653b 100644 --- a/changelog.md +++ b/changelog.md @@ -13,7 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed -- Fixed an issue where different engines were handing defaults +- Fixed an issue where different engines were handing defaults ## [6.3.1] - 2025-10-20 diff --git a/server-adobe@2021.json b/server-boxlang-cfml@1.json similarity index 57% rename from server-adobe@2021.json rename to server-boxlang-cfml@1.json index b3c86f39d..0cbda2ec3 100644 --- a/server-adobe@2021.json +++ b/server-boxlang-cfml@1.json @@ -1,13 +1,13 @@ { "app":{ - "cfengine":"adobe@2021", - "serverHomeDirectory":".engines/acf2021" + "cfengine":"boxlang@1", + "serverHomeDirectory":".engines/boxlang-cfml" }, - "name":"contentbox-adobe2021", + "name":"contentbox-boxlang-cfml", "JVM":{ - "javaVersion":"openjdk11_jre", + "javaVersion":"openjdk21_jre", "heapSize":"1024", - "args":"-Dcom.sun.net.ssl.enableECC=false" + "args":"-Dcom.sun.net.ssl.enableECC=false -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=9999" }, "web":{ "directoryBrowsing":true, @@ -28,10 +28,13 @@ "image":"modules_app/contentbox-custom/_content/ContentBoxIcon300.png" } ], - "scripts":{ - "onServerInstall":"cfpm install zip,orm,mysql,postgresql,sqlserver,document,feed,mail,debugger" - }, - "cfconfig":{ + "cfconfig":{ "file":".cfconfig.json" + }, + "env":{ + "BOXLANG_DEBUG":true + }, + "scripts":{ + "onServerInitialInstall":"install bx-mail,bx-pdf,bx-mysql,bx-oracle,bx-postgresql,bx-compat-cfml,bx-esapi,bx-orm --noSave" } } diff --git a/server-boxlang@1.json b/server-boxlang-cfml@be.json similarity index 92% rename from server-boxlang@1.json rename to server-boxlang-cfml@be.json index 6700f8bf5..561dc0222 100644 --- a/server-boxlang@1.json +++ b/server-boxlang-cfml@be.json @@ -1,9 +1,9 @@ { "app":{ "cfengine":"boxlang@be", - "serverHomeDirectory":".engines/boxlang" + "serverHomeDirectory":".engines/boxlang-be" }, - "name":"contentbox-boxlang", + "name":"contentbox-boxlang-be", "JVM":{ "javaVersion":"openjdk21_jre", "heapSize":"1024", From 7a92cc8f1c4a663d79836158133a5a8408591b63 Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Tue, 24 Mar 2026 19:14:43 +0100 Subject: [PATCH 009/104] node 25 --- .github/copilot-instructions.md | 22 ++++++++++++++++++++++ .github/workflows/release.yml | 4 ++-- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 21b3fce34..5e4f3013e 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -28,8 +28,10 @@ These instructions help AI coding agents work productively in this repository. - `http://127.0.0.1:8589/tests/runner-api.cfm` - Migrations: - `box run-script contentbox:migrate:create name=YourMigration` + - `box run-script contentbox:migrate` - `box run-script contentbox:migrate:up` - `box run-script contentbox:migrate:down` + - `box run-script contentbox:migrate:fresh` - Frontend builds: - `npm run dev` - `npm run watch` @@ -37,6 +39,21 @@ These instructions help AI coding agents work productively in this repository. - `npm run build-dev` - `npm run build-prod` +## Runtime And Persistence + +- ContentBox uses CFML ORM backed by Hibernate (`this.ormEnabled = true`) configured in `Application.cfc`. +- Keep ORM settings and entity locations compatible with existing mappings in `Application.cfc`. +- Do not bypass migration scripts with ad hoc schema changes; use CommandBox migrations for DB evolution. +- When schema behavior changes, update migrations and tests together. + +## Engine Support + +- Keep changes compatible with the 3 supported engines: + - Lucee + - Adobe ColdFusion + - BoxLang +- Use the existing server config files in repo root (`server-lucee@*.json`, `server-adobe@*.json`, `server-boxlang@*.json`) as the compatibility reference. + ## Architecture Boundaries - Root app (`/`) is the host ColdBox application and local development shell. @@ -64,6 +81,11 @@ When implementing features/fixes: - `tests/resources/BaseTest.cfc` - `tests/resources/BaseApiTest.cfc` - Keep tests scoped to impacted area (`tests/specs/contentbox-web/` or `tests/specs/contentbox-api/`). +- Follow the current test layout: + - API specs in `tests/specs/contentbox-api/` + - Web/unit/integration specs in `tests/specs/contentbox-web/` + - Shared fixtures/seeds/sql in `tests/resources/` +- If DB-backed tests are involved, verify test DB prerequisites from `tests/readme.md` and engine-specific DB scripts in `tests/resources/sql/`. ## Known Pitfalls diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c512a9573..b8fb44d4d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -59,10 +59,10 @@ jobs: box install commandbox-docbox cd modules/contentbox && box install --production - - name: Install Node 18 + - name: Install Node 25 uses: actions/setup-node@v6 with: - node-version: 18 + node-version: 25 - name: Compile Release Assets run: | From e0a2eec937d16ebcadc77e059c5772c4b06c37f6 Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Tue, 24 Mar 2026 19:27:22 +0100 Subject: [PATCH 010/104] CONTENTBOX-1557 Standardize upgrade to ColdBox 8 --- .../modules/contentbox-admin/views/contentTemplates/index.cfm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/contentbox/modules/contentbox-admin/views/contentTemplates/index.cfm b/modules/contentbox/modules/contentbox-admin/views/contentTemplates/index.cfm index b2b36befd..1aa43eadd 100644 --- a/modules/contentbox/modules/contentbox-admin/views/contentTemplates/index.cfm +++ b/modules/contentbox/modules/contentbox-admin/views/contentTemplates/index.cfm @@ -563,7 +563,7 @@ - #renderView( + #view( view = "_tags/dialog/import", args = { title = "Import Templates", From 1ae3afa2da05dba87ead1315fe6b0221a0188eef Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 24 Mar 2026 18:28:37 +0000 Subject: [PATCH 011/104] Bump svgo from 2.8.0 to 2.8.2 in /modules/contentbox/themes/default Bumps [svgo](https://github.com/svg/svgo) from 2.8.0 to 2.8.2. - [Release notes](https://github.com/svg/svgo/releases) - [Commits](https://github.com/svg/svgo/compare/v2.8.0...v2.8.2) --- updated-dependencies: - dependency-name: svgo dependency-version: 2.8.2 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- .../themes/default/package-lock.json | 42 +++++++++---------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/modules/contentbox/themes/default/package-lock.json b/modules/contentbox/themes/default/package-lock.json index aa6a85b24..fd53c9a5b 100644 --- a/modules/contentbox/themes/default/package-lock.json +++ b/modules/contentbox/themes/default/package-lock.json @@ -1584,14 +1584,6 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/@trysound/sax": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", - "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==", - "engines": { - "node": ">=10.13.0" - } - }, "node_modules/@types/eslint": { "version": "9.6.1", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", @@ -4584,6 +4576,14 @@ } } }, + "node_modules/sax": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.0.tgz", + "integrity": "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==", + "engines": { + "node": ">=11.0.0" + } + }, "node_modules/schema-utils": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", @@ -4715,16 +4715,16 @@ } }, "node_modules/svgo": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/svgo/-/svgo-2.8.0.tgz", - "integrity": "sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==", + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-2.8.2.tgz", + "integrity": "sha512-TyzE4NVGLUFy+H/Uy4N6c3G0HEeprsVfge6Lmq+0FdQQ/zqoVYB62IsBZORsiL+o96s6ff/V6/3UQo/C0cgCAA==", "dependencies": { - "@trysound/sax": "0.2.0", "commander": "^7.2.0", "css-select": "^4.1.3", "css-tree": "^1.1.3", "csso": "^4.2.0", "picocolors": "^1.0.0", + "sax": "^1.5.0", "stable": "^0.1.8" }, "bin": { @@ -6302,11 +6302,6 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "@trysound/sax": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", - "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==" - }, "@types/eslint": { "version": "9.6.1", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", @@ -8404,6 +8399,11 @@ "neo-async": "^2.6.2" } }, + "sax": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.0.tgz", + "integrity": "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==" + }, "schema-utils": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", @@ -8497,16 +8497,16 @@ "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==" }, "svgo": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/svgo/-/svgo-2.8.0.tgz", - "integrity": "sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==", + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-2.8.2.tgz", + "integrity": "sha512-TyzE4NVGLUFy+H/Uy4N6c3G0HEeprsVfge6Lmq+0FdQQ/zqoVYB62IsBZORsiL+o96s6ff/V6/3UQo/C0cgCAA==", "requires": { - "@trysound/sax": "0.2.0", "commander": "^7.2.0", "css-select": "^4.1.3", "css-tree": "^1.1.3", "csso": "^4.2.0", "picocolors": "^1.0.0", + "sax": "^1.5.0", "stable": "^0.1.8" } }, From a156784ea800fba4e1bb1c915dd167abd94c14d9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 24 Mar 2026 18:28:40 +0000 Subject: [PATCH 012/104] Bump underscore from 1.13.6 to 1.13.8 Bumps [underscore](https://github.com/jashkenas/underscore) from 1.13.6 to 1.13.8. - [Commits](https://github.com/jashkenas/underscore/compare/1.13.6...1.13.8) --- updated-dependencies: - dependency-name: underscore dependency-version: 1.13.8 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- package-lock.json | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index b31f3b3d3..5fbe7b787 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8624,9 +8624,10 @@ } }, "node_modules/underscore": { - "version": "1.13.6", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.6.tgz", - "integrity": "sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==" + "version": "1.13.8", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.8.tgz", + "integrity": "sha512-DXtD3ZtEQzc7M8m4cXotyHR+FAS18C64asBYY5vqZexfYryNNnDc02W4hKg3rdQuqOYas1jkseX0+nZXjTXnvQ==", + "license": "MIT" }, "node_modules/undici-types": { "version": "5.26.5", From 56986fb13086ef7a7bf2134bee23a720b4311bf7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 24 Mar 2026 18:28:42 +0000 Subject: [PATCH 013/104] Bump immutable from 4.3.4 to 4.3.8 Bumps [immutable](https://github.com/immutable-js/immutable-js) from 4.3.4 to 4.3.8. - [Release notes](https://github.com/immutable-js/immutable-js/releases) - [Changelog](https://github.com/immutable-js/immutable-js/blob/main/CHANGELOG.md) - [Commits](https://github.com/immutable-js/immutable-js/compare/v4.3.4...v4.3.8) --- updated-dependencies: - dependency-name: immutable dependency-version: 4.3.8 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- package-lock.json | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index b31f3b3d3..894154992 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5219,10 +5219,11 @@ } }, "node_modules/immutable": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.4.tgz", - "integrity": "sha512-fsXeu4J4i6WNWSikpI88v/PcVflZz+6kMhUfIwc5SY+poQRPnaf5V7qds6SUyUN3cVxEzuCab7QIoLOQ+DQ1wA==", - "dev": true + "version": "4.3.8", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.8.tgz", + "integrity": "sha512-d/Ld9aLbKpNwyl0KiM2CT1WYvkitQ1TSvmRtkcV8FKStiDoA7Slzgjmb/1G2yhKM1p0XeNOieaTbFZmU1d3Xuw==", + "dev": true, + "license": "MIT" }, "node_modules/import-fresh": { "version": "3.3.0", From 5a73b6af26a6c49d443d6cd58b9ce18457c5bd70 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 24 Mar 2026 18:28:43 +0000 Subject: [PATCH 014/104] Bump minimatch from 3.1.2 to 3.1.5 in /modules/contentbox/themes/default Bumps [minimatch](https://github.com/isaacs/minimatch) from 3.1.2 to 3.1.5. - [Changelog](https://github.com/isaacs/minimatch/blob/main/changelog.md) - [Commits](https://github.com/isaacs/minimatch/compare/v3.1.2...v3.1.5) --- updated-dependencies: - dependency-name: minimatch dependency-version: 3.1.5 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- modules/contentbox/themes/default/package-lock.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/modules/contentbox/themes/default/package-lock.json b/modules/contentbox/themes/default/package-lock.json index aa6a85b24..7d21d7870 100644 --- a/modules/contentbox/themes/default/package-lock.json +++ b/modules/contentbox/themes/default/package-lock.json @@ -3514,9 +3514,9 @@ } }, "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -7713,9 +7713,9 @@ } }, "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "requires": { "brace-expansion": "^1.1.7" } From d2a617d73147bc9dc9ffa2cc96928bc106eb92f8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 24 Mar 2026 18:28:46 +0000 Subject: [PATCH 015/104] Bump flatted from 3.2.9 to 3.4.2 Bumps [flatted](https://github.com/WebReflection/flatted) from 3.2.9 to 3.4.2. - [Commits](https://github.com/WebReflection/flatted/compare/v3.2.9...v3.4.2) --- updated-dependencies: - dependency-name: flatted dependency-version: 3.4.2 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- package-lock.json | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index b31f3b3d3..a9dd9f5f9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4794,10 +4794,11 @@ } }, "node_modules/flatted": { - "version": "3.2.9", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz", - "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", - "dev": true + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", + "dev": true, + "license": "ISC" }, "node_modules/focus-trap": { "version": "6.9.4", From 1f937cee2716f927373728aec41272cf159e8469 Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Tue, 24 Mar 2026 19:34:54 +0100 Subject: [PATCH 016/104] Updated to latest TestBox --- tests/index.bxm | 832 +++++++++++++++++++++++++++++++++++++++++++++++ tests/runner.bxm | 36 ++ tests/runner.cfm | 34 +- 3 files changed, 892 insertions(+), 10 deletions(-) create mode 100755 tests/index.bxm create mode 100755 tests/runner.bxm diff --git a/tests/index.bxm b/tests/index.bxm new file mode 100755 index 000000000..3d8e872be --- /dev/null +++ b/tests/index.bxm @@ -0,0 +1,832 @@ + + +// Configurable parameters (can be overridden via URL query params) +bx:param name="url.directory" default="tests.specs"; +bx:param name="url.recurse" default="true" type="boolean"; +bx:param name="url.bundlesPattern" default=""; +bx:param name="url.labels" default=""; +bx:param name="url.excludes" default=""; +bx:param name="url.runnerUrl" default="runner.bxm"; +bx:param name="url.editor" default="vscode"; + + + + + + + + + TestBox Run IDE + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + +
+
+
+ +
+
0
+
Passed
+
+
+ +
+
0
+
Failed
+
+
+ +
+
0
+
Errored
+
+
+ +
+
0
+
Skipped
+
+
+
+ +
+ + + + + + + + +
+ +
+ +
+ + + + + + + + + + + + + + + + + + + + + +
\ No newline at end of file diff --git a/tests/runner.bxm b/tests/runner.bxm new file mode 100755 index 000000000..b281326be --- /dev/null +++ b/tests/runner.bxm @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/runner.cfm b/tests/runner.cfm index 4716b800b..91e4d5d34 100644 --- a/tests/runner.cfm +++ b/tests/runner.cfm @@ -1,22 +1,36 @@ - - - - - - - + + + + + + + + + + + + + - + + + - - + + + + + + + + From c7a00cfd5bcab95440bc26ff51a4d8004319ca65 Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Wed, 25 Mar 2026 15:02:54 +0100 Subject: [PATCH 017/104] check for pagepool --- Application.cfc | 2 +- server-boxlang-cfml@be.json | 2 +- tests/Application.cfc | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Application.cfc b/Application.cfc index 7dbf3c473..485b7f727 100644 --- a/Application.cfc +++ b/Application.cfc @@ -169,7 +169,7 @@ component { if ( this.cbLoadDynamicEnvironment ) { loadEnv( force: true ); } - if ( structKeyExists( server, "lucee" ) ) { + if ( getFunctionList().keyExists( "pagePoolClear" ) ) { pagePoolClear(); } ormReload(); diff --git a/server-boxlang-cfml@be.json b/server-boxlang-cfml@be.json index 561dc0222..3d12bf8a4 100644 --- a/server-boxlang-cfml@be.json +++ b/server-boxlang-cfml@be.json @@ -35,6 +35,6 @@ "BOXLANG_DEBUG":true }, "scripts":{ - "onServerInitialInstall":"install bx-mail,bx-pdf,bx-mysql,bx-oracle,bx-postgresql,bx-compat-cfml,bx-esapi,bx-orm --noSave" + "onServerInitialInstall":"install bx-mail,bx-pdf,bx-mysql,bx-oracle,bx-postgresql,bx-compat-cfml,bx-esapi,bx-orm@be --noSave" } } diff --git a/tests/Application.cfc b/tests/Application.cfc index 1d3f11063..503c8629e 100755 --- a/tests/Application.cfc +++ b/tests/Application.cfc @@ -91,7 +91,7 @@ component { // Force reinit to clear out caches, reload ORM and restart virtual app. if ( structKeyExists( url, "fwreinit" ) ) { - if ( structKeyExists( server, "lucee" ) ) { + if ( getFunctionList().keyExists( "pagePoolClear" ) ) { pagePoolClear(); } ormReload(); From de0ebb3e4e0f8e48637e8f19111147f3cd7d7ada Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 25 Mar 2026 20:10:44 +0000 Subject: [PATCH 018/104] Bump yaml from 1.10.2 to 1.10.3 in /modules/contentbox/themes/default Bumps [yaml](https://github.com/eemeli/yaml) from 1.10.2 to 1.10.3. - [Release notes](https://github.com/eemeli/yaml/releases) - [Commits](https://github.com/eemeli/yaml/compare/v1.10.2...v1.10.3) --- updated-dependencies: - dependency-name: yaml dependency-version: 1.10.3 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- modules/contentbox/themes/default/package-lock.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/modules/contentbox/themes/default/package-lock.json b/modules/contentbox/themes/default/package-lock.json index aa6a85b24..dde623288 100644 --- a/modules/contentbox/themes/default/package-lock.json +++ b/modules/contentbox/themes/default/package-lock.json @@ -5233,9 +5233,9 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "node_modules/yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.3.tgz", + "integrity": "sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA==", "engines": { "node": ">= 6" } @@ -8832,9 +8832,9 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==" + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.3.tgz", + "integrity": "sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA==" } } } From 73df89c06f6cc2fbc3660940c6531890ec7a8f43 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 25 Mar 2026 21:51:49 +0000 Subject: [PATCH 019/104] Bump picomatch from 2.3.1 to 2.3.2 in /modules/contentbox/themes/default Bumps [picomatch](https://github.com/micromatch/picomatch) from 2.3.1 to 2.3.2. - [Release notes](https://github.com/micromatch/picomatch/releases) - [Changelog](https://github.com/micromatch/picomatch/blob/master/CHANGELOG.md) - [Commits](https://github.com/micromatch/picomatch/compare/2.3.1...2.3.2) --- updated-dependencies: - dependency-name: picomatch dependency-version: 2.3.2 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- modules/contentbox/themes/default/package-lock.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/modules/contentbox/themes/default/package-lock.json b/modules/contentbox/themes/default/package-lock.json index aa6a85b24..3e8f7b4b0 100644 --- a/modules/contentbox/themes/default/package-lock.json +++ b/modules/contentbox/themes/default/package-lock.json @@ -3722,9 +3722,9 @@ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" }, "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", "dev": true, "engines": { "node": ">=8.6" @@ -7855,9 +7855,9 @@ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" }, "picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", "dev": true }, "pify": { From 09ad922938b9b07d36c498bdf8e1aa33430c116c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 25 Mar 2026 22:12:43 +0000 Subject: [PATCH 020/104] Bump picomatch from 2.3.1 to 2.3.2 Bumps [picomatch](https://github.com/micromatch/picomatch) from 2.3.1 to 2.3.2. - [Release notes](https://github.com/micromatch/picomatch/releases) - [Changelog](https://github.com/micromatch/picomatch/blob/master/CHANGELOG.md) - [Commits](https://github.com/micromatch/picomatch/compare/2.3.1...2.3.2) --- updated-dependencies: - dependency-name: picomatch dependency-version: 2.3.2 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- package-lock.json | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index b31f3b3d3..f76f299ea 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6674,10 +6674,11 @@ "dev": true }, "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", "dev": true, + "license": "MIT", "engines": { "node": ">=8.6" }, From adc5bed7d064f65f5989091a5c9643b67ddd1053 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 26 Mar 2026 15:52:16 +0000 Subject: [PATCH 021/104] Bump immutable from 4.1.0 to 4.3.8 in /modules/contentbox/themes/default Bumps [immutable](https://github.com/immutable-js/immutable-js) from 4.1.0 to 4.3.8. - [Release notes](https://github.com/immutable-js/immutable-js/releases) - [Changelog](https://github.com/immutable-js/immutable-js/blob/main/CHANGELOG.md) - [Commits](https://github.com/immutable-js/immutable-js/compare/v4.1.0...v4.3.8) --- updated-dependencies: - dependency-name: immutable dependency-version: 4.3.8 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- modules/contentbox/themes/default/package-lock.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/modules/contentbox/themes/default/package-lock.json b/modules/contentbox/themes/default/package-lock.json index aa6a85b24..7957ec759 100644 --- a/modules/contentbox/themes/default/package-lock.json +++ b/modules/contentbox/themes/default/package-lock.json @@ -3082,9 +3082,9 @@ } }, "node_modules/immutable": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.1.0.tgz", - "integrity": "sha512-oNkuqVTA8jqG1Q6c+UglTOD1xhC1BtjKI7XkCXRkZHrN5m18/XsnUp8Q89GkQO/z+0WjonSvl0FLhDYftp46nQ==", + "version": "4.3.8", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.8.tgz", + "integrity": "sha512-d/Ld9aLbKpNwyl0KiM2CT1WYvkitQ1TSvmRtkcV8FKStiDoA7Slzgjmb/1G2yhKM1p0XeNOieaTbFZmU1d3Xuw==", "dev": true }, "node_modules/import-local": { @@ -7404,9 +7404,9 @@ "requires": {} }, "immutable": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.1.0.tgz", - "integrity": "sha512-oNkuqVTA8jqG1Q6c+UglTOD1xhC1BtjKI7XkCXRkZHrN5m18/XsnUp8Q89GkQO/z+0WjonSvl0FLhDYftp46nQ==", + "version": "4.3.8", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.8.tgz", + "integrity": "sha512-d/Ld9aLbKpNwyl0KiM2CT1WYvkitQ1TSvmRtkcV8FKStiDoA7Slzgjmb/1G2yhKM1p0XeNOieaTbFZmU1d3Xuw==", "dev": true }, "import-local": { From bd82c00d8ec9070f81652edc2ad16586434004dd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 26 Mar 2026 16:43:06 +0000 Subject: [PATCH 022/104] Bump svgo from 2.8.0 to 2.8.2 Bumps [svgo](https://github.com/svg/svgo) from 2.8.0 to 2.8.2. - [Release notes](https://github.com/svg/svgo/releases) - [Commits](https://github.com/svg/svgo/compare/v2.8.0...v2.8.2) --- updated-dependencies: - dependency-name: svgo dependency-version: 2.8.2 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- package-lock.json | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/package-lock.json b/package-lock.json index b31f3b3d3..3e1cb03ed 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2093,15 +2093,6 @@ "url": "https://opencollective.com/popperjs" } }, - "node_modules/@trysound/sax": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", - "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==", - "dev": true, - "engines": { - "node": ">=10.13.0" - } - }, "node_modules/@types/eslint": { "version": "8.44.7", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.44.7.tgz", @@ -8031,6 +8022,16 @@ } } }, + "node_modules/sax": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.0.tgz", + "integrity": "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=11.0.0" + } + }, "node_modules/schema-utils": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", @@ -8314,17 +8315,18 @@ } }, "node_modules/svgo": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/svgo/-/svgo-2.8.0.tgz", - "integrity": "sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==", + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-2.8.2.tgz", + "integrity": "sha512-TyzE4NVGLUFy+H/Uy4N6c3G0HEeprsVfge6Lmq+0FdQQ/zqoVYB62IsBZORsiL+o96s6ff/V6/3UQo/C0cgCAA==", "dev": true, + "license": "MIT", "dependencies": { - "@trysound/sax": "0.2.0", "commander": "^7.2.0", "css-select": "^4.1.3", "css-tree": "^1.1.3", "csso": "^4.2.0", "picocolors": "^1.0.0", + "sax": "^1.5.0", "stable": "^0.1.8" }, "bin": { From 41a72238d8413e2436e0c0312fd8a28787a70f8c Mon Sep 17 00:00:00 2001 From: Jon Clausen Date: Fri, 27 Mar 2026 15:45:07 -0400 Subject: [PATCH 023/104] update api use to newer versions of `fs` library --- .../modules/contentbox-admin/elixir-module.js | 2 +- .../contentbox-ckeditor/elixir-module.js | 2 +- .../themes/default/package-lock.json | 26 ++++++++++++++++++- 3 files changed, 27 insertions(+), 3 deletions(-) diff --git a/modules/contentbox/modules/contentbox-admin/elixir-module.js b/modules/contentbox/modules/contentbox-admin/elixir-module.js index abf8fe19c..7edf37b5e 100644 --- a/modules/contentbox/modules/contentbox-admin/elixir-module.js +++ b/modules/contentbox/modules/contentbox-admin/elixir-module.js @@ -56,7 +56,7 @@ module.exports = function( mix ) { if ( process.env.NODE_ENV != "development" ){ // Clean up compiled root assets - fs.rmdir( "includes/js", { recursive: true } ); + fs.rm( "includes/js", { recursive: true, force: true } ); fs.remove( "includes/rev-manifest.json" ); } } ); diff --git a/modules/contentbox/modules/contentbox-admin/modules/contentbox-ckeditor/elixir-module.js b/modules/contentbox/modules/contentbox-admin/modules/contentbox-ckeditor/elixir-module.js index bb96fafa0..91cc5cf32 100644 --- a/modules/contentbox/modules/contentbox-admin/modules/contentbox-ckeditor/elixir-module.js +++ b/modules/contentbox/modules/contentbox-admin/modules/contentbox-ckeditor/elixir-module.js @@ -15,7 +15,7 @@ module.exports = function( mix ) { const destination = "modules/contentbox/modules/contentbox-admin/modules/contentbox-ckeditor/includes/ckeditor"; if ( fs.existsSync( destination ) ){ - fs.rmdirSync( destination, { recursive: true } ); + fs.rmSync( destination, { recursive: true, force: true } ); } fs.ensureDirSync( destination ); diff --git a/modules/contentbox/themes/default/package-lock.json b/modules/contentbox/themes/default/package-lock.json index e7058bc00..51fbaac07 100644 --- a/modules/contentbox/themes/default/package-lock.json +++ b/modules/contentbox/themes/default/package-lock.json @@ -59,6 +59,7 @@ "version": "7.18.5", "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.18.5.tgz", "integrity": "sha512-MGY8vg3DxMnctw0LdvSEojOsumc70g0t18gNyUdAZqB1Rpd1Bqo/svHGvt+UJ6JcGX+DIekGFDxxIWofBxLCnQ==", + "peer": true, "dependencies": { "@ampproject/remapping": "^2.1.0", "@babel/code-frame": "^7.16.7", @@ -1805,6 +1806,7 @@ "version": "8.15.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -1839,6 +1841,7 @@ "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -2075,6 +2078,7 @@ "url": "https://github.com/sponsors/ai" } ], + "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -2401,6 +2405,7 @@ "version": "8.11.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "json-schema-traverse": "^1.0.0", @@ -3495,6 +3500,7 @@ "version": "8.11.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "json-schema-traverse": "^1.0.0", @@ -3816,6 +3822,7 @@ "url": "https://github.com/sponsors/ai" } ], + "peer": true, "dependencies": { "nanoid": "^3.3.6", "picocolors": "^1.0.0", @@ -4561,6 +4568,7 @@ "resolved": "https://registry.npmjs.org/sass/-/sass-1.52.3.tgz", "integrity": "sha512-LNNPJ9lafx+j1ArtA7GyEJm9eawXN8KlA1+5dF6IZyoONg1Tyo/g+muOsENWJH/2Q1FHbbV4UwliU0cXMa/VIA==", "dev": true, + "peer": true, "dependencies": { "chokidar": ">=3.0.0 <4.0.0", "immutable": "^4.0.0", @@ -4835,6 +4843,7 @@ "version": "8.17.1", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -5045,6 +5054,7 @@ "version": "5.105.0", "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.105.0.tgz", "integrity": "sha512-gX/dMkRQc7QOMzgTe6KsYFM7DxeIONQSui1s0n/0xht36HvrgbxtM1xBlgx596NbpHuQU8P7QpKwrZYwUX48nw==", + "peer": true, "dependencies": { "@types/eslint-scope": "^3.7.7", "@types/estree": "^1.0.8", @@ -5162,6 +5172,7 @@ "version": "4.10.0", "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.10.0.tgz", "integrity": "sha512-NLhDfH/h4O6UOy+0LSso42xvYypClINuMNBVVzX4vX98TmTaTUxwRbXdhucbFMd2qLaCTcLq/PdYrvi8onw90w==", + "peer": true, "dependencies": { "@discoveryjs/json-ext": "^0.5.0", "@webpack-cli/configtest": "^1.2.0", @@ -5266,6 +5277,7 @@ "version": "8.17.1", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -5375,6 +5387,7 @@ "version": "7.18.5", "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.18.5.tgz", "integrity": "sha512-MGY8vg3DxMnctw0LdvSEojOsumc70g0t18gNyUdAZqB1Rpd1Bqo/svHGvt+UJ6JcGX+DIekGFDxxIWofBxLCnQ==", + "peer": true, "requires": { "@ampproject/remapping": "^2.1.0", "@babel/code-frame": "^7.16.7", @@ -6617,7 +6630,8 @@ "acorn": { "version": "8.15.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", - "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==" + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "peer": true }, "acorn-import-phases": { "version": "1.0.4", @@ -6638,6 +6652,7 @@ "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "peer": true, "requires": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -6804,6 +6819,7 @@ "version": "4.28.1", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", + "peer": true, "requires": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -7026,6 +7042,7 @@ "version": "8.11.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "peer": true, "requires": { "fast-deep-equal": "^3.1.1", "json-schema-traverse": "^1.0.0", @@ -7795,6 +7812,7 @@ "version": "8.11.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "peer": true, "requires": { "fast-deep-equal": "^3.1.1", "json-schema-traverse": "^1.0.0", @@ -8006,6 +8024,7 @@ "version": "8.4.31", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", + "peer": true, "requires": { "nanoid": "^3.3.6", "picocolors": "^1.0.0", @@ -8504,6 +8523,7 @@ "resolved": "https://registry.npmjs.org/sass/-/sass-1.52.3.tgz", "integrity": "sha512-LNNPJ9lafx+j1ArtA7GyEJm9eawXN8KlA1+5dF6IZyoONg1Tyo/g+muOsENWJH/2Q1FHbbV4UwliU0cXMa/VIA==", "dev": true, + "peer": true, "requires": { "chokidar": ">=3.0.0 <4.0.0", "immutable": "^4.0.0", @@ -8670,6 +8690,7 @@ "version": "8.17.1", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "peer": true, "requires": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -8801,6 +8822,7 @@ "version": "5.105.0", "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.105.0.tgz", "integrity": "sha512-gX/dMkRQc7QOMzgTe6KsYFM7DxeIONQSui1s0n/0xht36HvrgbxtM1xBlgx596NbpHuQU8P7QpKwrZYwUX48nw==", + "peer": true, "requires": { "@types/eslint-scope": "^3.7.7", "@types/estree": "^1.0.8", @@ -8833,6 +8855,7 @@ "version": "8.17.1", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "peer": true, "requires": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -8924,6 +8947,7 @@ "version": "4.10.0", "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.10.0.tgz", "integrity": "sha512-NLhDfH/h4O6UOy+0LSso42xvYypClINuMNBVVzX4vX98TmTaTUxwRbXdhucbFMd2qLaCTcLq/PdYrvi8onw90w==", + "peer": true, "requires": { "@discoveryjs/json-ext": "^0.5.0", "@webpack-cli/configtest": "^1.2.0", From 24cff0968c7265403852dd07ca0a3e8e4134ee95 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 27 Mar 2026 19:46:08 +0000 Subject: [PATCH 024/104] Bump brace-expansion from 1.1.12 to 1.1.13 Bumps [brace-expansion](https://github.com/juliangruber/brace-expansion) from 1.1.12 to 1.1.13. - [Release notes](https://github.com/juliangruber/brace-expansion/releases) - [Commits](https://github.com/juliangruber/brace-expansion/compare/v1.1.12...v1.1.13) --- updated-dependencies: - dependency-name: brace-expansion dependency-version: 1.1.13 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- package-lock.json | 27 +++++++-------------------- 1 file changed, 7 insertions(+), 20 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4e60dc0ec..ccde3d8b5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -125,7 +125,6 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.3.tgz", "integrity": "sha512-Jg+msLuNuCJDyBvFv5+OKOUjWMZgd85bKjbICd3zWrKAo+bJ49HJufi7CQE0q0uR8NGyO6xkCACScNqyjHSZew==", "dev": true, - "peer": true, "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.22.13", @@ -2528,7 +2527,6 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz", "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==", "dev": true, - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -2572,7 +2570,6 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -2905,9 +2902,9 @@ "integrity": "sha512-AkYD/i3NGCEksIb/PmmUrrxpa0DYi7Si30e9Ldhou2t1HTd9npoXUsxJJjFPvlV7iLUptlysWTcYD7i3DKV6qg==" }, "node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", + "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", @@ -2951,7 +2948,6 @@ "url": "https://github.com/sponsors/ai" } ], - "peer": true, "dependencies": { "caniuse-lite": "^1.0.30001541", "electron-to-chromium": "^1.4.535", @@ -3635,7 +3631,6 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", "dev": true, - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "json-schema-traverse": "^1.0.0", @@ -4308,7 +4303,6 @@ "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.53.0.tgz", "integrity": "sha512-N4VuiPjXDUa4xVeV/GC/RV3hQW9Nw+Y463lkWaKKXKYMvmRiRDAtfpuPFLN+E1/6ZhyR8J2ig+eVREnYgUsiag==", "dev": true, - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -5586,8 +5580,7 @@ "node_modules/jquery": { "version": "3.7.1", "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.7.1.tgz", - "integrity": "sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg==", - "peer": true + "integrity": "sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg==" }, "node_modules/jquery-contextmenu": { "version": "2.9.2", @@ -6251,7 +6244,6 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", "dev": true, - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "json-schema-traverse": "^1.0.0", @@ -6367,9 +6359,9 @@ } }, "node_modules/mocha/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.3.tgz", + "integrity": "sha512-MCV/fYJEbqx68aE58kv2cA/kiky1G8vux3OR6/jbS+jIMe/6fJWa0DTzJU7dqijOWYwHi1t29FlfYI9uytqlpA==", "dev": true, "license": "MIT", "dependencies": { @@ -6899,7 +6891,6 @@ "url": "https://github.com/sponsors/ai" } ], - "peer": true, "dependencies": { "nanoid": "^3.3.6", "picocolors": "^1.0.0", @@ -8052,7 +8043,6 @@ "resolved": "https://registry.npmjs.org/sass/-/sass-1.69.5.tgz", "integrity": "sha512-qg2+UCJibLr2LCVOt3OlPhr/dqVHWOa9XtZf2OjbLs/T4VPSJ00udtgJxH3neXZm+QqX8B+3cU7RaLqp1iVfcQ==", "dev": true, - "peer": true, "dependencies": { "chokidar": ">=3.0.0 <4.0.0", "immutable": "^4.0.0", @@ -8730,7 +8720,6 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", "dev": true, - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -9025,7 +9014,6 @@ "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.94.0.tgz", "integrity": "sha512-KcsGn50VT+06JH/iunZJedYGUJS5FGjow8wb9c0v5n1Om8O1g4L6LjtfxwlXIATopoQu+vOXXa7gYisWxCoPyg==", "dev": true, - "peer": true, "dependencies": { "@types/estree": "^1.0.5", "@webassemblyjs/ast": "^1.12.1", @@ -9149,7 +9137,6 @@ "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.10.0.tgz", "integrity": "sha512-NLhDfH/h4O6UOy+0LSso42xvYypClINuMNBVVzX4vX98TmTaTUxwRbXdhucbFMd2qLaCTcLq/PdYrvi8onw90w==", "dev": true, - "peer": true, "dependencies": { "@discoveryjs/json-ext": "^0.5.0", "@webpack-cli/configtest": "^1.2.0", From ac0076bc1b73dd9081c0cc7ccdb525ae535faf89 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 27 Mar 2026 19:46:31 +0000 Subject: [PATCH 025/104] Bump ajv from 6.12.6 to 6.14.0 in /modules/contentbox/themes/default Bumps [ajv](https://github.com/ajv-validator/ajv) from 6.12.6 to 6.14.0. - [Release notes](https://github.com/ajv-validator/ajv/releases) - [Commits](https://github.com/ajv-validator/ajv/compare/v6.12.6...v6.14.0) --- updated-dependencies: - dependency-name: ajv dependency-version: 6.14.0 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- .../themes/default/package-lock.json | 134 +++++++----------- 1 file changed, 55 insertions(+), 79 deletions(-) diff --git a/modules/contentbox/themes/default/package-lock.json b/modules/contentbox/themes/default/package-lock.json index 51fbaac07..9546b8556 100644 --- a/modules/contentbox/themes/default/package-lock.json +++ b/modules/contentbox/themes/default/package-lock.json @@ -59,7 +59,6 @@ "version": "7.18.5", "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.18.5.tgz", "integrity": "sha512-MGY8vg3DxMnctw0LdvSEojOsumc70g0t18gNyUdAZqB1Rpd1Bqo/svHGvt+UJ6JcGX+DIekGFDxxIWofBxLCnQ==", - "peer": true, "dependencies": { "@ampproject/remapping": "^2.1.0", "@babel/code-frame": "^7.16.7", @@ -1806,7 +1805,6 @@ "version": "8.15.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -1838,10 +1836,9 @@ } }, "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "peer": true, + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", + "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -1870,14 +1867,14 @@ } }, "node_modules/ajv-formats/node_modules/ajv": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", - "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", "dependencies": { - "fast-deep-equal": "^3.1.1", + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" + "require-from-string": "^2.0.2" }, "funding": { "type": "github", @@ -2078,7 +2075,6 @@ "url": "https://github.com/sponsors/ai" } ], - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -2402,15 +2398,14 @@ } }, "node_modules/css-minimizer-webpack-plugin/node_modules/ajv": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", - "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", - "peer": true, + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", "dependencies": { - "fast-deep-equal": "^3.1.1", + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" + "require-from-string": "^2.0.2" }, "funding": { "type": "github", @@ -3497,15 +3492,14 @@ } }, "node_modules/mini-css-extract-plugin/node_modules/ajv": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", - "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", - "peer": true, + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", "dependencies": { - "fast-deep-equal": "^3.1.1", + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" + "require-from-string": "^2.0.2" }, "funding": { "type": "github", @@ -3822,7 +3816,6 @@ "url": "https://github.com/sponsors/ai" } ], - "peer": true, "dependencies": { "nanoid": "^3.3.6", "picocolors": "^1.0.0", @@ -4568,7 +4561,6 @@ "resolved": "https://registry.npmjs.org/sass/-/sass-1.52.3.tgz", "integrity": "sha512-LNNPJ9lafx+j1ArtA7GyEJm9eawXN8KlA1+5dF6IZyoONg1Tyo/g+muOsENWJH/2Q1FHbbV4UwliU0cXMa/VIA==", "dev": true, - "peer": true, "dependencies": { "chokidar": ">=3.0.0 <4.0.0", "immutable": "^4.0.0", @@ -4840,10 +4832,9 @@ } }, "node_modules/terser-webpack-plugin/node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", - "peer": true, + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -5054,7 +5045,6 @@ "version": "5.105.0", "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.105.0.tgz", "integrity": "sha512-gX/dMkRQc7QOMzgTe6KsYFM7DxeIONQSui1s0n/0xht36HvrgbxtM1xBlgx596NbpHuQU8P7QpKwrZYwUX48nw==", - "peer": true, "dependencies": { "@types/eslint-scope": "^3.7.7", "@types/estree": "^1.0.8", @@ -5172,7 +5162,6 @@ "version": "4.10.0", "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.10.0.tgz", "integrity": "sha512-NLhDfH/h4O6UOy+0LSso42xvYypClINuMNBVVzX4vX98TmTaTUxwRbXdhucbFMd2qLaCTcLq/PdYrvi8onw90w==", - "peer": true, "dependencies": { "@discoveryjs/json-ext": "^0.5.0", "@webpack-cli/configtest": "^1.2.0", @@ -5274,10 +5263,9 @@ } }, "node_modules/webpack/node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", - "peer": true, + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -5387,7 +5375,6 @@ "version": "7.18.5", "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.18.5.tgz", "integrity": "sha512-MGY8vg3DxMnctw0LdvSEojOsumc70g0t18gNyUdAZqB1Rpd1Bqo/svHGvt+UJ6JcGX+DIekGFDxxIWofBxLCnQ==", - "peer": true, "requires": { "@ampproject/remapping": "^2.1.0", "@babel/code-frame": "^7.16.7", @@ -6630,8 +6617,7 @@ "acorn": { "version": "8.15.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", - "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", - "peer": true + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==" }, "acorn-import-phases": { "version": "1.0.4", @@ -6649,10 +6635,9 @@ } }, "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "peer": true, + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", + "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", "requires": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -6669,14 +6654,14 @@ }, "dependencies": { "ajv": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", - "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", "requires": { - "fast-deep-equal": "^3.1.1", + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" + "require-from-string": "^2.0.2" } }, "json-schema-traverse": { @@ -6819,7 +6804,6 @@ "version": "4.28.1", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", - "peer": true, "requires": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -7039,15 +7023,14 @@ }, "dependencies": { "ajv": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", - "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", - "peer": true, + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", "requires": { - "fast-deep-equal": "^3.1.1", + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" + "require-from-string": "^2.0.2" } }, "ajv-keywords": { @@ -7809,15 +7792,14 @@ }, "dependencies": { "ajv": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", - "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", - "peer": true, + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", "requires": { - "fast-deep-equal": "^3.1.1", + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" + "require-from-string": "^2.0.2" } }, "ajv-keywords": { @@ -8024,7 +8006,6 @@ "version": "8.4.31", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", - "peer": true, "requires": { "nanoid": "^3.3.6", "picocolors": "^1.0.0", @@ -8523,7 +8504,6 @@ "resolved": "https://registry.npmjs.org/sass/-/sass-1.52.3.tgz", "integrity": "sha512-LNNPJ9lafx+j1ArtA7GyEJm9eawXN8KlA1+5dF6IZyoONg1Tyo/g+muOsENWJH/2Q1FHbbV4UwliU0cXMa/VIA==", "dev": true, - "peer": true, "requires": { "chokidar": ">=3.0.0 <4.0.0", "immutable": "^4.0.0", @@ -8687,10 +8667,9 @@ }, "dependencies": { "ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", - "peer": true, + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", "requires": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -8822,7 +8801,6 @@ "version": "5.105.0", "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.105.0.tgz", "integrity": "sha512-gX/dMkRQc7QOMzgTe6KsYFM7DxeIONQSui1s0n/0xht36HvrgbxtM1xBlgx596NbpHuQU8P7QpKwrZYwUX48nw==", - "peer": true, "requires": { "@types/eslint-scope": "^3.7.7", "@types/estree": "^1.0.8", @@ -8852,10 +8830,9 @@ }, "dependencies": { "ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", - "peer": true, + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", "requires": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -8947,7 +8924,6 @@ "version": "4.10.0", "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.10.0.tgz", "integrity": "sha512-NLhDfH/h4O6UOy+0LSso42xvYypClINuMNBVVzX4vX98TmTaTUxwRbXdhucbFMd2qLaCTcLq/PdYrvi8onw90w==", - "peer": true, "requires": { "@discoveryjs/json-ext": "^0.5.0", "@webpack-cli/configtest": "^1.2.0", From 5bb4cb57c6896bc7a125cca8c84f0c47f86e4ce0 Mon Sep 17 00:00:00 2001 From: Jon Clausen Date: Fri, 27 Mar 2026 16:07:17 -0400 Subject: [PATCH 026/104] fix artifacts path due to coldbox 7 tests removal --- .github/workflows/cron.yml | 10 ++++++++++ .github/workflows/release.yml | 6 +++--- 2 files changed, 13 insertions(+), 3 deletions(-) create mode 100644 .github/workflows/cron.yml diff --git a/.github/workflows/cron.yml b/.github/workflows/cron.yml new file mode 100644 index 000000000..fcf8c7366 --- /dev/null +++ b/.github/workflows/cron.yml @@ -0,0 +1,10 @@ +name: Daily Tests + +on: + schedule: + - cron: '0 0 * * 0' # Runs at 00:00 UTC every Sunday + +jobs: + tests: + uses: ./.github/workflows/tests.yml + secrets: inherit diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b8fb44d4d..171c4e01c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -114,10 +114,10 @@ jobs: - name: Build API Docs run: | # Normalize swagger doc results if they exist - if test -f tests/results/contentbox-test-results-lucee@5-coldbox^7-experimental-false/contentbox-swagger.json; then - cp -v tests/results/contentbox-test-results-lucee@5-coldbox^7-experimental-false/contentbox-swagger.json tests/results/ + if test -f tests/results/contentbox-test-results-lucee@5-coldbox^8-experimental-false/contentbox-swagger.json; then + cp -v tests/results/contentbox-test-results-lucee@5-coldbox^8-experimental-false/contentbox-swagger.json tests/results/ else - echo "File tests/results/contentbox-test-results-lucee@5-coldbox^7-experimental-false/contentbox-swagger.json does not exist" + echo "File tests/results/contentbox-test-results-lucee@5-coldbox^8-experimental-false/contentbox-swagger.json does not exist" fi # Run Docs Build box run-script build:allDocs From 02705f0fbefc1b6c05ec451417647d554f629d8c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 10 Apr 2026 03:24:54 +0000 Subject: [PATCH 027/104] Bump lodash from 4.17.23 to 4.18.1 Bumps [lodash](https://github.com/lodash/lodash) from 4.17.23 to 4.18.1. - [Release notes](https://github.com/lodash/lodash/releases) - [Commits](https://github.com/lodash/lodash/compare/4.17.23...4.18.1) --- updated-dependencies: - dependency-name: lodash dependency-version: 4.18.1 dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- package-lock.json | 23 +++++------------------ package.json | 2 +- 2 files changed, 6 insertions(+), 19 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4e60dc0ec..a0d0821ea 100644 --- a/package-lock.json +++ b/package-lock.json @@ -32,7 +32,7 @@ "jquery-nestable": "^0.8.0", "jquery-validation": "^1.20.0", "jquery.cookie": "^1.4.1", - "lodash": "^4.17.23", + "lodash": "^4.18.1", "luxon": "^3.4.3", "lz-string": "^1.5.0", "morris.js": "^0.5.0", @@ -125,7 +125,6 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.3.tgz", "integrity": "sha512-Jg+msLuNuCJDyBvFv5+OKOUjWMZgd85bKjbICd3zWrKAo+bJ49HJufi7CQE0q0uR8NGyO6xkCACScNqyjHSZew==", "dev": true, - "peer": true, "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.22.13", @@ -2528,7 +2527,6 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz", "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==", "dev": true, - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -2572,7 +2570,6 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -2951,7 +2948,6 @@ "url": "https://github.com/sponsors/ai" } ], - "peer": true, "dependencies": { "caniuse-lite": "^1.0.30001541", "electron-to-chromium": "^1.4.535", @@ -3635,7 +3631,6 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", "dev": true, - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "json-schema-traverse": "^1.0.0", @@ -4308,7 +4303,6 @@ "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.53.0.tgz", "integrity": "sha512-N4VuiPjXDUa4xVeV/GC/RV3hQW9Nw+Y463lkWaKKXKYMvmRiRDAtfpuPFLN+E1/6ZhyR8J2ig+eVREnYgUsiag==", "dev": true, - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -5586,8 +5580,7 @@ "node_modules/jquery": { "version": "3.7.1", "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.7.1.tgz", - "integrity": "sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg==", - "peer": true + "integrity": "sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg==" }, "node_modules/jquery-contextmenu": { "version": "2.9.2", @@ -5843,9 +5836,9 @@ } }, "node_modules/lodash": { - "version": "4.17.23", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", - "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", "license": "MIT" }, "node_modules/lodash.debounce": { @@ -6251,7 +6244,6 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", "dev": true, - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "json-schema-traverse": "^1.0.0", @@ -6899,7 +6891,6 @@ "url": "https://github.com/sponsors/ai" } ], - "peer": true, "dependencies": { "nanoid": "^3.3.6", "picocolors": "^1.0.0", @@ -8052,7 +8043,6 @@ "resolved": "https://registry.npmjs.org/sass/-/sass-1.69.5.tgz", "integrity": "sha512-qg2+UCJibLr2LCVOt3OlPhr/dqVHWOa9XtZf2OjbLs/T4VPSJ00udtgJxH3neXZm+QqX8B+3cU7RaLqp1iVfcQ==", "dev": true, - "peer": true, "dependencies": { "chokidar": ">=3.0.0 <4.0.0", "immutable": "^4.0.0", @@ -8730,7 +8720,6 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", "dev": true, - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -9025,7 +9014,6 @@ "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.94.0.tgz", "integrity": "sha512-KcsGn50VT+06JH/iunZJedYGUJS5FGjow8wb9c0v5n1Om8O1g4L6LjtfxwlXIATopoQu+vOXXa7gYisWxCoPyg==", "dev": true, - "peer": true, "dependencies": { "@types/estree": "^1.0.5", "@webassemblyjs/ast": "^1.12.1", @@ -9149,7 +9137,6 @@ "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.10.0.tgz", "integrity": "sha512-NLhDfH/h4O6UOy+0LSso42xvYypClINuMNBVVzX4vX98TmTaTUxwRbXdhucbFMd2qLaCTcLq/PdYrvi8onw90w==", "dev": true, - "peer": true, "dependencies": { "@discoveryjs/json-ext": "^0.5.0", "@webpack-cli/configtest": "^1.2.0", diff --git a/package.json b/package.json index 57b47887d..50198469e 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,7 @@ "jquery-nestable": "^0.8.0", "jquery-validation": "^1.20.0", "jquery.cookie": "^1.4.1", - "lodash": "^4.17.23", + "lodash": "^4.18.1", "luxon": "^3.4.3", "lz-string": "^1.5.0", "morris.js": "^0.5.0", From f99af84dd06377706db371f1b916a3221b18a9f0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 14 Apr 2026 01:13:27 +0000 Subject: [PATCH 028/104] Bump follow-redirects from 1.15.6 to 1.16.0 Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.15.6 to 1.16.0. - [Release notes](https://github.com/follow-redirects/follow-redirects/releases) - [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.15.6...v1.16.0) --- updated-dependencies: - dependency-name: follow-redirects dependency-version: 1.16.0 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- package-lock.json | 22 +++++----------------- 1 file changed, 5 insertions(+), 17 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4e60dc0ec..fec6977ed 100644 --- a/package-lock.json +++ b/package-lock.json @@ -125,7 +125,6 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.3.tgz", "integrity": "sha512-Jg+msLuNuCJDyBvFv5+OKOUjWMZgd85bKjbICd3zWrKAo+bJ49HJufi7CQE0q0uR8NGyO6xkCACScNqyjHSZew==", "dev": true, - "peer": true, "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.22.13", @@ -2528,7 +2527,6 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz", "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==", "dev": true, - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -2572,7 +2570,6 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -2951,7 +2948,6 @@ "url": "https://github.com/sponsors/ai" } ], - "peer": true, "dependencies": { "caniuse-lite": "^1.0.30001541", "electron-to-chromium": "^1.4.535", @@ -3635,7 +3631,6 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", "dev": true, - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "json-schema-traverse": "^1.0.0", @@ -4308,7 +4303,6 @@ "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.53.0.tgz", "integrity": "sha512-N4VuiPjXDUa4xVeV/GC/RV3hQW9Nw+Y463lkWaKKXKYMvmRiRDAtfpuPFLN+E1/6ZhyR8J2ig+eVREnYgUsiag==", "dev": true, - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -4853,15 +4847,16 @@ } }, "node_modules/follow-redirects": { - "version": "1.15.6", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", - "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz", + "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==", "funding": [ { "type": "individual", "url": "https://github.com/sponsors/RubenVerborgh" } ], + "license": "MIT", "engines": { "node": ">=4.0" }, @@ -5586,8 +5581,7 @@ "node_modules/jquery": { "version": "3.7.1", "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.7.1.tgz", - "integrity": "sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg==", - "peer": true + "integrity": "sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg==" }, "node_modules/jquery-contextmenu": { "version": "2.9.2", @@ -6251,7 +6245,6 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", "dev": true, - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "json-schema-traverse": "^1.0.0", @@ -6899,7 +6892,6 @@ "url": "https://github.com/sponsors/ai" } ], - "peer": true, "dependencies": { "nanoid": "^3.3.6", "picocolors": "^1.0.0", @@ -8052,7 +8044,6 @@ "resolved": "https://registry.npmjs.org/sass/-/sass-1.69.5.tgz", "integrity": "sha512-qg2+UCJibLr2LCVOt3OlPhr/dqVHWOa9XtZf2OjbLs/T4VPSJ00udtgJxH3neXZm+QqX8B+3cU7RaLqp1iVfcQ==", "dev": true, - "peer": true, "dependencies": { "chokidar": ">=3.0.0 <4.0.0", "immutable": "^4.0.0", @@ -8730,7 +8721,6 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", "dev": true, - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -9025,7 +9015,6 @@ "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.94.0.tgz", "integrity": "sha512-KcsGn50VT+06JH/iunZJedYGUJS5FGjow8wb9c0v5n1Om8O1g4L6LjtfxwlXIATopoQu+vOXXa7gYisWxCoPyg==", "dev": true, - "peer": true, "dependencies": { "@types/estree": "^1.0.5", "@webassemblyjs/ast": "^1.12.1", @@ -9149,7 +9138,6 @@ "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.10.0.tgz", "integrity": "sha512-NLhDfH/h4O6UOy+0LSso42xvYypClINuMNBVVzX4vX98TmTaTUxwRbXdhucbFMd2qLaCTcLq/PdYrvi8onw90w==", "dev": true, - "peer": true, "dependencies": { "@discoveryjs/json-ext": "^0.5.0", "@webpack-cli/configtest": "^1.2.0", From ec2a9ad51b41505c95ecf0bd5959211e8e5a3bd3 Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Tue, 19 May 2026 12:42:22 +0200 Subject: [PATCH 029/104] test runner --- tests/assets/css/run.css | 696 +++++++++ tests/assets/css/vendor/bootstrap.min.css | 6 + tests/assets/images/testbox-icon-full-L.svg | 14 + .../images/testbox-logo-full-dark-L.svg | 23 + .../images/testbox-logo-full-light-L.svg | 23 + tests/assets/js/run.js | 1239 +++++++++++++++++ tests/assets/js/vendor/alpine-collapse.min.js | 1 + tests/assets/js/vendor/alpine.min.js | 5 + .../assets/js/vendor/bootstrap.bundle.min.js | 7 + 9 files changed, 2014 insertions(+) create mode 100755 tests/assets/css/run.css create mode 100755 tests/assets/css/vendor/bootstrap.min.css create mode 100755 tests/assets/images/testbox-icon-full-L.svg create mode 100755 tests/assets/images/testbox-logo-full-dark-L.svg create mode 100755 tests/assets/images/testbox-logo-full-light-L.svg create mode 100755 tests/assets/js/run.js create mode 100755 tests/assets/js/vendor/alpine-collapse.min.js create mode 100755 tests/assets/js/vendor/alpine.min.js create mode 100755 tests/assets/js/vendor/bootstrap.bundle.min.js diff --git a/tests/assets/css/run.css b/tests/assets/css/run.css new file mode 100755 index 000000000..1ba1833b5 --- /dev/null +++ b/tests/assets/css/run.css @@ -0,0 +1,696 @@ +/* TestBox Run IDE - BoxLang Glow Theme */ + +:root { + --tb-green-1: #00ffb3; + --tb-green-2: #00d991; + --tb-green-3: #00a372; + --tb-teal-1: #38e9d0; + --tb-teal-2: #00c2ad; + --tb-deep-1: #071313; + --tb-deep-2: #0b2020; + --tb-deep-3: #133636; + --tb-border-dark: #1f4a49; + --tb-glow-soft: 0 0 0.75rem rgba(0, 217, 145, 0.25); + --tb-glow-strong: 0 0 1.2rem rgba(56, 233, 208, 0.42); +} + +[data-bs-theme=light] { + --bs-primary: #00a872; + --bs-primary-rgb: 0, 168, 114; + --bs-success: #129f67; + --bs-success-rgb: 18, 159, 103; + --bs-info: #0aa9c3; + --bs-info-rgb: 10, 169, 195; + --bs-body-bg: #f2fbf8; + --bs-body-color: #173533; + --tb-tree-border: #bcded8; + --tb-surface-1: #ffffff; + --tb-surface-2: #ecf9f5; + --tb-surface-3: #e2f4ef; + --tb-text-dim: #4f6b68; + --tb-glow-card: 0 12px 28px rgba(0, 93, 70, 0.12); + --tb-focus-ring: 0 0 0 0.18rem rgba(0, 168, 114, 0.2); +} + +[data-bs-theme=dark] { + --bs-primary: var(--tb-green-2); + --bs-primary-rgb: 0, 217, 145; + --bs-success: var(--tb-green-1); + --bs-success-rgb: 0, 255, 179; + --bs-info: var(--tb-teal-1); + --bs-info-rgb: 56, 233, 208; + --bs-body-bg: var(--tb-deep-1); + --bs-body-color: #dffcf3; + --tb-tree-border: var(--tb-border-dark); + --tb-surface-1: rgba(9, 31, 30, 0.92); + --tb-surface-2: rgba(16, 44, 43, 0.9); + --tb-surface-3: rgba(21, 57, 56, 0.9); + --tb-text-dim: #89b7af; + --tb-glow-card: 0 14px 34px rgba(0, 255, 179, 0.08), inset 0 1px 0 rgba(110, 255, 226, 0.08); + --tb-focus-ring: 0 0 0 0.2rem rgba(56, 233, 208, 0.24); +} + +body { + padding-top: 75px; + background: + radial-gradient(90rem 40rem at -10% -20%, rgba(56, 233, 208, 0.18), transparent 58%), + radial-gradient(70rem 30rem at 110% -15%, rgba(0, 217, 145, 0.16), transparent 62%), + linear-gradient(180deg, var(--bs-body-bg), var(--bs-body-bg)); + color: var(--bs-body-color); + transition: background-color 0.25s ease, color 0.25s ease; +} + +.cursor-pointer { cursor: pointer; } + +.navbar { + backdrop-filter: blur(8px); + background: color-mix(in srgb, var(--tb-surface-1) 86%, transparent) !important; + border-bottom: 1px solid color-mix(in srgb, var(--tb-tree-border) 60%, transparent); + box-shadow: var(--tb-glow-card); +} + +.navbar-brand { + font-weight: 700; + letter-spacing: 0.03em; + text-transform: uppercase; + text-shadow: 0 0 0.45rem rgba(var(--bs-primary-rgb), 0.35); +} + +.form-control { + background: var(--tb-surface-2); + border-color: color-mix(in srgb, var(--tb-tree-border) 72%, transparent); + color: var(--bs-body-color); +} + +.form-control:focus { + background: var(--tb-surface-1); + color: var(--bs-body-color); + border-color: var(--bs-primary); + box-shadow: var(--tb-focus-ring); +} + +.btn { + border-radius: 0.55rem; + transition: transform 0.15s ease, box-shadow 0.2s ease, background-color 0.2s ease; +} + +.btn:hover { + transform: translateY(-1px); +} + +.btn-outline-secondary:hover, +.btn-outline-primary:hover { + box-shadow: var(--tb-glow-soft); +} + +.spinner-icon { + animation: spinner-border 1.4s linear infinite; + display: inline-block; + filter: drop-shadow(0 0 0.25rem rgba(var(--bs-primary-rgb), 0.4)); +} + +.results-summary-card { + border: 1px solid color-mix(in srgb, var(--tb-tree-border) 72%, transparent); + background: linear-gradient(160deg, var(--tb-surface-1), var(--tb-surface-2)); + box-shadow: var(--tb-glow-card); + overflow: hidden; +} + +.results-summary-card .results-stat-count { + font-size: 2.15rem; + font-weight: 800; + line-height: 1.08; + text-shadow: 0 0 0.35rem rgba(var(--bs-primary-rgb), 0.24); +} + +.results-summary-card .results-stat-label { + font-size: 0.68rem; + text-transform: uppercase; + letter-spacing: 0.08em; + margin-top: 0.22rem; + font-weight: 650; +} + +.results-summary-card .results-stat-item { + transition: opacity 0.18s ease, background-color 0.18s ease, box-shadow 0.18s ease; +} + +.results-summary-card .results-stat-item:hover { + background: color-mix(in srgb, var(--tb-surface-3) 64%, transparent); + box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--bs-primary) 22%, transparent); +} + +.results-summary-card .filter-off { opacity: 0.3; } + +.bundle-card { + border: 1px solid color-mix(in srgb, var(--tb-tree-border) 70%, transparent); + border-radius: 0.75rem; + overflow: hidden; + background: linear-gradient(150deg, var(--tb-surface-1), var(--tb-surface-2)); + box-shadow: var(--tb-glow-card); + transition: transform 0.18s ease, box-shadow 0.18s ease, border-color 0.18s ease; +} + +.bundle-card:hover { + transform: translateY(-2px); + box-shadow: var(--tb-glow-card), var(--tb-glow-soft); + border-color: color-mix(in srgb, var(--bs-primary) 58%, transparent); +} + +.bundle-card .card-header { + background: color-mix(in srgb, var(--tb-surface-3) 66%, transparent) !important; + border-bottom: 1px solid color-mix(in srgb, var(--tb-tree-border) 60%, transparent); +} + +.bundle-play-btn { + opacity: 0; + transition: opacity 0.14s ease; + text-shadow: 0 0 0.45rem rgba(var(--bs-primary-rgb), 0.55); +} + +.card-header:hover .bundle-play-btn { opacity: 1; } + +/* Suite-level run button: sits in the header row, always subtly visible */ +.suite-play-btn { + opacity: 0.35; + transition: opacity 0.14s ease; + text-shadow: 0 0 0.4rem rgba(var(--bs-primary-rgb), 0.5); +} +.suite-play-btn:hover { opacity: 1 !important; } +.suite-play-btn:disabled { opacity: 0.15 !important; cursor: not-allowed; } + +/* Spec-level run button: hidden until the row is hovered */ +.spec-play-btn { + opacity: 0; + font-size: 1.1rem; + width: 1.4rem; + height: 1.4rem; + transition: opacity 0.14s ease; + text-shadow: 0 0 0.35rem rgba(var(--bs-primary-rgb), 0.5); + flex-shrink: 0; +} +li:hover .spec-play-btn-hover { opacity: 0.75; } +li:hover .spec-play-btn-hover:hover { opacity: 1 !important; } +.spec-play-btn:disabled { opacity: 0.15 !important; cursor: not-allowed; } + +.status-bd-passed { border-left: 5px solid var(--bs-success) !important; } +.status-bd-failed { border-left: 5px solid var(--bs-danger) !important; } +.status-bd-error { border-left: 5px solid var(--bs-warning) !important; } +.status-bd-skipped { border-left: 5px solid var(--bs-info) !important; } +.status-bd-running { border-left: 5px solid var(--bs-primary) !important; box-shadow: inset 0 0 1rem rgba(var(--bs-primary-rgb), 0.14); } +.status-bd-pending { border-left: 5px solid var(--tb-tree-border) !important; } + +.bundle-results-strip { + font-size: 0.8rem; + border-top: 1px solid color-mix(in srgb, var(--tb-tree-border) 50%, transparent); +} + +.strip-passed { background: rgba(var(--bs-success-rgb), 0.1); } +.strip-failed { background: rgba(var(--bs-danger-rgb), 0.1); } +.strip-error { background: rgba(var(--bs-warning-rgb), 0.1); } +.strip-skipped { background: rgba(var(--bs-info-rgb), 0.1); } + +.strip-stat { + font-weight: 600; + white-space: nowrap; +} + +.strip-debug-btn { + font-size: 1rem; + color: #ffc96c !important; + opacity: 0.76; + transition: opacity 0.14s ease, transform 0.14s ease; +} + +.strip-debug-btn:hover, +.strip-debug-btn.active { + opacity: 1; + transform: scale(1.12); + filter: drop-shadow(0 0 0.34rem rgba(255, 204, 102, 0.8)); +} + +.bundle-debug-panel { + background: color-mix(in srgb, var(--bs-warning) 8%, transparent); +} + +.debug-entry { + border: 1px solid color-mix(in srgb, var(--bs-warning) 28%, transparent); + overflow: hidden; + border-radius: 0.5rem; +} + +.debug-entry-header { + background: color-mix(in srgb, var(--bs-warning) 16%, transparent); + border-bottom: 1px solid color-mix(in srgb, var(--bs-warning) 22%, transparent); +} + +.debug-entry-label { color: var(--bs-warning); } + +.debug-entry-body { + font-size: 0.78rem; + font-family: var(--bs-font-monospace); + white-space: pre-wrap; + word-break: break-all; + max-height: 300px; + overflow-y: auto; + color: var(--bs-body-color); + background: transparent; +} + +.box-border-left { + border-left: 2px solid var(--tb-tree-border); + transition: border-color 0.25s ease, box-shadow 0.25s ease; +} + +.box-border-left.border-success { border-color: var(--bs-success) !important; box-shadow: -1px 0 0 0 rgba(var(--bs-success-rgb), 0.35); } +.box-border-left.border-danger { border-color: var(--bs-danger) !important; } +.box-border-left.border-warning { border-color: var(--bs-warning) !important; } +.box-border-left.border-info { border-color: var(--bs-info) !important; } +.box-border-left.border-primary { border-color: var(--bs-primary) !important; box-shadow: -1px 0 0 0 rgba(var(--bs-primary-rgb), 0.4); } + +.status-bg-pending { opacity: 0.68; } + +.list-group-item { + transition: background-color 0.16s ease, border-color 0.16s ease; + background-color: transparent; + border-color: color-mix(in srgb, var(--tb-tree-border) 50%, transparent); +} + +.list-group-item:hover { + background-color: color-mix(in srgb, var(--tb-surface-3) 56%, transparent) !important; +} + +.spec-origin-row { + display: flex; + align-items: baseline; + gap: 0.35rem; + flex-wrap: wrap; + font-size: 0.9rem; + font-weight: 600; +} + +.spec-origin-link { + font-family: var(--bs-font-monospace); + font-size: 0.92rem; + text-decoration: none; + word-break: break-all; + color: color-mix(in srgb, var(--bs-info) 85%, white); + text-shadow: 0 0 0.45rem rgba(var(--bs-info-rgb), 0.45); +} + +.spec-origin-link:hover { + text-decoration: underline; + color: color-mix(in srgb, var(--bs-info) 70%, white); +} + +.spec-show-all-btn { + font-size: 0.88rem; + font-weight: 700; + text-decoration: none; + opacity: 0.95; + white-space: nowrap; + color: color-mix(in srgb, var(--bs-primary) 92%, white); + padding: 0.24rem 0.5rem !important; + border-radius: 0.5rem; + background: color-mix(in srgb, var(--tb-surface-3) 72%, transparent); + border: 1px solid color-mix(in srgb, var(--bs-primary) 34%, var(--tb-tree-border)); + box-shadow: 0 0 0.45rem rgba(var(--bs-primary-rgb), 0.2); +} + +.spec-show-all-btn:hover { + opacity: 1; + text-shadow: 0 0 0.45rem rgba(var(--bs-primary-rgb), 0.5); + box-shadow: 0 0 0.75rem rgba(var(--bs-primary-rgb), 0.35); +} + +.spec-show-all-btn .bi { + font-size: 0.95rem; +} + +.spec-show-stack-btn { + font-size: 0.88rem; + font-weight: 700; + text-decoration: none; + opacity: 0.95; + white-space: nowrap; + color: color-mix(in srgb, var(--bs-info) 92%, white); + padding: 0.24rem 0.5rem !important; + border-radius: 0.5rem; + background: color-mix(in srgb, var(--tb-surface-3) 72%, transparent); + border: 1px solid color-mix(in srgb, var(--bs-info) 34%, var(--tb-tree-border)); + box-shadow: 0 0 0.45rem rgba(var(--bs-info-rgb), 0.2); +} + +.spec-show-stack-btn:hover { + opacity: 1; + text-shadow: 0 0 0.45rem rgba(var(--bs-info-rgb), 0.5); + box-shadow: 0 0 0.75rem rgba(var(--bs-info-rgb), 0.35); +} + +.spec-show-stack-btn .bi { + font-size: 0.95rem; +} + +.spec-fail-panel { + background: color-mix(in srgb, var(--tb-surface-3) 66%, transparent); + border: 1px solid color-mix(in srgb, var(--tb-tree-border) 58%, transparent); + border-radius: 0.55rem; + padding: 0.58rem 0.7rem; + margin-top: 0.42rem; + box-shadow: inset 0 0 0.65rem rgba(var(--bs-primary-rgb), 0.08); +} + +.spec-fail-section-label { + font-size: 0.78rem; + text-transform: uppercase; + letter-spacing: 0.09em; + opacity: 0.85; + margin-bottom: 0.25rem; + font-weight: 650; +} + +.spec-origin-item { + display: flex; + align-items: baseline; + gap: 0.3rem; + margin-bottom: 0.14rem; +} + +.spec-origin-idx { + font-family: var(--bs-font-monospace); + font-size: 0.82rem; + font-weight: 700; +} + +.spec-fail-panel .border-primary-subtle { + border-color: color-mix(in srgb, var(--bs-primary) 58%, var(--tb-tree-border)) !important; + border-width: 2px !important; + background: color-mix(in srgb, var(--tb-surface-2) 88%, transparent); + box-shadow: 0 0 0.6rem rgba(var(--bs-primary-rgb), 0.18); +} + +.spec-fail-panel .cursor-pointer:hover { + box-shadow: 0 0 0.9rem rgba(var(--bs-primary-rgb), 0.28); + transform: translateY(-1px); + transition: box-shadow 0.15s ease, transform 0.15s ease; +} + +.spec-fail-stacktrace { + font-size: 0.72rem; + font-family: var(--bs-font-monospace); + white-space: pre-wrap; + word-break: break-word; + max-height: 260px; + overflow-y: auto; + background: transparent; + margin: 0; + padding: 0; + color: var(--bs-body-color); + opacity: 0.88; +} + +.spec-stacktrace-panel { + border-color: color-mix(in srgb, var(--bs-info) 30%, var(--tb-tree-border)); + box-shadow: inset 0 0 0.85rem rgba(var(--bs-info-rgb), 0.08); +} + +.spec-fail-stacktrace-full { + font-family: var(--bs-font-monospace); + font-size: 0.86rem; + line-height: 1.5; + white-space: pre-wrap; + word-break: break-word; + color: var(--bs-body-color); + padding: 0.1rem; +} + +.stacktrace-line { + margin-bottom: 0.12rem; +} + +.stacktrace-location { + background: color-mix(in srgb, var(--tb-surface-2) 70%, transparent); + border: 1px solid color-mix(in srgb, var(--bs-info) 40%, var(--tb-tree-border)); + border-radius: 0.4rem; + padding: 0.05rem 0.32rem; + display: inline-block; +} + +.stacktrace-link { + color: color-mix(in srgb, var(--bs-info) 88%, white); + text-decoration: none; + font-weight: 700; + text-shadow: 0 0 0.3rem rgba(var(--bs-info-rgb), 0.35); +} + +.stacktrace-link:hover { + text-decoration: underline; + color: color-mix(in srgb, var(--bs-info) 70%, white); +} + +.stacktrace-caused-by { + color: #ff7b7b; + font-weight: 800; + text-transform: uppercase; + letter-spacing: 0.04em; + text-shadow: 0 0 0.35rem rgba(255, 123, 123, 0.5); +} + +/* Floating progress widget */ +.floating-progress-widget { + position: fixed; + right: 1.15rem; + bottom: 1.15rem; + width: min(520px, calc(100vw - 2rem)); + z-index: 1060; + padding: 0.85rem 1rem 0.9rem; + border-radius: 0.9rem; + background: linear-gradient(155deg, color-mix(in srgb, var(--tb-surface-1) 92%, transparent), color-mix(in srgb, var(--tb-surface-2) 88%, transparent)); + border: 1px solid color-mix(in srgb, var(--bs-primary) 36%, var(--tb-tree-border)); + box-shadow: var(--tb-glow-card), var(--tb-glow-strong); + backdrop-filter: blur(7px); + overflow: hidden; + animation: floatingProgressIn 180ms ease-out; +} + +.floating-progress-glow { + position: absolute; + inset: 0; + pointer-events: none; + background: radial-gradient(65% 140% at 100% 100%, rgba(var(--bs-primary-rgb), 0.2), transparent 58%); + animation: floatingPulse 1.7s ease-in-out infinite; +} + +.floating-progress-header, +.floating-progress-track, +.floating-progress-subtext { + position: relative; + z-index: 1; +} + +.floating-progress-title { + font-size: 0.95rem; + font-weight: 650; + letter-spacing: 0.02em; + text-shadow: 0 0 0.34rem rgba(var(--bs-primary-rgb), 0.36); +} + +.floating-progress-count { + font-size: 0.88rem; + font-weight: 700; + color: color-mix(in srgb, var(--bs-primary) 80%, white); + text-shadow: 0 0 0.32rem rgba(var(--bs-primary-rgb), 0.4); +} + +.floating-progress-track { + height: 12px; + border-radius: 999px; + background: color-mix(in srgb, var(--tb-surface-3) 80%, transparent); + border: 1px solid color-mix(in srgb, var(--tb-tree-border) 78%, transparent); + overflow: hidden; +} + +.floating-progress-track .progress-bar { + box-shadow: 0 0 0.65rem rgba(var(--bs-primary-rgb), 0.62); + background-image: linear-gradient(45deg, rgba(255,255,255,0.22) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.22) 50%, rgba(255,255,255,0.22) 75%, transparent 75%, transparent); + background-size: 1rem 1rem; +} + +.floating-progress-subtext { + font-size: 0.74rem; + letter-spacing: 0.04em; + text-transform: uppercase; + color: var(--tb-text-dim); +} + +@keyframes floatingProgressIn { + from { + opacity: 0; + transform: translateY(10px) scale(0.98); + } + to { + opacity: 1; + transform: translateY(0) scale(1); + } +} + +@keyframes floatingPulse { + 0% { opacity: 0.35; } + 50% { opacity: 0.75; } + 100% { opacity: 0.35; } +} + +@media (max-width: 768px) { + body { + padding-top: 112px; + } + + .results-summary-card .results-stat-count { + font-size: 1.65rem; + } + + .bundle-card .card-header { + padding-top: 0.65rem; + padding-bottom: 0.65rem; + } + + .floating-progress-widget { + right: 0.65rem; + bottom: 0.65rem; + width: calc(100vw - 1.3rem); + padding: 0.72rem 0.78rem 0.78rem; + } + + .floating-progress-track { + height: 10px; + } +} +/* ─── About modal ───────────────────────────────────────────────── */ +.about-logo { + width: 220px; + max-width: 100%; +} + +.about-shortcuts { + background: var(--bs-tertiary-bg); + border: 1px solid var(--bs-border-color); + border-radius: 0.5rem; + padding: 0.85rem 1rem; +} + +.about-shortcut-grid { + display: grid; + grid-template-columns: 1fr auto; + row-gap: 0.45rem; + column-gap: 1.5rem; + align-items: center; +} + +.about-shortcut-desc { + font-size: 0.85rem; + color: var(--bs-body-color); +} + +.about-shortcut-keys { + display: flex; + align-items: center; + gap: 3px; + justify-content: flex-end; +} + +.about-shortcut-keys kbd { + display: inline-flex; + align-items: center; + justify-content: center; + padding: 1px 6px; + font-size: 0.7rem; + font-family: inherit; + background: var(--bs-body-bg); + border: 1px solid var(--bs-border-color); + border-radius: 4px; + color: var(--bs-secondary-color, #6c757d); + line-height: 1.5; + min-width: 1.6em; +} + +.about-link { + font-size: 0.85rem; + text-decoration: none; + color: var(--bs-primary); + border: 1px solid var(--bs-primary); + border-radius: 20px; + padding: 0.25rem 0.75rem; + transition: background 0.15s, color 0.15s; +} + +.about-link:hover { + background: var(--bs-primary); + color: #fff; +} + +.about-link-support { + color: var(--bs-warning); + border-color: var(--bs-warning); +} + +.about-link-support:hover { + background: var(--bs-warning); + color: #000; +} + +/* ─── Search bar ─────────────────────────────────────────────────── */ +.search-icon-addon { + background: transparent; + color: var(--bs-secondary-color, #6c757d); +} + +.search-kbd-addon { + background: transparent; + gap: 4px; +} + +.search-kbd { + display: inline-flex; + align-items: center; + justify-content: center; + padding: 1px 5px; + font-size: 0.68rem; + font-family: inherit; + background: var(--bs-body-bg); + border: 1px solid var(--bs-border-color); + border-radius: 4px; + color: var(--bs-secondary-color, #6c757d); + line-height: 1.4; + min-width: 1.4em; +} + +/* Compact controls (mobile collapsed navbar) */ +.tb-compact-controls { + display: flex; + align-items: center; + gap: 0.4rem; + width: 100%; +} + +.tb-compact-search { + flex: 1 1 auto; + min-width: 0; +} + +.tb-compact-actions { + display: flex; + align-items: center; + gap: 0.35rem; + flex: 0 0 auto; +} + +@media (max-width: 520px) { + .tb-compact-actions .btn { + padding-left: 0.5rem; + padding-right: 0.5rem; + } + + .tb-compact-actions .btn span { + display: none; + } +} \ No newline at end of file diff --git a/tests/assets/css/vendor/bootstrap.min.css b/tests/assets/css/vendor/bootstrap.min.css new file mode 100755 index 000000000..39934146f --- /dev/null +++ b/tests/assets/css/vendor/bootstrap.min.css @@ -0,0 +1,6 @@ +@charset "UTF-8";/*! + * Bootstrap v5.3.3 (https://getbootstrap.com/) + * Copyright 2011-2024 The Bootstrap Authors + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + */:root,[data-bs-theme=light]{--bs-blue:#0d6efd;--bs-indigo:#6610f2;--bs-purple:#6f42c1;--bs-pink:#d63384;--bs-red:#dc3545;--bs-orange:#fd7e14;--bs-yellow:#ffc107;--bs-green:#198754;--bs-teal:#20c997;--bs-cyan:#0dcaf0;--bs-black:#000;--bs-white:#fff;--bs-gray:#6c757d;--bs-gray-dark:#343a40;--bs-gray-100:#f8f9fa;--bs-gray-200:#e9ecef;--bs-gray-300:#dee2e6;--bs-gray-400:#ced4da;--bs-gray-500:#adb5bd;--bs-gray-600:#6c757d;--bs-gray-700:#495057;--bs-gray-800:#343a40;--bs-gray-900:#212529;--bs-primary:#0d6efd;--bs-secondary:#6c757d;--bs-success:#198754;--bs-info:#0dcaf0;--bs-warning:#ffc107;--bs-danger:#dc3545;--bs-light:#f8f9fa;--bs-dark:#212529;--bs-primary-rgb:13,110,253;--bs-secondary-rgb:108,117,125;--bs-success-rgb:25,135,84;--bs-info-rgb:13,202,240;--bs-warning-rgb:255,193,7;--bs-danger-rgb:220,53,69;--bs-light-rgb:248,249,250;--bs-dark-rgb:33,37,41;--bs-primary-text-emphasis:#052c65;--bs-secondary-text-emphasis:#2b2f32;--bs-success-text-emphasis:#0a3622;--bs-info-text-emphasis:#055160;--bs-warning-text-emphasis:#664d03;--bs-danger-text-emphasis:#58151c;--bs-light-text-emphasis:#495057;--bs-dark-text-emphasis:#495057;--bs-primary-bg-subtle:#cfe2ff;--bs-secondary-bg-subtle:#e2e3e5;--bs-success-bg-subtle:#d1e7dd;--bs-info-bg-subtle:#cff4fc;--bs-warning-bg-subtle:#fff3cd;--bs-danger-bg-subtle:#f8d7da;--bs-light-bg-subtle:#fcfcfd;--bs-dark-bg-subtle:#ced4da;--bs-primary-border-subtle:#9ec5fe;--bs-secondary-border-subtle:#c4c8cb;--bs-success-border-subtle:#a3cfbb;--bs-info-border-subtle:#9eeaf9;--bs-warning-border-subtle:#ffe69c;--bs-danger-border-subtle:#f1aeb5;--bs-light-border-subtle:#e9ecef;--bs-dark-border-subtle:#adb5bd;--bs-white-rgb:255,255,255;--bs-black-rgb:0,0,0;--bs-font-sans-serif: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";--bs-font-monospace:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--bs-gradient:linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0));--bs-body-font-family:var(--bs-font-sans-serif);--bs-body-font-size:1rem;--bs-body-font-weight:400;--bs-body-line-height:1.5;--bs-body-color:#212529;--bs-body-color-rgb:33,37,41;--bs-body-bg:#fff;--bs-body-bg-rgb:255,255,255;--bs-emphasis-color:#000;--bs-emphasis-color-rgb:0,0,0;--bs-secondary-color:rgba(33, 37, 41, 0.75);--bs-secondary-color-rgb:33,37,41;--bs-secondary-bg:#e9ecef;--bs-secondary-bg-rgb:233,236,239;--bs-tertiary-color:rgba(33, 37, 41, 0.5);--bs-tertiary-color-rgb:33,37,41;--bs-tertiary-bg:#f8f9fa;--bs-tertiary-bg-rgb:248,249,250;--bs-heading-color:inherit;--bs-link-color:#0d6efd;--bs-link-color-rgb:13,110,253;--bs-link-decoration:underline;--bs-link-hover-color:#0a58ca;--bs-link-hover-color-rgb:10,88,202;--bs-code-color:#d63384;--bs-highlight-color:#212529;--bs-highlight-bg:#fff3cd;--bs-border-width:1px;--bs-border-style:solid;--bs-border-color:#dee2e6;--bs-border-color-translucent:rgba(0, 0, 0, 0.175);--bs-border-radius:0.375rem;--bs-border-radius-sm:0.25rem;--bs-border-radius-lg:0.5rem;--bs-border-radius-xl:1rem;--bs-border-radius-xxl:2rem;--bs-border-radius-2xl:var(--bs-border-radius-xxl);--bs-border-radius-pill:50rem;--bs-box-shadow:0 0.5rem 1rem rgba(0, 0, 0, 0.15);--bs-box-shadow-sm:0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);--bs-box-shadow-lg:0 1rem 3rem rgba(0, 0, 0, 0.175);--bs-box-shadow-inset:inset 0 1px 2px rgba(0, 0, 0, 0.075);--bs-focus-ring-width:0.25rem;--bs-focus-ring-opacity:0.25;--bs-focus-ring-color:rgba(13, 110, 253, 0.25);--bs-form-valid-color:#198754;--bs-form-valid-border-color:#198754;--bs-form-invalid-color:#dc3545;--bs-form-invalid-border-color:#dc3545}[data-bs-theme=dark]{color-scheme:dark;--bs-body-color:#dee2e6;--bs-body-color-rgb:222,226,230;--bs-body-bg:#212529;--bs-body-bg-rgb:33,37,41;--bs-emphasis-color:#fff;--bs-emphasis-color-rgb:255,255,255;--bs-secondary-color:rgba(222, 226, 230, 0.75);--bs-secondary-color-rgb:222,226,230;--bs-secondary-bg:#343a40;--bs-secondary-bg-rgb:52,58,64;--bs-tertiary-color:rgba(222, 226, 230, 0.5);--bs-tertiary-color-rgb:222,226,230;--bs-tertiary-bg:#2b3035;--bs-tertiary-bg-rgb:43,48,53;--bs-primary-text-emphasis:#6ea8fe;--bs-secondary-text-emphasis:#a7acb1;--bs-success-text-emphasis:#75b798;--bs-info-text-emphasis:#6edff6;--bs-warning-text-emphasis:#ffda6a;--bs-danger-text-emphasis:#ea868f;--bs-light-text-emphasis:#f8f9fa;--bs-dark-text-emphasis:#dee2e6;--bs-primary-bg-subtle:#031633;--bs-secondary-bg-subtle:#161719;--bs-success-bg-subtle:#051b11;--bs-info-bg-subtle:#032830;--bs-warning-bg-subtle:#332701;--bs-danger-bg-subtle:#2c0b0e;--bs-light-bg-subtle:#343a40;--bs-dark-bg-subtle:#1a1d20;--bs-primary-border-subtle:#084298;--bs-secondary-border-subtle:#41464b;--bs-success-border-subtle:#0f5132;--bs-info-border-subtle:#087990;--bs-warning-border-subtle:#997404;--bs-danger-border-subtle:#842029;--bs-light-border-subtle:#495057;--bs-dark-border-subtle:#343a40;--bs-heading-color:inherit;--bs-link-color:#6ea8fe;--bs-link-hover-color:#8bb9fe;--bs-link-color-rgb:110,168,254;--bs-link-hover-color-rgb:139,185,254;--bs-code-color:#e685b5;--bs-highlight-color:#dee2e6;--bs-highlight-bg:#664d03;--bs-border-color:#495057;--bs-border-color-translucent:rgba(255, 255, 255, 0.15);--bs-form-valid-color:#75b798;--bs-form-valid-border-color:#75b798;--bs-form-invalid-color:#ea868f;--bs-form-invalid-border-color:#ea868f}*,::after,::before{box-sizing:border-box}@media (prefers-reduced-motion:no-preference){:root{scroll-behavior:smooth}}body{margin:0;font-family:var(--bs-body-font-family);font-size:var(--bs-body-font-size);font-weight:var(--bs-body-font-weight);line-height:var(--bs-body-line-height);color:var(--bs-body-color);text-align:var(--bs-body-text-align);background-color:var(--bs-body-bg);-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}hr{margin:1rem 0;color:inherit;border:0;border-top:var(--bs-border-width) solid;opacity:.25}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem;font-weight:500;line-height:1.2;color:var(--bs-heading-color)}.h1,h1{font-size:calc(1.375rem + 1.5vw)}@media (min-width:1200px){.h1,h1{font-size:2.5rem}}.h2,h2{font-size:calc(1.325rem + .9vw)}@media (min-width:1200px){.h2,h2{font-size:2rem}}.h3,h3{font-size:calc(1.3rem + .6vw)}@media (min-width:1200px){.h3,h3{font-size:1.75rem}}.h4,h4{font-size:calc(1.275rem + .3vw)}@media (min-width:1200px){.h4,h4{font-size:1.5rem}}.h5,h5{font-size:1.25rem}.h6,h6{font-size:1rem}p{margin-top:0;margin-bottom:1rem}abbr[title]{-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}ol,ul{padding-left:2rem}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}.small,small{font-size:.875em}.mark,mark{padding:.1875em;color:var(--bs-highlight-color);background-color:var(--bs-highlight-bg)}sub,sup{position:relative;font-size:.75em;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:rgba(var(--bs-link-color-rgb),var(--bs-link-opacity,1));text-decoration:underline}a:hover{--bs-link-color-rgb:var(--bs-link-hover-color-rgb)}a:not([href]):not([class]),a:not([href]):not([class]):hover{color:inherit;text-decoration:none}code,kbd,pre,samp{font-family:var(--bs-font-monospace);font-size:1em}pre{display:block;margin-top:0;margin-bottom:1rem;overflow:auto;font-size:.875em}pre code{font-size:inherit;color:inherit;word-break:normal}code{font-size:.875em;color:var(--bs-code-color);word-wrap:break-word}a>code{color:inherit}kbd{padding:.1875rem .375rem;font-size:.875em;color:var(--bs-body-bg);background-color:var(--bs-body-color);border-radius:.25rem}kbd kbd{padding:0;font-size:1em}figure{margin:0 0 1rem}img,svg{vertical-align:middle}table{caption-side:bottom;border-collapse:collapse}caption{padding-top:.5rem;padding-bottom:.5rem;color:var(--bs-secondary-color);text-align:left}th{text-align:inherit;text-align:-webkit-match-parent}tbody,td,tfoot,th,thead,tr{border-color:inherit;border-style:solid;border-width:0}label{display:inline-block}button{border-radius:0}button:focus:not(:focus-visible){outline:0}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,select{text-transform:none}[role=button]{cursor:pointer}select{word-wrap:normal}select:disabled{opacity:1}[list]:not([type=date]):not([type=datetime-local]):not([type=month]):not([type=week]):not([type=time])::-webkit-calendar-picker-indicator{display:none!important}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}::-moz-focus-inner{padding:0;border-style:none}textarea{resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{float:left;width:100%;padding:0;margin-bottom:.5rem;font-size:calc(1.275rem + .3vw);line-height:inherit}@media (min-width:1200px){legend{font-size:1.5rem}}legend+*{clear:left}::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-fields-wrapper,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-minute,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-text,::-webkit-datetime-edit-year-field{padding:0}::-webkit-inner-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-color-swatch-wrapper{padding:0}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}::file-selector-button{font:inherit;-webkit-appearance:button}output{display:inline-block}iframe{border:0}summary{display:list-item;cursor:pointer}progress{vertical-align:baseline}[hidden]{display:none!important}.lead{font-size:1.25rem;font-weight:300}.display-1{font-size:calc(1.625rem + 4.5vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-1{font-size:5rem}}.display-2{font-size:calc(1.575rem + 3.9vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-2{font-size:4.5rem}}.display-3{font-size:calc(1.525rem + 3.3vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-3{font-size:4rem}}.display-4{font-size:calc(1.475rem + 2.7vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-4{font-size:3.5rem}}.display-5{font-size:calc(1.425rem + 2.1vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-5{font-size:3rem}}.display-6{font-size:calc(1.375rem + 1.5vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-6{font-size:2.5rem}}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none}.list-inline-item{display:inline-block}.list-inline-item:not(:last-child){margin-right:.5rem}.initialism{font-size:.875em;text-transform:uppercase}.blockquote{margin-bottom:1rem;font-size:1.25rem}.blockquote>:last-child{margin-bottom:0}.blockquote-footer{margin-top:-1rem;margin-bottom:1rem;font-size:.875em;color:#6c757d}.blockquote-footer::before{content:"— "}.img-fluid{max-width:100%;height:auto}.img-thumbnail{padding:.25rem;background-color:var(--bs-body-bg);border:var(--bs-border-width) solid var(--bs-border-color);border-radius:var(--bs-border-radius);max-width:100%;height:auto}.figure{display:inline-block}.figure-img{margin-bottom:.5rem;line-height:1}.figure-caption{font-size:.875em;color:var(--bs-secondary-color)}.container,.container-fluid,.container-lg,.container-md,.container-sm,.container-xl,.container-xxl{--bs-gutter-x:1.5rem;--bs-gutter-y:0;width:100%;padding-right:calc(var(--bs-gutter-x) * .5);padding-left:calc(var(--bs-gutter-x) * .5);margin-right:auto;margin-left:auto}@media (min-width:576px){.container,.container-sm{max-width:540px}}@media (min-width:768px){.container,.container-md,.container-sm{max-width:720px}}@media (min-width:992px){.container,.container-lg,.container-md,.container-sm{max-width:960px}}@media (min-width:1200px){.container,.container-lg,.container-md,.container-sm,.container-xl{max-width:1140px}}@media (min-width:1400px){.container,.container-lg,.container-md,.container-sm,.container-xl,.container-xxl{max-width:1320px}}:root{--bs-breakpoint-xs:0;--bs-breakpoint-sm:576px;--bs-breakpoint-md:768px;--bs-breakpoint-lg:992px;--bs-breakpoint-xl:1200px;--bs-breakpoint-xxl:1400px}.row{--bs-gutter-x:1.5rem;--bs-gutter-y:0;display:flex;flex-wrap:wrap;margin-top:calc(-1 * var(--bs-gutter-y));margin-right:calc(-.5 * var(--bs-gutter-x));margin-left:calc(-.5 * var(--bs-gutter-x))}.row>*{flex-shrink:0;width:100%;max-width:100%;padding-right:calc(var(--bs-gutter-x) * .5);padding-left:calc(var(--bs-gutter-x) * .5);margin-top:var(--bs-gutter-y)}.col{flex:1 0 0%}.row-cols-auto>*{flex:0 0 auto;width:auto}.row-cols-1>*{flex:0 0 auto;width:100%}.row-cols-2>*{flex:0 0 auto;width:50%}.row-cols-3>*{flex:0 0 auto;width:33.33333333%}.row-cols-4>*{flex:0 0 auto;width:25%}.row-cols-5>*{flex:0 0 auto;width:20%}.row-cols-6>*{flex:0 0 auto;width:16.66666667%}.col-auto{flex:0 0 auto;width:auto}.col-1{flex:0 0 auto;width:8.33333333%}.col-2{flex:0 0 auto;width:16.66666667%}.col-3{flex:0 0 auto;width:25%}.col-4{flex:0 0 auto;width:33.33333333%}.col-5{flex:0 0 auto;width:41.66666667%}.col-6{flex:0 0 auto;width:50%}.col-7{flex:0 0 auto;width:58.33333333%}.col-8{flex:0 0 auto;width:66.66666667%}.col-9{flex:0 0 auto;width:75%}.col-10{flex:0 0 auto;width:83.33333333%}.col-11{flex:0 0 auto;width:91.66666667%}.col-12{flex:0 0 auto;width:100%}.offset-1{margin-left:8.33333333%}.offset-2{margin-left:16.66666667%}.offset-3{margin-left:25%}.offset-4{margin-left:33.33333333%}.offset-5{margin-left:41.66666667%}.offset-6{margin-left:50%}.offset-7{margin-left:58.33333333%}.offset-8{margin-left:66.66666667%}.offset-9{margin-left:75%}.offset-10{margin-left:83.33333333%}.offset-11{margin-left:91.66666667%}.g-0,.gx-0{--bs-gutter-x:0}.g-0,.gy-0{--bs-gutter-y:0}.g-1,.gx-1{--bs-gutter-x:0.25rem}.g-1,.gy-1{--bs-gutter-y:0.25rem}.g-2,.gx-2{--bs-gutter-x:0.5rem}.g-2,.gy-2{--bs-gutter-y:0.5rem}.g-3,.gx-3{--bs-gutter-x:1rem}.g-3,.gy-3{--bs-gutter-y:1rem}.g-4,.gx-4{--bs-gutter-x:1.5rem}.g-4,.gy-4{--bs-gutter-y:1.5rem}.g-5,.gx-5{--bs-gutter-x:3rem}.g-5,.gy-5{--bs-gutter-y:3rem}@media (min-width:576px){.col-sm{flex:1 0 0%}.row-cols-sm-auto>*{flex:0 0 auto;width:auto}.row-cols-sm-1>*{flex:0 0 auto;width:100%}.row-cols-sm-2>*{flex:0 0 auto;width:50%}.row-cols-sm-3>*{flex:0 0 auto;width:33.33333333%}.row-cols-sm-4>*{flex:0 0 auto;width:25%}.row-cols-sm-5>*{flex:0 0 auto;width:20%}.row-cols-sm-6>*{flex:0 0 auto;width:16.66666667%}.col-sm-auto{flex:0 0 auto;width:auto}.col-sm-1{flex:0 0 auto;width:8.33333333%}.col-sm-2{flex:0 0 auto;width:16.66666667%}.col-sm-3{flex:0 0 auto;width:25%}.col-sm-4{flex:0 0 auto;width:33.33333333%}.col-sm-5{flex:0 0 auto;width:41.66666667%}.col-sm-6{flex:0 0 auto;width:50%}.col-sm-7{flex:0 0 auto;width:58.33333333%}.col-sm-8{flex:0 0 auto;width:66.66666667%}.col-sm-9{flex:0 0 auto;width:75%}.col-sm-10{flex:0 0 auto;width:83.33333333%}.col-sm-11{flex:0 0 auto;width:91.66666667%}.col-sm-12{flex:0 0 auto;width:100%}.offset-sm-0{margin-left:0}.offset-sm-1{margin-left:8.33333333%}.offset-sm-2{margin-left:16.66666667%}.offset-sm-3{margin-left:25%}.offset-sm-4{margin-left:33.33333333%}.offset-sm-5{margin-left:41.66666667%}.offset-sm-6{margin-left:50%}.offset-sm-7{margin-left:58.33333333%}.offset-sm-8{margin-left:66.66666667%}.offset-sm-9{margin-left:75%}.offset-sm-10{margin-left:83.33333333%}.offset-sm-11{margin-left:91.66666667%}.g-sm-0,.gx-sm-0{--bs-gutter-x:0}.g-sm-0,.gy-sm-0{--bs-gutter-y:0}.g-sm-1,.gx-sm-1{--bs-gutter-x:0.25rem}.g-sm-1,.gy-sm-1{--bs-gutter-y:0.25rem}.g-sm-2,.gx-sm-2{--bs-gutter-x:0.5rem}.g-sm-2,.gy-sm-2{--bs-gutter-y:0.5rem}.g-sm-3,.gx-sm-3{--bs-gutter-x:1rem}.g-sm-3,.gy-sm-3{--bs-gutter-y:1rem}.g-sm-4,.gx-sm-4{--bs-gutter-x:1.5rem}.g-sm-4,.gy-sm-4{--bs-gutter-y:1.5rem}.g-sm-5,.gx-sm-5{--bs-gutter-x:3rem}.g-sm-5,.gy-sm-5{--bs-gutter-y:3rem}}@media (min-width:768px){.col-md{flex:1 0 0%}.row-cols-md-auto>*{flex:0 0 auto;width:auto}.row-cols-md-1>*{flex:0 0 auto;width:100%}.row-cols-md-2>*{flex:0 0 auto;width:50%}.row-cols-md-3>*{flex:0 0 auto;width:33.33333333%}.row-cols-md-4>*{flex:0 0 auto;width:25%}.row-cols-md-5>*{flex:0 0 auto;width:20%}.row-cols-md-6>*{flex:0 0 auto;width:16.66666667%}.col-md-auto{flex:0 0 auto;width:auto}.col-md-1{flex:0 0 auto;width:8.33333333%}.col-md-2{flex:0 0 auto;width:16.66666667%}.col-md-3{flex:0 0 auto;width:25%}.col-md-4{flex:0 0 auto;width:33.33333333%}.col-md-5{flex:0 0 auto;width:41.66666667%}.col-md-6{flex:0 0 auto;width:50%}.col-md-7{flex:0 0 auto;width:58.33333333%}.col-md-8{flex:0 0 auto;width:66.66666667%}.col-md-9{flex:0 0 auto;width:75%}.col-md-10{flex:0 0 auto;width:83.33333333%}.col-md-11{flex:0 0 auto;width:91.66666667%}.col-md-12{flex:0 0 auto;width:100%}.offset-md-0{margin-left:0}.offset-md-1{margin-left:8.33333333%}.offset-md-2{margin-left:16.66666667%}.offset-md-3{margin-left:25%}.offset-md-4{margin-left:33.33333333%}.offset-md-5{margin-left:41.66666667%}.offset-md-6{margin-left:50%}.offset-md-7{margin-left:58.33333333%}.offset-md-8{margin-left:66.66666667%}.offset-md-9{margin-left:75%}.offset-md-10{margin-left:83.33333333%}.offset-md-11{margin-left:91.66666667%}.g-md-0,.gx-md-0{--bs-gutter-x:0}.g-md-0,.gy-md-0{--bs-gutter-y:0}.g-md-1,.gx-md-1{--bs-gutter-x:0.25rem}.g-md-1,.gy-md-1{--bs-gutter-y:0.25rem}.g-md-2,.gx-md-2{--bs-gutter-x:0.5rem}.g-md-2,.gy-md-2{--bs-gutter-y:0.5rem}.g-md-3,.gx-md-3{--bs-gutter-x:1rem}.g-md-3,.gy-md-3{--bs-gutter-y:1rem}.g-md-4,.gx-md-4{--bs-gutter-x:1.5rem}.g-md-4,.gy-md-4{--bs-gutter-y:1.5rem}.g-md-5,.gx-md-5{--bs-gutter-x:3rem}.g-md-5,.gy-md-5{--bs-gutter-y:3rem}}@media (min-width:992px){.col-lg{flex:1 0 0%}.row-cols-lg-auto>*{flex:0 0 auto;width:auto}.row-cols-lg-1>*{flex:0 0 auto;width:100%}.row-cols-lg-2>*{flex:0 0 auto;width:50%}.row-cols-lg-3>*{flex:0 0 auto;width:33.33333333%}.row-cols-lg-4>*{flex:0 0 auto;width:25%}.row-cols-lg-5>*{flex:0 0 auto;width:20%}.row-cols-lg-6>*{flex:0 0 auto;width:16.66666667%}.col-lg-auto{flex:0 0 auto;width:auto}.col-lg-1{flex:0 0 auto;width:8.33333333%}.col-lg-2{flex:0 0 auto;width:16.66666667%}.col-lg-3{flex:0 0 auto;width:25%}.col-lg-4{flex:0 0 auto;width:33.33333333%}.col-lg-5{flex:0 0 auto;width:41.66666667%}.col-lg-6{flex:0 0 auto;width:50%}.col-lg-7{flex:0 0 auto;width:58.33333333%}.col-lg-8{flex:0 0 auto;width:66.66666667%}.col-lg-9{flex:0 0 auto;width:75%}.col-lg-10{flex:0 0 auto;width:83.33333333%}.col-lg-11{flex:0 0 auto;width:91.66666667%}.col-lg-12{flex:0 0 auto;width:100%}.offset-lg-0{margin-left:0}.offset-lg-1{margin-left:8.33333333%}.offset-lg-2{margin-left:16.66666667%}.offset-lg-3{margin-left:25%}.offset-lg-4{margin-left:33.33333333%}.offset-lg-5{margin-left:41.66666667%}.offset-lg-6{margin-left:50%}.offset-lg-7{margin-left:58.33333333%}.offset-lg-8{margin-left:66.66666667%}.offset-lg-9{margin-left:75%}.offset-lg-10{margin-left:83.33333333%}.offset-lg-11{margin-left:91.66666667%}.g-lg-0,.gx-lg-0{--bs-gutter-x:0}.g-lg-0,.gy-lg-0{--bs-gutter-y:0}.g-lg-1,.gx-lg-1{--bs-gutter-x:0.25rem}.g-lg-1,.gy-lg-1{--bs-gutter-y:0.25rem}.g-lg-2,.gx-lg-2{--bs-gutter-x:0.5rem}.g-lg-2,.gy-lg-2{--bs-gutter-y:0.5rem}.g-lg-3,.gx-lg-3{--bs-gutter-x:1rem}.g-lg-3,.gy-lg-3{--bs-gutter-y:1rem}.g-lg-4,.gx-lg-4{--bs-gutter-x:1.5rem}.g-lg-4,.gy-lg-4{--bs-gutter-y:1.5rem}.g-lg-5,.gx-lg-5{--bs-gutter-x:3rem}.g-lg-5,.gy-lg-5{--bs-gutter-y:3rem}}@media (min-width:1200px){.col-xl{flex:1 0 0%}.row-cols-xl-auto>*{flex:0 0 auto;width:auto}.row-cols-xl-1>*{flex:0 0 auto;width:100%}.row-cols-xl-2>*{flex:0 0 auto;width:50%}.row-cols-xl-3>*{flex:0 0 auto;width:33.33333333%}.row-cols-xl-4>*{flex:0 0 auto;width:25%}.row-cols-xl-5>*{flex:0 0 auto;width:20%}.row-cols-xl-6>*{flex:0 0 auto;width:16.66666667%}.col-xl-auto{flex:0 0 auto;width:auto}.col-xl-1{flex:0 0 auto;width:8.33333333%}.col-xl-2{flex:0 0 auto;width:16.66666667%}.col-xl-3{flex:0 0 auto;width:25%}.col-xl-4{flex:0 0 auto;width:33.33333333%}.col-xl-5{flex:0 0 auto;width:41.66666667%}.col-xl-6{flex:0 0 auto;width:50%}.col-xl-7{flex:0 0 auto;width:58.33333333%}.col-xl-8{flex:0 0 auto;width:66.66666667%}.col-xl-9{flex:0 0 auto;width:75%}.col-xl-10{flex:0 0 auto;width:83.33333333%}.col-xl-11{flex:0 0 auto;width:91.66666667%}.col-xl-12{flex:0 0 auto;width:100%}.offset-xl-0{margin-left:0}.offset-xl-1{margin-left:8.33333333%}.offset-xl-2{margin-left:16.66666667%}.offset-xl-3{margin-left:25%}.offset-xl-4{margin-left:33.33333333%}.offset-xl-5{margin-left:41.66666667%}.offset-xl-6{margin-left:50%}.offset-xl-7{margin-left:58.33333333%}.offset-xl-8{margin-left:66.66666667%}.offset-xl-9{margin-left:75%}.offset-xl-10{margin-left:83.33333333%}.offset-xl-11{margin-left:91.66666667%}.g-xl-0,.gx-xl-0{--bs-gutter-x:0}.g-xl-0,.gy-xl-0{--bs-gutter-y:0}.g-xl-1,.gx-xl-1{--bs-gutter-x:0.25rem}.g-xl-1,.gy-xl-1{--bs-gutter-y:0.25rem}.g-xl-2,.gx-xl-2{--bs-gutter-x:0.5rem}.g-xl-2,.gy-xl-2{--bs-gutter-y:0.5rem}.g-xl-3,.gx-xl-3{--bs-gutter-x:1rem}.g-xl-3,.gy-xl-3{--bs-gutter-y:1rem}.g-xl-4,.gx-xl-4{--bs-gutter-x:1.5rem}.g-xl-4,.gy-xl-4{--bs-gutter-y:1.5rem}.g-xl-5,.gx-xl-5{--bs-gutter-x:3rem}.g-xl-5,.gy-xl-5{--bs-gutter-y:3rem}}@media (min-width:1400px){.col-xxl{flex:1 0 0%}.row-cols-xxl-auto>*{flex:0 0 auto;width:auto}.row-cols-xxl-1>*{flex:0 0 auto;width:100%}.row-cols-xxl-2>*{flex:0 0 auto;width:50%}.row-cols-xxl-3>*{flex:0 0 auto;width:33.33333333%}.row-cols-xxl-4>*{flex:0 0 auto;width:25%}.row-cols-xxl-5>*{flex:0 0 auto;width:20%}.row-cols-xxl-6>*{flex:0 0 auto;width:16.66666667%}.col-xxl-auto{flex:0 0 auto;width:auto}.col-xxl-1{flex:0 0 auto;width:8.33333333%}.col-xxl-2{flex:0 0 auto;width:16.66666667%}.col-xxl-3{flex:0 0 auto;width:25%}.col-xxl-4{flex:0 0 auto;width:33.33333333%}.col-xxl-5{flex:0 0 auto;width:41.66666667%}.col-xxl-6{flex:0 0 auto;width:50%}.col-xxl-7{flex:0 0 auto;width:58.33333333%}.col-xxl-8{flex:0 0 auto;width:66.66666667%}.col-xxl-9{flex:0 0 auto;width:75%}.col-xxl-10{flex:0 0 auto;width:83.33333333%}.col-xxl-11{flex:0 0 auto;width:91.66666667%}.col-xxl-12{flex:0 0 auto;width:100%}.offset-xxl-0{margin-left:0}.offset-xxl-1{margin-left:8.33333333%}.offset-xxl-2{margin-left:16.66666667%}.offset-xxl-3{margin-left:25%}.offset-xxl-4{margin-left:33.33333333%}.offset-xxl-5{margin-left:41.66666667%}.offset-xxl-6{margin-left:50%}.offset-xxl-7{margin-left:58.33333333%}.offset-xxl-8{margin-left:66.66666667%}.offset-xxl-9{margin-left:75%}.offset-xxl-10{margin-left:83.33333333%}.offset-xxl-11{margin-left:91.66666667%}.g-xxl-0,.gx-xxl-0{--bs-gutter-x:0}.g-xxl-0,.gy-xxl-0{--bs-gutter-y:0}.g-xxl-1,.gx-xxl-1{--bs-gutter-x:0.25rem}.g-xxl-1,.gy-xxl-1{--bs-gutter-y:0.25rem}.g-xxl-2,.gx-xxl-2{--bs-gutter-x:0.5rem}.g-xxl-2,.gy-xxl-2{--bs-gutter-y:0.5rem}.g-xxl-3,.gx-xxl-3{--bs-gutter-x:1rem}.g-xxl-3,.gy-xxl-3{--bs-gutter-y:1rem}.g-xxl-4,.gx-xxl-4{--bs-gutter-x:1.5rem}.g-xxl-4,.gy-xxl-4{--bs-gutter-y:1.5rem}.g-xxl-5,.gx-xxl-5{--bs-gutter-x:3rem}.g-xxl-5,.gy-xxl-5{--bs-gutter-y:3rem}}.table{--bs-table-color-type:initial;--bs-table-bg-type:initial;--bs-table-color-state:initial;--bs-table-bg-state:initial;--bs-table-color:var(--bs-emphasis-color);--bs-table-bg:var(--bs-body-bg);--bs-table-border-color:var(--bs-border-color);--bs-table-accent-bg:transparent;--bs-table-striped-color:var(--bs-emphasis-color);--bs-table-striped-bg:rgba(var(--bs-emphasis-color-rgb), 0.05);--bs-table-active-color:var(--bs-emphasis-color);--bs-table-active-bg:rgba(var(--bs-emphasis-color-rgb), 0.1);--bs-table-hover-color:var(--bs-emphasis-color);--bs-table-hover-bg:rgba(var(--bs-emphasis-color-rgb), 0.075);width:100%;margin-bottom:1rem;vertical-align:top;border-color:var(--bs-table-border-color)}.table>:not(caption)>*>*{padding:.5rem .5rem;color:var(--bs-table-color-state,var(--bs-table-color-type,var(--bs-table-color)));background-color:var(--bs-table-bg);border-bottom-width:var(--bs-border-width);box-shadow:inset 0 0 0 9999px var(--bs-table-bg-state,var(--bs-table-bg-type,var(--bs-table-accent-bg)))}.table>tbody{vertical-align:inherit}.table>thead{vertical-align:bottom}.table-group-divider{border-top:calc(var(--bs-border-width) * 2) solid currentcolor}.caption-top{caption-side:top}.table-sm>:not(caption)>*>*{padding:.25rem .25rem}.table-bordered>:not(caption)>*{border-width:var(--bs-border-width) 0}.table-bordered>:not(caption)>*>*{border-width:0 var(--bs-border-width)}.table-borderless>:not(caption)>*>*{border-bottom-width:0}.table-borderless>:not(:first-child){border-top-width:0}.table-striped>tbody>tr:nth-of-type(odd)>*{--bs-table-color-type:var(--bs-table-striped-color);--bs-table-bg-type:var(--bs-table-striped-bg)}.table-striped-columns>:not(caption)>tr>:nth-child(2n){--bs-table-color-type:var(--bs-table-striped-color);--bs-table-bg-type:var(--bs-table-striped-bg)}.table-active{--bs-table-color-state:var(--bs-table-active-color);--bs-table-bg-state:var(--bs-table-active-bg)}.table-hover>tbody>tr:hover>*{--bs-table-color-state:var(--bs-table-hover-color);--bs-table-bg-state:var(--bs-table-hover-bg)}.table-primary{--bs-table-color:#000;--bs-table-bg:#cfe2ff;--bs-table-border-color:#a6b5cc;--bs-table-striped-bg:#c5d7f2;--bs-table-striped-color:#000;--bs-table-active-bg:#bacbe6;--bs-table-active-color:#000;--bs-table-hover-bg:#bfd1ec;--bs-table-hover-color:#000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-secondary{--bs-table-color:#000;--bs-table-bg:#e2e3e5;--bs-table-border-color:#b5b6b7;--bs-table-striped-bg:#d7d8da;--bs-table-striped-color:#000;--bs-table-active-bg:#cbccce;--bs-table-active-color:#000;--bs-table-hover-bg:#d1d2d4;--bs-table-hover-color:#000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-success{--bs-table-color:#000;--bs-table-bg:#d1e7dd;--bs-table-border-color:#a7b9b1;--bs-table-striped-bg:#c7dbd2;--bs-table-striped-color:#000;--bs-table-active-bg:#bcd0c7;--bs-table-active-color:#000;--bs-table-hover-bg:#c1d6cc;--bs-table-hover-color:#000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-info{--bs-table-color:#000;--bs-table-bg:#cff4fc;--bs-table-border-color:#a6c3ca;--bs-table-striped-bg:#c5e8ef;--bs-table-striped-color:#000;--bs-table-active-bg:#badce3;--bs-table-active-color:#000;--bs-table-hover-bg:#bfe2e9;--bs-table-hover-color:#000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-warning{--bs-table-color:#000;--bs-table-bg:#fff3cd;--bs-table-border-color:#ccc2a4;--bs-table-striped-bg:#f2e7c3;--bs-table-striped-color:#000;--bs-table-active-bg:#e6dbb9;--bs-table-active-color:#000;--bs-table-hover-bg:#ece1be;--bs-table-hover-color:#000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-danger{--bs-table-color:#000;--bs-table-bg:#f8d7da;--bs-table-border-color:#c6acae;--bs-table-striped-bg:#eccccf;--bs-table-striped-color:#000;--bs-table-active-bg:#dfc2c4;--bs-table-active-color:#000;--bs-table-hover-bg:#e5c7ca;--bs-table-hover-color:#000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-light{--bs-table-color:#000;--bs-table-bg:#f8f9fa;--bs-table-border-color:#c6c7c8;--bs-table-striped-bg:#ecedee;--bs-table-striped-color:#000;--bs-table-active-bg:#dfe0e1;--bs-table-active-color:#000;--bs-table-hover-bg:#e5e6e7;--bs-table-hover-color:#000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-dark{--bs-table-color:#fff;--bs-table-bg:#212529;--bs-table-border-color:#4d5154;--bs-table-striped-bg:#2c3034;--bs-table-striped-color:#fff;--bs-table-active-bg:#373b3e;--bs-table-active-color:#fff;--bs-table-hover-bg:#323539;--bs-table-hover-color:#fff;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-responsive{overflow-x:auto;-webkit-overflow-scrolling:touch}@media (max-width:575.98px){.table-responsive-sm{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media (max-width:767.98px){.table-responsive-md{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media (max-width:991.98px){.table-responsive-lg{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media (max-width:1199.98px){.table-responsive-xl{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media (max-width:1399.98px){.table-responsive-xxl{overflow-x:auto;-webkit-overflow-scrolling:touch}}.form-label{margin-bottom:.5rem}.col-form-label{padding-top:calc(.375rem + var(--bs-border-width));padding-bottom:calc(.375rem + var(--bs-border-width));margin-bottom:0;font-size:inherit;line-height:1.5}.col-form-label-lg{padding-top:calc(.5rem + var(--bs-border-width));padding-bottom:calc(.5rem + var(--bs-border-width));font-size:1.25rem}.col-form-label-sm{padding-top:calc(.25rem + var(--bs-border-width));padding-bottom:calc(.25rem + var(--bs-border-width));font-size:.875rem}.form-text{margin-top:.25rem;font-size:.875em;color:var(--bs-secondary-color)}.form-control{display:block;width:100%;padding:.375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:var(--bs-body-color);-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:var(--bs-body-bg);background-clip:padding-box;border:var(--bs-border-width) solid var(--bs-border-color);border-radius:var(--bs-border-radius);transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-control{transition:none}}.form-control[type=file]{overflow:hidden}.form-control[type=file]:not(:disabled):not([readonly]){cursor:pointer}.form-control:focus{color:var(--bs-body-color);background-color:var(--bs-body-bg);border-color:#86b7fe;outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.form-control::-webkit-date-and-time-value{min-width:85px;height:1.5em;margin:0}.form-control::-webkit-datetime-edit{display:block;padding:0}.form-control::-moz-placeholder{color:var(--bs-secondary-color);opacity:1}.form-control::placeholder{color:var(--bs-secondary-color);opacity:1}.form-control:disabled{background-color:var(--bs-secondary-bg);opacity:1}.form-control::-webkit-file-upload-button{padding:.375rem .75rem;margin:-.375rem -.75rem;-webkit-margin-end:.75rem;margin-inline-end:.75rem;color:var(--bs-body-color);background-color:var(--bs-tertiary-bg);pointer-events:none;border-color:inherit;border-style:solid;border-width:0;border-inline-end-width:var(--bs-border-width);border-radius:0;-webkit-transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}.form-control::file-selector-button{padding:.375rem .75rem;margin:-.375rem -.75rem;-webkit-margin-end:.75rem;margin-inline-end:.75rem;color:var(--bs-body-color);background-color:var(--bs-tertiary-bg);pointer-events:none;border-color:inherit;border-style:solid;border-width:0;border-inline-end-width:var(--bs-border-width);border-radius:0;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-control::-webkit-file-upload-button{-webkit-transition:none;transition:none}.form-control::file-selector-button{transition:none}}.form-control:hover:not(:disabled):not([readonly])::-webkit-file-upload-button{background-color:var(--bs-secondary-bg)}.form-control:hover:not(:disabled):not([readonly])::file-selector-button{background-color:var(--bs-secondary-bg)}.form-control-plaintext{display:block;width:100%;padding:.375rem 0;margin-bottom:0;line-height:1.5;color:var(--bs-body-color);background-color:transparent;border:solid transparent;border-width:var(--bs-border-width) 0}.form-control-plaintext:focus{outline:0}.form-control-plaintext.form-control-lg,.form-control-plaintext.form-control-sm{padding-right:0;padding-left:0}.form-control-sm{min-height:calc(1.5em + .5rem + calc(var(--bs-border-width) * 2));padding:.25rem .5rem;font-size:.875rem;border-radius:var(--bs-border-radius-sm)}.form-control-sm::-webkit-file-upload-button{padding:.25rem .5rem;margin:-.25rem -.5rem;-webkit-margin-end:.5rem;margin-inline-end:.5rem}.form-control-sm::file-selector-button{padding:.25rem .5rem;margin:-.25rem -.5rem;-webkit-margin-end:.5rem;margin-inline-end:.5rem}.form-control-lg{min-height:calc(1.5em + 1rem + calc(var(--bs-border-width) * 2));padding:.5rem 1rem;font-size:1.25rem;border-radius:var(--bs-border-radius-lg)}.form-control-lg::-webkit-file-upload-button{padding:.5rem 1rem;margin:-.5rem -1rem;-webkit-margin-end:1rem;margin-inline-end:1rem}.form-control-lg::file-selector-button{padding:.5rem 1rem;margin:-.5rem -1rem;-webkit-margin-end:1rem;margin-inline-end:1rem}textarea.form-control{min-height:calc(1.5em + .75rem + calc(var(--bs-border-width) * 2))}textarea.form-control-sm{min-height:calc(1.5em + .5rem + calc(var(--bs-border-width) * 2))}textarea.form-control-lg{min-height:calc(1.5em + 1rem + calc(var(--bs-border-width) * 2))}.form-control-color{width:3rem;height:calc(1.5em + .75rem + calc(var(--bs-border-width) * 2));padding:.375rem}.form-control-color:not(:disabled):not([readonly]){cursor:pointer}.form-control-color::-moz-color-swatch{border:0!important;border-radius:var(--bs-border-radius)}.form-control-color::-webkit-color-swatch{border:0!important;border-radius:var(--bs-border-radius)}.form-control-color.form-control-sm{height:calc(1.5em + .5rem + calc(var(--bs-border-width) * 2))}.form-control-color.form-control-lg{height:calc(1.5em + 1rem + calc(var(--bs-border-width) * 2))}.form-select{--bs-form-select-bg-img:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e");display:block;width:100%;padding:.375rem 2.25rem .375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:var(--bs-body-color);-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:var(--bs-body-bg);background-image:var(--bs-form-select-bg-img),var(--bs-form-select-bg-icon,none);background-repeat:no-repeat;background-position:right .75rem center;background-size:16px 12px;border:var(--bs-border-width) solid var(--bs-border-color);border-radius:var(--bs-border-radius);transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-select{transition:none}}.form-select:focus{border-color:#86b7fe;outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.form-select[multiple],.form-select[size]:not([size="1"]){padding-right:.75rem;background-image:none}.form-select:disabled{background-color:var(--bs-secondary-bg)}.form-select:-moz-focusring{color:transparent;text-shadow:0 0 0 var(--bs-body-color)}.form-select-sm{padding-top:.25rem;padding-bottom:.25rem;padding-left:.5rem;font-size:.875rem;border-radius:var(--bs-border-radius-sm)}.form-select-lg{padding-top:.5rem;padding-bottom:.5rem;padding-left:1rem;font-size:1.25rem;border-radius:var(--bs-border-radius-lg)}[data-bs-theme=dark] .form-select{--bs-form-select-bg-img:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23dee2e6' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e")}.form-check{display:block;min-height:1.5rem;padding-left:1.5em;margin-bottom:.125rem}.form-check .form-check-input{float:left;margin-left:-1.5em}.form-check-reverse{padding-right:1.5em;padding-left:0;text-align:right}.form-check-reverse .form-check-input{float:right;margin-right:-1.5em;margin-left:0}.form-check-input{--bs-form-check-bg:var(--bs-body-bg);flex-shrink:0;width:1em;height:1em;margin-top:.25em;vertical-align:top;-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:var(--bs-form-check-bg);background-image:var(--bs-form-check-bg-image);background-repeat:no-repeat;background-position:center;background-size:contain;border:var(--bs-border-width) solid var(--bs-border-color);-webkit-print-color-adjust:exact;color-adjust:exact;print-color-adjust:exact}.form-check-input[type=checkbox]{border-radius:.25em}.form-check-input[type=radio]{border-radius:50%}.form-check-input:active{filter:brightness(90%)}.form-check-input:focus{border-color:#86b7fe;outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.form-check-input:checked{background-color:#0d6efd;border-color:#0d6efd}.form-check-input:checked[type=checkbox]{--bs-form-check-bg-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='m6 10 3 3 6-6'/%3e%3c/svg%3e")}.form-check-input:checked[type=radio]{--bs-form-check-bg-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='2' fill='%23fff'/%3e%3c/svg%3e")}.form-check-input[type=checkbox]:indeterminate{background-color:#0d6efd;border-color:#0d6efd;--bs-form-check-bg-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10h8'/%3e%3c/svg%3e")}.form-check-input:disabled{pointer-events:none;filter:none;opacity:.5}.form-check-input:disabled~.form-check-label,.form-check-input[disabled]~.form-check-label{cursor:default;opacity:.5}.form-switch{padding-left:2.5em}.form-switch .form-check-input{--bs-form-switch-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='rgba%280, 0, 0, 0.25%29'/%3e%3c/svg%3e");width:2em;margin-left:-2.5em;background-image:var(--bs-form-switch-bg);background-position:left center;border-radius:2em;transition:background-position .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-switch .form-check-input{transition:none}}.form-switch .form-check-input:focus{--bs-form-switch-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%2386b7fe'/%3e%3c/svg%3e")}.form-switch .form-check-input:checked{background-position:right center;--bs-form-switch-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e")}.form-switch.form-check-reverse{padding-right:2.5em;padding-left:0}.form-switch.form-check-reverse .form-check-input{margin-right:-2.5em;margin-left:0}.form-check-inline{display:inline-block;margin-right:1rem}.btn-check{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.btn-check:disabled+.btn,.btn-check[disabled]+.btn{pointer-events:none;filter:none;opacity:.65}[data-bs-theme=dark] .form-switch .form-check-input:not(:checked):not(:focus){--bs-form-switch-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='rgba%28255, 255, 255, 0.25%29'/%3e%3c/svg%3e")}.form-range{width:100%;height:1.5rem;padding:0;-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:transparent}.form-range:focus{outline:0}.form-range:focus::-webkit-slider-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .25rem rgba(13,110,253,.25)}.form-range:focus::-moz-range-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .25rem rgba(13,110,253,.25)}.form-range::-moz-focus-outer{border:0}.form-range::-webkit-slider-thumb{width:1rem;height:1rem;margin-top:-.25rem;-webkit-appearance:none;appearance:none;background-color:#0d6efd;border:0;border-radius:1rem;-webkit-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-range::-webkit-slider-thumb{-webkit-transition:none;transition:none}}.form-range::-webkit-slider-thumb:active{background-color:#b6d4fe}.form-range::-webkit-slider-runnable-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:var(--bs-secondary-bg);border-color:transparent;border-radius:1rem}.form-range::-moz-range-thumb{width:1rem;height:1rem;-moz-appearance:none;appearance:none;background-color:#0d6efd;border:0;border-radius:1rem;-moz-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-range::-moz-range-thumb{-moz-transition:none;transition:none}}.form-range::-moz-range-thumb:active{background-color:#b6d4fe}.form-range::-moz-range-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:var(--bs-secondary-bg);border-color:transparent;border-radius:1rem}.form-range:disabled{pointer-events:none}.form-range:disabled::-webkit-slider-thumb{background-color:var(--bs-secondary-color)}.form-range:disabled::-moz-range-thumb{background-color:var(--bs-secondary-color)}.form-floating{position:relative}.form-floating>.form-control,.form-floating>.form-control-plaintext,.form-floating>.form-select{height:calc(3.5rem + calc(var(--bs-border-width) * 2));min-height:calc(3.5rem + calc(var(--bs-border-width) * 2));line-height:1.25}.form-floating>label{position:absolute;top:0;left:0;z-index:2;height:100%;padding:1rem .75rem;overflow:hidden;text-align:start;text-overflow:ellipsis;white-space:nowrap;pointer-events:none;border:var(--bs-border-width) solid transparent;transform-origin:0 0;transition:opacity .1s ease-in-out,transform .1s ease-in-out}@media (prefers-reduced-motion:reduce){.form-floating>label{transition:none}}.form-floating>.form-control,.form-floating>.form-control-plaintext{padding:1rem .75rem}.form-floating>.form-control-plaintext::-moz-placeholder,.form-floating>.form-control::-moz-placeholder{color:transparent}.form-floating>.form-control-plaintext::placeholder,.form-floating>.form-control::placeholder{color:transparent}.form-floating>.form-control-plaintext:not(:-moz-placeholder-shown),.form-floating>.form-control:not(:-moz-placeholder-shown){padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-control-plaintext:focus,.form-floating>.form-control-plaintext:not(:placeholder-shown),.form-floating>.form-control:focus,.form-floating>.form-control:not(:placeholder-shown){padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-control-plaintext:-webkit-autofill,.form-floating>.form-control:-webkit-autofill{padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-select{padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-control:not(:-moz-placeholder-shown)~label{color:rgba(var(--bs-body-color-rgb),.65);transform:scale(.85) translateY(-.5rem) translateX(.15rem)}.form-floating>.form-control-plaintext~label,.form-floating>.form-control:focus~label,.form-floating>.form-control:not(:placeholder-shown)~label,.form-floating>.form-select~label{color:rgba(var(--bs-body-color-rgb),.65);transform:scale(.85) translateY(-.5rem) translateX(.15rem)}.form-floating>.form-control:not(:-moz-placeholder-shown)~label::after{position:absolute;inset:1rem 0.375rem;z-index:-1;height:1.5em;content:"";background-color:var(--bs-body-bg);border-radius:var(--bs-border-radius)}.form-floating>.form-control-plaintext~label::after,.form-floating>.form-control:focus~label::after,.form-floating>.form-control:not(:placeholder-shown)~label::after,.form-floating>.form-select~label::after{position:absolute;inset:1rem 0.375rem;z-index:-1;height:1.5em;content:"";background-color:var(--bs-body-bg);border-radius:var(--bs-border-radius)}.form-floating>.form-control:-webkit-autofill~label{color:rgba(var(--bs-body-color-rgb),.65);transform:scale(.85) translateY(-.5rem) translateX(.15rem)}.form-floating>.form-control-plaintext~label{border-width:var(--bs-border-width) 0}.form-floating>.form-control:disabled~label,.form-floating>:disabled~label{color:#6c757d}.form-floating>.form-control:disabled~label::after,.form-floating>:disabled~label::after{background-color:var(--bs-secondary-bg)}.input-group{position:relative;display:flex;flex-wrap:wrap;align-items:stretch;width:100%}.input-group>.form-control,.input-group>.form-floating,.input-group>.form-select{position:relative;flex:1 1 auto;width:1%;min-width:0}.input-group>.form-control:focus,.input-group>.form-floating:focus-within,.input-group>.form-select:focus{z-index:5}.input-group .btn{position:relative;z-index:2}.input-group .btn:focus{z-index:5}.input-group-text{display:flex;align-items:center;padding:.375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:var(--bs-body-color);text-align:center;white-space:nowrap;background-color:var(--bs-tertiary-bg);border:var(--bs-border-width) solid var(--bs-border-color);border-radius:var(--bs-border-radius)}.input-group-lg>.btn,.input-group-lg>.form-control,.input-group-lg>.form-select,.input-group-lg>.input-group-text{padding:.5rem 1rem;font-size:1.25rem;border-radius:var(--bs-border-radius-lg)}.input-group-sm>.btn,.input-group-sm>.form-control,.input-group-sm>.form-select,.input-group-sm>.input-group-text{padding:.25rem .5rem;font-size:.875rem;border-radius:var(--bs-border-radius-sm)}.input-group-lg>.form-select,.input-group-sm>.form-select{padding-right:3rem}.input-group:not(.has-validation)>.dropdown-toggle:nth-last-child(n+3),.input-group:not(.has-validation)>.form-floating:not(:last-child)>.form-control,.input-group:not(.has-validation)>.form-floating:not(:last-child)>.form-select,.input-group:not(.has-validation)>:not(:last-child):not(.dropdown-toggle):not(.dropdown-menu):not(.form-floating){border-top-right-radius:0;border-bottom-right-radius:0}.input-group.has-validation>.dropdown-toggle:nth-last-child(n+4),.input-group.has-validation>.form-floating:nth-last-child(n+3)>.form-control,.input-group.has-validation>.form-floating:nth-last-child(n+3)>.form-select,.input-group.has-validation>:nth-last-child(n+3):not(.dropdown-toggle):not(.dropdown-menu):not(.form-floating){border-top-right-radius:0;border-bottom-right-radius:0}.input-group>:not(:first-child):not(.dropdown-menu):not(.valid-tooltip):not(.valid-feedback):not(.invalid-tooltip):not(.invalid-feedback){margin-left:calc(var(--bs-border-width) * -1);border-top-left-radius:0;border-bottom-left-radius:0}.input-group>.form-floating:not(:first-child)>.form-control,.input-group>.form-floating:not(:first-child)>.form-select{border-top-left-radius:0;border-bottom-left-radius:0}.valid-feedback{display:none;width:100%;margin-top:.25rem;font-size:.875em;color:var(--bs-form-valid-color)}.valid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;color:#fff;background-color:var(--bs-success);border-radius:var(--bs-border-radius)}.is-valid~.valid-feedback,.is-valid~.valid-tooltip,.was-validated :valid~.valid-feedback,.was-validated :valid~.valid-tooltip{display:block}.form-control.is-valid,.was-validated .form-control:valid{border-color:var(--bs-form-valid-border-color);padding-right:calc(1.5em + .75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23198754' d='M2.3 6.73.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right calc(.375em + .1875rem) center;background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-valid:focus,.was-validated .form-control:valid:focus{border-color:var(--bs-form-valid-border-color);box-shadow:0 0 0 .25rem rgba(var(--bs-success-rgb),.25)}.was-validated textarea.form-control:valid,textarea.form-control.is-valid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.form-select.is-valid,.was-validated .form-select:valid{border-color:var(--bs-form-valid-border-color)}.form-select.is-valid:not([multiple]):not([size]),.form-select.is-valid:not([multiple])[size="1"],.was-validated .form-select:valid:not([multiple]):not([size]),.was-validated .form-select:valid:not([multiple])[size="1"]{--bs-form-select-bg-icon:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23198754' d='M2.3 6.73.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");padding-right:4.125rem;background-position:right .75rem center,center right 2.25rem;background-size:16px 12px,calc(.75em + .375rem) calc(.75em + .375rem)}.form-select.is-valid:focus,.was-validated .form-select:valid:focus{border-color:var(--bs-form-valid-border-color);box-shadow:0 0 0 .25rem rgba(var(--bs-success-rgb),.25)}.form-control-color.is-valid,.was-validated .form-control-color:valid{width:calc(3rem + calc(1.5em + .75rem))}.form-check-input.is-valid,.was-validated .form-check-input:valid{border-color:var(--bs-form-valid-border-color)}.form-check-input.is-valid:checked,.was-validated .form-check-input:valid:checked{background-color:var(--bs-form-valid-color)}.form-check-input.is-valid:focus,.was-validated .form-check-input:valid:focus{box-shadow:0 0 0 .25rem rgba(var(--bs-success-rgb),.25)}.form-check-input.is-valid~.form-check-label,.was-validated .form-check-input:valid~.form-check-label{color:var(--bs-form-valid-color)}.form-check-inline .form-check-input~.valid-feedback{margin-left:.5em}.input-group>.form-control:not(:focus).is-valid,.input-group>.form-floating:not(:focus-within).is-valid,.input-group>.form-select:not(:focus).is-valid,.was-validated .input-group>.form-control:not(:focus):valid,.was-validated .input-group>.form-floating:not(:focus-within):valid,.was-validated .input-group>.form-select:not(:focus):valid{z-index:3}.invalid-feedback{display:none;width:100%;margin-top:.25rem;font-size:.875em;color:var(--bs-form-invalid-color)}.invalid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;color:#fff;background-color:var(--bs-danger);border-radius:var(--bs-border-radius)}.is-invalid~.invalid-feedback,.is-invalid~.invalid-tooltip,.was-validated :invalid~.invalid-feedback,.was-validated :invalid~.invalid-tooltip{display:block}.form-control.is-invalid,.was-validated .form-control:invalid{border-color:var(--bs-form-invalid-border-color);padding-right:calc(1.5em + .75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23dc3545'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right calc(.375em + .1875rem) center;background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-invalid:focus,.was-validated .form-control:invalid:focus{border-color:var(--bs-form-invalid-border-color);box-shadow:0 0 0 .25rem rgba(var(--bs-danger-rgb),.25)}.was-validated textarea.form-control:invalid,textarea.form-control.is-invalid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.form-select.is-invalid,.was-validated .form-select:invalid{border-color:var(--bs-form-invalid-border-color)}.form-select.is-invalid:not([multiple]):not([size]),.form-select.is-invalid:not([multiple])[size="1"],.was-validated .form-select:invalid:not([multiple]):not([size]),.was-validated .form-select:invalid:not([multiple])[size="1"]{--bs-form-select-bg-icon:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23dc3545'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e");padding-right:4.125rem;background-position:right .75rem center,center right 2.25rem;background-size:16px 12px,calc(.75em + .375rem) calc(.75em + .375rem)}.form-select.is-invalid:focus,.was-validated .form-select:invalid:focus{border-color:var(--bs-form-invalid-border-color);box-shadow:0 0 0 .25rem rgba(var(--bs-danger-rgb),.25)}.form-control-color.is-invalid,.was-validated .form-control-color:invalid{width:calc(3rem + calc(1.5em + .75rem))}.form-check-input.is-invalid,.was-validated .form-check-input:invalid{border-color:var(--bs-form-invalid-border-color)}.form-check-input.is-invalid:checked,.was-validated .form-check-input:invalid:checked{background-color:var(--bs-form-invalid-color)}.form-check-input.is-invalid:focus,.was-validated .form-check-input:invalid:focus{box-shadow:0 0 0 .25rem rgba(var(--bs-danger-rgb),.25)}.form-check-input.is-invalid~.form-check-label,.was-validated .form-check-input:invalid~.form-check-label{color:var(--bs-form-invalid-color)}.form-check-inline .form-check-input~.invalid-feedback{margin-left:.5em}.input-group>.form-control:not(:focus).is-invalid,.input-group>.form-floating:not(:focus-within).is-invalid,.input-group>.form-select:not(:focus).is-invalid,.was-validated .input-group>.form-control:not(:focus):invalid,.was-validated .input-group>.form-floating:not(:focus-within):invalid,.was-validated .input-group>.form-select:not(:focus):invalid{z-index:4}.btn{--bs-btn-padding-x:0.75rem;--bs-btn-padding-y:0.375rem;--bs-btn-font-family: ;--bs-btn-font-size:1rem;--bs-btn-font-weight:400;--bs-btn-line-height:1.5;--bs-btn-color:var(--bs-body-color);--bs-btn-bg:transparent;--bs-btn-border-width:var(--bs-border-width);--bs-btn-border-color:transparent;--bs-btn-border-radius:var(--bs-border-radius);--bs-btn-hover-border-color:transparent;--bs-btn-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.15),0 1px 1px rgba(0, 0, 0, 0.075);--bs-btn-disabled-opacity:0.65;--bs-btn-focus-box-shadow:0 0 0 0.25rem rgba(var(--bs-btn-focus-shadow-rgb), .5);display:inline-block;padding:var(--bs-btn-padding-y) var(--bs-btn-padding-x);font-family:var(--bs-btn-font-family);font-size:var(--bs-btn-font-size);font-weight:var(--bs-btn-font-weight);line-height:var(--bs-btn-line-height);color:var(--bs-btn-color);text-align:center;text-decoration:none;vertical-align:middle;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;user-select:none;border:var(--bs-btn-border-width) solid var(--bs-btn-border-color);border-radius:var(--bs-btn-border-radius);background-color:var(--bs-btn-bg);transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.btn{transition:none}}.btn:hover{color:var(--bs-btn-hover-color);background-color:var(--bs-btn-hover-bg);border-color:var(--bs-btn-hover-border-color)}.btn-check+.btn:hover{color:var(--bs-btn-color);background-color:var(--bs-btn-bg);border-color:var(--bs-btn-border-color)}.btn:focus-visible{color:var(--bs-btn-hover-color);background-color:var(--bs-btn-hover-bg);border-color:var(--bs-btn-hover-border-color);outline:0;box-shadow:var(--bs-btn-focus-box-shadow)}.btn-check:focus-visible+.btn{border-color:var(--bs-btn-hover-border-color);outline:0;box-shadow:var(--bs-btn-focus-box-shadow)}.btn-check:checked+.btn,.btn.active,.btn.show,.btn:first-child:active,:not(.btn-check)+.btn:active{color:var(--bs-btn-active-color);background-color:var(--bs-btn-active-bg);border-color:var(--bs-btn-active-border-color)}.btn-check:checked+.btn:focus-visible,.btn.active:focus-visible,.btn.show:focus-visible,.btn:first-child:active:focus-visible,:not(.btn-check)+.btn:active:focus-visible{box-shadow:var(--bs-btn-focus-box-shadow)}.btn-check:checked:focus-visible+.btn{box-shadow:var(--bs-btn-focus-box-shadow)}.btn.disabled,.btn:disabled,fieldset:disabled .btn{color:var(--bs-btn-disabled-color);pointer-events:none;background-color:var(--bs-btn-disabled-bg);border-color:var(--bs-btn-disabled-border-color);opacity:var(--bs-btn-disabled-opacity)}.btn-primary{--bs-btn-color:#fff;--bs-btn-bg:#0d6efd;--bs-btn-border-color:#0d6efd;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#0b5ed7;--bs-btn-hover-border-color:#0a58ca;--bs-btn-focus-shadow-rgb:49,132,253;--bs-btn-active-color:#fff;--bs-btn-active-bg:#0a58ca;--bs-btn-active-border-color:#0a53be;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#fff;--bs-btn-disabled-bg:#0d6efd;--bs-btn-disabled-border-color:#0d6efd}.btn-secondary{--bs-btn-color:#fff;--bs-btn-bg:#6c757d;--bs-btn-border-color:#6c757d;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#5c636a;--bs-btn-hover-border-color:#565e64;--bs-btn-focus-shadow-rgb:130,138,145;--bs-btn-active-color:#fff;--bs-btn-active-bg:#565e64;--bs-btn-active-border-color:#51585e;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#fff;--bs-btn-disabled-bg:#6c757d;--bs-btn-disabled-border-color:#6c757d}.btn-success{--bs-btn-color:#fff;--bs-btn-bg:#198754;--bs-btn-border-color:#198754;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#157347;--bs-btn-hover-border-color:#146c43;--bs-btn-focus-shadow-rgb:60,153,110;--bs-btn-active-color:#fff;--bs-btn-active-bg:#146c43;--bs-btn-active-border-color:#13653f;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#fff;--bs-btn-disabled-bg:#198754;--bs-btn-disabled-border-color:#198754}.btn-info{--bs-btn-color:#000;--bs-btn-bg:#0dcaf0;--bs-btn-border-color:#0dcaf0;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#31d2f2;--bs-btn-hover-border-color:#25cff2;--bs-btn-focus-shadow-rgb:11,172,204;--bs-btn-active-color:#000;--bs-btn-active-bg:#3dd5f3;--bs-btn-active-border-color:#25cff2;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#000;--bs-btn-disabled-bg:#0dcaf0;--bs-btn-disabled-border-color:#0dcaf0}.btn-warning{--bs-btn-color:#000;--bs-btn-bg:#ffc107;--bs-btn-border-color:#ffc107;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#ffca2c;--bs-btn-hover-border-color:#ffc720;--bs-btn-focus-shadow-rgb:217,164,6;--bs-btn-active-color:#000;--bs-btn-active-bg:#ffcd39;--bs-btn-active-border-color:#ffc720;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#000;--bs-btn-disabled-bg:#ffc107;--bs-btn-disabled-border-color:#ffc107}.btn-danger{--bs-btn-color:#fff;--bs-btn-bg:#dc3545;--bs-btn-border-color:#dc3545;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#bb2d3b;--bs-btn-hover-border-color:#b02a37;--bs-btn-focus-shadow-rgb:225,83,97;--bs-btn-active-color:#fff;--bs-btn-active-bg:#b02a37;--bs-btn-active-border-color:#a52834;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#fff;--bs-btn-disabled-bg:#dc3545;--bs-btn-disabled-border-color:#dc3545}.btn-light{--bs-btn-color:#000;--bs-btn-bg:#f8f9fa;--bs-btn-border-color:#f8f9fa;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#d3d4d5;--bs-btn-hover-border-color:#c6c7c8;--bs-btn-focus-shadow-rgb:211,212,213;--bs-btn-active-color:#000;--bs-btn-active-bg:#c6c7c8;--bs-btn-active-border-color:#babbbc;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#000;--bs-btn-disabled-bg:#f8f9fa;--bs-btn-disabled-border-color:#f8f9fa}.btn-dark{--bs-btn-color:#fff;--bs-btn-bg:#212529;--bs-btn-border-color:#212529;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#424649;--bs-btn-hover-border-color:#373b3e;--bs-btn-focus-shadow-rgb:66,70,73;--bs-btn-active-color:#fff;--bs-btn-active-bg:#4d5154;--bs-btn-active-border-color:#373b3e;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#fff;--bs-btn-disabled-bg:#212529;--bs-btn-disabled-border-color:#212529}.btn-outline-primary{--bs-btn-color:#0d6efd;--bs-btn-border-color:#0d6efd;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#0d6efd;--bs-btn-hover-border-color:#0d6efd;--bs-btn-focus-shadow-rgb:13,110,253;--bs-btn-active-color:#fff;--bs-btn-active-bg:#0d6efd;--bs-btn-active-border-color:#0d6efd;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#0d6efd;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#0d6efd;--bs-gradient:none}.btn-outline-secondary{--bs-btn-color:#6c757d;--bs-btn-border-color:#6c757d;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#6c757d;--bs-btn-hover-border-color:#6c757d;--bs-btn-focus-shadow-rgb:108,117,125;--bs-btn-active-color:#fff;--bs-btn-active-bg:#6c757d;--bs-btn-active-border-color:#6c757d;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#6c757d;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#6c757d;--bs-gradient:none}.btn-outline-success{--bs-btn-color:#198754;--bs-btn-border-color:#198754;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#198754;--bs-btn-hover-border-color:#198754;--bs-btn-focus-shadow-rgb:25,135,84;--bs-btn-active-color:#fff;--bs-btn-active-bg:#198754;--bs-btn-active-border-color:#198754;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#198754;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#198754;--bs-gradient:none}.btn-outline-info{--bs-btn-color:#0dcaf0;--bs-btn-border-color:#0dcaf0;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#0dcaf0;--bs-btn-hover-border-color:#0dcaf0;--bs-btn-focus-shadow-rgb:13,202,240;--bs-btn-active-color:#000;--bs-btn-active-bg:#0dcaf0;--bs-btn-active-border-color:#0dcaf0;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#0dcaf0;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#0dcaf0;--bs-gradient:none}.btn-outline-warning{--bs-btn-color:#ffc107;--bs-btn-border-color:#ffc107;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#ffc107;--bs-btn-hover-border-color:#ffc107;--bs-btn-focus-shadow-rgb:255,193,7;--bs-btn-active-color:#000;--bs-btn-active-bg:#ffc107;--bs-btn-active-border-color:#ffc107;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#ffc107;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#ffc107;--bs-gradient:none}.btn-outline-danger{--bs-btn-color:#dc3545;--bs-btn-border-color:#dc3545;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#dc3545;--bs-btn-hover-border-color:#dc3545;--bs-btn-focus-shadow-rgb:220,53,69;--bs-btn-active-color:#fff;--bs-btn-active-bg:#dc3545;--bs-btn-active-border-color:#dc3545;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#dc3545;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#dc3545;--bs-gradient:none}.btn-outline-light{--bs-btn-color:#f8f9fa;--bs-btn-border-color:#f8f9fa;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#f8f9fa;--bs-btn-hover-border-color:#f8f9fa;--bs-btn-focus-shadow-rgb:248,249,250;--bs-btn-active-color:#000;--bs-btn-active-bg:#f8f9fa;--bs-btn-active-border-color:#f8f9fa;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#f8f9fa;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#f8f9fa;--bs-gradient:none}.btn-outline-dark{--bs-btn-color:#212529;--bs-btn-border-color:#212529;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#212529;--bs-btn-hover-border-color:#212529;--bs-btn-focus-shadow-rgb:33,37,41;--bs-btn-active-color:#fff;--bs-btn-active-bg:#212529;--bs-btn-active-border-color:#212529;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#212529;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#212529;--bs-gradient:none}.btn-link{--bs-btn-font-weight:400;--bs-btn-color:var(--bs-link-color);--bs-btn-bg:transparent;--bs-btn-border-color:transparent;--bs-btn-hover-color:var(--bs-link-hover-color);--bs-btn-hover-border-color:transparent;--bs-btn-active-color:var(--bs-link-hover-color);--bs-btn-active-border-color:transparent;--bs-btn-disabled-color:#6c757d;--bs-btn-disabled-border-color:transparent;--bs-btn-box-shadow:0 0 0 #000;--bs-btn-focus-shadow-rgb:49,132,253;text-decoration:underline}.btn-link:focus-visible{color:var(--bs-btn-color)}.btn-link:hover{color:var(--bs-btn-hover-color)}.btn-group-lg>.btn,.btn-lg{--bs-btn-padding-y:0.5rem;--bs-btn-padding-x:1rem;--bs-btn-font-size:1.25rem;--bs-btn-border-radius:var(--bs-border-radius-lg)}.btn-group-sm>.btn,.btn-sm{--bs-btn-padding-y:0.25rem;--bs-btn-padding-x:0.5rem;--bs-btn-font-size:0.875rem;--bs-btn-border-radius:var(--bs-border-radius-sm)}.fade{transition:opacity .15s linear}@media (prefers-reduced-motion:reduce){.fade{transition:none}}.fade:not(.show){opacity:0}.collapse:not(.show){display:none}.collapsing{height:0;overflow:hidden;transition:height .35s ease}@media (prefers-reduced-motion:reduce){.collapsing{transition:none}}.collapsing.collapse-horizontal{width:0;height:auto;transition:width .35s ease}@media (prefers-reduced-motion:reduce){.collapsing.collapse-horizontal{transition:none}}.dropdown,.dropdown-center,.dropend,.dropstart,.dropup,.dropup-center{position:relative}.dropdown-toggle{white-space:nowrap}.dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid;border-right:.3em solid transparent;border-bottom:0;border-left:.3em solid transparent}.dropdown-toggle:empty::after{margin-left:0}.dropdown-menu{--bs-dropdown-zindex:1000;--bs-dropdown-min-width:10rem;--bs-dropdown-padding-x:0;--bs-dropdown-padding-y:0.5rem;--bs-dropdown-spacer:0.125rem;--bs-dropdown-font-size:1rem;--bs-dropdown-color:var(--bs-body-color);--bs-dropdown-bg:var(--bs-body-bg);--bs-dropdown-border-color:var(--bs-border-color-translucent);--bs-dropdown-border-radius:var(--bs-border-radius);--bs-dropdown-border-width:var(--bs-border-width);--bs-dropdown-inner-border-radius:calc(var(--bs-border-radius) - var(--bs-border-width));--bs-dropdown-divider-bg:var(--bs-border-color-translucent);--bs-dropdown-divider-margin-y:0.5rem;--bs-dropdown-box-shadow:var(--bs-box-shadow);--bs-dropdown-link-color:var(--bs-body-color);--bs-dropdown-link-hover-color:var(--bs-body-color);--bs-dropdown-link-hover-bg:var(--bs-tertiary-bg);--bs-dropdown-link-active-color:#fff;--bs-dropdown-link-active-bg:#0d6efd;--bs-dropdown-link-disabled-color:var(--bs-tertiary-color);--bs-dropdown-item-padding-x:1rem;--bs-dropdown-item-padding-y:0.25rem;--bs-dropdown-header-color:#6c757d;--bs-dropdown-header-padding-x:1rem;--bs-dropdown-header-padding-y:0.5rem;position:absolute;z-index:var(--bs-dropdown-zindex);display:none;min-width:var(--bs-dropdown-min-width);padding:var(--bs-dropdown-padding-y) var(--bs-dropdown-padding-x);margin:0;font-size:var(--bs-dropdown-font-size);color:var(--bs-dropdown-color);text-align:left;list-style:none;background-color:var(--bs-dropdown-bg);background-clip:padding-box;border:var(--bs-dropdown-border-width) solid var(--bs-dropdown-border-color);border-radius:var(--bs-dropdown-border-radius)}.dropdown-menu[data-bs-popper]{top:100%;left:0;margin-top:var(--bs-dropdown-spacer)}.dropdown-menu-start{--bs-position:start}.dropdown-menu-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-end{--bs-position:end}.dropdown-menu-end[data-bs-popper]{right:0;left:auto}@media (min-width:576px){.dropdown-menu-sm-start{--bs-position:start}.dropdown-menu-sm-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-sm-end{--bs-position:end}.dropdown-menu-sm-end[data-bs-popper]{right:0;left:auto}}@media (min-width:768px){.dropdown-menu-md-start{--bs-position:start}.dropdown-menu-md-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-md-end{--bs-position:end}.dropdown-menu-md-end[data-bs-popper]{right:0;left:auto}}@media (min-width:992px){.dropdown-menu-lg-start{--bs-position:start}.dropdown-menu-lg-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-lg-end{--bs-position:end}.dropdown-menu-lg-end[data-bs-popper]{right:0;left:auto}}@media (min-width:1200px){.dropdown-menu-xl-start{--bs-position:start}.dropdown-menu-xl-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-xl-end{--bs-position:end}.dropdown-menu-xl-end[data-bs-popper]{right:0;left:auto}}@media (min-width:1400px){.dropdown-menu-xxl-start{--bs-position:start}.dropdown-menu-xxl-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-xxl-end{--bs-position:end}.dropdown-menu-xxl-end[data-bs-popper]{right:0;left:auto}}.dropup .dropdown-menu[data-bs-popper]{top:auto;bottom:100%;margin-top:0;margin-bottom:var(--bs-dropdown-spacer)}.dropup .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:0;border-right:.3em solid transparent;border-bottom:.3em solid;border-left:.3em solid transparent}.dropup .dropdown-toggle:empty::after{margin-left:0}.dropend .dropdown-menu[data-bs-popper]{top:0;right:auto;left:100%;margin-top:0;margin-left:var(--bs-dropdown-spacer)}.dropend .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:0;border-bottom:.3em solid transparent;border-left:.3em solid}.dropend .dropdown-toggle:empty::after{margin-left:0}.dropend .dropdown-toggle::after{vertical-align:0}.dropstart .dropdown-menu[data-bs-popper]{top:0;right:100%;left:auto;margin-top:0;margin-right:var(--bs-dropdown-spacer)}.dropstart .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:""}.dropstart .dropdown-toggle::after{display:none}.dropstart .dropdown-toggle::before{display:inline-block;margin-right:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:.3em solid;border-bottom:.3em solid transparent}.dropstart .dropdown-toggle:empty::after{margin-left:0}.dropstart .dropdown-toggle::before{vertical-align:0}.dropdown-divider{height:0;margin:var(--bs-dropdown-divider-margin-y) 0;overflow:hidden;border-top:1px solid var(--bs-dropdown-divider-bg);opacity:1}.dropdown-item{display:block;width:100%;padding:var(--bs-dropdown-item-padding-y) var(--bs-dropdown-item-padding-x);clear:both;font-weight:400;color:var(--bs-dropdown-link-color);text-align:inherit;text-decoration:none;white-space:nowrap;background-color:transparent;border:0;border-radius:var(--bs-dropdown-item-border-radius,0)}.dropdown-item:focus,.dropdown-item:hover{color:var(--bs-dropdown-link-hover-color);background-color:var(--bs-dropdown-link-hover-bg)}.dropdown-item.active,.dropdown-item:active{color:var(--bs-dropdown-link-active-color);text-decoration:none;background-color:var(--bs-dropdown-link-active-bg)}.dropdown-item.disabled,.dropdown-item:disabled{color:var(--bs-dropdown-link-disabled-color);pointer-events:none;background-color:transparent}.dropdown-menu.show{display:block}.dropdown-header{display:block;padding:var(--bs-dropdown-header-padding-y) var(--bs-dropdown-header-padding-x);margin-bottom:0;font-size:.875rem;color:var(--bs-dropdown-header-color);white-space:nowrap}.dropdown-item-text{display:block;padding:var(--bs-dropdown-item-padding-y) var(--bs-dropdown-item-padding-x);color:var(--bs-dropdown-link-color)}.dropdown-menu-dark{--bs-dropdown-color:#dee2e6;--bs-dropdown-bg:#343a40;--bs-dropdown-border-color:var(--bs-border-color-translucent);--bs-dropdown-box-shadow: ;--bs-dropdown-link-color:#dee2e6;--bs-dropdown-link-hover-color:#fff;--bs-dropdown-divider-bg:var(--bs-border-color-translucent);--bs-dropdown-link-hover-bg:rgba(255, 255, 255, 0.15);--bs-dropdown-link-active-color:#fff;--bs-dropdown-link-active-bg:#0d6efd;--bs-dropdown-link-disabled-color:#adb5bd;--bs-dropdown-header-color:#adb5bd}.btn-group,.btn-group-vertical{position:relative;display:inline-flex;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;flex:1 1 auto}.btn-group-vertical>.btn-check:checked+.btn,.btn-group-vertical>.btn-check:focus+.btn,.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:hover,.btn-group>.btn-check:checked+.btn,.btn-group>.btn-check:focus+.btn,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus,.btn-group>.btn:hover{z-index:1}.btn-toolbar{display:flex;flex-wrap:wrap;justify-content:flex-start}.btn-toolbar .input-group{width:auto}.btn-group{border-radius:var(--bs-border-radius)}.btn-group>.btn-group:not(:first-child),.btn-group>:not(.btn-check:first-child)+.btn{margin-left:calc(var(--bs-border-width) * -1)}.btn-group>.btn-group:not(:last-child)>.btn,.btn-group>.btn.dropdown-toggle-split:first-child,.btn-group>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:not(:first-child)>.btn,.btn-group>.btn:nth-child(n+3),.btn-group>:not(.btn-check)+.btn{border-top-left-radius:0;border-bottom-left-radius:0}.dropdown-toggle-split{padding-right:.5625rem;padding-left:.5625rem}.dropdown-toggle-split::after,.dropend .dropdown-toggle-split::after,.dropup .dropdown-toggle-split::after{margin-left:0}.dropstart .dropdown-toggle-split::before{margin-right:0}.btn-group-sm>.btn+.dropdown-toggle-split,.btn-sm+.dropdown-toggle-split{padding-right:.375rem;padding-left:.375rem}.btn-group-lg>.btn+.dropdown-toggle-split,.btn-lg+.dropdown-toggle-split{padding-right:.75rem;padding-left:.75rem}.btn-group-vertical{flex-direction:column;align-items:flex-start;justify-content:center}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group{width:100%}.btn-group-vertical>.btn-group:not(:first-child),.btn-group-vertical>.btn:not(:first-child){margin-top:calc(var(--bs-border-width) * -1)}.btn-group-vertical>.btn-group:not(:last-child)>.btn,.btn-group-vertical>.btn:not(:last-child):not(.dropdown-toggle){border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:not(:first-child)>.btn,.btn-group-vertical>.btn~.btn{border-top-left-radius:0;border-top-right-radius:0}.nav{--bs-nav-link-padding-x:1rem;--bs-nav-link-padding-y:0.5rem;--bs-nav-link-font-weight: ;--bs-nav-link-color:var(--bs-link-color);--bs-nav-link-hover-color:var(--bs-link-hover-color);--bs-nav-link-disabled-color:var(--bs-secondary-color);display:flex;flex-wrap:wrap;padding-left:0;margin-bottom:0;list-style:none}.nav-link{display:block;padding:var(--bs-nav-link-padding-y) var(--bs-nav-link-padding-x);font-size:var(--bs-nav-link-font-size);font-weight:var(--bs-nav-link-font-weight);color:var(--bs-nav-link-color);text-decoration:none;background:0 0;border:0;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out}@media (prefers-reduced-motion:reduce){.nav-link{transition:none}}.nav-link:focus,.nav-link:hover{color:var(--bs-nav-link-hover-color)}.nav-link:focus-visible{outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.nav-link.disabled,.nav-link:disabled{color:var(--bs-nav-link-disabled-color);pointer-events:none;cursor:default}.nav-tabs{--bs-nav-tabs-border-width:var(--bs-border-width);--bs-nav-tabs-border-color:var(--bs-border-color);--bs-nav-tabs-border-radius:var(--bs-border-radius);--bs-nav-tabs-link-hover-border-color:var(--bs-secondary-bg) var(--bs-secondary-bg) var(--bs-border-color);--bs-nav-tabs-link-active-color:var(--bs-emphasis-color);--bs-nav-tabs-link-active-bg:var(--bs-body-bg);--bs-nav-tabs-link-active-border-color:var(--bs-border-color) var(--bs-border-color) var(--bs-body-bg);border-bottom:var(--bs-nav-tabs-border-width) solid var(--bs-nav-tabs-border-color)}.nav-tabs .nav-link{margin-bottom:calc(-1 * var(--bs-nav-tabs-border-width));border:var(--bs-nav-tabs-border-width) solid transparent;border-top-left-radius:var(--bs-nav-tabs-border-radius);border-top-right-radius:var(--bs-nav-tabs-border-radius)}.nav-tabs .nav-link:focus,.nav-tabs .nav-link:hover{isolation:isolate;border-color:var(--bs-nav-tabs-link-hover-border-color)}.nav-tabs .nav-item.show .nav-link,.nav-tabs .nav-link.active{color:var(--bs-nav-tabs-link-active-color);background-color:var(--bs-nav-tabs-link-active-bg);border-color:var(--bs-nav-tabs-link-active-border-color)}.nav-tabs .dropdown-menu{margin-top:calc(-1 * var(--bs-nav-tabs-border-width));border-top-left-radius:0;border-top-right-radius:0}.nav-pills{--bs-nav-pills-border-radius:var(--bs-border-radius);--bs-nav-pills-link-active-color:#fff;--bs-nav-pills-link-active-bg:#0d6efd}.nav-pills .nav-link{border-radius:var(--bs-nav-pills-border-radius)}.nav-pills .nav-link.active,.nav-pills .show>.nav-link{color:var(--bs-nav-pills-link-active-color);background-color:var(--bs-nav-pills-link-active-bg)}.nav-underline{--bs-nav-underline-gap:1rem;--bs-nav-underline-border-width:0.125rem;--bs-nav-underline-link-active-color:var(--bs-emphasis-color);gap:var(--bs-nav-underline-gap)}.nav-underline .nav-link{padding-right:0;padding-left:0;border-bottom:var(--bs-nav-underline-border-width) solid transparent}.nav-underline .nav-link:focus,.nav-underline .nav-link:hover{border-bottom-color:currentcolor}.nav-underline .nav-link.active,.nav-underline .show>.nav-link{font-weight:700;color:var(--bs-nav-underline-link-active-color);border-bottom-color:currentcolor}.nav-fill .nav-item,.nav-fill>.nav-link{flex:1 1 auto;text-align:center}.nav-justified .nav-item,.nav-justified>.nav-link{flex-basis:0;flex-grow:1;text-align:center}.nav-fill .nav-item .nav-link,.nav-justified .nav-item .nav-link{width:100%}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.navbar{--bs-navbar-padding-x:0;--bs-navbar-padding-y:0.5rem;--bs-navbar-color:rgba(var(--bs-emphasis-color-rgb), 0.65);--bs-navbar-hover-color:rgba(var(--bs-emphasis-color-rgb), 0.8);--bs-navbar-disabled-color:rgba(var(--bs-emphasis-color-rgb), 0.3);--bs-navbar-active-color:rgba(var(--bs-emphasis-color-rgb), 1);--bs-navbar-brand-padding-y:0.3125rem;--bs-navbar-brand-margin-end:1rem;--bs-navbar-brand-font-size:1.25rem;--bs-navbar-brand-color:rgba(var(--bs-emphasis-color-rgb), 1);--bs-navbar-brand-hover-color:rgba(var(--bs-emphasis-color-rgb), 1);--bs-navbar-nav-link-padding-x:0.5rem;--bs-navbar-toggler-padding-y:0.25rem;--bs-navbar-toggler-padding-x:0.75rem;--bs-navbar-toggler-font-size:1.25rem;--bs-navbar-toggler-icon-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%2833, 37, 41, 0.75%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e");--bs-navbar-toggler-border-color:rgba(var(--bs-emphasis-color-rgb), 0.15);--bs-navbar-toggler-border-radius:var(--bs-border-radius);--bs-navbar-toggler-focus-width:0.25rem;--bs-navbar-toggler-transition:box-shadow 0.15s ease-in-out;position:relative;display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;padding:var(--bs-navbar-padding-y) var(--bs-navbar-padding-x)}.navbar>.container,.navbar>.container-fluid,.navbar>.container-lg,.navbar>.container-md,.navbar>.container-sm,.navbar>.container-xl,.navbar>.container-xxl{display:flex;flex-wrap:inherit;align-items:center;justify-content:space-between}.navbar-brand{padding-top:var(--bs-navbar-brand-padding-y);padding-bottom:var(--bs-navbar-brand-padding-y);margin-right:var(--bs-navbar-brand-margin-end);font-size:var(--bs-navbar-brand-font-size);color:var(--bs-navbar-brand-color);text-decoration:none;white-space:nowrap}.navbar-brand:focus,.navbar-brand:hover{color:var(--bs-navbar-brand-hover-color)}.navbar-nav{--bs-nav-link-padding-x:0;--bs-nav-link-padding-y:0.5rem;--bs-nav-link-font-weight: ;--bs-nav-link-color:var(--bs-navbar-color);--bs-nav-link-hover-color:var(--bs-navbar-hover-color);--bs-nav-link-disabled-color:var(--bs-navbar-disabled-color);display:flex;flex-direction:column;padding-left:0;margin-bottom:0;list-style:none}.navbar-nav .nav-link.active,.navbar-nav .nav-link.show{color:var(--bs-navbar-active-color)}.navbar-nav .dropdown-menu{position:static}.navbar-text{padding-top:.5rem;padding-bottom:.5rem;color:var(--bs-navbar-color)}.navbar-text a,.navbar-text a:focus,.navbar-text a:hover{color:var(--bs-navbar-active-color)}.navbar-collapse{flex-basis:100%;flex-grow:1;align-items:center}.navbar-toggler{padding:var(--bs-navbar-toggler-padding-y) var(--bs-navbar-toggler-padding-x);font-size:var(--bs-navbar-toggler-font-size);line-height:1;color:var(--bs-navbar-color);background-color:transparent;border:var(--bs-border-width) solid var(--bs-navbar-toggler-border-color);border-radius:var(--bs-navbar-toggler-border-radius);transition:var(--bs-navbar-toggler-transition)}@media (prefers-reduced-motion:reduce){.navbar-toggler{transition:none}}.navbar-toggler:hover{text-decoration:none}.navbar-toggler:focus{text-decoration:none;outline:0;box-shadow:0 0 0 var(--bs-navbar-toggler-focus-width)}.navbar-toggler-icon{display:inline-block;width:1.5em;height:1.5em;vertical-align:middle;background-image:var(--bs-navbar-toggler-icon-bg);background-repeat:no-repeat;background-position:center;background-size:100%}.navbar-nav-scroll{max-height:var(--bs-scroll-height,75vh);overflow-y:auto}@media (min-width:576px){.navbar-expand-sm{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-sm .navbar-nav{flex-direction:row}.navbar-expand-sm .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-sm .navbar-nav .nav-link{padding-right:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.navbar-expand-sm .navbar-nav-scroll{overflow:visible}.navbar-expand-sm .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-sm .navbar-toggler{display:none}.navbar-expand-sm .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:transparent!important;border:0!important;transform:none!important;transition:none}.navbar-expand-sm .offcanvas .offcanvas-header{display:none}.navbar-expand-sm .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}@media (min-width:768px){.navbar-expand-md{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-md .navbar-nav{flex-direction:row}.navbar-expand-md .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-md .navbar-nav .nav-link{padding-right:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.navbar-expand-md .navbar-nav-scroll{overflow:visible}.navbar-expand-md .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-md .navbar-toggler{display:none}.navbar-expand-md .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:transparent!important;border:0!important;transform:none!important;transition:none}.navbar-expand-md .offcanvas .offcanvas-header{display:none}.navbar-expand-md .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}@media (min-width:992px){.navbar-expand-lg{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-lg .navbar-nav{flex-direction:row}.navbar-expand-lg .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-lg .navbar-nav .nav-link{padding-right:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.navbar-expand-lg .navbar-nav-scroll{overflow:visible}.navbar-expand-lg .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-lg .navbar-toggler{display:none}.navbar-expand-lg .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:transparent!important;border:0!important;transform:none!important;transition:none}.navbar-expand-lg .offcanvas .offcanvas-header{display:none}.navbar-expand-lg .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}@media (min-width:1200px){.navbar-expand-xl{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-xl .navbar-nav{flex-direction:row}.navbar-expand-xl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xl .navbar-nav .nav-link{padding-right:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.navbar-expand-xl .navbar-nav-scroll{overflow:visible}.navbar-expand-xl .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-xl .navbar-toggler{display:none}.navbar-expand-xl .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:transparent!important;border:0!important;transform:none!important;transition:none}.navbar-expand-xl .offcanvas .offcanvas-header{display:none}.navbar-expand-xl .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}@media (min-width:1400px){.navbar-expand-xxl{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-xxl .navbar-nav{flex-direction:row}.navbar-expand-xxl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xxl .navbar-nav .nav-link{padding-right:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.navbar-expand-xxl .navbar-nav-scroll{overflow:visible}.navbar-expand-xxl .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-xxl .navbar-toggler{display:none}.navbar-expand-xxl .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:transparent!important;border:0!important;transform:none!important;transition:none}.navbar-expand-xxl .offcanvas .offcanvas-header{display:none}.navbar-expand-xxl .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}.navbar-expand{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand .navbar-nav{flex-direction:row}.navbar-expand .navbar-nav .dropdown-menu{position:absolute}.navbar-expand .navbar-nav .nav-link{padding-right:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.navbar-expand .navbar-nav-scroll{overflow:visible}.navbar-expand .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand .navbar-toggler{display:none}.navbar-expand .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:transparent!important;border:0!important;transform:none!important;transition:none}.navbar-expand .offcanvas .offcanvas-header{display:none}.navbar-expand .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}.navbar-dark,.navbar[data-bs-theme=dark]{--bs-navbar-color:rgba(255, 255, 255, 0.55);--bs-navbar-hover-color:rgba(255, 255, 255, 0.75);--bs-navbar-disabled-color:rgba(255, 255, 255, 0.25);--bs-navbar-active-color:#fff;--bs-navbar-brand-color:#fff;--bs-navbar-brand-hover-color:#fff;--bs-navbar-toggler-border-color:rgba(255, 255, 255, 0.1);--bs-navbar-toggler-icon-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}[data-bs-theme=dark] .navbar-toggler-icon{--bs-navbar-toggler-icon-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.card{--bs-card-spacer-y:1rem;--bs-card-spacer-x:1rem;--bs-card-title-spacer-y:0.5rem;--bs-card-title-color: ;--bs-card-subtitle-color: ;--bs-card-border-width:var(--bs-border-width);--bs-card-border-color:var(--bs-border-color-translucent);--bs-card-border-radius:var(--bs-border-radius);--bs-card-box-shadow: ;--bs-card-inner-border-radius:calc(var(--bs-border-radius) - (var(--bs-border-width)));--bs-card-cap-padding-y:0.5rem;--bs-card-cap-padding-x:1rem;--bs-card-cap-bg:rgba(var(--bs-body-color-rgb), 0.03);--bs-card-cap-color: ;--bs-card-height: ;--bs-card-color: ;--bs-card-bg:var(--bs-body-bg);--bs-card-img-overlay-padding:1rem;--bs-card-group-margin:0.75rem;position:relative;display:flex;flex-direction:column;min-width:0;height:var(--bs-card-height);color:var(--bs-body-color);word-wrap:break-word;background-color:var(--bs-card-bg);background-clip:border-box;border:var(--bs-card-border-width) solid var(--bs-card-border-color);border-radius:var(--bs-card-border-radius)}.card>hr{margin-right:0;margin-left:0}.card>.list-group{border-top:inherit;border-bottom:inherit}.card>.list-group:first-child{border-top-width:0;border-top-left-radius:var(--bs-card-inner-border-radius);border-top-right-radius:var(--bs-card-inner-border-radius)}.card>.list-group:last-child{border-bottom-width:0;border-bottom-right-radius:var(--bs-card-inner-border-radius);border-bottom-left-radius:var(--bs-card-inner-border-radius)}.card>.card-header+.list-group,.card>.list-group+.card-footer{border-top:0}.card-body{flex:1 1 auto;padding:var(--bs-card-spacer-y) var(--bs-card-spacer-x);color:var(--bs-card-color)}.card-title{margin-bottom:var(--bs-card-title-spacer-y);color:var(--bs-card-title-color)}.card-subtitle{margin-top:calc(-.5 * var(--bs-card-title-spacer-y));margin-bottom:0;color:var(--bs-card-subtitle-color)}.card-text:last-child{margin-bottom:0}.card-link+.card-link{margin-left:var(--bs-card-spacer-x)}.card-header{padding:var(--bs-card-cap-padding-y) var(--bs-card-cap-padding-x);margin-bottom:0;color:var(--bs-card-cap-color);background-color:var(--bs-card-cap-bg);border-bottom:var(--bs-card-border-width) solid var(--bs-card-border-color)}.card-header:first-child{border-radius:var(--bs-card-inner-border-radius) var(--bs-card-inner-border-radius) 0 0}.card-footer{padding:var(--bs-card-cap-padding-y) var(--bs-card-cap-padding-x);color:var(--bs-card-cap-color);background-color:var(--bs-card-cap-bg);border-top:var(--bs-card-border-width) solid var(--bs-card-border-color)}.card-footer:last-child{border-radius:0 0 var(--bs-card-inner-border-radius) var(--bs-card-inner-border-radius)}.card-header-tabs{margin-right:calc(-.5 * var(--bs-card-cap-padding-x));margin-bottom:calc(-1 * var(--bs-card-cap-padding-y));margin-left:calc(-.5 * var(--bs-card-cap-padding-x));border-bottom:0}.card-header-tabs .nav-link.active{background-color:var(--bs-card-bg);border-bottom-color:var(--bs-card-bg)}.card-header-pills{margin-right:calc(-.5 * var(--bs-card-cap-padding-x));margin-left:calc(-.5 * var(--bs-card-cap-padding-x))}.card-img-overlay{position:absolute;top:0;right:0;bottom:0;left:0;padding:var(--bs-card-img-overlay-padding);border-radius:var(--bs-card-inner-border-radius)}.card-img,.card-img-bottom,.card-img-top{width:100%}.card-img,.card-img-top{border-top-left-radius:var(--bs-card-inner-border-radius);border-top-right-radius:var(--bs-card-inner-border-radius)}.card-img,.card-img-bottom{border-bottom-right-radius:var(--bs-card-inner-border-radius);border-bottom-left-radius:var(--bs-card-inner-border-radius)}.card-group>.card{margin-bottom:var(--bs-card-group-margin)}@media (min-width:576px){.card-group{display:flex;flex-flow:row wrap}.card-group>.card{flex:1 0 0%;margin-bottom:0}.card-group>.card+.card{margin-left:0;border-left:0}.card-group>.card:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.card-group>.card:not(:last-child) .card-header,.card-group>.card:not(:last-child) .card-img-top{border-top-right-radius:0}.card-group>.card:not(:last-child) .card-footer,.card-group>.card:not(:last-child) .card-img-bottom{border-bottom-right-radius:0}.card-group>.card:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.card-group>.card:not(:first-child) .card-header,.card-group>.card:not(:first-child) .card-img-top{border-top-left-radius:0}.card-group>.card:not(:first-child) .card-footer,.card-group>.card:not(:first-child) .card-img-bottom{border-bottom-left-radius:0}}.accordion{--bs-accordion-color:var(--bs-body-color);--bs-accordion-bg:var(--bs-body-bg);--bs-accordion-transition:color 0.15s ease-in-out,background-color 0.15s ease-in-out,border-color 0.15s ease-in-out,box-shadow 0.15s ease-in-out,border-radius 0.15s ease;--bs-accordion-border-color:var(--bs-border-color);--bs-accordion-border-width:var(--bs-border-width);--bs-accordion-border-radius:var(--bs-border-radius);--bs-accordion-inner-border-radius:calc(var(--bs-border-radius) - (var(--bs-border-width)));--bs-accordion-btn-padding-x:1.25rem;--bs-accordion-btn-padding-y:1rem;--bs-accordion-btn-color:var(--bs-body-color);--bs-accordion-btn-bg:var(--bs-accordion-bg);--bs-accordion-btn-icon:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23212529' stroke-linecap='round' stroke-linejoin='round'%3e%3cpath d='M2 5L8 11L14 5'/%3e%3c/svg%3e");--bs-accordion-btn-icon-width:1.25rem;--bs-accordion-btn-icon-transform:rotate(-180deg);--bs-accordion-btn-icon-transition:transform 0.2s ease-in-out;--bs-accordion-btn-active-icon:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23052c65' stroke-linecap='round' stroke-linejoin='round'%3e%3cpath d='M2 5L8 11L14 5'/%3e%3c/svg%3e");--bs-accordion-btn-focus-box-shadow:0 0 0 0.25rem rgba(13, 110, 253, 0.25);--bs-accordion-body-padding-x:1.25rem;--bs-accordion-body-padding-y:1rem;--bs-accordion-active-color:var(--bs-primary-text-emphasis);--bs-accordion-active-bg:var(--bs-primary-bg-subtle)}.accordion-button{position:relative;display:flex;align-items:center;width:100%;padding:var(--bs-accordion-btn-padding-y) var(--bs-accordion-btn-padding-x);font-size:1rem;color:var(--bs-accordion-btn-color);text-align:left;background-color:var(--bs-accordion-btn-bg);border:0;border-radius:0;overflow-anchor:none;transition:var(--bs-accordion-transition)}@media (prefers-reduced-motion:reduce){.accordion-button{transition:none}}.accordion-button:not(.collapsed){color:var(--bs-accordion-active-color);background-color:var(--bs-accordion-active-bg);box-shadow:inset 0 calc(-1 * var(--bs-accordion-border-width)) 0 var(--bs-accordion-border-color)}.accordion-button:not(.collapsed)::after{background-image:var(--bs-accordion-btn-active-icon);transform:var(--bs-accordion-btn-icon-transform)}.accordion-button::after{flex-shrink:0;width:var(--bs-accordion-btn-icon-width);height:var(--bs-accordion-btn-icon-width);margin-left:auto;content:"";background-image:var(--bs-accordion-btn-icon);background-repeat:no-repeat;background-size:var(--bs-accordion-btn-icon-width);transition:var(--bs-accordion-btn-icon-transition)}@media (prefers-reduced-motion:reduce){.accordion-button::after{transition:none}}.accordion-button:hover{z-index:2}.accordion-button:focus{z-index:3;outline:0;box-shadow:var(--bs-accordion-btn-focus-box-shadow)}.accordion-header{margin-bottom:0}.accordion-item{color:var(--bs-accordion-color);background-color:var(--bs-accordion-bg);border:var(--bs-accordion-border-width) solid var(--bs-accordion-border-color)}.accordion-item:first-of-type{border-top-left-radius:var(--bs-accordion-border-radius);border-top-right-radius:var(--bs-accordion-border-radius)}.accordion-item:first-of-type>.accordion-header .accordion-button{border-top-left-radius:var(--bs-accordion-inner-border-radius);border-top-right-radius:var(--bs-accordion-inner-border-radius)}.accordion-item:not(:first-of-type){border-top:0}.accordion-item:last-of-type{border-bottom-right-radius:var(--bs-accordion-border-radius);border-bottom-left-radius:var(--bs-accordion-border-radius)}.accordion-item:last-of-type>.accordion-header .accordion-button.collapsed{border-bottom-right-radius:var(--bs-accordion-inner-border-radius);border-bottom-left-radius:var(--bs-accordion-inner-border-radius)}.accordion-item:last-of-type>.accordion-collapse{border-bottom-right-radius:var(--bs-accordion-border-radius);border-bottom-left-radius:var(--bs-accordion-border-radius)}.accordion-body{padding:var(--bs-accordion-body-padding-y) var(--bs-accordion-body-padding-x)}.accordion-flush>.accordion-item{border-right:0;border-left:0;border-radius:0}.accordion-flush>.accordion-item:first-child{border-top:0}.accordion-flush>.accordion-item:last-child{border-bottom:0}.accordion-flush>.accordion-item>.accordion-header .accordion-button,.accordion-flush>.accordion-item>.accordion-header .accordion-button.collapsed{border-radius:0}.accordion-flush>.accordion-item>.accordion-collapse{border-radius:0}[data-bs-theme=dark] .accordion-button::after{--bs-accordion-btn-icon:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%236ea8fe'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e");--bs-accordion-btn-active-icon:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%236ea8fe'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e")}.breadcrumb{--bs-breadcrumb-padding-x:0;--bs-breadcrumb-padding-y:0;--bs-breadcrumb-margin-bottom:1rem;--bs-breadcrumb-bg: ;--bs-breadcrumb-border-radius: ;--bs-breadcrumb-divider-color:var(--bs-secondary-color);--bs-breadcrumb-item-padding-x:0.5rem;--bs-breadcrumb-item-active-color:var(--bs-secondary-color);display:flex;flex-wrap:wrap;padding:var(--bs-breadcrumb-padding-y) var(--bs-breadcrumb-padding-x);margin-bottom:var(--bs-breadcrumb-margin-bottom);font-size:var(--bs-breadcrumb-font-size);list-style:none;background-color:var(--bs-breadcrumb-bg);border-radius:var(--bs-breadcrumb-border-radius)}.breadcrumb-item+.breadcrumb-item{padding-left:var(--bs-breadcrumb-item-padding-x)}.breadcrumb-item+.breadcrumb-item::before{float:left;padding-right:var(--bs-breadcrumb-item-padding-x);color:var(--bs-breadcrumb-divider-color);content:var(--bs-breadcrumb-divider, "/")}.breadcrumb-item.active{color:var(--bs-breadcrumb-item-active-color)}.pagination{--bs-pagination-padding-x:0.75rem;--bs-pagination-padding-y:0.375rem;--bs-pagination-font-size:1rem;--bs-pagination-color:var(--bs-link-color);--bs-pagination-bg:var(--bs-body-bg);--bs-pagination-border-width:var(--bs-border-width);--bs-pagination-border-color:var(--bs-border-color);--bs-pagination-border-radius:var(--bs-border-radius);--bs-pagination-hover-color:var(--bs-link-hover-color);--bs-pagination-hover-bg:var(--bs-tertiary-bg);--bs-pagination-hover-border-color:var(--bs-border-color);--bs-pagination-focus-color:var(--bs-link-hover-color);--bs-pagination-focus-bg:var(--bs-secondary-bg);--bs-pagination-focus-box-shadow:0 0 0 0.25rem rgba(13, 110, 253, 0.25);--bs-pagination-active-color:#fff;--bs-pagination-active-bg:#0d6efd;--bs-pagination-active-border-color:#0d6efd;--bs-pagination-disabled-color:var(--bs-secondary-color);--bs-pagination-disabled-bg:var(--bs-secondary-bg);--bs-pagination-disabled-border-color:var(--bs-border-color);display:flex;padding-left:0;list-style:none}.page-link{position:relative;display:block;padding:var(--bs-pagination-padding-y) var(--bs-pagination-padding-x);font-size:var(--bs-pagination-font-size);color:var(--bs-pagination-color);text-decoration:none;background-color:var(--bs-pagination-bg);border:var(--bs-pagination-border-width) solid var(--bs-pagination-border-color);transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.page-link{transition:none}}.page-link:hover{z-index:2;color:var(--bs-pagination-hover-color);background-color:var(--bs-pagination-hover-bg);border-color:var(--bs-pagination-hover-border-color)}.page-link:focus{z-index:3;color:var(--bs-pagination-focus-color);background-color:var(--bs-pagination-focus-bg);outline:0;box-shadow:var(--bs-pagination-focus-box-shadow)}.active>.page-link,.page-link.active{z-index:3;color:var(--bs-pagination-active-color);background-color:var(--bs-pagination-active-bg);border-color:var(--bs-pagination-active-border-color)}.disabled>.page-link,.page-link.disabled{color:var(--bs-pagination-disabled-color);pointer-events:none;background-color:var(--bs-pagination-disabled-bg);border-color:var(--bs-pagination-disabled-border-color)}.page-item:not(:first-child) .page-link{margin-left:calc(var(--bs-border-width) * -1)}.page-item:first-child .page-link{border-top-left-radius:var(--bs-pagination-border-radius);border-bottom-left-radius:var(--bs-pagination-border-radius)}.page-item:last-child .page-link{border-top-right-radius:var(--bs-pagination-border-radius);border-bottom-right-radius:var(--bs-pagination-border-radius)}.pagination-lg{--bs-pagination-padding-x:1.5rem;--bs-pagination-padding-y:0.75rem;--bs-pagination-font-size:1.25rem;--bs-pagination-border-radius:var(--bs-border-radius-lg)}.pagination-sm{--bs-pagination-padding-x:0.5rem;--bs-pagination-padding-y:0.25rem;--bs-pagination-font-size:0.875rem;--bs-pagination-border-radius:var(--bs-border-radius-sm)}.badge{--bs-badge-padding-x:0.65em;--bs-badge-padding-y:0.35em;--bs-badge-font-size:0.75em;--bs-badge-font-weight:700;--bs-badge-color:#fff;--bs-badge-border-radius:var(--bs-border-radius);display:inline-block;padding:var(--bs-badge-padding-y) var(--bs-badge-padding-x);font-size:var(--bs-badge-font-size);font-weight:var(--bs-badge-font-weight);line-height:1;color:var(--bs-badge-color);text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:var(--bs-badge-border-radius)}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.alert{--bs-alert-bg:transparent;--bs-alert-padding-x:1rem;--bs-alert-padding-y:1rem;--bs-alert-margin-bottom:1rem;--bs-alert-color:inherit;--bs-alert-border-color:transparent;--bs-alert-border:var(--bs-border-width) solid var(--bs-alert-border-color);--bs-alert-border-radius:var(--bs-border-radius);--bs-alert-link-color:inherit;position:relative;padding:var(--bs-alert-padding-y) var(--bs-alert-padding-x);margin-bottom:var(--bs-alert-margin-bottom);color:var(--bs-alert-color);background-color:var(--bs-alert-bg);border:var(--bs-alert-border);border-radius:var(--bs-alert-border-radius)}.alert-heading{color:inherit}.alert-link{font-weight:700;color:var(--bs-alert-link-color)}.alert-dismissible{padding-right:3rem}.alert-dismissible .btn-close{position:absolute;top:0;right:0;z-index:2;padding:1.25rem 1rem}.alert-primary{--bs-alert-color:var(--bs-primary-text-emphasis);--bs-alert-bg:var(--bs-primary-bg-subtle);--bs-alert-border-color:var(--bs-primary-border-subtle);--bs-alert-link-color:var(--bs-primary-text-emphasis)}.alert-secondary{--bs-alert-color:var(--bs-secondary-text-emphasis);--bs-alert-bg:var(--bs-secondary-bg-subtle);--bs-alert-border-color:var(--bs-secondary-border-subtle);--bs-alert-link-color:var(--bs-secondary-text-emphasis)}.alert-success{--bs-alert-color:var(--bs-success-text-emphasis);--bs-alert-bg:var(--bs-success-bg-subtle);--bs-alert-border-color:var(--bs-success-border-subtle);--bs-alert-link-color:var(--bs-success-text-emphasis)}.alert-info{--bs-alert-color:var(--bs-info-text-emphasis);--bs-alert-bg:var(--bs-info-bg-subtle);--bs-alert-border-color:var(--bs-info-border-subtle);--bs-alert-link-color:var(--bs-info-text-emphasis)}.alert-warning{--bs-alert-color:var(--bs-warning-text-emphasis);--bs-alert-bg:var(--bs-warning-bg-subtle);--bs-alert-border-color:var(--bs-warning-border-subtle);--bs-alert-link-color:var(--bs-warning-text-emphasis)}.alert-danger{--bs-alert-color:var(--bs-danger-text-emphasis);--bs-alert-bg:var(--bs-danger-bg-subtle);--bs-alert-border-color:var(--bs-danger-border-subtle);--bs-alert-link-color:var(--bs-danger-text-emphasis)}.alert-light{--bs-alert-color:var(--bs-light-text-emphasis);--bs-alert-bg:var(--bs-light-bg-subtle);--bs-alert-border-color:var(--bs-light-border-subtle);--bs-alert-link-color:var(--bs-light-text-emphasis)}.alert-dark{--bs-alert-color:var(--bs-dark-text-emphasis);--bs-alert-bg:var(--bs-dark-bg-subtle);--bs-alert-border-color:var(--bs-dark-border-subtle);--bs-alert-link-color:var(--bs-dark-text-emphasis)}@keyframes progress-bar-stripes{0%{background-position-x:1rem}}.progress,.progress-stacked{--bs-progress-height:1rem;--bs-progress-font-size:0.75rem;--bs-progress-bg:var(--bs-secondary-bg);--bs-progress-border-radius:var(--bs-border-radius);--bs-progress-box-shadow:var(--bs-box-shadow-inset);--bs-progress-bar-color:#fff;--bs-progress-bar-bg:#0d6efd;--bs-progress-bar-transition:width 0.6s ease;display:flex;height:var(--bs-progress-height);overflow:hidden;font-size:var(--bs-progress-font-size);background-color:var(--bs-progress-bg);border-radius:var(--bs-progress-border-radius)}.progress-bar{display:flex;flex-direction:column;justify-content:center;overflow:hidden;color:var(--bs-progress-bar-color);text-align:center;white-space:nowrap;background-color:var(--bs-progress-bar-bg);transition:var(--bs-progress-bar-transition)}@media (prefers-reduced-motion:reduce){.progress-bar{transition:none}}.progress-bar-striped{background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-size:var(--bs-progress-height) var(--bs-progress-height)}.progress-stacked>.progress{overflow:visible}.progress-stacked>.progress>.progress-bar{width:100%}.progress-bar-animated{animation:1s linear infinite progress-bar-stripes}@media (prefers-reduced-motion:reduce){.progress-bar-animated{animation:none}}.list-group{--bs-list-group-color:var(--bs-body-color);--bs-list-group-bg:var(--bs-body-bg);--bs-list-group-border-color:var(--bs-border-color);--bs-list-group-border-width:var(--bs-border-width);--bs-list-group-border-radius:var(--bs-border-radius);--bs-list-group-item-padding-x:1rem;--bs-list-group-item-padding-y:0.5rem;--bs-list-group-action-color:var(--bs-secondary-color);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-tertiary-bg);--bs-list-group-action-active-color:var(--bs-body-color);--bs-list-group-action-active-bg:var(--bs-secondary-bg);--bs-list-group-disabled-color:var(--bs-secondary-color);--bs-list-group-disabled-bg:var(--bs-body-bg);--bs-list-group-active-color:#fff;--bs-list-group-active-bg:#0d6efd;--bs-list-group-active-border-color:#0d6efd;display:flex;flex-direction:column;padding-left:0;margin-bottom:0;border-radius:var(--bs-list-group-border-radius)}.list-group-numbered{list-style-type:none;counter-reset:section}.list-group-numbered>.list-group-item::before{content:counters(section, ".") ". ";counter-increment:section}.list-group-item-action{width:100%;color:var(--bs-list-group-action-color);text-align:inherit}.list-group-item-action:focus,.list-group-item-action:hover{z-index:1;color:var(--bs-list-group-action-hover-color);text-decoration:none;background-color:var(--bs-list-group-action-hover-bg)}.list-group-item-action:active{color:var(--bs-list-group-action-active-color);background-color:var(--bs-list-group-action-active-bg)}.list-group-item{position:relative;display:block;padding:var(--bs-list-group-item-padding-y) var(--bs-list-group-item-padding-x);color:var(--bs-list-group-color);text-decoration:none;background-color:var(--bs-list-group-bg);border:var(--bs-list-group-border-width) solid var(--bs-list-group-border-color)}.list-group-item:first-child{border-top-left-radius:inherit;border-top-right-radius:inherit}.list-group-item:last-child{border-bottom-right-radius:inherit;border-bottom-left-radius:inherit}.list-group-item.disabled,.list-group-item:disabled{color:var(--bs-list-group-disabled-color);pointer-events:none;background-color:var(--bs-list-group-disabled-bg)}.list-group-item.active{z-index:2;color:var(--bs-list-group-active-color);background-color:var(--bs-list-group-active-bg);border-color:var(--bs-list-group-active-border-color)}.list-group-item+.list-group-item{border-top-width:0}.list-group-item+.list-group-item.active{margin-top:calc(-1 * var(--bs-list-group-border-width));border-top-width:var(--bs-list-group-border-width)}.list-group-horizontal{flex-direction:row}.list-group-horizontal>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);border-bottom-left-radius:0}.list-group-horizontal>.list-group-item.active{margin-top:0}.list-group-horizontal>.list-group-item+.list-group-item{border-top-width:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal>.list-group-item+.list-group-item.active{margin-left:calc(-1 * var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}@media (min-width:576px){.list-group-horizontal-sm{flex-direction:row}.list-group-horizontal-sm>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal-sm>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);border-bottom-left-radius:0}.list-group-horizontal-sm>.list-group-item.active{margin-top:0}.list-group-horizontal-sm>.list-group-item+.list-group-item{border-top-width:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal-sm>.list-group-item+.list-group-item.active{margin-left:calc(-1 * var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}}@media (min-width:768px){.list-group-horizontal-md{flex-direction:row}.list-group-horizontal-md>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal-md>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);border-bottom-left-radius:0}.list-group-horizontal-md>.list-group-item.active{margin-top:0}.list-group-horizontal-md>.list-group-item+.list-group-item{border-top-width:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal-md>.list-group-item+.list-group-item.active{margin-left:calc(-1 * var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}}@media (min-width:992px){.list-group-horizontal-lg{flex-direction:row}.list-group-horizontal-lg>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal-lg>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);border-bottom-left-radius:0}.list-group-horizontal-lg>.list-group-item.active{margin-top:0}.list-group-horizontal-lg>.list-group-item+.list-group-item{border-top-width:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal-lg>.list-group-item+.list-group-item.active{margin-left:calc(-1 * var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}}@media (min-width:1200px){.list-group-horizontal-xl{flex-direction:row}.list-group-horizontal-xl>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal-xl>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);border-bottom-left-radius:0}.list-group-horizontal-xl>.list-group-item.active{margin-top:0}.list-group-horizontal-xl>.list-group-item+.list-group-item{border-top-width:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal-xl>.list-group-item+.list-group-item.active{margin-left:calc(-1 * var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}}@media (min-width:1400px){.list-group-horizontal-xxl{flex-direction:row}.list-group-horizontal-xxl>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal-xxl>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);border-bottom-left-radius:0}.list-group-horizontal-xxl>.list-group-item.active{margin-top:0}.list-group-horizontal-xxl>.list-group-item+.list-group-item{border-top-width:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal-xxl>.list-group-item+.list-group-item.active{margin-left:calc(-1 * var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}}.list-group-flush{border-radius:0}.list-group-flush>.list-group-item{border-width:0 0 var(--bs-list-group-border-width)}.list-group-flush>.list-group-item:last-child{border-bottom-width:0}.list-group-item-primary{--bs-list-group-color:var(--bs-primary-text-emphasis);--bs-list-group-bg:var(--bs-primary-bg-subtle);--bs-list-group-border-color:var(--bs-primary-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-primary-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-primary-border-subtle);--bs-list-group-active-color:var(--bs-primary-bg-subtle);--bs-list-group-active-bg:var(--bs-primary-text-emphasis);--bs-list-group-active-border-color:var(--bs-primary-text-emphasis)}.list-group-item-secondary{--bs-list-group-color:var(--bs-secondary-text-emphasis);--bs-list-group-bg:var(--bs-secondary-bg-subtle);--bs-list-group-border-color:var(--bs-secondary-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-secondary-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-secondary-border-subtle);--bs-list-group-active-color:var(--bs-secondary-bg-subtle);--bs-list-group-active-bg:var(--bs-secondary-text-emphasis);--bs-list-group-active-border-color:var(--bs-secondary-text-emphasis)}.list-group-item-success{--bs-list-group-color:var(--bs-success-text-emphasis);--bs-list-group-bg:var(--bs-success-bg-subtle);--bs-list-group-border-color:var(--bs-success-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-success-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-success-border-subtle);--bs-list-group-active-color:var(--bs-success-bg-subtle);--bs-list-group-active-bg:var(--bs-success-text-emphasis);--bs-list-group-active-border-color:var(--bs-success-text-emphasis)}.list-group-item-info{--bs-list-group-color:var(--bs-info-text-emphasis);--bs-list-group-bg:var(--bs-info-bg-subtle);--bs-list-group-border-color:var(--bs-info-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-info-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-info-border-subtle);--bs-list-group-active-color:var(--bs-info-bg-subtle);--bs-list-group-active-bg:var(--bs-info-text-emphasis);--bs-list-group-active-border-color:var(--bs-info-text-emphasis)}.list-group-item-warning{--bs-list-group-color:var(--bs-warning-text-emphasis);--bs-list-group-bg:var(--bs-warning-bg-subtle);--bs-list-group-border-color:var(--bs-warning-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-warning-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-warning-border-subtle);--bs-list-group-active-color:var(--bs-warning-bg-subtle);--bs-list-group-active-bg:var(--bs-warning-text-emphasis);--bs-list-group-active-border-color:var(--bs-warning-text-emphasis)}.list-group-item-danger{--bs-list-group-color:var(--bs-danger-text-emphasis);--bs-list-group-bg:var(--bs-danger-bg-subtle);--bs-list-group-border-color:var(--bs-danger-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-danger-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-danger-border-subtle);--bs-list-group-active-color:var(--bs-danger-bg-subtle);--bs-list-group-active-bg:var(--bs-danger-text-emphasis);--bs-list-group-active-border-color:var(--bs-danger-text-emphasis)}.list-group-item-light{--bs-list-group-color:var(--bs-light-text-emphasis);--bs-list-group-bg:var(--bs-light-bg-subtle);--bs-list-group-border-color:var(--bs-light-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-light-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-light-border-subtle);--bs-list-group-active-color:var(--bs-light-bg-subtle);--bs-list-group-active-bg:var(--bs-light-text-emphasis);--bs-list-group-active-border-color:var(--bs-light-text-emphasis)}.list-group-item-dark{--bs-list-group-color:var(--bs-dark-text-emphasis);--bs-list-group-bg:var(--bs-dark-bg-subtle);--bs-list-group-border-color:var(--bs-dark-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-dark-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-dark-border-subtle);--bs-list-group-active-color:var(--bs-dark-bg-subtle);--bs-list-group-active-bg:var(--bs-dark-text-emphasis);--bs-list-group-active-border-color:var(--bs-dark-text-emphasis)}.btn-close{--bs-btn-close-color:#000;--bs-btn-close-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23000'%3e%3cpath d='M.293.293a1 1 0 0 1 1.414 0L8 6.586 14.293.293a1 1 0 1 1 1.414 1.414L9.414 8l6.293 6.293a1 1 0 0 1-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 0 1-1.414-1.414L6.586 8 .293 1.707a1 1 0 0 1 0-1.414z'/%3e%3c/svg%3e");--bs-btn-close-opacity:0.5;--bs-btn-close-hover-opacity:0.75;--bs-btn-close-focus-shadow:0 0 0 0.25rem rgba(13, 110, 253, 0.25);--bs-btn-close-focus-opacity:1;--bs-btn-close-disabled-opacity:0.25;--bs-btn-close-white-filter:invert(1) grayscale(100%) brightness(200%);box-sizing:content-box;width:1em;height:1em;padding:.25em .25em;color:var(--bs-btn-close-color);background:transparent var(--bs-btn-close-bg) center/1em auto no-repeat;border:0;border-radius:.375rem;opacity:var(--bs-btn-close-opacity)}.btn-close:hover{color:var(--bs-btn-close-color);text-decoration:none;opacity:var(--bs-btn-close-hover-opacity)}.btn-close:focus{outline:0;box-shadow:var(--bs-btn-close-focus-shadow);opacity:var(--bs-btn-close-focus-opacity)}.btn-close.disabled,.btn-close:disabled{pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none;opacity:var(--bs-btn-close-disabled-opacity)}.btn-close-white{filter:var(--bs-btn-close-white-filter)}[data-bs-theme=dark] .btn-close{filter:var(--bs-btn-close-white-filter)}.toast{--bs-toast-zindex:1090;--bs-toast-padding-x:0.75rem;--bs-toast-padding-y:0.5rem;--bs-toast-spacing:1.5rem;--bs-toast-max-width:350px;--bs-toast-font-size:0.875rem;--bs-toast-color: ;--bs-toast-bg:rgba(var(--bs-body-bg-rgb), 0.85);--bs-toast-border-width:var(--bs-border-width);--bs-toast-border-color:var(--bs-border-color-translucent);--bs-toast-border-radius:var(--bs-border-radius);--bs-toast-box-shadow:var(--bs-box-shadow);--bs-toast-header-color:var(--bs-secondary-color);--bs-toast-header-bg:rgba(var(--bs-body-bg-rgb), 0.85);--bs-toast-header-border-color:var(--bs-border-color-translucent);width:var(--bs-toast-max-width);max-width:100%;font-size:var(--bs-toast-font-size);color:var(--bs-toast-color);pointer-events:auto;background-color:var(--bs-toast-bg);background-clip:padding-box;border:var(--bs-toast-border-width) solid var(--bs-toast-border-color);box-shadow:var(--bs-toast-box-shadow);border-radius:var(--bs-toast-border-radius)}.toast.showing{opacity:0}.toast:not(.show){display:none}.toast-container{--bs-toast-zindex:1090;position:absolute;z-index:var(--bs-toast-zindex);width:-webkit-max-content;width:-moz-max-content;width:max-content;max-width:100%;pointer-events:none}.toast-container>:not(:last-child){margin-bottom:var(--bs-toast-spacing)}.toast-header{display:flex;align-items:center;padding:var(--bs-toast-padding-y) var(--bs-toast-padding-x);color:var(--bs-toast-header-color);background-color:var(--bs-toast-header-bg);background-clip:padding-box;border-bottom:var(--bs-toast-border-width) solid var(--bs-toast-header-border-color);border-top-left-radius:calc(var(--bs-toast-border-radius) - var(--bs-toast-border-width));border-top-right-radius:calc(var(--bs-toast-border-radius) - var(--bs-toast-border-width))}.toast-header .btn-close{margin-right:calc(-.5 * var(--bs-toast-padding-x));margin-left:var(--bs-toast-padding-x)}.toast-body{padding:var(--bs-toast-padding-x);word-wrap:break-word}.modal{--bs-modal-zindex:1055;--bs-modal-width:500px;--bs-modal-padding:1rem;--bs-modal-margin:0.5rem;--bs-modal-color: ;--bs-modal-bg:var(--bs-body-bg);--bs-modal-border-color:var(--bs-border-color-translucent);--bs-modal-border-width:var(--bs-border-width);--bs-modal-border-radius:var(--bs-border-radius-lg);--bs-modal-box-shadow:var(--bs-box-shadow-sm);--bs-modal-inner-border-radius:calc(var(--bs-border-radius-lg) - (var(--bs-border-width)));--bs-modal-header-padding-x:1rem;--bs-modal-header-padding-y:1rem;--bs-modal-header-padding:1rem 1rem;--bs-modal-header-border-color:var(--bs-border-color);--bs-modal-header-border-width:var(--bs-border-width);--bs-modal-title-line-height:1.5;--bs-modal-footer-gap:0.5rem;--bs-modal-footer-bg: ;--bs-modal-footer-border-color:var(--bs-border-color);--bs-modal-footer-border-width:var(--bs-border-width);position:fixed;top:0;left:0;z-index:var(--bs-modal-zindex);display:none;width:100%;height:100%;overflow-x:hidden;overflow-y:auto;outline:0}.modal-dialog{position:relative;width:auto;margin:var(--bs-modal-margin);pointer-events:none}.modal.fade .modal-dialog{transition:transform .3s ease-out;transform:translate(0,-50px)}@media (prefers-reduced-motion:reduce){.modal.fade .modal-dialog{transition:none}}.modal.show .modal-dialog{transform:none}.modal.modal-static .modal-dialog{transform:scale(1.02)}.modal-dialog-scrollable{height:calc(100% - var(--bs-modal-margin) * 2)}.modal-dialog-scrollable .modal-content{max-height:100%;overflow:hidden}.modal-dialog-scrollable .modal-body{overflow-y:auto}.modal-dialog-centered{display:flex;align-items:center;min-height:calc(100% - var(--bs-modal-margin) * 2)}.modal-content{position:relative;display:flex;flex-direction:column;width:100%;color:var(--bs-modal-color);pointer-events:auto;background-color:var(--bs-modal-bg);background-clip:padding-box;border:var(--bs-modal-border-width) solid var(--bs-modal-border-color);border-radius:var(--bs-modal-border-radius);outline:0}.modal-backdrop{--bs-backdrop-zindex:1050;--bs-backdrop-bg:#000;--bs-backdrop-opacity:0.5;position:fixed;top:0;left:0;z-index:var(--bs-backdrop-zindex);width:100vw;height:100vh;background-color:var(--bs-backdrop-bg)}.modal-backdrop.fade{opacity:0}.modal-backdrop.show{opacity:var(--bs-backdrop-opacity)}.modal-header{display:flex;flex-shrink:0;align-items:center;padding:var(--bs-modal-header-padding);border-bottom:var(--bs-modal-header-border-width) solid var(--bs-modal-header-border-color);border-top-left-radius:var(--bs-modal-inner-border-radius);border-top-right-radius:var(--bs-modal-inner-border-radius)}.modal-header .btn-close{padding:calc(var(--bs-modal-header-padding-y) * .5) calc(var(--bs-modal-header-padding-x) * .5);margin:calc(-.5 * var(--bs-modal-header-padding-y)) calc(-.5 * var(--bs-modal-header-padding-x)) calc(-.5 * var(--bs-modal-header-padding-y)) auto}.modal-title{margin-bottom:0;line-height:var(--bs-modal-title-line-height)}.modal-body{position:relative;flex:1 1 auto;padding:var(--bs-modal-padding)}.modal-footer{display:flex;flex-shrink:0;flex-wrap:wrap;align-items:center;justify-content:flex-end;padding:calc(var(--bs-modal-padding) - var(--bs-modal-footer-gap) * .5);background-color:var(--bs-modal-footer-bg);border-top:var(--bs-modal-footer-border-width) solid var(--bs-modal-footer-border-color);border-bottom-right-radius:var(--bs-modal-inner-border-radius);border-bottom-left-radius:var(--bs-modal-inner-border-radius)}.modal-footer>*{margin:calc(var(--bs-modal-footer-gap) * .5)}@media (min-width:576px){.modal{--bs-modal-margin:1.75rem;--bs-modal-box-shadow:var(--bs-box-shadow)}.modal-dialog{max-width:var(--bs-modal-width);margin-right:auto;margin-left:auto}.modal-sm{--bs-modal-width:300px}}@media (min-width:992px){.modal-lg,.modal-xl{--bs-modal-width:800px}}@media (min-width:1200px){.modal-xl{--bs-modal-width:1140px}}.modal-fullscreen{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen .modal-footer,.modal-fullscreen .modal-header{border-radius:0}.modal-fullscreen .modal-body{overflow-y:auto}@media (max-width:575.98px){.modal-fullscreen-sm-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-sm-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-sm-down .modal-footer,.modal-fullscreen-sm-down .modal-header{border-radius:0}.modal-fullscreen-sm-down .modal-body{overflow-y:auto}}@media (max-width:767.98px){.modal-fullscreen-md-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-md-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-md-down .modal-footer,.modal-fullscreen-md-down .modal-header{border-radius:0}.modal-fullscreen-md-down .modal-body{overflow-y:auto}}@media (max-width:991.98px){.modal-fullscreen-lg-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-lg-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-lg-down .modal-footer,.modal-fullscreen-lg-down .modal-header{border-radius:0}.modal-fullscreen-lg-down .modal-body{overflow-y:auto}}@media (max-width:1199.98px){.modal-fullscreen-xl-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-xl-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-xl-down .modal-footer,.modal-fullscreen-xl-down .modal-header{border-radius:0}.modal-fullscreen-xl-down .modal-body{overflow-y:auto}}@media (max-width:1399.98px){.modal-fullscreen-xxl-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-xxl-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-xxl-down .modal-footer,.modal-fullscreen-xxl-down .modal-header{border-radius:0}.modal-fullscreen-xxl-down .modal-body{overflow-y:auto}}.tooltip{--bs-tooltip-zindex:1080;--bs-tooltip-max-width:200px;--bs-tooltip-padding-x:0.5rem;--bs-tooltip-padding-y:0.25rem;--bs-tooltip-margin: ;--bs-tooltip-font-size:0.875rem;--bs-tooltip-color:var(--bs-body-bg);--bs-tooltip-bg:var(--bs-emphasis-color);--bs-tooltip-border-radius:var(--bs-border-radius);--bs-tooltip-opacity:0.9;--bs-tooltip-arrow-width:0.8rem;--bs-tooltip-arrow-height:0.4rem;z-index:var(--bs-tooltip-zindex);display:block;margin:var(--bs-tooltip-margin);font-family:var(--bs-font-sans-serif);font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;white-space:normal;word-spacing:normal;line-break:auto;font-size:var(--bs-tooltip-font-size);word-wrap:break-word;opacity:0}.tooltip.show{opacity:var(--bs-tooltip-opacity)}.tooltip .tooltip-arrow{display:block;width:var(--bs-tooltip-arrow-width);height:var(--bs-tooltip-arrow-height)}.tooltip .tooltip-arrow::before{position:absolute;content:"";border-color:transparent;border-style:solid}.bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow,.bs-tooltip-top .tooltip-arrow{bottom:calc(-1 * var(--bs-tooltip-arrow-height))}.bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow::before,.bs-tooltip-top .tooltip-arrow::before{top:-1px;border-width:var(--bs-tooltip-arrow-height) calc(var(--bs-tooltip-arrow-width) * .5) 0;border-top-color:var(--bs-tooltip-bg)}.bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow,.bs-tooltip-end .tooltip-arrow{left:calc(-1 * var(--bs-tooltip-arrow-height));width:var(--bs-tooltip-arrow-height);height:var(--bs-tooltip-arrow-width)}.bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow::before,.bs-tooltip-end .tooltip-arrow::before{right:-1px;border-width:calc(var(--bs-tooltip-arrow-width) * .5) var(--bs-tooltip-arrow-height) calc(var(--bs-tooltip-arrow-width) * .5) 0;border-right-color:var(--bs-tooltip-bg)}.bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow,.bs-tooltip-bottom .tooltip-arrow{top:calc(-1 * var(--bs-tooltip-arrow-height))}.bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow::before,.bs-tooltip-bottom .tooltip-arrow::before{bottom:-1px;border-width:0 calc(var(--bs-tooltip-arrow-width) * .5) var(--bs-tooltip-arrow-height);border-bottom-color:var(--bs-tooltip-bg)}.bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow,.bs-tooltip-start .tooltip-arrow{right:calc(-1 * var(--bs-tooltip-arrow-height));width:var(--bs-tooltip-arrow-height);height:var(--bs-tooltip-arrow-width)}.bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow::before,.bs-tooltip-start .tooltip-arrow::before{left:-1px;border-width:calc(var(--bs-tooltip-arrow-width) * .5) 0 calc(var(--bs-tooltip-arrow-width) * .5) var(--bs-tooltip-arrow-height);border-left-color:var(--bs-tooltip-bg)}.tooltip-inner{max-width:var(--bs-tooltip-max-width);padding:var(--bs-tooltip-padding-y) var(--bs-tooltip-padding-x);color:var(--bs-tooltip-color);text-align:center;background-color:var(--bs-tooltip-bg);border-radius:var(--bs-tooltip-border-radius)}.popover{--bs-popover-zindex:1070;--bs-popover-max-width:276px;--bs-popover-font-size:0.875rem;--bs-popover-bg:var(--bs-body-bg);--bs-popover-border-width:var(--bs-border-width);--bs-popover-border-color:var(--bs-border-color-translucent);--bs-popover-border-radius:var(--bs-border-radius-lg);--bs-popover-inner-border-radius:calc(var(--bs-border-radius-lg) - var(--bs-border-width));--bs-popover-box-shadow:var(--bs-box-shadow);--bs-popover-header-padding-x:1rem;--bs-popover-header-padding-y:0.5rem;--bs-popover-header-font-size:1rem;--bs-popover-header-color:inherit;--bs-popover-header-bg:var(--bs-secondary-bg);--bs-popover-body-padding-x:1rem;--bs-popover-body-padding-y:1rem;--bs-popover-body-color:var(--bs-body-color);--bs-popover-arrow-width:1rem;--bs-popover-arrow-height:0.5rem;--bs-popover-arrow-border:var(--bs-popover-border-color);z-index:var(--bs-popover-zindex);display:block;max-width:var(--bs-popover-max-width);font-family:var(--bs-font-sans-serif);font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;white-space:normal;word-spacing:normal;line-break:auto;font-size:var(--bs-popover-font-size);word-wrap:break-word;background-color:var(--bs-popover-bg);background-clip:padding-box;border:var(--bs-popover-border-width) solid var(--bs-popover-border-color);border-radius:var(--bs-popover-border-radius)}.popover .popover-arrow{display:block;width:var(--bs-popover-arrow-width);height:var(--bs-popover-arrow-height)}.popover .popover-arrow::after,.popover .popover-arrow::before{position:absolute;display:block;content:"";border-color:transparent;border-style:solid;border-width:0}.bs-popover-auto[data-popper-placement^=top]>.popover-arrow,.bs-popover-top>.popover-arrow{bottom:calc(-1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width))}.bs-popover-auto[data-popper-placement^=top]>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=top]>.popover-arrow::before,.bs-popover-top>.popover-arrow::after,.bs-popover-top>.popover-arrow::before{border-width:var(--bs-popover-arrow-height) calc(var(--bs-popover-arrow-width) * .5) 0}.bs-popover-auto[data-popper-placement^=top]>.popover-arrow::before,.bs-popover-top>.popover-arrow::before{bottom:0;border-top-color:var(--bs-popover-arrow-border)}.bs-popover-auto[data-popper-placement^=top]>.popover-arrow::after,.bs-popover-top>.popover-arrow::after{bottom:var(--bs-popover-border-width);border-top-color:var(--bs-popover-bg)}.bs-popover-auto[data-popper-placement^=right]>.popover-arrow,.bs-popover-end>.popover-arrow{left:calc(-1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width));width:var(--bs-popover-arrow-height);height:var(--bs-popover-arrow-width)}.bs-popover-auto[data-popper-placement^=right]>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=right]>.popover-arrow::before,.bs-popover-end>.popover-arrow::after,.bs-popover-end>.popover-arrow::before{border-width:calc(var(--bs-popover-arrow-width) * .5) var(--bs-popover-arrow-height) calc(var(--bs-popover-arrow-width) * .5) 0}.bs-popover-auto[data-popper-placement^=right]>.popover-arrow::before,.bs-popover-end>.popover-arrow::before{left:0;border-right-color:var(--bs-popover-arrow-border)}.bs-popover-auto[data-popper-placement^=right]>.popover-arrow::after,.bs-popover-end>.popover-arrow::after{left:var(--bs-popover-border-width);border-right-color:var(--bs-popover-bg)}.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow,.bs-popover-bottom>.popover-arrow{top:calc(-1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width))}.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow::before,.bs-popover-bottom>.popover-arrow::after,.bs-popover-bottom>.popover-arrow::before{border-width:0 calc(var(--bs-popover-arrow-width) * .5) var(--bs-popover-arrow-height)}.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow::before,.bs-popover-bottom>.popover-arrow::before{top:0;border-bottom-color:var(--bs-popover-arrow-border)}.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow::after,.bs-popover-bottom>.popover-arrow::after{top:var(--bs-popover-border-width);border-bottom-color:var(--bs-popover-bg)}.bs-popover-auto[data-popper-placement^=bottom] .popover-header::before,.bs-popover-bottom .popover-header::before{position:absolute;top:0;left:50%;display:block;width:var(--bs-popover-arrow-width);margin-left:calc(-.5 * var(--bs-popover-arrow-width));content:"";border-bottom:var(--bs-popover-border-width) solid var(--bs-popover-header-bg)}.bs-popover-auto[data-popper-placement^=left]>.popover-arrow,.bs-popover-start>.popover-arrow{right:calc(-1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width));width:var(--bs-popover-arrow-height);height:var(--bs-popover-arrow-width)}.bs-popover-auto[data-popper-placement^=left]>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=left]>.popover-arrow::before,.bs-popover-start>.popover-arrow::after,.bs-popover-start>.popover-arrow::before{border-width:calc(var(--bs-popover-arrow-width) * .5) 0 calc(var(--bs-popover-arrow-width) * .5) var(--bs-popover-arrow-height)}.bs-popover-auto[data-popper-placement^=left]>.popover-arrow::before,.bs-popover-start>.popover-arrow::before{right:0;border-left-color:var(--bs-popover-arrow-border)}.bs-popover-auto[data-popper-placement^=left]>.popover-arrow::after,.bs-popover-start>.popover-arrow::after{right:var(--bs-popover-border-width);border-left-color:var(--bs-popover-bg)}.popover-header{padding:var(--bs-popover-header-padding-y) var(--bs-popover-header-padding-x);margin-bottom:0;font-size:var(--bs-popover-header-font-size);color:var(--bs-popover-header-color);background-color:var(--bs-popover-header-bg);border-bottom:var(--bs-popover-border-width) solid var(--bs-popover-border-color);border-top-left-radius:var(--bs-popover-inner-border-radius);border-top-right-radius:var(--bs-popover-inner-border-radius)}.popover-header:empty{display:none}.popover-body{padding:var(--bs-popover-body-padding-y) var(--bs-popover-body-padding-x);color:var(--bs-popover-body-color)}.carousel{position:relative}.carousel.pointer-event{touch-action:pan-y}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner::after{display:block;clear:both;content:""}.carousel-item{position:relative;display:none;float:left;width:100%;margin-right:-100%;-webkit-backface-visibility:hidden;backface-visibility:hidden;transition:transform .6s ease-in-out}@media (prefers-reduced-motion:reduce){.carousel-item{transition:none}}.carousel-item-next,.carousel-item-prev,.carousel-item.active{display:block}.active.carousel-item-end,.carousel-item-next:not(.carousel-item-start){transform:translateX(100%)}.active.carousel-item-start,.carousel-item-prev:not(.carousel-item-end){transform:translateX(-100%)}.carousel-fade .carousel-item{opacity:0;transition-property:opacity;transform:none}.carousel-fade .carousel-item-next.carousel-item-start,.carousel-fade .carousel-item-prev.carousel-item-end,.carousel-fade .carousel-item.active{z-index:1;opacity:1}.carousel-fade .active.carousel-item-end,.carousel-fade .active.carousel-item-start{z-index:0;opacity:0;transition:opacity 0s .6s}@media (prefers-reduced-motion:reduce){.carousel-fade .active.carousel-item-end,.carousel-fade .active.carousel-item-start{transition:none}}.carousel-control-next,.carousel-control-prev{position:absolute;top:0;bottom:0;z-index:1;display:flex;align-items:center;justify-content:center;width:15%;padding:0;color:#fff;text-align:center;background:0 0;border:0;opacity:.5;transition:opacity .15s ease}@media (prefers-reduced-motion:reduce){.carousel-control-next,.carousel-control-prev{transition:none}}.carousel-control-next:focus,.carousel-control-next:hover,.carousel-control-prev:focus,.carousel-control-prev:hover{color:#fff;text-decoration:none;outline:0;opacity:.9}.carousel-control-prev{left:0}.carousel-control-next{right:0}.carousel-control-next-icon,.carousel-control-prev-icon{display:inline-block;width:2rem;height:2rem;background-repeat:no-repeat;background-position:50%;background-size:100% 100%}.carousel-control-prev-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M11.354 1.646a.5.5 0 0 1 0 .708L5.707 8l5.647 5.646a.5.5 0 0 1-.708.708l-6-6a.5.5 0 0 1 0-.708l6-6a.5.5 0 0 1 .708 0z'/%3e%3c/svg%3e")}.carousel-control-next-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e")}.carousel-indicators{position:absolute;right:0;bottom:0;left:0;z-index:2;display:flex;justify-content:center;padding:0;margin-right:15%;margin-bottom:1rem;margin-left:15%}.carousel-indicators [data-bs-target]{box-sizing:content-box;flex:0 1 auto;width:30px;height:3px;padding:0;margin-right:3px;margin-left:3px;text-indent:-999px;cursor:pointer;background-color:#fff;background-clip:padding-box;border:0;border-top:10px solid transparent;border-bottom:10px solid transparent;opacity:.5;transition:opacity .6s ease}@media (prefers-reduced-motion:reduce){.carousel-indicators [data-bs-target]{transition:none}}.carousel-indicators .active{opacity:1}.carousel-caption{position:absolute;right:15%;bottom:1.25rem;left:15%;padding-top:1.25rem;padding-bottom:1.25rem;color:#fff;text-align:center}.carousel-dark .carousel-control-next-icon,.carousel-dark .carousel-control-prev-icon{filter:invert(1) grayscale(100)}.carousel-dark .carousel-indicators [data-bs-target]{background-color:#000}.carousel-dark .carousel-caption{color:#000}[data-bs-theme=dark] .carousel .carousel-control-next-icon,[data-bs-theme=dark] .carousel .carousel-control-prev-icon,[data-bs-theme=dark].carousel .carousel-control-next-icon,[data-bs-theme=dark].carousel .carousel-control-prev-icon{filter:invert(1) grayscale(100)}[data-bs-theme=dark] .carousel .carousel-indicators [data-bs-target],[data-bs-theme=dark].carousel .carousel-indicators [data-bs-target]{background-color:#000}[data-bs-theme=dark] .carousel .carousel-caption,[data-bs-theme=dark].carousel .carousel-caption{color:#000}.spinner-border,.spinner-grow{display:inline-block;width:var(--bs-spinner-width);height:var(--bs-spinner-height);vertical-align:var(--bs-spinner-vertical-align);border-radius:50%;animation:var(--bs-spinner-animation-speed) linear infinite var(--bs-spinner-animation-name)}@keyframes spinner-border{to{transform:rotate(360deg)}}.spinner-border{--bs-spinner-width:2rem;--bs-spinner-height:2rem;--bs-spinner-vertical-align:-0.125em;--bs-spinner-border-width:0.25em;--bs-spinner-animation-speed:0.75s;--bs-spinner-animation-name:spinner-border;border:var(--bs-spinner-border-width) solid currentcolor;border-right-color:transparent}.spinner-border-sm{--bs-spinner-width:1rem;--bs-spinner-height:1rem;--bs-spinner-border-width:0.2em}@keyframes spinner-grow{0%{transform:scale(0)}50%{opacity:1;transform:none}}.spinner-grow{--bs-spinner-width:2rem;--bs-spinner-height:2rem;--bs-spinner-vertical-align:-0.125em;--bs-spinner-animation-speed:0.75s;--bs-spinner-animation-name:spinner-grow;background-color:currentcolor;opacity:0}.spinner-grow-sm{--bs-spinner-width:1rem;--bs-spinner-height:1rem}@media (prefers-reduced-motion:reduce){.spinner-border,.spinner-grow{--bs-spinner-animation-speed:1.5s}}.offcanvas,.offcanvas-lg,.offcanvas-md,.offcanvas-sm,.offcanvas-xl,.offcanvas-xxl{--bs-offcanvas-zindex:1045;--bs-offcanvas-width:400px;--bs-offcanvas-height:30vh;--bs-offcanvas-padding-x:1rem;--bs-offcanvas-padding-y:1rem;--bs-offcanvas-color:var(--bs-body-color);--bs-offcanvas-bg:var(--bs-body-bg);--bs-offcanvas-border-width:var(--bs-border-width);--bs-offcanvas-border-color:var(--bs-border-color-translucent);--bs-offcanvas-box-shadow:var(--bs-box-shadow-sm);--bs-offcanvas-transition:transform 0.3s ease-in-out;--bs-offcanvas-title-line-height:1.5}@media (max-width:575.98px){.offcanvas-sm{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;transition:var(--bs-offcanvas-transition)}}@media (max-width:575.98px) and (prefers-reduced-motion:reduce){.offcanvas-sm{transition:none}}@media (max-width:575.98px){.offcanvas-sm.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}.offcanvas-sm.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(100%)}.offcanvas-sm.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}.offcanvas-sm.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(100%)}.offcanvas-sm.show:not(.hiding),.offcanvas-sm.showing{transform:none}.offcanvas-sm.hiding,.offcanvas-sm.show,.offcanvas-sm.showing{visibility:visible}}@media (min-width:576px){.offcanvas-sm{--bs-offcanvas-height:auto;--bs-offcanvas-border-width:0;background-color:transparent!important}.offcanvas-sm .offcanvas-header{display:none}.offcanvas-sm .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;background-color:transparent!important}}@media (max-width:767.98px){.offcanvas-md{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;transition:var(--bs-offcanvas-transition)}}@media (max-width:767.98px) and (prefers-reduced-motion:reduce){.offcanvas-md{transition:none}}@media (max-width:767.98px){.offcanvas-md.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}.offcanvas-md.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(100%)}.offcanvas-md.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}.offcanvas-md.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(100%)}.offcanvas-md.show:not(.hiding),.offcanvas-md.showing{transform:none}.offcanvas-md.hiding,.offcanvas-md.show,.offcanvas-md.showing{visibility:visible}}@media (min-width:768px){.offcanvas-md{--bs-offcanvas-height:auto;--bs-offcanvas-border-width:0;background-color:transparent!important}.offcanvas-md .offcanvas-header{display:none}.offcanvas-md .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;background-color:transparent!important}}@media (max-width:991.98px){.offcanvas-lg{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;transition:var(--bs-offcanvas-transition)}}@media (max-width:991.98px) and (prefers-reduced-motion:reduce){.offcanvas-lg{transition:none}}@media (max-width:991.98px){.offcanvas-lg.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}.offcanvas-lg.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(100%)}.offcanvas-lg.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}.offcanvas-lg.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(100%)}.offcanvas-lg.show:not(.hiding),.offcanvas-lg.showing{transform:none}.offcanvas-lg.hiding,.offcanvas-lg.show,.offcanvas-lg.showing{visibility:visible}}@media (min-width:992px){.offcanvas-lg{--bs-offcanvas-height:auto;--bs-offcanvas-border-width:0;background-color:transparent!important}.offcanvas-lg .offcanvas-header{display:none}.offcanvas-lg .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;background-color:transparent!important}}@media (max-width:1199.98px){.offcanvas-xl{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;transition:var(--bs-offcanvas-transition)}}@media (max-width:1199.98px) and (prefers-reduced-motion:reduce){.offcanvas-xl{transition:none}}@media (max-width:1199.98px){.offcanvas-xl.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}.offcanvas-xl.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(100%)}.offcanvas-xl.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}.offcanvas-xl.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(100%)}.offcanvas-xl.show:not(.hiding),.offcanvas-xl.showing{transform:none}.offcanvas-xl.hiding,.offcanvas-xl.show,.offcanvas-xl.showing{visibility:visible}}@media (min-width:1200px){.offcanvas-xl{--bs-offcanvas-height:auto;--bs-offcanvas-border-width:0;background-color:transparent!important}.offcanvas-xl .offcanvas-header{display:none}.offcanvas-xl .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;background-color:transparent!important}}@media (max-width:1399.98px){.offcanvas-xxl{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;transition:var(--bs-offcanvas-transition)}}@media (max-width:1399.98px) and (prefers-reduced-motion:reduce){.offcanvas-xxl{transition:none}}@media (max-width:1399.98px){.offcanvas-xxl.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}.offcanvas-xxl.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(100%)}.offcanvas-xxl.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}.offcanvas-xxl.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(100%)}.offcanvas-xxl.show:not(.hiding),.offcanvas-xxl.showing{transform:none}.offcanvas-xxl.hiding,.offcanvas-xxl.show,.offcanvas-xxl.showing{visibility:visible}}@media (min-width:1400px){.offcanvas-xxl{--bs-offcanvas-height:auto;--bs-offcanvas-border-width:0;background-color:transparent!important}.offcanvas-xxl .offcanvas-header{display:none}.offcanvas-xxl .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;background-color:transparent!important}}.offcanvas{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;transition:var(--bs-offcanvas-transition)}@media (prefers-reduced-motion:reduce){.offcanvas{transition:none}}.offcanvas.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}.offcanvas.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(100%)}.offcanvas.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}.offcanvas.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(100%)}.offcanvas.show:not(.hiding),.offcanvas.showing{transform:none}.offcanvas.hiding,.offcanvas.show,.offcanvas.showing{visibility:visible}.offcanvas-backdrop{position:fixed;top:0;left:0;z-index:1040;width:100vw;height:100vh;background-color:#000}.offcanvas-backdrop.fade{opacity:0}.offcanvas-backdrop.show{opacity:.5}.offcanvas-header{display:flex;align-items:center;padding:var(--bs-offcanvas-padding-y) var(--bs-offcanvas-padding-x)}.offcanvas-header .btn-close{padding:calc(var(--bs-offcanvas-padding-y) * .5) calc(var(--bs-offcanvas-padding-x) * .5);margin:calc(-.5 * var(--bs-offcanvas-padding-y)) calc(-.5 * var(--bs-offcanvas-padding-x)) calc(-.5 * var(--bs-offcanvas-padding-y)) auto}.offcanvas-title{margin-bottom:0;line-height:var(--bs-offcanvas-title-line-height)}.offcanvas-body{flex-grow:1;padding:var(--bs-offcanvas-padding-y) var(--bs-offcanvas-padding-x);overflow-y:auto}.placeholder{display:inline-block;min-height:1em;vertical-align:middle;cursor:wait;background-color:currentcolor;opacity:.5}.placeholder.btn::before{display:inline-block;content:""}.placeholder-xs{min-height:.6em}.placeholder-sm{min-height:.8em}.placeholder-lg{min-height:1.2em}.placeholder-glow .placeholder{animation:placeholder-glow 2s ease-in-out infinite}@keyframes placeholder-glow{50%{opacity:.2}}.placeholder-wave{-webkit-mask-image:linear-gradient(130deg,#000 55%,rgba(0,0,0,0.8) 75%,#000 95%);mask-image:linear-gradient(130deg,#000 55%,rgba(0,0,0,0.8) 75%,#000 95%);-webkit-mask-size:200% 100%;mask-size:200% 100%;animation:placeholder-wave 2s linear infinite}@keyframes placeholder-wave{100%{-webkit-mask-position:-200% 0%;mask-position:-200% 0%}}.clearfix::after{display:block;clear:both;content:""}.text-bg-primary{color:#fff!important;background-color:RGBA(var(--bs-primary-rgb),var(--bs-bg-opacity,1))!important}.text-bg-secondary{color:#fff!important;background-color:RGBA(var(--bs-secondary-rgb),var(--bs-bg-opacity,1))!important}.text-bg-success{color:#fff!important;background-color:RGBA(var(--bs-success-rgb),var(--bs-bg-opacity,1))!important}.text-bg-info{color:#000!important;background-color:RGBA(var(--bs-info-rgb),var(--bs-bg-opacity,1))!important}.text-bg-warning{color:#000!important;background-color:RGBA(var(--bs-warning-rgb),var(--bs-bg-opacity,1))!important}.text-bg-danger{color:#fff!important;background-color:RGBA(var(--bs-danger-rgb),var(--bs-bg-opacity,1))!important}.text-bg-light{color:#000!important;background-color:RGBA(var(--bs-light-rgb),var(--bs-bg-opacity,1))!important}.text-bg-dark{color:#fff!important;background-color:RGBA(var(--bs-dark-rgb),var(--bs-bg-opacity,1))!important}.link-primary{color:RGBA(var(--bs-primary-rgb),var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(var(--bs-primary-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(var(--bs-primary-rgb),var(--bs-link-underline-opacity,1))!important}.link-primary:focus,.link-primary:hover{color:RGBA(10,88,202,var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(10,88,202,var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(10,88,202,var(--bs-link-underline-opacity,1))!important}.link-secondary{color:RGBA(var(--bs-secondary-rgb),var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(var(--bs-secondary-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(var(--bs-secondary-rgb),var(--bs-link-underline-opacity,1))!important}.link-secondary:focus,.link-secondary:hover{color:RGBA(86,94,100,var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(86,94,100,var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(86,94,100,var(--bs-link-underline-opacity,1))!important}.link-success{color:RGBA(var(--bs-success-rgb),var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(var(--bs-success-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(var(--bs-success-rgb),var(--bs-link-underline-opacity,1))!important}.link-success:focus,.link-success:hover{color:RGBA(20,108,67,var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(20,108,67,var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(20,108,67,var(--bs-link-underline-opacity,1))!important}.link-info{color:RGBA(var(--bs-info-rgb),var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(var(--bs-info-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(var(--bs-info-rgb),var(--bs-link-underline-opacity,1))!important}.link-info:focus,.link-info:hover{color:RGBA(61,213,243,var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(61,213,243,var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(61,213,243,var(--bs-link-underline-opacity,1))!important}.link-warning{color:RGBA(var(--bs-warning-rgb),var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(var(--bs-warning-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(var(--bs-warning-rgb),var(--bs-link-underline-opacity,1))!important}.link-warning:focus,.link-warning:hover{color:RGBA(255,205,57,var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(255,205,57,var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(255,205,57,var(--bs-link-underline-opacity,1))!important}.link-danger{color:RGBA(var(--bs-danger-rgb),var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(var(--bs-danger-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(var(--bs-danger-rgb),var(--bs-link-underline-opacity,1))!important}.link-danger:focus,.link-danger:hover{color:RGBA(176,42,55,var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(176,42,55,var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(176,42,55,var(--bs-link-underline-opacity,1))!important}.link-light{color:RGBA(var(--bs-light-rgb),var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(var(--bs-light-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(var(--bs-light-rgb),var(--bs-link-underline-opacity,1))!important}.link-light:focus,.link-light:hover{color:RGBA(249,250,251,var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(249,250,251,var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(249,250,251,var(--bs-link-underline-opacity,1))!important}.link-dark{color:RGBA(var(--bs-dark-rgb),var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(var(--bs-dark-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(var(--bs-dark-rgb),var(--bs-link-underline-opacity,1))!important}.link-dark:focus,.link-dark:hover{color:RGBA(26,30,33,var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(26,30,33,var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(26,30,33,var(--bs-link-underline-opacity,1))!important}.link-body-emphasis{color:RGBA(var(--bs-emphasis-color-rgb),var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(var(--bs-emphasis-color-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(var(--bs-emphasis-color-rgb),var(--bs-link-underline-opacity,1))!important}.link-body-emphasis:focus,.link-body-emphasis:hover{color:RGBA(var(--bs-emphasis-color-rgb),var(--bs-link-opacity,.75))!important;-webkit-text-decoration-color:RGBA(var(--bs-emphasis-color-rgb),var(--bs-link-underline-opacity,0.75))!important;text-decoration-color:RGBA(var(--bs-emphasis-color-rgb),var(--bs-link-underline-opacity,0.75))!important}.focus-ring:focus{outline:0;box-shadow:var(--bs-focus-ring-x,0) var(--bs-focus-ring-y,0) var(--bs-focus-ring-blur,0) var(--bs-focus-ring-width) var(--bs-focus-ring-color)}.icon-link{display:inline-flex;gap:.375rem;align-items:center;-webkit-text-decoration-color:rgba(var(--bs-link-color-rgb),var(--bs-link-opacity,0.5));text-decoration-color:rgba(var(--bs-link-color-rgb),var(--bs-link-opacity,0.5));text-underline-offset:0.25em;-webkit-backface-visibility:hidden;backface-visibility:hidden}.icon-link>.bi{flex-shrink:0;width:1em;height:1em;fill:currentcolor;transition:.2s ease-in-out transform}@media (prefers-reduced-motion:reduce){.icon-link>.bi{transition:none}}.icon-link-hover:focus-visible>.bi,.icon-link-hover:hover>.bi{transform:var(--bs-icon-link-transform,translate3d(.25em,0,0))}.ratio{position:relative;width:100%}.ratio::before{display:block;padding-top:var(--bs-aspect-ratio);content:""}.ratio>*{position:absolute;top:0;left:0;width:100%;height:100%}.ratio-1x1{--bs-aspect-ratio:100%}.ratio-4x3{--bs-aspect-ratio:75%}.ratio-16x9{--bs-aspect-ratio:56.25%}.ratio-21x9{--bs-aspect-ratio:42.8571428571%}.fixed-top{position:fixed;top:0;right:0;left:0;z-index:1030}.fixed-bottom{position:fixed;right:0;bottom:0;left:0;z-index:1030}.sticky-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}.sticky-bottom{position:-webkit-sticky;position:sticky;bottom:0;z-index:1020}@media (min-width:576px){.sticky-sm-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}.sticky-sm-bottom{position:-webkit-sticky;position:sticky;bottom:0;z-index:1020}}@media (min-width:768px){.sticky-md-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}.sticky-md-bottom{position:-webkit-sticky;position:sticky;bottom:0;z-index:1020}}@media (min-width:992px){.sticky-lg-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}.sticky-lg-bottom{position:-webkit-sticky;position:sticky;bottom:0;z-index:1020}}@media (min-width:1200px){.sticky-xl-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}.sticky-xl-bottom{position:-webkit-sticky;position:sticky;bottom:0;z-index:1020}}@media (min-width:1400px){.sticky-xxl-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}.sticky-xxl-bottom{position:-webkit-sticky;position:sticky;bottom:0;z-index:1020}}.hstack{display:flex;flex-direction:row;align-items:center;align-self:stretch}.vstack{display:flex;flex:1 1 auto;flex-direction:column;align-self:stretch}.visually-hidden,.visually-hidden-focusable:not(:focus):not(:focus-within){width:1px!important;height:1px!important;padding:0!important;margin:-1px!important;overflow:hidden!important;clip:rect(0,0,0,0)!important;white-space:nowrap!important;border:0!important}.visually-hidden-focusable:not(:focus):not(:focus-within):not(caption),.visually-hidden:not(caption){position:absolute!important}.stretched-link::after{position:absolute;top:0;right:0;bottom:0;left:0;z-index:1;content:""}.text-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.vr{display:inline-block;align-self:stretch;width:var(--bs-border-width);min-height:1em;background-color:currentcolor;opacity:.25}.align-baseline{vertical-align:baseline!important}.align-top{vertical-align:top!important}.align-middle{vertical-align:middle!important}.align-bottom{vertical-align:bottom!important}.align-text-bottom{vertical-align:text-bottom!important}.align-text-top{vertical-align:text-top!important}.float-start{float:left!important}.float-end{float:right!important}.float-none{float:none!important}.object-fit-contain{-o-object-fit:contain!important;object-fit:contain!important}.object-fit-cover{-o-object-fit:cover!important;object-fit:cover!important}.object-fit-fill{-o-object-fit:fill!important;object-fit:fill!important}.object-fit-scale{-o-object-fit:scale-down!important;object-fit:scale-down!important}.object-fit-none{-o-object-fit:none!important;object-fit:none!important}.opacity-0{opacity:0!important}.opacity-25{opacity:.25!important}.opacity-50{opacity:.5!important}.opacity-75{opacity:.75!important}.opacity-100{opacity:1!important}.overflow-auto{overflow:auto!important}.overflow-hidden{overflow:hidden!important}.overflow-visible{overflow:visible!important}.overflow-scroll{overflow:scroll!important}.overflow-x-auto{overflow-x:auto!important}.overflow-x-hidden{overflow-x:hidden!important}.overflow-x-visible{overflow-x:visible!important}.overflow-x-scroll{overflow-x:scroll!important}.overflow-y-auto{overflow-y:auto!important}.overflow-y-hidden{overflow-y:hidden!important}.overflow-y-visible{overflow-y:visible!important}.overflow-y-scroll{overflow-y:scroll!important}.d-inline{display:inline!important}.d-inline-block{display:inline-block!important}.d-block{display:block!important}.d-grid{display:grid!important}.d-inline-grid{display:inline-grid!important}.d-table{display:table!important}.d-table-row{display:table-row!important}.d-table-cell{display:table-cell!important}.d-flex{display:flex!important}.d-inline-flex{display:inline-flex!important}.d-none{display:none!important}.shadow{box-shadow:var(--bs-box-shadow)!important}.shadow-sm{box-shadow:var(--bs-box-shadow-sm)!important}.shadow-lg{box-shadow:var(--bs-box-shadow-lg)!important}.shadow-none{box-shadow:none!important}.focus-ring-primary{--bs-focus-ring-color:rgba(var(--bs-primary-rgb), var(--bs-focus-ring-opacity))}.focus-ring-secondary{--bs-focus-ring-color:rgba(var(--bs-secondary-rgb), var(--bs-focus-ring-opacity))}.focus-ring-success{--bs-focus-ring-color:rgba(var(--bs-success-rgb), var(--bs-focus-ring-opacity))}.focus-ring-info{--bs-focus-ring-color:rgba(var(--bs-info-rgb), var(--bs-focus-ring-opacity))}.focus-ring-warning{--bs-focus-ring-color:rgba(var(--bs-warning-rgb), var(--bs-focus-ring-opacity))}.focus-ring-danger{--bs-focus-ring-color:rgba(var(--bs-danger-rgb), var(--bs-focus-ring-opacity))}.focus-ring-light{--bs-focus-ring-color:rgba(var(--bs-light-rgb), var(--bs-focus-ring-opacity))}.focus-ring-dark{--bs-focus-ring-color:rgba(var(--bs-dark-rgb), var(--bs-focus-ring-opacity))}.position-static{position:static!important}.position-relative{position:relative!important}.position-absolute{position:absolute!important}.position-fixed{position:fixed!important}.position-sticky{position:-webkit-sticky!important;position:sticky!important}.top-0{top:0!important}.top-50{top:50%!important}.top-100{top:100%!important}.bottom-0{bottom:0!important}.bottom-50{bottom:50%!important}.bottom-100{bottom:100%!important}.start-0{left:0!important}.start-50{left:50%!important}.start-100{left:100%!important}.end-0{right:0!important}.end-50{right:50%!important}.end-100{right:100%!important}.translate-middle{transform:translate(-50%,-50%)!important}.translate-middle-x{transform:translateX(-50%)!important}.translate-middle-y{transform:translateY(-50%)!important}.border{border:var(--bs-border-width) var(--bs-border-style) var(--bs-border-color)!important}.border-0{border:0!important}.border-top{border-top:var(--bs-border-width) var(--bs-border-style) var(--bs-border-color)!important}.border-top-0{border-top:0!important}.border-end{border-right:var(--bs-border-width) var(--bs-border-style) var(--bs-border-color)!important}.border-end-0{border-right:0!important}.border-bottom{border-bottom:var(--bs-border-width) var(--bs-border-style) var(--bs-border-color)!important}.border-bottom-0{border-bottom:0!important}.border-start{border-left:var(--bs-border-width) var(--bs-border-style) var(--bs-border-color)!important}.border-start-0{border-left:0!important}.border-primary{--bs-border-opacity:1;border-color:rgba(var(--bs-primary-rgb),var(--bs-border-opacity))!important}.border-secondary{--bs-border-opacity:1;border-color:rgba(var(--bs-secondary-rgb),var(--bs-border-opacity))!important}.border-success{--bs-border-opacity:1;border-color:rgba(var(--bs-success-rgb),var(--bs-border-opacity))!important}.border-info{--bs-border-opacity:1;border-color:rgba(var(--bs-info-rgb),var(--bs-border-opacity))!important}.border-warning{--bs-border-opacity:1;border-color:rgba(var(--bs-warning-rgb),var(--bs-border-opacity))!important}.border-danger{--bs-border-opacity:1;border-color:rgba(var(--bs-danger-rgb),var(--bs-border-opacity))!important}.border-light{--bs-border-opacity:1;border-color:rgba(var(--bs-light-rgb),var(--bs-border-opacity))!important}.border-dark{--bs-border-opacity:1;border-color:rgba(var(--bs-dark-rgb),var(--bs-border-opacity))!important}.border-black{--bs-border-opacity:1;border-color:rgba(var(--bs-black-rgb),var(--bs-border-opacity))!important}.border-white{--bs-border-opacity:1;border-color:rgba(var(--bs-white-rgb),var(--bs-border-opacity))!important}.border-primary-subtle{border-color:var(--bs-primary-border-subtle)!important}.border-secondary-subtle{border-color:var(--bs-secondary-border-subtle)!important}.border-success-subtle{border-color:var(--bs-success-border-subtle)!important}.border-info-subtle{border-color:var(--bs-info-border-subtle)!important}.border-warning-subtle{border-color:var(--bs-warning-border-subtle)!important}.border-danger-subtle{border-color:var(--bs-danger-border-subtle)!important}.border-light-subtle{border-color:var(--bs-light-border-subtle)!important}.border-dark-subtle{border-color:var(--bs-dark-border-subtle)!important}.border-1{border-width:1px!important}.border-2{border-width:2px!important}.border-3{border-width:3px!important}.border-4{border-width:4px!important}.border-5{border-width:5px!important}.border-opacity-10{--bs-border-opacity:0.1}.border-opacity-25{--bs-border-opacity:0.25}.border-opacity-50{--bs-border-opacity:0.5}.border-opacity-75{--bs-border-opacity:0.75}.border-opacity-100{--bs-border-opacity:1}.w-25{width:25%!important}.w-50{width:50%!important}.w-75{width:75%!important}.w-100{width:100%!important}.w-auto{width:auto!important}.mw-100{max-width:100%!important}.vw-100{width:100vw!important}.min-vw-100{min-width:100vw!important}.h-25{height:25%!important}.h-50{height:50%!important}.h-75{height:75%!important}.h-100{height:100%!important}.h-auto{height:auto!important}.mh-100{max-height:100%!important}.vh-100{height:100vh!important}.min-vh-100{min-height:100vh!important}.flex-fill{flex:1 1 auto!important}.flex-row{flex-direction:row!important}.flex-column{flex-direction:column!important}.flex-row-reverse{flex-direction:row-reverse!important}.flex-column-reverse{flex-direction:column-reverse!important}.flex-grow-0{flex-grow:0!important}.flex-grow-1{flex-grow:1!important}.flex-shrink-0{flex-shrink:0!important}.flex-shrink-1{flex-shrink:1!important}.flex-wrap{flex-wrap:wrap!important}.flex-nowrap{flex-wrap:nowrap!important}.flex-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-start{justify-content:flex-start!important}.justify-content-end{justify-content:flex-end!important}.justify-content-center{justify-content:center!important}.justify-content-between{justify-content:space-between!important}.justify-content-around{justify-content:space-around!important}.justify-content-evenly{justify-content:space-evenly!important}.align-items-start{align-items:flex-start!important}.align-items-end{align-items:flex-end!important}.align-items-center{align-items:center!important}.align-items-baseline{align-items:baseline!important}.align-items-stretch{align-items:stretch!important}.align-content-start{align-content:flex-start!important}.align-content-end{align-content:flex-end!important}.align-content-center{align-content:center!important}.align-content-between{align-content:space-between!important}.align-content-around{align-content:space-around!important}.align-content-stretch{align-content:stretch!important}.align-self-auto{align-self:auto!important}.align-self-start{align-self:flex-start!important}.align-self-end{align-self:flex-end!important}.align-self-center{align-self:center!important}.align-self-baseline{align-self:baseline!important}.align-self-stretch{align-self:stretch!important}.order-first{order:-1!important}.order-0{order:0!important}.order-1{order:1!important}.order-2{order:2!important}.order-3{order:3!important}.order-4{order:4!important}.order-5{order:5!important}.order-last{order:6!important}.m-0{margin:0!important}.m-1{margin:.25rem!important}.m-2{margin:.5rem!important}.m-3{margin:1rem!important}.m-4{margin:1.5rem!important}.m-5{margin:3rem!important}.m-auto{margin:auto!important}.mx-0{margin-right:0!important;margin-left:0!important}.mx-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-3{margin-right:1rem!important;margin-left:1rem!important}.mx-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-5{margin-right:3rem!important;margin-left:3rem!important}.mx-auto{margin-right:auto!important;margin-left:auto!important}.my-0{margin-top:0!important;margin-bottom:0!important}.my-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-0{margin-top:0!important}.mt-1{margin-top:.25rem!important}.mt-2{margin-top:.5rem!important}.mt-3{margin-top:1rem!important}.mt-4{margin-top:1.5rem!important}.mt-5{margin-top:3rem!important}.mt-auto{margin-top:auto!important}.me-0{margin-right:0!important}.me-1{margin-right:.25rem!important}.me-2{margin-right:.5rem!important}.me-3{margin-right:1rem!important}.me-4{margin-right:1.5rem!important}.me-5{margin-right:3rem!important}.me-auto{margin-right:auto!important}.mb-0{margin-bottom:0!important}.mb-1{margin-bottom:.25rem!important}.mb-2{margin-bottom:.5rem!important}.mb-3{margin-bottom:1rem!important}.mb-4{margin-bottom:1.5rem!important}.mb-5{margin-bottom:3rem!important}.mb-auto{margin-bottom:auto!important}.ms-0{margin-left:0!important}.ms-1{margin-left:.25rem!important}.ms-2{margin-left:.5rem!important}.ms-3{margin-left:1rem!important}.ms-4{margin-left:1.5rem!important}.ms-5{margin-left:3rem!important}.ms-auto{margin-left:auto!important}.p-0{padding:0!important}.p-1{padding:.25rem!important}.p-2{padding:.5rem!important}.p-3{padding:1rem!important}.p-4{padding:1.5rem!important}.p-5{padding:3rem!important}.px-0{padding-right:0!important;padding-left:0!important}.px-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-3{padding-right:1rem!important;padding-left:1rem!important}.px-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-5{padding-right:3rem!important;padding-left:3rem!important}.py-0{padding-top:0!important;padding-bottom:0!important}.py-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-0{padding-top:0!important}.pt-1{padding-top:.25rem!important}.pt-2{padding-top:.5rem!important}.pt-3{padding-top:1rem!important}.pt-4{padding-top:1.5rem!important}.pt-5{padding-top:3rem!important}.pe-0{padding-right:0!important}.pe-1{padding-right:.25rem!important}.pe-2{padding-right:.5rem!important}.pe-3{padding-right:1rem!important}.pe-4{padding-right:1.5rem!important}.pe-5{padding-right:3rem!important}.pb-0{padding-bottom:0!important}.pb-1{padding-bottom:.25rem!important}.pb-2{padding-bottom:.5rem!important}.pb-3{padding-bottom:1rem!important}.pb-4{padding-bottom:1.5rem!important}.pb-5{padding-bottom:3rem!important}.ps-0{padding-left:0!important}.ps-1{padding-left:.25rem!important}.ps-2{padding-left:.5rem!important}.ps-3{padding-left:1rem!important}.ps-4{padding-left:1.5rem!important}.ps-5{padding-left:3rem!important}.gap-0{gap:0!important}.gap-1{gap:.25rem!important}.gap-2{gap:.5rem!important}.gap-3{gap:1rem!important}.gap-4{gap:1.5rem!important}.gap-5{gap:3rem!important}.row-gap-0{row-gap:0!important}.row-gap-1{row-gap:.25rem!important}.row-gap-2{row-gap:.5rem!important}.row-gap-3{row-gap:1rem!important}.row-gap-4{row-gap:1.5rem!important}.row-gap-5{row-gap:3rem!important}.column-gap-0{-moz-column-gap:0!important;column-gap:0!important}.column-gap-1{-moz-column-gap:0.25rem!important;column-gap:.25rem!important}.column-gap-2{-moz-column-gap:0.5rem!important;column-gap:.5rem!important}.column-gap-3{-moz-column-gap:1rem!important;column-gap:1rem!important}.column-gap-4{-moz-column-gap:1.5rem!important;column-gap:1.5rem!important}.column-gap-5{-moz-column-gap:3rem!important;column-gap:3rem!important}.font-monospace{font-family:var(--bs-font-monospace)!important}.fs-1{font-size:calc(1.375rem + 1.5vw)!important}.fs-2{font-size:calc(1.325rem + .9vw)!important}.fs-3{font-size:calc(1.3rem + .6vw)!important}.fs-4{font-size:calc(1.275rem + .3vw)!important}.fs-5{font-size:1.25rem!important}.fs-6{font-size:1rem!important}.fst-italic{font-style:italic!important}.fst-normal{font-style:normal!important}.fw-lighter{font-weight:lighter!important}.fw-light{font-weight:300!important}.fw-normal{font-weight:400!important}.fw-medium{font-weight:500!important}.fw-semibold{font-weight:600!important}.fw-bold{font-weight:700!important}.fw-bolder{font-weight:bolder!important}.lh-1{line-height:1!important}.lh-sm{line-height:1.25!important}.lh-base{line-height:1.5!important}.lh-lg{line-height:2!important}.text-start{text-align:left!important}.text-end{text-align:right!important}.text-center{text-align:center!important}.text-decoration-none{text-decoration:none!important}.text-decoration-underline{text-decoration:underline!important}.text-decoration-line-through{text-decoration:line-through!important}.text-lowercase{text-transform:lowercase!important}.text-uppercase{text-transform:uppercase!important}.text-capitalize{text-transform:capitalize!important}.text-wrap{white-space:normal!important}.text-nowrap{white-space:nowrap!important}.text-break{word-wrap:break-word!important;word-break:break-word!important}.text-primary{--bs-text-opacity:1;color:rgba(var(--bs-primary-rgb),var(--bs-text-opacity))!important}.text-secondary{--bs-text-opacity:1;color:rgba(var(--bs-secondary-rgb),var(--bs-text-opacity))!important}.text-success{--bs-text-opacity:1;color:rgba(var(--bs-success-rgb),var(--bs-text-opacity))!important}.text-info{--bs-text-opacity:1;color:rgba(var(--bs-info-rgb),var(--bs-text-opacity))!important}.text-warning{--bs-text-opacity:1;color:rgba(var(--bs-warning-rgb),var(--bs-text-opacity))!important}.text-danger{--bs-text-opacity:1;color:rgba(var(--bs-danger-rgb),var(--bs-text-opacity))!important}.text-light{--bs-text-opacity:1;color:rgba(var(--bs-light-rgb),var(--bs-text-opacity))!important}.text-dark{--bs-text-opacity:1;color:rgba(var(--bs-dark-rgb),var(--bs-text-opacity))!important}.text-black{--bs-text-opacity:1;color:rgba(var(--bs-black-rgb),var(--bs-text-opacity))!important}.text-white{--bs-text-opacity:1;color:rgba(var(--bs-white-rgb),var(--bs-text-opacity))!important}.text-body{--bs-text-opacity:1;color:rgba(var(--bs-body-color-rgb),var(--bs-text-opacity))!important}.text-muted{--bs-text-opacity:1;color:var(--bs-secondary-color)!important}.text-black-50{--bs-text-opacity:1;color:rgba(0,0,0,.5)!important}.text-white-50{--bs-text-opacity:1;color:rgba(255,255,255,.5)!important}.text-body-secondary{--bs-text-opacity:1;color:var(--bs-secondary-color)!important}.text-body-tertiary{--bs-text-opacity:1;color:var(--bs-tertiary-color)!important}.text-body-emphasis{--bs-text-opacity:1;color:var(--bs-emphasis-color)!important}.text-reset{--bs-text-opacity:1;color:inherit!important}.text-opacity-25{--bs-text-opacity:0.25}.text-opacity-50{--bs-text-opacity:0.5}.text-opacity-75{--bs-text-opacity:0.75}.text-opacity-100{--bs-text-opacity:1}.text-primary-emphasis{color:var(--bs-primary-text-emphasis)!important}.text-secondary-emphasis{color:var(--bs-secondary-text-emphasis)!important}.text-success-emphasis{color:var(--bs-success-text-emphasis)!important}.text-info-emphasis{color:var(--bs-info-text-emphasis)!important}.text-warning-emphasis{color:var(--bs-warning-text-emphasis)!important}.text-danger-emphasis{color:var(--bs-danger-text-emphasis)!important}.text-light-emphasis{color:var(--bs-light-text-emphasis)!important}.text-dark-emphasis{color:var(--bs-dark-text-emphasis)!important}.link-opacity-10{--bs-link-opacity:0.1}.link-opacity-10-hover:hover{--bs-link-opacity:0.1}.link-opacity-25{--bs-link-opacity:0.25}.link-opacity-25-hover:hover{--bs-link-opacity:0.25}.link-opacity-50{--bs-link-opacity:0.5}.link-opacity-50-hover:hover{--bs-link-opacity:0.5}.link-opacity-75{--bs-link-opacity:0.75}.link-opacity-75-hover:hover{--bs-link-opacity:0.75}.link-opacity-100{--bs-link-opacity:1}.link-opacity-100-hover:hover{--bs-link-opacity:1}.link-offset-1{text-underline-offset:0.125em!important}.link-offset-1-hover:hover{text-underline-offset:0.125em!important}.link-offset-2{text-underline-offset:0.25em!important}.link-offset-2-hover:hover{text-underline-offset:0.25em!important}.link-offset-3{text-underline-offset:0.375em!important}.link-offset-3-hover:hover{text-underline-offset:0.375em!important}.link-underline-primary{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-primary-rgb),var(--bs-link-underline-opacity))!important;text-decoration-color:rgba(var(--bs-primary-rgb),var(--bs-link-underline-opacity))!important}.link-underline-secondary{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-secondary-rgb),var(--bs-link-underline-opacity))!important;text-decoration-color:rgba(var(--bs-secondary-rgb),var(--bs-link-underline-opacity))!important}.link-underline-success{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-success-rgb),var(--bs-link-underline-opacity))!important;text-decoration-color:rgba(var(--bs-success-rgb),var(--bs-link-underline-opacity))!important}.link-underline-info{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-info-rgb),var(--bs-link-underline-opacity))!important;text-decoration-color:rgba(var(--bs-info-rgb),var(--bs-link-underline-opacity))!important}.link-underline-warning{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-warning-rgb),var(--bs-link-underline-opacity))!important;text-decoration-color:rgba(var(--bs-warning-rgb),var(--bs-link-underline-opacity))!important}.link-underline-danger{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-danger-rgb),var(--bs-link-underline-opacity))!important;text-decoration-color:rgba(var(--bs-danger-rgb),var(--bs-link-underline-opacity))!important}.link-underline-light{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-light-rgb),var(--bs-link-underline-opacity))!important;text-decoration-color:rgba(var(--bs-light-rgb),var(--bs-link-underline-opacity))!important}.link-underline-dark{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-dark-rgb),var(--bs-link-underline-opacity))!important;text-decoration-color:rgba(var(--bs-dark-rgb),var(--bs-link-underline-opacity))!important}.link-underline{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-link-color-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:rgba(var(--bs-link-color-rgb),var(--bs-link-underline-opacity,1))!important}.link-underline-opacity-0{--bs-link-underline-opacity:0}.link-underline-opacity-0-hover:hover{--bs-link-underline-opacity:0}.link-underline-opacity-10{--bs-link-underline-opacity:0.1}.link-underline-opacity-10-hover:hover{--bs-link-underline-opacity:0.1}.link-underline-opacity-25{--bs-link-underline-opacity:0.25}.link-underline-opacity-25-hover:hover{--bs-link-underline-opacity:0.25}.link-underline-opacity-50{--bs-link-underline-opacity:0.5}.link-underline-opacity-50-hover:hover{--bs-link-underline-opacity:0.5}.link-underline-opacity-75{--bs-link-underline-opacity:0.75}.link-underline-opacity-75-hover:hover{--bs-link-underline-opacity:0.75}.link-underline-opacity-100{--bs-link-underline-opacity:1}.link-underline-opacity-100-hover:hover{--bs-link-underline-opacity:1}.bg-primary{--bs-bg-opacity:1;background-color:rgba(var(--bs-primary-rgb),var(--bs-bg-opacity))!important}.bg-secondary{--bs-bg-opacity:1;background-color:rgba(var(--bs-secondary-rgb),var(--bs-bg-opacity))!important}.bg-success{--bs-bg-opacity:1;background-color:rgba(var(--bs-success-rgb),var(--bs-bg-opacity))!important}.bg-info{--bs-bg-opacity:1;background-color:rgba(var(--bs-info-rgb),var(--bs-bg-opacity))!important}.bg-warning{--bs-bg-opacity:1;background-color:rgba(var(--bs-warning-rgb),var(--bs-bg-opacity))!important}.bg-danger{--bs-bg-opacity:1;background-color:rgba(var(--bs-danger-rgb),var(--bs-bg-opacity))!important}.bg-light{--bs-bg-opacity:1;background-color:rgba(var(--bs-light-rgb),var(--bs-bg-opacity))!important}.bg-dark{--bs-bg-opacity:1;background-color:rgba(var(--bs-dark-rgb),var(--bs-bg-opacity))!important}.bg-black{--bs-bg-opacity:1;background-color:rgba(var(--bs-black-rgb),var(--bs-bg-opacity))!important}.bg-white{--bs-bg-opacity:1;background-color:rgba(var(--bs-white-rgb),var(--bs-bg-opacity))!important}.bg-body{--bs-bg-opacity:1;background-color:rgba(var(--bs-body-bg-rgb),var(--bs-bg-opacity))!important}.bg-transparent{--bs-bg-opacity:1;background-color:transparent!important}.bg-body-secondary{--bs-bg-opacity:1;background-color:rgba(var(--bs-secondary-bg-rgb),var(--bs-bg-opacity))!important}.bg-body-tertiary{--bs-bg-opacity:1;background-color:rgba(var(--bs-tertiary-bg-rgb),var(--bs-bg-opacity))!important}.bg-opacity-10{--bs-bg-opacity:0.1}.bg-opacity-25{--bs-bg-opacity:0.25}.bg-opacity-50{--bs-bg-opacity:0.5}.bg-opacity-75{--bs-bg-opacity:0.75}.bg-opacity-100{--bs-bg-opacity:1}.bg-primary-subtle{background-color:var(--bs-primary-bg-subtle)!important}.bg-secondary-subtle{background-color:var(--bs-secondary-bg-subtle)!important}.bg-success-subtle{background-color:var(--bs-success-bg-subtle)!important}.bg-info-subtle{background-color:var(--bs-info-bg-subtle)!important}.bg-warning-subtle{background-color:var(--bs-warning-bg-subtle)!important}.bg-danger-subtle{background-color:var(--bs-danger-bg-subtle)!important}.bg-light-subtle{background-color:var(--bs-light-bg-subtle)!important}.bg-dark-subtle{background-color:var(--bs-dark-bg-subtle)!important}.bg-gradient{background-image:var(--bs-gradient)!important}.user-select-all{-webkit-user-select:all!important;-moz-user-select:all!important;user-select:all!important}.user-select-auto{-webkit-user-select:auto!important;-moz-user-select:auto!important;user-select:auto!important}.user-select-none{-webkit-user-select:none!important;-moz-user-select:none!important;user-select:none!important}.pe-none{pointer-events:none!important}.pe-auto{pointer-events:auto!important}.rounded{border-radius:var(--bs-border-radius)!important}.rounded-0{border-radius:0!important}.rounded-1{border-radius:var(--bs-border-radius-sm)!important}.rounded-2{border-radius:var(--bs-border-radius)!important}.rounded-3{border-radius:var(--bs-border-radius-lg)!important}.rounded-4{border-radius:var(--bs-border-radius-xl)!important}.rounded-5{border-radius:var(--bs-border-radius-xxl)!important}.rounded-circle{border-radius:50%!important}.rounded-pill{border-radius:var(--bs-border-radius-pill)!important}.rounded-top{border-top-left-radius:var(--bs-border-radius)!important;border-top-right-radius:var(--bs-border-radius)!important}.rounded-top-0{border-top-left-radius:0!important;border-top-right-radius:0!important}.rounded-top-1{border-top-left-radius:var(--bs-border-radius-sm)!important;border-top-right-radius:var(--bs-border-radius-sm)!important}.rounded-top-2{border-top-left-radius:var(--bs-border-radius)!important;border-top-right-radius:var(--bs-border-radius)!important}.rounded-top-3{border-top-left-radius:var(--bs-border-radius-lg)!important;border-top-right-radius:var(--bs-border-radius-lg)!important}.rounded-top-4{border-top-left-radius:var(--bs-border-radius-xl)!important;border-top-right-radius:var(--bs-border-radius-xl)!important}.rounded-top-5{border-top-left-radius:var(--bs-border-radius-xxl)!important;border-top-right-radius:var(--bs-border-radius-xxl)!important}.rounded-top-circle{border-top-left-radius:50%!important;border-top-right-radius:50%!important}.rounded-top-pill{border-top-left-radius:var(--bs-border-radius-pill)!important;border-top-right-radius:var(--bs-border-radius-pill)!important}.rounded-end{border-top-right-radius:var(--bs-border-radius)!important;border-bottom-right-radius:var(--bs-border-radius)!important}.rounded-end-0{border-top-right-radius:0!important;border-bottom-right-radius:0!important}.rounded-end-1{border-top-right-radius:var(--bs-border-radius-sm)!important;border-bottom-right-radius:var(--bs-border-radius-sm)!important}.rounded-end-2{border-top-right-radius:var(--bs-border-radius)!important;border-bottom-right-radius:var(--bs-border-radius)!important}.rounded-end-3{border-top-right-radius:var(--bs-border-radius-lg)!important;border-bottom-right-radius:var(--bs-border-radius-lg)!important}.rounded-end-4{border-top-right-radius:var(--bs-border-radius-xl)!important;border-bottom-right-radius:var(--bs-border-radius-xl)!important}.rounded-end-5{border-top-right-radius:var(--bs-border-radius-xxl)!important;border-bottom-right-radius:var(--bs-border-radius-xxl)!important}.rounded-end-circle{border-top-right-radius:50%!important;border-bottom-right-radius:50%!important}.rounded-end-pill{border-top-right-radius:var(--bs-border-radius-pill)!important;border-bottom-right-radius:var(--bs-border-radius-pill)!important}.rounded-bottom{border-bottom-right-radius:var(--bs-border-radius)!important;border-bottom-left-radius:var(--bs-border-radius)!important}.rounded-bottom-0{border-bottom-right-radius:0!important;border-bottom-left-radius:0!important}.rounded-bottom-1{border-bottom-right-radius:var(--bs-border-radius-sm)!important;border-bottom-left-radius:var(--bs-border-radius-sm)!important}.rounded-bottom-2{border-bottom-right-radius:var(--bs-border-radius)!important;border-bottom-left-radius:var(--bs-border-radius)!important}.rounded-bottom-3{border-bottom-right-radius:var(--bs-border-radius-lg)!important;border-bottom-left-radius:var(--bs-border-radius-lg)!important}.rounded-bottom-4{border-bottom-right-radius:var(--bs-border-radius-xl)!important;border-bottom-left-radius:var(--bs-border-radius-xl)!important}.rounded-bottom-5{border-bottom-right-radius:var(--bs-border-radius-xxl)!important;border-bottom-left-radius:var(--bs-border-radius-xxl)!important}.rounded-bottom-circle{border-bottom-right-radius:50%!important;border-bottom-left-radius:50%!important}.rounded-bottom-pill{border-bottom-right-radius:var(--bs-border-radius-pill)!important;border-bottom-left-radius:var(--bs-border-radius-pill)!important}.rounded-start{border-bottom-left-radius:var(--bs-border-radius)!important;border-top-left-radius:var(--bs-border-radius)!important}.rounded-start-0{border-bottom-left-radius:0!important;border-top-left-radius:0!important}.rounded-start-1{border-bottom-left-radius:var(--bs-border-radius-sm)!important;border-top-left-radius:var(--bs-border-radius-sm)!important}.rounded-start-2{border-bottom-left-radius:var(--bs-border-radius)!important;border-top-left-radius:var(--bs-border-radius)!important}.rounded-start-3{border-bottom-left-radius:var(--bs-border-radius-lg)!important;border-top-left-radius:var(--bs-border-radius-lg)!important}.rounded-start-4{border-bottom-left-radius:var(--bs-border-radius-xl)!important;border-top-left-radius:var(--bs-border-radius-xl)!important}.rounded-start-5{border-bottom-left-radius:var(--bs-border-radius-xxl)!important;border-top-left-radius:var(--bs-border-radius-xxl)!important}.rounded-start-circle{border-bottom-left-radius:50%!important;border-top-left-radius:50%!important}.rounded-start-pill{border-bottom-left-radius:var(--bs-border-radius-pill)!important;border-top-left-radius:var(--bs-border-radius-pill)!important}.visible{visibility:visible!important}.invisible{visibility:hidden!important}.z-n1{z-index:-1!important}.z-0{z-index:0!important}.z-1{z-index:1!important}.z-2{z-index:2!important}.z-3{z-index:3!important}@media (min-width:576px){.float-sm-start{float:left!important}.float-sm-end{float:right!important}.float-sm-none{float:none!important}.object-fit-sm-contain{-o-object-fit:contain!important;object-fit:contain!important}.object-fit-sm-cover{-o-object-fit:cover!important;object-fit:cover!important}.object-fit-sm-fill{-o-object-fit:fill!important;object-fit:fill!important}.object-fit-sm-scale{-o-object-fit:scale-down!important;object-fit:scale-down!important}.object-fit-sm-none{-o-object-fit:none!important;object-fit:none!important}.d-sm-inline{display:inline!important}.d-sm-inline-block{display:inline-block!important}.d-sm-block{display:block!important}.d-sm-grid{display:grid!important}.d-sm-inline-grid{display:inline-grid!important}.d-sm-table{display:table!important}.d-sm-table-row{display:table-row!important}.d-sm-table-cell{display:table-cell!important}.d-sm-flex{display:flex!important}.d-sm-inline-flex{display:inline-flex!important}.d-sm-none{display:none!important}.flex-sm-fill{flex:1 1 auto!important}.flex-sm-row{flex-direction:row!important}.flex-sm-column{flex-direction:column!important}.flex-sm-row-reverse{flex-direction:row-reverse!important}.flex-sm-column-reverse{flex-direction:column-reverse!important}.flex-sm-grow-0{flex-grow:0!important}.flex-sm-grow-1{flex-grow:1!important}.flex-sm-shrink-0{flex-shrink:0!important}.flex-sm-shrink-1{flex-shrink:1!important}.flex-sm-wrap{flex-wrap:wrap!important}.flex-sm-nowrap{flex-wrap:nowrap!important}.flex-sm-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-sm-start{justify-content:flex-start!important}.justify-content-sm-end{justify-content:flex-end!important}.justify-content-sm-center{justify-content:center!important}.justify-content-sm-between{justify-content:space-between!important}.justify-content-sm-around{justify-content:space-around!important}.justify-content-sm-evenly{justify-content:space-evenly!important}.align-items-sm-start{align-items:flex-start!important}.align-items-sm-end{align-items:flex-end!important}.align-items-sm-center{align-items:center!important}.align-items-sm-baseline{align-items:baseline!important}.align-items-sm-stretch{align-items:stretch!important}.align-content-sm-start{align-content:flex-start!important}.align-content-sm-end{align-content:flex-end!important}.align-content-sm-center{align-content:center!important}.align-content-sm-between{align-content:space-between!important}.align-content-sm-around{align-content:space-around!important}.align-content-sm-stretch{align-content:stretch!important}.align-self-sm-auto{align-self:auto!important}.align-self-sm-start{align-self:flex-start!important}.align-self-sm-end{align-self:flex-end!important}.align-self-sm-center{align-self:center!important}.align-self-sm-baseline{align-self:baseline!important}.align-self-sm-stretch{align-self:stretch!important}.order-sm-first{order:-1!important}.order-sm-0{order:0!important}.order-sm-1{order:1!important}.order-sm-2{order:2!important}.order-sm-3{order:3!important}.order-sm-4{order:4!important}.order-sm-5{order:5!important}.order-sm-last{order:6!important}.m-sm-0{margin:0!important}.m-sm-1{margin:.25rem!important}.m-sm-2{margin:.5rem!important}.m-sm-3{margin:1rem!important}.m-sm-4{margin:1.5rem!important}.m-sm-5{margin:3rem!important}.m-sm-auto{margin:auto!important}.mx-sm-0{margin-right:0!important;margin-left:0!important}.mx-sm-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-sm-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-sm-3{margin-right:1rem!important;margin-left:1rem!important}.mx-sm-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-sm-5{margin-right:3rem!important;margin-left:3rem!important}.mx-sm-auto{margin-right:auto!important;margin-left:auto!important}.my-sm-0{margin-top:0!important;margin-bottom:0!important}.my-sm-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-sm-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-sm-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-sm-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-sm-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-sm-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-sm-0{margin-top:0!important}.mt-sm-1{margin-top:.25rem!important}.mt-sm-2{margin-top:.5rem!important}.mt-sm-3{margin-top:1rem!important}.mt-sm-4{margin-top:1.5rem!important}.mt-sm-5{margin-top:3rem!important}.mt-sm-auto{margin-top:auto!important}.me-sm-0{margin-right:0!important}.me-sm-1{margin-right:.25rem!important}.me-sm-2{margin-right:.5rem!important}.me-sm-3{margin-right:1rem!important}.me-sm-4{margin-right:1.5rem!important}.me-sm-5{margin-right:3rem!important}.me-sm-auto{margin-right:auto!important}.mb-sm-0{margin-bottom:0!important}.mb-sm-1{margin-bottom:.25rem!important}.mb-sm-2{margin-bottom:.5rem!important}.mb-sm-3{margin-bottom:1rem!important}.mb-sm-4{margin-bottom:1.5rem!important}.mb-sm-5{margin-bottom:3rem!important}.mb-sm-auto{margin-bottom:auto!important}.ms-sm-0{margin-left:0!important}.ms-sm-1{margin-left:.25rem!important}.ms-sm-2{margin-left:.5rem!important}.ms-sm-3{margin-left:1rem!important}.ms-sm-4{margin-left:1.5rem!important}.ms-sm-5{margin-left:3rem!important}.ms-sm-auto{margin-left:auto!important}.p-sm-0{padding:0!important}.p-sm-1{padding:.25rem!important}.p-sm-2{padding:.5rem!important}.p-sm-3{padding:1rem!important}.p-sm-4{padding:1.5rem!important}.p-sm-5{padding:3rem!important}.px-sm-0{padding-right:0!important;padding-left:0!important}.px-sm-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-sm-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-sm-3{padding-right:1rem!important;padding-left:1rem!important}.px-sm-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-sm-5{padding-right:3rem!important;padding-left:3rem!important}.py-sm-0{padding-top:0!important;padding-bottom:0!important}.py-sm-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-sm-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-sm-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-sm-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-sm-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-sm-0{padding-top:0!important}.pt-sm-1{padding-top:.25rem!important}.pt-sm-2{padding-top:.5rem!important}.pt-sm-3{padding-top:1rem!important}.pt-sm-4{padding-top:1.5rem!important}.pt-sm-5{padding-top:3rem!important}.pe-sm-0{padding-right:0!important}.pe-sm-1{padding-right:.25rem!important}.pe-sm-2{padding-right:.5rem!important}.pe-sm-3{padding-right:1rem!important}.pe-sm-4{padding-right:1.5rem!important}.pe-sm-5{padding-right:3rem!important}.pb-sm-0{padding-bottom:0!important}.pb-sm-1{padding-bottom:.25rem!important}.pb-sm-2{padding-bottom:.5rem!important}.pb-sm-3{padding-bottom:1rem!important}.pb-sm-4{padding-bottom:1.5rem!important}.pb-sm-5{padding-bottom:3rem!important}.ps-sm-0{padding-left:0!important}.ps-sm-1{padding-left:.25rem!important}.ps-sm-2{padding-left:.5rem!important}.ps-sm-3{padding-left:1rem!important}.ps-sm-4{padding-left:1.5rem!important}.ps-sm-5{padding-left:3rem!important}.gap-sm-0{gap:0!important}.gap-sm-1{gap:.25rem!important}.gap-sm-2{gap:.5rem!important}.gap-sm-3{gap:1rem!important}.gap-sm-4{gap:1.5rem!important}.gap-sm-5{gap:3rem!important}.row-gap-sm-0{row-gap:0!important}.row-gap-sm-1{row-gap:.25rem!important}.row-gap-sm-2{row-gap:.5rem!important}.row-gap-sm-3{row-gap:1rem!important}.row-gap-sm-4{row-gap:1.5rem!important}.row-gap-sm-5{row-gap:3rem!important}.column-gap-sm-0{-moz-column-gap:0!important;column-gap:0!important}.column-gap-sm-1{-moz-column-gap:0.25rem!important;column-gap:.25rem!important}.column-gap-sm-2{-moz-column-gap:0.5rem!important;column-gap:.5rem!important}.column-gap-sm-3{-moz-column-gap:1rem!important;column-gap:1rem!important}.column-gap-sm-4{-moz-column-gap:1.5rem!important;column-gap:1.5rem!important}.column-gap-sm-5{-moz-column-gap:3rem!important;column-gap:3rem!important}.text-sm-start{text-align:left!important}.text-sm-end{text-align:right!important}.text-sm-center{text-align:center!important}}@media (min-width:768px){.float-md-start{float:left!important}.float-md-end{float:right!important}.float-md-none{float:none!important}.object-fit-md-contain{-o-object-fit:contain!important;object-fit:contain!important}.object-fit-md-cover{-o-object-fit:cover!important;object-fit:cover!important}.object-fit-md-fill{-o-object-fit:fill!important;object-fit:fill!important}.object-fit-md-scale{-o-object-fit:scale-down!important;object-fit:scale-down!important}.object-fit-md-none{-o-object-fit:none!important;object-fit:none!important}.d-md-inline{display:inline!important}.d-md-inline-block{display:inline-block!important}.d-md-block{display:block!important}.d-md-grid{display:grid!important}.d-md-inline-grid{display:inline-grid!important}.d-md-table{display:table!important}.d-md-table-row{display:table-row!important}.d-md-table-cell{display:table-cell!important}.d-md-flex{display:flex!important}.d-md-inline-flex{display:inline-flex!important}.d-md-none{display:none!important}.flex-md-fill{flex:1 1 auto!important}.flex-md-row{flex-direction:row!important}.flex-md-column{flex-direction:column!important}.flex-md-row-reverse{flex-direction:row-reverse!important}.flex-md-column-reverse{flex-direction:column-reverse!important}.flex-md-grow-0{flex-grow:0!important}.flex-md-grow-1{flex-grow:1!important}.flex-md-shrink-0{flex-shrink:0!important}.flex-md-shrink-1{flex-shrink:1!important}.flex-md-wrap{flex-wrap:wrap!important}.flex-md-nowrap{flex-wrap:nowrap!important}.flex-md-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-md-start{justify-content:flex-start!important}.justify-content-md-end{justify-content:flex-end!important}.justify-content-md-center{justify-content:center!important}.justify-content-md-between{justify-content:space-between!important}.justify-content-md-around{justify-content:space-around!important}.justify-content-md-evenly{justify-content:space-evenly!important}.align-items-md-start{align-items:flex-start!important}.align-items-md-end{align-items:flex-end!important}.align-items-md-center{align-items:center!important}.align-items-md-baseline{align-items:baseline!important}.align-items-md-stretch{align-items:stretch!important}.align-content-md-start{align-content:flex-start!important}.align-content-md-end{align-content:flex-end!important}.align-content-md-center{align-content:center!important}.align-content-md-between{align-content:space-between!important}.align-content-md-around{align-content:space-around!important}.align-content-md-stretch{align-content:stretch!important}.align-self-md-auto{align-self:auto!important}.align-self-md-start{align-self:flex-start!important}.align-self-md-end{align-self:flex-end!important}.align-self-md-center{align-self:center!important}.align-self-md-baseline{align-self:baseline!important}.align-self-md-stretch{align-self:stretch!important}.order-md-first{order:-1!important}.order-md-0{order:0!important}.order-md-1{order:1!important}.order-md-2{order:2!important}.order-md-3{order:3!important}.order-md-4{order:4!important}.order-md-5{order:5!important}.order-md-last{order:6!important}.m-md-0{margin:0!important}.m-md-1{margin:.25rem!important}.m-md-2{margin:.5rem!important}.m-md-3{margin:1rem!important}.m-md-4{margin:1.5rem!important}.m-md-5{margin:3rem!important}.m-md-auto{margin:auto!important}.mx-md-0{margin-right:0!important;margin-left:0!important}.mx-md-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-md-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-md-3{margin-right:1rem!important;margin-left:1rem!important}.mx-md-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-md-5{margin-right:3rem!important;margin-left:3rem!important}.mx-md-auto{margin-right:auto!important;margin-left:auto!important}.my-md-0{margin-top:0!important;margin-bottom:0!important}.my-md-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-md-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-md-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-md-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-md-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-md-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-md-0{margin-top:0!important}.mt-md-1{margin-top:.25rem!important}.mt-md-2{margin-top:.5rem!important}.mt-md-3{margin-top:1rem!important}.mt-md-4{margin-top:1.5rem!important}.mt-md-5{margin-top:3rem!important}.mt-md-auto{margin-top:auto!important}.me-md-0{margin-right:0!important}.me-md-1{margin-right:.25rem!important}.me-md-2{margin-right:.5rem!important}.me-md-3{margin-right:1rem!important}.me-md-4{margin-right:1.5rem!important}.me-md-5{margin-right:3rem!important}.me-md-auto{margin-right:auto!important}.mb-md-0{margin-bottom:0!important}.mb-md-1{margin-bottom:.25rem!important}.mb-md-2{margin-bottom:.5rem!important}.mb-md-3{margin-bottom:1rem!important}.mb-md-4{margin-bottom:1.5rem!important}.mb-md-5{margin-bottom:3rem!important}.mb-md-auto{margin-bottom:auto!important}.ms-md-0{margin-left:0!important}.ms-md-1{margin-left:.25rem!important}.ms-md-2{margin-left:.5rem!important}.ms-md-3{margin-left:1rem!important}.ms-md-4{margin-left:1.5rem!important}.ms-md-5{margin-left:3rem!important}.ms-md-auto{margin-left:auto!important}.p-md-0{padding:0!important}.p-md-1{padding:.25rem!important}.p-md-2{padding:.5rem!important}.p-md-3{padding:1rem!important}.p-md-4{padding:1.5rem!important}.p-md-5{padding:3rem!important}.px-md-0{padding-right:0!important;padding-left:0!important}.px-md-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-md-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-md-3{padding-right:1rem!important;padding-left:1rem!important}.px-md-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-md-5{padding-right:3rem!important;padding-left:3rem!important}.py-md-0{padding-top:0!important;padding-bottom:0!important}.py-md-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-md-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-md-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-md-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-md-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-md-0{padding-top:0!important}.pt-md-1{padding-top:.25rem!important}.pt-md-2{padding-top:.5rem!important}.pt-md-3{padding-top:1rem!important}.pt-md-4{padding-top:1.5rem!important}.pt-md-5{padding-top:3rem!important}.pe-md-0{padding-right:0!important}.pe-md-1{padding-right:.25rem!important}.pe-md-2{padding-right:.5rem!important}.pe-md-3{padding-right:1rem!important}.pe-md-4{padding-right:1.5rem!important}.pe-md-5{padding-right:3rem!important}.pb-md-0{padding-bottom:0!important}.pb-md-1{padding-bottom:.25rem!important}.pb-md-2{padding-bottom:.5rem!important}.pb-md-3{padding-bottom:1rem!important}.pb-md-4{padding-bottom:1.5rem!important}.pb-md-5{padding-bottom:3rem!important}.ps-md-0{padding-left:0!important}.ps-md-1{padding-left:.25rem!important}.ps-md-2{padding-left:.5rem!important}.ps-md-3{padding-left:1rem!important}.ps-md-4{padding-left:1.5rem!important}.ps-md-5{padding-left:3rem!important}.gap-md-0{gap:0!important}.gap-md-1{gap:.25rem!important}.gap-md-2{gap:.5rem!important}.gap-md-3{gap:1rem!important}.gap-md-4{gap:1.5rem!important}.gap-md-5{gap:3rem!important}.row-gap-md-0{row-gap:0!important}.row-gap-md-1{row-gap:.25rem!important}.row-gap-md-2{row-gap:.5rem!important}.row-gap-md-3{row-gap:1rem!important}.row-gap-md-4{row-gap:1.5rem!important}.row-gap-md-5{row-gap:3rem!important}.column-gap-md-0{-moz-column-gap:0!important;column-gap:0!important}.column-gap-md-1{-moz-column-gap:0.25rem!important;column-gap:.25rem!important}.column-gap-md-2{-moz-column-gap:0.5rem!important;column-gap:.5rem!important}.column-gap-md-3{-moz-column-gap:1rem!important;column-gap:1rem!important}.column-gap-md-4{-moz-column-gap:1.5rem!important;column-gap:1.5rem!important}.column-gap-md-5{-moz-column-gap:3rem!important;column-gap:3rem!important}.text-md-start{text-align:left!important}.text-md-end{text-align:right!important}.text-md-center{text-align:center!important}}@media (min-width:992px){.float-lg-start{float:left!important}.float-lg-end{float:right!important}.float-lg-none{float:none!important}.object-fit-lg-contain{-o-object-fit:contain!important;object-fit:contain!important}.object-fit-lg-cover{-o-object-fit:cover!important;object-fit:cover!important}.object-fit-lg-fill{-o-object-fit:fill!important;object-fit:fill!important}.object-fit-lg-scale{-o-object-fit:scale-down!important;object-fit:scale-down!important}.object-fit-lg-none{-o-object-fit:none!important;object-fit:none!important}.d-lg-inline{display:inline!important}.d-lg-inline-block{display:inline-block!important}.d-lg-block{display:block!important}.d-lg-grid{display:grid!important}.d-lg-inline-grid{display:inline-grid!important}.d-lg-table{display:table!important}.d-lg-table-row{display:table-row!important}.d-lg-table-cell{display:table-cell!important}.d-lg-flex{display:flex!important}.d-lg-inline-flex{display:inline-flex!important}.d-lg-none{display:none!important}.flex-lg-fill{flex:1 1 auto!important}.flex-lg-row{flex-direction:row!important}.flex-lg-column{flex-direction:column!important}.flex-lg-row-reverse{flex-direction:row-reverse!important}.flex-lg-column-reverse{flex-direction:column-reverse!important}.flex-lg-grow-0{flex-grow:0!important}.flex-lg-grow-1{flex-grow:1!important}.flex-lg-shrink-0{flex-shrink:0!important}.flex-lg-shrink-1{flex-shrink:1!important}.flex-lg-wrap{flex-wrap:wrap!important}.flex-lg-nowrap{flex-wrap:nowrap!important}.flex-lg-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-lg-start{justify-content:flex-start!important}.justify-content-lg-end{justify-content:flex-end!important}.justify-content-lg-center{justify-content:center!important}.justify-content-lg-between{justify-content:space-between!important}.justify-content-lg-around{justify-content:space-around!important}.justify-content-lg-evenly{justify-content:space-evenly!important}.align-items-lg-start{align-items:flex-start!important}.align-items-lg-end{align-items:flex-end!important}.align-items-lg-center{align-items:center!important}.align-items-lg-baseline{align-items:baseline!important}.align-items-lg-stretch{align-items:stretch!important}.align-content-lg-start{align-content:flex-start!important}.align-content-lg-end{align-content:flex-end!important}.align-content-lg-center{align-content:center!important}.align-content-lg-between{align-content:space-between!important}.align-content-lg-around{align-content:space-around!important}.align-content-lg-stretch{align-content:stretch!important}.align-self-lg-auto{align-self:auto!important}.align-self-lg-start{align-self:flex-start!important}.align-self-lg-end{align-self:flex-end!important}.align-self-lg-center{align-self:center!important}.align-self-lg-baseline{align-self:baseline!important}.align-self-lg-stretch{align-self:stretch!important}.order-lg-first{order:-1!important}.order-lg-0{order:0!important}.order-lg-1{order:1!important}.order-lg-2{order:2!important}.order-lg-3{order:3!important}.order-lg-4{order:4!important}.order-lg-5{order:5!important}.order-lg-last{order:6!important}.m-lg-0{margin:0!important}.m-lg-1{margin:.25rem!important}.m-lg-2{margin:.5rem!important}.m-lg-3{margin:1rem!important}.m-lg-4{margin:1.5rem!important}.m-lg-5{margin:3rem!important}.m-lg-auto{margin:auto!important}.mx-lg-0{margin-right:0!important;margin-left:0!important}.mx-lg-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-lg-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-lg-3{margin-right:1rem!important;margin-left:1rem!important}.mx-lg-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-lg-5{margin-right:3rem!important;margin-left:3rem!important}.mx-lg-auto{margin-right:auto!important;margin-left:auto!important}.my-lg-0{margin-top:0!important;margin-bottom:0!important}.my-lg-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-lg-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-lg-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-lg-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-lg-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-lg-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-lg-0{margin-top:0!important}.mt-lg-1{margin-top:.25rem!important}.mt-lg-2{margin-top:.5rem!important}.mt-lg-3{margin-top:1rem!important}.mt-lg-4{margin-top:1.5rem!important}.mt-lg-5{margin-top:3rem!important}.mt-lg-auto{margin-top:auto!important}.me-lg-0{margin-right:0!important}.me-lg-1{margin-right:.25rem!important}.me-lg-2{margin-right:.5rem!important}.me-lg-3{margin-right:1rem!important}.me-lg-4{margin-right:1.5rem!important}.me-lg-5{margin-right:3rem!important}.me-lg-auto{margin-right:auto!important}.mb-lg-0{margin-bottom:0!important}.mb-lg-1{margin-bottom:.25rem!important}.mb-lg-2{margin-bottom:.5rem!important}.mb-lg-3{margin-bottom:1rem!important}.mb-lg-4{margin-bottom:1.5rem!important}.mb-lg-5{margin-bottom:3rem!important}.mb-lg-auto{margin-bottom:auto!important}.ms-lg-0{margin-left:0!important}.ms-lg-1{margin-left:.25rem!important}.ms-lg-2{margin-left:.5rem!important}.ms-lg-3{margin-left:1rem!important}.ms-lg-4{margin-left:1.5rem!important}.ms-lg-5{margin-left:3rem!important}.ms-lg-auto{margin-left:auto!important}.p-lg-0{padding:0!important}.p-lg-1{padding:.25rem!important}.p-lg-2{padding:.5rem!important}.p-lg-3{padding:1rem!important}.p-lg-4{padding:1.5rem!important}.p-lg-5{padding:3rem!important}.px-lg-0{padding-right:0!important;padding-left:0!important}.px-lg-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-lg-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-lg-3{padding-right:1rem!important;padding-left:1rem!important}.px-lg-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-lg-5{padding-right:3rem!important;padding-left:3rem!important}.py-lg-0{padding-top:0!important;padding-bottom:0!important}.py-lg-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-lg-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-lg-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-lg-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-lg-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-lg-0{padding-top:0!important}.pt-lg-1{padding-top:.25rem!important}.pt-lg-2{padding-top:.5rem!important}.pt-lg-3{padding-top:1rem!important}.pt-lg-4{padding-top:1.5rem!important}.pt-lg-5{padding-top:3rem!important}.pe-lg-0{padding-right:0!important}.pe-lg-1{padding-right:.25rem!important}.pe-lg-2{padding-right:.5rem!important}.pe-lg-3{padding-right:1rem!important}.pe-lg-4{padding-right:1.5rem!important}.pe-lg-5{padding-right:3rem!important}.pb-lg-0{padding-bottom:0!important}.pb-lg-1{padding-bottom:.25rem!important}.pb-lg-2{padding-bottom:.5rem!important}.pb-lg-3{padding-bottom:1rem!important}.pb-lg-4{padding-bottom:1.5rem!important}.pb-lg-5{padding-bottom:3rem!important}.ps-lg-0{padding-left:0!important}.ps-lg-1{padding-left:.25rem!important}.ps-lg-2{padding-left:.5rem!important}.ps-lg-3{padding-left:1rem!important}.ps-lg-4{padding-left:1.5rem!important}.ps-lg-5{padding-left:3rem!important}.gap-lg-0{gap:0!important}.gap-lg-1{gap:.25rem!important}.gap-lg-2{gap:.5rem!important}.gap-lg-3{gap:1rem!important}.gap-lg-4{gap:1.5rem!important}.gap-lg-5{gap:3rem!important}.row-gap-lg-0{row-gap:0!important}.row-gap-lg-1{row-gap:.25rem!important}.row-gap-lg-2{row-gap:.5rem!important}.row-gap-lg-3{row-gap:1rem!important}.row-gap-lg-4{row-gap:1.5rem!important}.row-gap-lg-5{row-gap:3rem!important}.column-gap-lg-0{-moz-column-gap:0!important;column-gap:0!important}.column-gap-lg-1{-moz-column-gap:0.25rem!important;column-gap:.25rem!important}.column-gap-lg-2{-moz-column-gap:0.5rem!important;column-gap:.5rem!important}.column-gap-lg-3{-moz-column-gap:1rem!important;column-gap:1rem!important}.column-gap-lg-4{-moz-column-gap:1.5rem!important;column-gap:1.5rem!important}.column-gap-lg-5{-moz-column-gap:3rem!important;column-gap:3rem!important}.text-lg-start{text-align:left!important}.text-lg-end{text-align:right!important}.text-lg-center{text-align:center!important}}@media (min-width:1200px){.float-xl-start{float:left!important}.float-xl-end{float:right!important}.float-xl-none{float:none!important}.object-fit-xl-contain{-o-object-fit:contain!important;object-fit:contain!important}.object-fit-xl-cover{-o-object-fit:cover!important;object-fit:cover!important}.object-fit-xl-fill{-o-object-fit:fill!important;object-fit:fill!important}.object-fit-xl-scale{-o-object-fit:scale-down!important;object-fit:scale-down!important}.object-fit-xl-none{-o-object-fit:none!important;object-fit:none!important}.d-xl-inline{display:inline!important}.d-xl-inline-block{display:inline-block!important}.d-xl-block{display:block!important}.d-xl-grid{display:grid!important}.d-xl-inline-grid{display:inline-grid!important}.d-xl-table{display:table!important}.d-xl-table-row{display:table-row!important}.d-xl-table-cell{display:table-cell!important}.d-xl-flex{display:flex!important}.d-xl-inline-flex{display:inline-flex!important}.d-xl-none{display:none!important}.flex-xl-fill{flex:1 1 auto!important}.flex-xl-row{flex-direction:row!important}.flex-xl-column{flex-direction:column!important}.flex-xl-row-reverse{flex-direction:row-reverse!important}.flex-xl-column-reverse{flex-direction:column-reverse!important}.flex-xl-grow-0{flex-grow:0!important}.flex-xl-grow-1{flex-grow:1!important}.flex-xl-shrink-0{flex-shrink:0!important}.flex-xl-shrink-1{flex-shrink:1!important}.flex-xl-wrap{flex-wrap:wrap!important}.flex-xl-nowrap{flex-wrap:nowrap!important}.flex-xl-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-xl-start{justify-content:flex-start!important}.justify-content-xl-end{justify-content:flex-end!important}.justify-content-xl-center{justify-content:center!important}.justify-content-xl-between{justify-content:space-between!important}.justify-content-xl-around{justify-content:space-around!important}.justify-content-xl-evenly{justify-content:space-evenly!important}.align-items-xl-start{align-items:flex-start!important}.align-items-xl-end{align-items:flex-end!important}.align-items-xl-center{align-items:center!important}.align-items-xl-baseline{align-items:baseline!important}.align-items-xl-stretch{align-items:stretch!important}.align-content-xl-start{align-content:flex-start!important}.align-content-xl-end{align-content:flex-end!important}.align-content-xl-center{align-content:center!important}.align-content-xl-between{align-content:space-between!important}.align-content-xl-around{align-content:space-around!important}.align-content-xl-stretch{align-content:stretch!important}.align-self-xl-auto{align-self:auto!important}.align-self-xl-start{align-self:flex-start!important}.align-self-xl-end{align-self:flex-end!important}.align-self-xl-center{align-self:center!important}.align-self-xl-baseline{align-self:baseline!important}.align-self-xl-stretch{align-self:stretch!important}.order-xl-first{order:-1!important}.order-xl-0{order:0!important}.order-xl-1{order:1!important}.order-xl-2{order:2!important}.order-xl-3{order:3!important}.order-xl-4{order:4!important}.order-xl-5{order:5!important}.order-xl-last{order:6!important}.m-xl-0{margin:0!important}.m-xl-1{margin:.25rem!important}.m-xl-2{margin:.5rem!important}.m-xl-3{margin:1rem!important}.m-xl-4{margin:1.5rem!important}.m-xl-5{margin:3rem!important}.m-xl-auto{margin:auto!important}.mx-xl-0{margin-right:0!important;margin-left:0!important}.mx-xl-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-xl-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-xl-3{margin-right:1rem!important;margin-left:1rem!important}.mx-xl-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-xl-5{margin-right:3rem!important;margin-left:3rem!important}.mx-xl-auto{margin-right:auto!important;margin-left:auto!important}.my-xl-0{margin-top:0!important;margin-bottom:0!important}.my-xl-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-xl-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-xl-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-xl-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-xl-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-xl-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-xl-0{margin-top:0!important}.mt-xl-1{margin-top:.25rem!important}.mt-xl-2{margin-top:.5rem!important}.mt-xl-3{margin-top:1rem!important}.mt-xl-4{margin-top:1.5rem!important}.mt-xl-5{margin-top:3rem!important}.mt-xl-auto{margin-top:auto!important}.me-xl-0{margin-right:0!important}.me-xl-1{margin-right:.25rem!important}.me-xl-2{margin-right:.5rem!important}.me-xl-3{margin-right:1rem!important}.me-xl-4{margin-right:1.5rem!important}.me-xl-5{margin-right:3rem!important}.me-xl-auto{margin-right:auto!important}.mb-xl-0{margin-bottom:0!important}.mb-xl-1{margin-bottom:.25rem!important}.mb-xl-2{margin-bottom:.5rem!important}.mb-xl-3{margin-bottom:1rem!important}.mb-xl-4{margin-bottom:1.5rem!important}.mb-xl-5{margin-bottom:3rem!important}.mb-xl-auto{margin-bottom:auto!important}.ms-xl-0{margin-left:0!important}.ms-xl-1{margin-left:.25rem!important}.ms-xl-2{margin-left:.5rem!important}.ms-xl-3{margin-left:1rem!important}.ms-xl-4{margin-left:1.5rem!important}.ms-xl-5{margin-left:3rem!important}.ms-xl-auto{margin-left:auto!important}.p-xl-0{padding:0!important}.p-xl-1{padding:.25rem!important}.p-xl-2{padding:.5rem!important}.p-xl-3{padding:1rem!important}.p-xl-4{padding:1.5rem!important}.p-xl-5{padding:3rem!important}.px-xl-0{padding-right:0!important;padding-left:0!important}.px-xl-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-xl-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-xl-3{padding-right:1rem!important;padding-left:1rem!important}.px-xl-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-xl-5{padding-right:3rem!important;padding-left:3rem!important}.py-xl-0{padding-top:0!important;padding-bottom:0!important}.py-xl-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-xl-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-xl-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-xl-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-xl-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-xl-0{padding-top:0!important}.pt-xl-1{padding-top:.25rem!important}.pt-xl-2{padding-top:.5rem!important}.pt-xl-3{padding-top:1rem!important}.pt-xl-4{padding-top:1.5rem!important}.pt-xl-5{padding-top:3rem!important}.pe-xl-0{padding-right:0!important}.pe-xl-1{padding-right:.25rem!important}.pe-xl-2{padding-right:.5rem!important}.pe-xl-3{padding-right:1rem!important}.pe-xl-4{padding-right:1.5rem!important}.pe-xl-5{padding-right:3rem!important}.pb-xl-0{padding-bottom:0!important}.pb-xl-1{padding-bottom:.25rem!important}.pb-xl-2{padding-bottom:.5rem!important}.pb-xl-3{padding-bottom:1rem!important}.pb-xl-4{padding-bottom:1.5rem!important}.pb-xl-5{padding-bottom:3rem!important}.ps-xl-0{padding-left:0!important}.ps-xl-1{padding-left:.25rem!important}.ps-xl-2{padding-left:.5rem!important}.ps-xl-3{padding-left:1rem!important}.ps-xl-4{padding-left:1.5rem!important}.ps-xl-5{padding-left:3rem!important}.gap-xl-0{gap:0!important}.gap-xl-1{gap:.25rem!important}.gap-xl-2{gap:.5rem!important}.gap-xl-3{gap:1rem!important}.gap-xl-4{gap:1.5rem!important}.gap-xl-5{gap:3rem!important}.row-gap-xl-0{row-gap:0!important}.row-gap-xl-1{row-gap:.25rem!important}.row-gap-xl-2{row-gap:.5rem!important}.row-gap-xl-3{row-gap:1rem!important}.row-gap-xl-4{row-gap:1.5rem!important}.row-gap-xl-5{row-gap:3rem!important}.column-gap-xl-0{-moz-column-gap:0!important;column-gap:0!important}.column-gap-xl-1{-moz-column-gap:0.25rem!important;column-gap:.25rem!important}.column-gap-xl-2{-moz-column-gap:0.5rem!important;column-gap:.5rem!important}.column-gap-xl-3{-moz-column-gap:1rem!important;column-gap:1rem!important}.column-gap-xl-4{-moz-column-gap:1.5rem!important;column-gap:1.5rem!important}.column-gap-xl-5{-moz-column-gap:3rem!important;column-gap:3rem!important}.text-xl-start{text-align:left!important}.text-xl-end{text-align:right!important}.text-xl-center{text-align:center!important}}@media (min-width:1400px){.float-xxl-start{float:left!important}.float-xxl-end{float:right!important}.float-xxl-none{float:none!important}.object-fit-xxl-contain{-o-object-fit:contain!important;object-fit:contain!important}.object-fit-xxl-cover{-o-object-fit:cover!important;object-fit:cover!important}.object-fit-xxl-fill{-o-object-fit:fill!important;object-fit:fill!important}.object-fit-xxl-scale{-o-object-fit:scale-down!important;object-fit:scale-down!important}.object-fit-xxl-none{-o-object-fit:none!important;object-fit:none!important}.d-xxl-inline{display:inline!important}.d-xxl-inline-block{display:inline-block!important}.d-xxl-block{display:block!important}.d-xxl-grid{display:grid!important}.d-xxl-inline-grid{display:inline-grid!important}.d-xxl-table{display:table!important}.d-xxl-table-row{display:table-row!important}.d-xxl-table-cell{display:table-cell!important}.d-xxl-flex{display:flex!important}.d-xxl-inline-flex{display:inline-flex!important}.d-xxl-none{display:none!important}.flex-xxl-fill{flex:1 1 auto!important}.flex-xxl-row{flex-direction:row!important}.flex-xxl-column{flex-direction:column!important}.flex-xxl-row-reverse{flex-direction:row-reverse!important}.flex-xxl-column-reverse{flex-direction:column-reverse!important}.flex-xxl-grow-0{flex-grow:0!important}.flex-xxl-grow-1{flex-grow:1!important}.flex-xxl-shrink-0{flex-shrink:0!important}.flex-xxl-shrink-1{flex-shrink:1!important}.flex-xxl-wrap{flex-wrap:wrap!important}.flex-xxl-nowrap{flex-wrap:nowrap!important}.flex-xxl-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-xxl-start{justify-content:flex-start!important}.justify-content-xxl-end{justify-content:flex-end!important}.justify-content-xxl-center{justify-content:center!important}.justify-content-xxl-between{justify-content:space-between!important}.justify-content-xxl-around{justify-content:space-around!important}.justify-content-xxl-evenly{justify-content:space-evenly!important}.align-items-xxl-start{align-items:flex-start!important}.align-items-xxl-end{align-items:flex-end!important}.align-items-xxl-center{align-items:center!important}.align-items-xxl-baseline{align-items:baseline!important}.align-items-xxl-stretch{align-items:stretch!important}.align-content-xxl-start{align-content:flex-start!important}.align-content-xxl-end{align-content:flex-end!important}.align-content-xxl-center{align-content:center!important}.align-content-xxl-between{align-content:space-between!important}.align-content-xxl-around{align-content:space-around!important}.align-content-xxl-stretch{align-content:stretch!important}.align-self-xxl-auto{align-self:auto!important}.align-self-xxl-start{align-self:flex-start!important}.align-self-xxl-end{align-self:flex-end!important}.align-self-xxl-center{align-self:center!important}.align-self-xxl-baseline{align-self:baseline!important}.align-self-xxl-stretch{align-self:stretch!important}.order-xxl-first{order:-1!important}.order-xxl-0{order:0!important}.order-xxl-1{order:1!important}.order-xxl-2{order:2!important}.order-xxl-3{order:3!important}.order-xxl-4{order:4!important}.order-xxl-5{order:5!important}.order-xxl-last{order:6!important}.m-xxl-0{margin:0!important}.m-xxl-1{margin:.25rem!important}.m-xxl-2{margin:.5rem!important}.m-xxl-3{margin:1rem!important}.m-xxl-4{margin:1.5rem!important}.m-xxl-5{margin:3rem!important}.m-xxl-auto{margin:auto!important}.mx-xxl-0{margin-right:0!important;margin-left:0!important}.mx-xxl-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-xxl-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-xxl-3{margin-right:1rem!important;margin-left:1rem!important}.mx-xxl-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-xxl-5{margin-right:3rem!important;margin-left:3rem!important}.mx-xxl-auto{margin-right:auto!important;margin-left:auto!important}.my-xxl-0{margin-top:0!important;margin-bottom:0!important}.my-xxl-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-xxl-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-xxl-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-xxl-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-xxl-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-xxl-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-xxl-0{margin-top:0!important}.mt-xxl-1{margin-top:.25rem!important}.mt-xxl-2{margin-top:.5rem!important}.mt-xxl-3{margin-top:1rem!important}.mt-xxl-4{margin-top:1.5rem!important}.mt-xxl-5{margin-top:3rem!important}.mt-xxl-auto{margin-top:auto!important}.me-xxl-0{margin-right:0!important}.me-xxl-1{margin-right:.25rem!important}.me-xxl-2{margin-right:.5rem!important}.me-xxl-3{margin-right:1rem!important}.me-xxl-4{margin-right:1.5rem!important}.me-xxl-5{margin-right:3rem!important}.me-xxl-auto{margin-right:auto!important}.mb-xxl-0{margin-bottom:0!important}.mb-xxl-1{margin-bottom:.25rem!important}.mb-xxl-2{margin-bottom:.5rem!important}.mb-xxl-3{margin-bottom:1rem!important}.mb-xxl-4{margin-bottom:1.5rem!important}.mb-xxl-5{margin-bottom:3rem!important}.mb-xxl-auto{margin-bottom:auto!important}.ms-xxl-0{margin-left:0!important}.ms-xxl-1{margin-left:.25rem!important}.ms-xxl-2{margin-left:.5rem!important}.ms-xxl-3{margin-left:1rem!important}.ms-xxl-4{margin-left:1.5rem!important}.ms-xxl-5{margin-left:3rem!important}.ms-xxl-auto{margin-left:auto!important}.p-xxl-0{padding:0!important}.p-xxl-1{padding:.25rem!important}.p-xxl-2{padding:.5rem!important}.p-xxl-3{padding:1rem!important}.p-xxl-4{padding:1.5rem!important}.p-xxl-5{padding:3rem!important}.px-xxl-0{padding-right:0!important;padding-left:0!important}.px-xxl-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-xxl-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-xxl-3{padding-right:1rem!important;padding-left:1rem!important}.px-xxl-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-xxl-5{padding-right:3rem!important;padding-left:3rem!important}.py-xxl-0{padding-top:0!important;padding-bottom:0!important}.py-xxl-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-xxl-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-xxl-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-xxl-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-xxl-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-xxl-0{padding-top:0!important}.pt-xxl-1{padding-top:.25rem!important}.pt-xxl-2{padding-top:.5rem!important}.pt-xxl-3{padding-top:1rem!important}.pt-xxl-4{padding-top:1.5rem!important}.pt-xxl-5{padding-top:3rem!important}.pe-xxl-0{padding-right:0!important}.pe-xxl-1{padding-right:.25rem!important}.pe-xxl-2{padding-right:.5rem!important}.pe-xxl-3{padding-right:1rem!important}.pe-xxl-4{padding-right:1.5rem!important}.pe-xxl-5{padding-right:3rem!important}.pb-xxl-0{padding-bottom:0!important}.pb-xxl-1{padding-bottom:.25rem!important}.pb-xxl-2{padding-bottom:.5rem!important}.pb-xxl-3{padding-bottom:1rem!important}.pb-xxl-4{padding-bottom:1.5rem!important}.pb-xxl-5{padding-bottom:3rem!important}.ps-xxl-0{padding-left:0!important}.ps-xxl-1{padding-left:.25rem!important}.ps-xxl-2{padding-left:.5rem!important}.ps-xxl-3{padding-left:1rem!important}.ps-xxl-4{padding-left:1.5rem!important}.ps-xxl-5{padding-left:3rem!important}.gap-xxl-0{gap:0!important}.gap-xxl-1{gap:.25rem!important}.gap-xxl-2{gap:.5rem!important}.gap-xxl-3{gap:1rem!important}.gap-xxl-4{gap:1.5rem!important}.gap-xxl-5{gap:3rem!important}.row-gap-xxl-0{row-gap:0!important}.row-gap-xxl-1{row-gap:.25rem!important}.row-gap-xxl-2{row-gap:.5rem!important}.row-gap-xxl-3{row-gap:1rem!important}.row-gap-xxl-4{row-gap:1.5rem!important}.row-gap-xxl-5{row-gap:3rem!important}.column-gap-xxl-0{-moz-column-gap:0!important;column-gap:0!important}.column-gap-xxl-1{-moz-column-gap:0.25rem!important;column-gap:.25rem!important}.column-gap-xxl-2{-moz-column-gap:0.5rem!important;column-gap:.5rem!important}.column-gap-xxl-3{-moz-column-gap:1rem!important;column-gap:1rem!important}.column-gap-xxl-4{-moz-column-gap:1.5rem!important;column-gap:1.5rem!important}.column-gap-xxl-5{-moz-column-gap:3rem!important;column-gap:3rem!important}.text-xxl-start{text-align:left!important}.text-xxl-end{text-align:right!important}.text-xxl-center{text-align:center!important}}@media (min-width:1200px){.fs-1{font-size:2.5rem!important}.fs-2{font-size:2rem!important}.fs-3{font-size:1.75rem!important}.fs-4{font-size:1.5rem!important}}@media print{.d-print-inline{display:inline!important}.d-print-inline-block{display:inline-block!important}.d-print-block{display:block!important}.d-print-grid{display:grid!important}.d-print-inline-grid{display:inline-grid!important}.d-print-table{display:table!important}.d-print-table-row{display:table-row!important}.d-print-table-cell{display:table-cell!important}.d-print-flex{display:flex!important}.d-print-inline-flex{display:inline-flex!important}.d-print-none{display:none!important}} +/*# sourceMappingURL=bootstrap.min.css.map */ \ No newline at end of file diff --git a/tests/assets/images/testbox-icon-full-L.svg b/tests/assets/images/testbox-icon-full-L.svg new file mode 100755 index 000000000..a3932c87f --- /dev/null +++ b/tests/assets/images/testbox-icon-full-L.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/tests/assets/images/testbox-logo-full-dark-L.svg b/tests/assets/images/testbox-logo-full-dark-L.svg new file mode 100755 index 000000000..f2c98de00 --- /dev/null +++ b/tests/assets/images/testbox-logo-full-dark-L.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/assets/images/testbox-logo-full-light-L.svg b/tests/assets/images/testbox-logo-full-light-L.svg new file mode 100755 index 000000000..57c7d784f --- /dev/null +++ b/tests/assets/images/testbox-logo-full-light-L.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/assets/js/run.js b/tests/assets/js/run.js new file mode 100755 index 000000000..b8cea069f --- /dev/null +++ b/tests/assets/js/run.js @@ -0,0 +1,1239 @@ +document.addEventListener( "alpine:init", () => { + Alpine.data( "testboxRun", () => ( { + // State + isLoading: true, + isRunning: false, + globalError: null, + globalErrorDetail: null, + globalErrorUrl: null, + + // Preferences (Merged with URL options) + preferences: { + theme: "dark", + editor: "vscode", + runnerUrl: "", + directory: "", + recurse: true, + bundlesPattern: "", + labels: "", + excludes: "" + }, + + // Data + bundles: [], + globalStats: { + totalBundles: 0, + totalSuites: 0, + totalSpecs: 0, + totalDuration: 0, + totalPass: 0, + totalFail: 0, + totalError: 0, + totalSkipped: 0 + }, + + // UI Filters + searchQuery: "", + statusFilters: { + passed: true, + failed: true, + error: true, + skipped: true + }, + + // The active EventSource connection for streaming test results + eventSource: null, + // Indicates testRunEnd was received for the current stream + runCompleted: false, + // Indicates we are intentionally stopping/closing the current stream + isStopping: false, + // Set to the bundle path when running a single bundle; null when running all + activeBundlePath: null, + // Counts specEnd events received during the current run for progress tracking + specsCompleted: 0, + // Internal flag to ensure init runs only once + _initialized: false, + + /** + * Initializes the Alpine Component. Runs exactly once to load preferences and kick off the dry run. + */ + init() { + if ( this._initialized ) return; + this._initialized = true; + + this.loadPreferences(); + this.fetchDryRun(); + this.initKeyboardShortcuts(); + }, + + /** + * Loads configuration from LocalStorage and merges it with the URL initial options. + */ + loadPreferences() { + // Start with defaults, override with localStorage, then override with URL params on first load + let savedPref = localStorage.getItem( "testboxPreferences" ); + let urlParams = new URLSearchParams( window.location.search ); + let hasSavedPreferences = false; + + if ( savedPref ) { + try { + Object.assign( this.preferences, JSON.parse( savedPref ) ); + hasSavedPreferences = true; + } catch ( e ) { + console.error( "Failed to parse saved preferences", e ); + localStorage.removeItem( "testboxPreferences" ); + } + } + + // Apply window.initialOptions injected from run.bxm URL params + if ( window.initialOptions ) { + // First load (no localStorage): apply all server-provided defaults. + // After preferences exist: only apply explicit URL overrides. + for ( let key in window.initialOptions ) { + let hasValue = window.initialOptions[ key ] !== null && window.initialOptions[ key ] !== undefined && window.initialOptions[ key ] !== ""; + let shouldApply = hasValue && ( !hasSavedPreferences || urlParams.has( key ) ); + + if ( shouldApply ) { + // typecast recurse correctly + if ( key === "recurse" ) { + this.preferences[ key ] = ( window.initialOptions[ key ] === true || window.initialOptions[ key ] === "true" ); + } else { + this.preferences[ key ] = window.initialOptions[ key ]; + } + } + } + } + }, + + /** + * Persists the current configuration to LocalStorage and reloads the window to apply them. + */ + savePreferences() { + localStorage.setItem( "testboxPreferences", JSON.stringify( this.preferences ) ); + // Reload page to apply new settings via URL or we could just fetchDryRun again, but reload is cleaner to reset state + window.location.reload(); + }, + + /** + * Clears all saved preferences from LocalStorage and reloads the window to restore defaults. + */ + resetPreferences() { + localStorage.removeItem( "testboxPreferences" ); + window.location.reload(); + }, + + /** + * Toggles the UI theme between dark and light modes, persisting the choice. + */ + toggleTheme() { + this.preferences.theme = this.preferences.theme === "dark" ? "light" : "dark"; + localStorage.setItem( "testboxPreferences", JSON.stringify( this.preferences ) ); + }, + + /** + * Builds the runner URL with query parameters based on preferences and any additional params provided. + * This allows us to easily switch between dry run and actual run with streaming, as well as apply filters. + * + * @param {object} params - Dictionary of additional URL overrides to inject + * @returns {string} The fully constructed URL. + */ + buildRunnerUrl( params = {} ) { + // Start with the base runner URL and append query parameters + let url = new URL( this.preferences.runnerUrl, window.location.href ); + + // Core parameters from preferences + url.searchParams.append( "directory", this.preferences.directory ); + url.searchParams.append( "recurse", this.preferences.recurse ); + url.searchParams.append( "bundlesPattern", this.preferences.bundlesPattern ); + + if ( this.preferences.labels ) { + url.searchParams.append( "labels", this.preferences.labels ); + } + + if ( this.preferences.excludes ) { + url.searchParams.append( "excludes", this.preferences.excludes ); + } + + for ( let key in params ) { + url.searchParams.append( key, params[ key ] ); + } + + return url.toString(); + }, + + /** + * Performs a dry run to fetch the test structure and initialize the UI state before actual execution. + * This allows us to display all bundles/suites/specs in a pending state and then update them in real-time as the tests run. + */ + async fetchDryRun() { + this.isLoading = true; + this.globalError = null; + this.globalErrorUrl = null; + + let url = this.buildRunnerUrl( { dryRun : true } ); + + try { + let response = await fetch( url ); + if ( !response.ok ) { + throw new Error( `HTTP Error: ${ response.status } ${ response.statusText }` ); + } + + let text = await response.text(); + let data; + try { + data = JSON.parse( text ); + } catch ( e ) { + throw new Error( "Invalid JSON returned from runner." ); + } + + this.initializeState( data ); + } catch ( e ) { + this.globalError = "Failed to load test structure."; + this.globalErrorDetail = e.message; + this.globalErrorUrl = url; + } finally { + this.isLoading = false; + } + }, + + /** + * Parses the dry run JSON payload and maps it into the reactive bundles state array. + * + * @param {object} data - The dry run JSON payload from the server. + */ + initializeState( data ) { + // Reset stats + this.globalStats = { + totalBundles: 0, + totalSuites: 0, + totalSpecs: 0, + totalDuration: 0, + totalPass: 0, + totalFail: 0, + totalError: 0, + totalSkipped: 0 + }; + + this.bundles = []; + + if ( !data.bundles ) return; + + data.bundles.forEach( ( b, bundleIdx ) => { + let bundleKey = b.path || b.name || ( "bundle-" + bundleIdx ); + let bundle = { + id: b.id || bundleKey, + uid: bundleKey, + name: b.name, + path: b.path, + status: "pending", + expanded: false, + type: "bundle", + totalDuration: 0, + totalPass: 0, + totalFail: 0, + totalError: 0, + totalSkipped: 0, + hasStats: false, + debugBuffer: [], + showDebug: false, + suites: [], + specs: [] // top-level specs + }; + + if ( b.suites && b.suites.length ) { + this.collectSuiteNodes( { + suites: b.suites, + bundle, + bundleUid: bundle.uid + } ); + } else if ( b.specs && b.specs.length ) { + // xUnit or no suites + b.specs.forEach( ( sp, specIdx ) => { + bundle.specs.push( this.createSpecNode( sp, bundle.uid, null, specIdx ) ); + } ); + } + + this.bundles.push( bundle ); + } ); + }, + + /** + * Recursively collects suite nodes so nested dry-run suites are visible in the UI. + * + * @param {array} suites - The incoming dry-run suite array. + * @param {object} bundle - The mutable bundle state node. + * @param {string} bundleUid - Stable bundle identifier for local UI node IDs. + * @param {string} parentKey - Internal parent key to keep generated IDs unique. + */ + collectSuiteNodes( { suites, bundle, bundleUid, parentKey = "root" } ) { + suites.forEach( ( suitePayload, suiteIdx ) => { + let suiteSourceId = suitePayload.id || suitePayload.name || ( "suite-" + suiteIdx ); + let suiteUid = bundleUid + "::suite::" + parentKey + "::" + suiteSourceId + "::" + suiteIdx; + let suite = { + id: suiteUid, + sourceId: suiteSourceId, + name: suitePayload.name, + status: suitePayload.skip ? "skipped" : "pending", + expanded: false, + specs: [] + }; + + if ( suitePayload.specs && suitePayload.specs.length ) { + suitePayload.specs.forEach( ( sp, specIdx ) => { + suite.specs.push( this.createSpecNode( sp, bundleUid, suiteUid, specIdx ) ); + } ); + } + + bundle.suites.push( suite ); + + if ( suitePayload.suites && suitePayload.suites.length ) { + this.collectSuiteNodes( { + suites: suitePayload.suites, + bundle, + bundleUid, + parentKey: suiteUid + } ); + } + } ); + }, + + /** + * Helper to factory generate a spec node containing unified baseline reactive properties. + * + * @param {object} sp - The target spec metadata payload. + * @returns {object} The initialized reactive node specification. + */ + createSpecNode( sp, bundleUid, suiteUid = null, specIdx = 0 ) { + let sourceId = sp.id || sp.name || ( "spec-" + specIdx ); + let uidBase = suiteUid || bundleUid || "bundle"; + return { + id: uidBase + "::spec::" + sourceId + "::" + specIdx, + sourceId, + bundleUid, + suiteUid, + name: sp.name, + status: sp.skip ? "skipped" : "pending", + totalDuration: 0, + failMessage: "", + failDetail: "", + failOrigin: [], + failStacktrace: "", + error: null, + hasExecuted: false, + showFailureOrigin: false, + showStacktrace: false + }; + }, + + /** + * Returns the primary failure context to preview (first fail origin or first error tagContext). + */ + getPrimaryContext( spec ) { + if ( spec?.status === "failed" && Array.isArray( spec.failOrigin ) && spec.failOrigin.length ) { + return spec.failOrigin[ 0 ]; + } + + if ( spec?.error && Array.isArray( spec.error.tagContext ) && spec.error.tagContext.length ) { + return spec.error.tagContext[ 0 ]; + } + + if ( Array.isArray( spec?.failOrigin ) && spec.failOrigin.length ) { + return spec.failOrigin[ 0 ]; + } + + return null; + }, + + /** + * Whether a spec status should render failure tooling. + */ + isFailedOrErrored( spec ) { + return !!spec && spec.hasExecuted === true && ( spec.status === "failed" || spec.status === "error" ); + }, + + /** + * Single source of truth: should the failure controls toolbar be shown for this spec? + * A spec that has executed and landed in a failed/error state always has something to show. + */ + shouldShowFailureControls( spec ) { + return this.isFailedOrErrored( spec ); + }, + + /** + * Whether this spec has any extended failure/error data to render in the expandable panel. + */ + hasFailureDetails( spec ) { + if ( !this.isFailedOrErrored( spec ) ) return false; + + return !!( + this.getPrimaryContext( spec ) || + ( Array.isArray( spec.failOrigin ) && spec.failOrigin.length ) || + spec.failDetail || + spec.failStacktrace || + spec.error?.stackTrace + ); + }, + + /** + * Formats context location as template:line for UI labels. + */ + formatContextLabel( context ) { + if ( !context?.template ) return ""; + return context.template + ( context.line ? ":" + context.line : "" ); + }, + + /** + * Normalizes code print HTML for rendering and falls back to escaped plain code print. + */ + getContextCodeHTML( context ) { + if ( !context ) return ""; + if ( context.codePrintHTML ) return String( context.codePrintHTML ); + if ( context.codePrintPlain ) return "
" + this.escapeHtml( context.codePrintPlain ) + "
"; + return ""; + }, + + /** + * Escapes unsafe HTML chars for safe x-html fallback rendering. + */ + escapeHtml( value ) { + return String( value ) + .replaceAll( "&", "&" ) + .replaceAll( "<", "<" ) + .replaceAll( ">", ">" ) + .replaceAll( '"', """ ) + .replaceAll( "'", "'" ); + }, + + /** + * Opens file+line in VS Code (or configured editor scheme) from failure origin/tag context. + */ + openInEditor( template, line = 1 ) { + if ( !template ) return; + + let editorScheme = this.preferences.editor || "vscode"; + let safePath = encodeURI( template ).replaceAll( "#", "%23" ); + let safeLine = Number.isFinite( Number( line ) ) ? Number( line ) : 1; + + window.location.href = `${ editorScheme }://file${ safePath }:${ safeLine }`; + }, + + /** + * Generates an editor deep-link href for a file+line. + */ + getEditorHref( template, line = 1 ) { + if ( !template ) return "#"; + let editorScheme = this.preferences.editor || "vscode"; + let safePath = encodeURI( template ).replaceAll( "#", "%23" ); + let safeLine = Number.isFinite( Number( line ) ) ? Number( line ) : 1; + return `${ editorScheme }://file${ safePath }:${ safeLine }`; + }, + + /** + * Extracts and normalizes a lowercase extension from a stacktrace location path. + */ + getFileExtension( filePath ) { + if ( !filePath ) return ""; + let normalized = String( filePath ).trim(); + let lastDot = normalized.lastIndexOf( "." ); + if ( lastDot < 0 ) return ""; + return normalized.slice( lastDot + 1 ).toLowerCase(); + }, + + /** + * Returns Java class FQN from a stacktrace line like: + * at ortus.boxlang.runtime.interop.DynamicInteropService.dereferenceAndInvoke(DynamicInteropService.java:2266) + */ + extractJavaClassFqn( line ) { + if ( !line ) return ""; + let match = String( line ).match( /^\s*at\s+([A-Za-z0-9_$.]+)\.[A-Za-z0-9_$<>]+\([^)]*\.java:\d+\)\s*$/ ); + return match ? match[ 1 ] : ""; + }, + + /** + * Builds a GitHub URL to a BoxLang runtime Java source file. + */ + getBoxLangGithubHref( classFqn, line = 1 ) { + if ( !classFqn ) return ""; + let safeLine = Number.isFinite( Number( line ) ) ? Number( line ) : 1; + let classPath = classFqn.replaceAll( ".", "/" ) + ".java"; + return `https://github.com/ortus-boxlang/boxlang/blob/main/src/main/java/${ classPath }#L${ safeLine }`; + }, + + /** + * Resolves whether a stacktrace location should be linked and where. + */ + resolveStacktraceLocationLink( filePath, lineNo, stackLine ) { + const ext = this.getFileExtension( filePath ); + const editorExtensions = new Set( [ "cfc", "cfm", "bxm", "bx", "bxs" ] ); + + if ( editorExtensions.has( ext ) ) { + return { + href: this.getEditorHref( filePath, lineNo ), + newWindow: false + }; + } + + if ( ext === "java" ) { + let classFqn = this.extractJavaClassFqn( stackLine ); + if ( classFqn.startsWith( "ortus.boxlang.runtime." ) ) { + return { + href: this.getBoxLangGithubHref( classFqn, lineNo ), + newWindow: true + }; + } + } + + return null; + }, + + /** + * Returns the stacktrace text for the spec (failure first, then error fallback). + */ + getStacktrace( spec ) { + return spec?.failStacktrace || spec?.error?.stackTrace || ""; + }, + + /** + * Whether stacktrace exists for display. + */ + hasStacktrace( spec ) { + if ( !this.isFailedOrErrored( spec ) ) return false; + return !!this.getStacktrace( spec ); + }, + + /** + * Formats stacktrace text to HTML with highlighted locations and caused-by lines. + */ + formatStacktraceHTML( stacktrace ) { + if ( !stacktrace ) return ""; + + const locationPattern = /\(([^\)]+?):(\d+)\)/g; + return String( stacktrace ) + .split( /\r?\n/ ) + .map( ( line ) => { + let rendered = ""; + let lastIndex = 0; + + locationPattern.lastIndex = 0; + for ( const match of line.matchAll( locationPattern ) ) { + const full = match[ 0 ]; + const filePath = match[ 1 ]; + const lineNo = Number( match[ 2 ] ); + const matchIndex = match.index ?? 0; + + rendered += this.escapeHtml( line.slice( lastIndex, matchIndex ) ); + + const linkInfo = this.resolveStacktraceLocationLink( filePath, lineNo, line ); + const label = this.escapeHtml( `${ filePath }:${ lineNo }` ); + + if ( linkInfo?.href ) { + const href = this.escapeHtml( linkInfo.href ); + const targetAttrs = linkInfo.newWindow ? ' target="_blank" rel="noopener noreferrer"' : ""; + rendered += `(${ label })`; + } else { + rendered += `${ this.escapeHtml( full ) }`; + } + + lastIndex = matchIndex + full.length; + } + + rendered += this.escapeHtml( line.slice( lastIndex ) ); + + if ( /^\s*caused by\s*:/i.test( line ) ) { + rendered = `${ rendered }`; + } + + return `
${ rendered }
`; + } ) + .join( "" ); + }, + + /** + * Dynamically calculates global counting statistics across all bundles, suites, and specs (Getter). + * - After a completed run: returns the accurate stats received from the testRunEnd SSE event. + * - During a run or before any run: computes structure from the bundle tree. For single-bundle + * runs the structure is scoped to the active bundle, giving an accurate progress bar denominator. + */ + get metaGlobalStats() { + // Post-run: return the accurate server-reported counts verbatim. + if ( this.runCompleted ) { + return { ...this.globalStats }; + } + + // During a run / initial load: derive structure from the bundle tree. + // For single-bundle runs scope to only the active bundle so the progress + // bar denominator matches what the server is actually executing. + let targetBundles = ( this.isRunning && this.activeBundlePath ) + ? this.bundles.filter( b => b.path === this.activeBundlePath ) + : this.bundles; + + let totalB = ( this.isRunning && this.activeBundlePath ) ? 1 : this.bundles.length; + let totalSu = 0; + let totalSp = 0; + + targetBundles.forEach( b => { + totalSu += b.suites.length; + totalSp += b.specs.length; + b.suites.forEach( s => { + totalSp += s.specs.length; + } ); + } ); + + return { + totalBundles: totalB, + totalSuites: totalSu, + totalSpecs: totalSp, + totalDuration: this.globalStats.totalDuration, + totalPass: this.globalStats.totalPass, + totalFail: this.globalStats.totalFail, + totalError: this.globalStats.totalError, + totalSkipped: this.globalStats.totalSkipped + }; + }, + + /** + * Overall run status for styling the results summary card border. + */ + get globalRunStatus() { + if ( this.isRunning ) return 'running'; + const s = this.metaGlobalStats; + if ( s.totalFail > 0 ) return 'failed'; + if ( s.totalError > 0 ) return 'error'; + if ( s.totalPass > 0 ) return 'passed'; + if ( s.totalSkipped > 0 ) return 'skipped'; + return 'pending'; + }, + + /** + * Returns the tree of bundles recursively filtered by search query and status selections (Getter). + */ + get filteredBundles() { + return this.bundles.filter( b => { + // if search matches bundle name, show it + let searchMatch = b.path.toLowerCase().includes( this.searchQuery.toLowerCase() ); + let statusMatch = this.isStatusVisible( b.status ); + + if ( !statusMatch ) return false; + + if ( searchMatch ) return true; + + // check children + let hasVisibleSuite = b.suites.some( s => this.isSuiteVisible( s, true ) ); + let hasVisibleSpec = b.specs.some( sp => this.isSpecVisible( sp, true ) ); + + return hasVisibleSuite || hasVisibleSpec; + } ); + }, + + /** + * Evaluates a suite's visibility based on active user filters and direct matches. + * + * @param {object} suite - Target suite configuration. + * @param {boolean} ignoreParentMatch - Ignores parent scoping matches. + * @returns {boolean} Whether the item should be visible in HTML tree. + */ + isSuiteVisible( suite, ignoreParentMatch = false ) { + let statusMatch = this.isStatusVisible( suite.status ); + if ( !statusMatch ) return false; + + if ( !ignoreParentMatch && this.searchQuery && suite.name.toLowerCase().includes( this.searchQuery.toLowerCase() ) ) return true; + + return suite.specs.some( sp => this.isSpecVisible( sp, true ) ) || ( suite.name.toLowerCase().includes( this.searchQuery.toLowerCase() ) ); + }, + + /** + * Evaluates a spec's visibility based on name search and explicit status filters. + * + * @param {object} spec - Target spec configuration. + * @param {boolean} ignoreStatus - Force ignoring status filters. + * @returns {boolean} Whether the item should be visible in HTML tree. + */ + isSpecVisible( spec, ignoreStatus = false ) { + if ( !ignoreStatus && !this.isStatusVisible( spec.status ) ) return false; + return spec.name.toLowerCase().includes( this.searchQuery.toLowerCase() ); + }, + + /** + * Returns whether a spec duration badge should be shown. + * Completed specs can legitimately report 0ms and should still display a badge. + * + * @param {object} spec - Target spec node. + * @returns {boolean} True when duration is renderable for this spec. + */ + shouldShowSpecDuration( spec ) { + if ( !spec ) return false; + if ( !spec.hasExecuted ) return false; + if ( spec.status === "pending" || spec.status === "running" ) return false; + return spec.totalDuration !== undefined && spec.totalDuration !== null; + }, + + /** + * Formats a spec duration value as a badge label. + * + * @param {object} spec - Target spec node. + * @returns {string} Duration text. + */ + formatSpecDuration( spec ) { + return ( spec?.totalDuration ?? 0 ) + "ms"; + }, + + /** + * Checks if a specific status type is active in the global toggles. + * Pending and Running states bypass strict filter. + * + * @param {string} status - Test execution status condition. + * @returns {boolean} Resultant status eligibility. + */ + isStatusVisible( status ) { + // 'pending' and 'running' are always visible unless we are strictly filtering + if ( status === "pending" || status === "running" ) return true; + if ( status === "passed" && !this.statusFilters.passed ) return false; + if ( status === "failed" && !this.statusFilters.failed ) return false; + if ( status === "error" && !this.statusFilters.error ) return false; + if ( status === "skipped" && !this.statusFilters.skipped ) return false; + return true; + }, + + /** + * Retrieves the bootstrap icon visual representation for a given status or node type. + * + * @param {string} status - Node execution status. + * @param {string} type - Node classification (bundle, suite, spec). + * @returns {string} Fully qualified bootstrap icon string CSS. + */ + getStatusIcon( status, type = "" ) { + switch ( status ) { + case "passed": return "bi-check-circle-fill text-success"; + case "failed": return "bi-x-circle-fill text-danger"; + case "error": return "bi-exclamation-octagon-fill text-warning"; + case "skipped": return "bi-dash-circle-fill text-info"; + case "running": return "bi-hourglass-split text-primary spinner-icon"; + default: + if ( type === "bundle" ) return "bi-box"; + if ( type === "suite" ) return "bi-folder2-open"; + // spec + return "bi-circle text-secondary"; + } + }, + + /** + * Transforms a test execution status into a specific Bootstrap contextual color class. + * + * @param {string} status - Target execution status label. + * @returns {string} Resolving BS semantic intent class (e.g., success/danger). + */ + getStatusColorClass( status ) { + switch ( status ) { + case "passed": return "success"; + case "failed": return "danger"; + case "error": return "warning"; + case "skipped": return "info"; + case "running": return "primary"; + default: return "secondary"; + } + }, + + /** + * Whether a bundle has aggregate results worth showing in the strip. + * + * @param {object} bundle - Target bundle node. + * @returns {boolean} + */ + computeBundleHasStats( bundle ) { + if ( !bundle ) return false; + return ( ( bundle.totalPass || 0 ) + ( bundle.totalFail || 0 ) + ( bundle.totalError || 0 ) + ( bundle.totalSkipped || 0 ) ) > 0; + }, + + /** + * Resets duration, statistics, and state indicators back to "pending". + * When bundlePath is provided, only that bundle (and its children) is reset; + * all other bundles are left untouched so their previous results remain visible. + * + * @param {string|null} bundlePath - If set, only reset this bundle; otherwise reset all. + */ + resetExecutionState( bundlePath = null ) { + this.runCompleted = false; + this.isStopping = false; + + const resetSpec = ( sp ) => { + sp.status = "pending"; + sp.totalDuration = 0; + sp.failMessage = ""; + sp.failDetail = ""; + sp.failOrigin = []; + sp.failStacktrace = ""; + sp.error = null; + sp.hasExecuted = false; + sp.showFailureOrigin = false; + sp.showStacktrace = false; + }; + + const resetBundle = ( b ) => { + b.status = "pending"; + b.totalDuration = 0; + b.totalPass = 0; + b.totalFail = 0; + b.totalError = 0; + b.totalSkipped = 0; + b.hasStats = false; + b.suites.forEach( s => { + s.status = "pending"; + s.specs.forEach( resetSpec ); + } ); + b.specs.forEach( resetSpec ); + }; + + if ( bundlePath ) { + const b = this.bundles.find( b => b.path === bundlePath ); + if ( b ) resetBundle( b ); + } else { + this.bundles.forEach( resetBundle ); + } + + // Always wipe run stats so metaGlobalStats reflects the new run, not the previous one. + this.globalStats = { + totalBundles: 0, totalSuites: 0, totalSpecs: 0, totalDuration: 0, + totalPass: 0, totalFail: 0, totalError: 0, totalSkipped: 0 + }; + this.specsCompleted = 0; + this.globalError = null; + this.globalErrorDetail = null; + this.globalErrorUrl = null; + }, + + /** + * Initiates a full systematic test run handling all loaded framework bundles. + */ + runAllTests() { + this.activeBundlePath = null; + this.resetExecutionState(); + this.isRunning = true; + this.startEventSource( this.buildRunnerUrl( { streaming : true } ) ); + }, + + /** + * Initiates a targeted isolated test run for a single bundle. + * Only that bundle resets to pending; all other bundles keep their last result (dimmed). + * + * @param {string} bundlePath - Bundle path to run. + */ + runBundle( bundlePath ) { + this.activeBundlePath = bundlePath; + this.resetExecutionState( bundlePath ); + this.isRunning = true; + + // Expand the target bundle and all its suites so results are immediately visible + const b = this.bundles.find( b => b.path === bundlePath ); + if ( b ) { + b.expanded = true; + b.suites.forEach( s => s.expanded = true ); + } + + // Single-bundle run: only pass streaming + bundles — no directory/recurse/pattern + let url = new URL( this.preferences.runnerUrl, window.location.href ); + url.searchParams.append( "streaming", "true" ); + url.searchParams.append( "bundles", bundlePath ); + this.startEventSource( url.toString() ); + }, + + /** + * Initiates a targeted isolated test run for a single suite within a bundle. + * Only that bundle resets to pending; other bundles keep their last result (dimmed). + * + * @param {string} bundlePath - Bundle that owns the suite. + * @param {string} suiteId - Suite unique identifier to run (passed as testSuites param). + */ + runSuite( bundlePath, suiteId ) { + this.activeBundlePath = bundlePath; + this.resetExecutionState( bundlePath ); + this.isRunning = true; + + const b = this.bundles.find( b => b.path === bundlePath ); + if ( b ) { + b.expanded = true; + const suite = b.suites.find( s => s.sourceId === suiteId ); + if ( suite ) suite.expanded = true; + } + + let url = new URL( this.preferences.runnerUrl, window.location.href ); + url.searchParams.append( "streaming", "true" ); + url.searchParams.append( "bundles", bundlePath ); + url.searchParams.append( "testSuites", suiteId ); + this.startEventSource( url.toString() ); + }, + + /** + * Initiates a targeted isolated test run for a single spec within a bundle. + * Only that bundle resets to pending; other bundles keep their last result (dimmed). + * + * @param {string} bundlePath - Bundle that owns the spec. + * @param {string} specId - Spec unique identifier to run (passed as testSpecs param). + */ + runSpec( bundlePath, specId ) { + this.activeBundlePath = bundlePath; + this.resetExecutionState( bundlePath ); + this.isRunning = true; + + const b = this.bundles.find( b => b.path === bundlePath ); + if ( b ) { + b.expanded = true; + b.suites.forEach( s => s.expanded = true ); + } + + let url = new URL( this.preferences.runnerUrl, window.location.href ); + url.searchParams.append( "streaming", "true" ); + url.searchParams.append( "bundles", bundlePath ); + url.searchParams.append( "testSpecs", specId ); + + this.startEventSource( url.toString() ); + }, + + /** + * Safely initiates connection with the underlying BoxLang runner's Server-Sent Events stream + * and subsequently wires listeners mapping real-time broadcast payloads logically to the interface states. + * + * @param {string} url - The targeted SSE endpoint string. + */ + startEventSource( url ) { + this.eventSource = new EventSource( url ); + + this.eventSource.addEventListener( "bundleStart", ( e ) => { + let data = JSON.parse( e.data ); + let bundle = this.bundles.find( b => b.path === data.path || b.id === data.id ); + if ( bundle ) bundle.status = "running"; + } ); + + this.eventSource.addEventListener( "bundleEnd", ( e ) => { + let data = JSON.parse( e.data ); + let bundle = this.bundles.find( b => b.path === data.path || b.id === data.id ); + if ( bundle ) { + bundle.status = this.determineBundleStatus( data ); + bundle.totalDuration = data.totalDuration || 0; + bundle.totalPass = data.totalPass || 0; + bundle.totalFail = data.totalFail || 0; + bundle.totalError = data.totalError || 0; + bundle.totalSkipped = data.totalSkipped || 0; + bundle.hasStats = this.computeBundleHasStats( bundle ); + bundle.debugBuffer = data.debugBuffer || []; + } + } ); + + this.eventSource.addEventListener( "suiteStart", ( e ) => { + let data = JSON.parse( e.data ); + let suiteAndBundle = this.findSuite( data.id, data.bundlePath ); + if ( suiteAndBundle ) suiteAndBundle.suite.status = "running"; + } ); + + this.eventSource.addEventListener( "suiteEnd", ( e ) => { + let data = JSON.parse( e.data ); + let suiteAndBundle = this.findSuite( data.id, data.bundlePath ); + if ( suiteAndBundle ) suiteAndBundle.suite.status = this.determineBundleStatus( data ); + } ); + + this.eventSource.addEventListener( "specStart", ( e ) => { + let data = JSON.parse( e.data ); + let specInfo = this.findSpec( data.id, data.bundlePath, data.suiteId ); + if ( specInfo ) { + specInfo.spec.status = "running"; + specInfo.spec.hasExecuted = true; + } + } ); + + this.eventSource.addEventListener( "specEnd", ( e ) => { + let data = JSON.parse( e.data ); + let specInfo = this.findSpec( data.id, data.bundlePath, data.suiteId ); + if ( specInfo ) { + specInfo.spec.status = data.status.toLowerCase(); + specInfo.spec.totalDuration = data.totalDuration || 0; + specInfo.spec.failMessage = data.failMessage || ""; + specInfo.spec.failDetail = data.failDetail || ""; + specInfo.spec.failOrigin = Array.isArray( data.failOrigin ) ? data.failOrigin : []; + specInfo.spec.failStacktrace = data.failStacktrace || ""; + specInfo.spec.error = data.error || null; + specInfo.spec.hasExecuted = true; + specInfo.spec.showFailureOrigin = false; + specInfo.spec.showStacktrace = false; + } + this.specsCompleted++; + } ); + + // Server-sent fatal error (event: error with JSON payload) + this.eventSource.addEventListener( "error", ( e ) => { + if ( !e.data ) return; // native connection close fires with no data — let onerror handle it + let data = JSON.parse( e.data ); + this.globalError = data.message || "A fatal error occurred during testing."; + this.globalErrorDetail = data.detail || ""; + this.isStopping = true; + this.stopTests(); + } ); + + this.eventSource.addEventListener( "testRunEnd", ( e ) => { + let data = JSON.parse( e.data ); + // Capture all run-level counters so metaGlobalStats can reflect exactly + // what was executed (full harness *or* a single-bundle run). + this.globalStats.totalBundles = data.totalBundles; + this.globalStats.totalSuites = data.totalSuites; + this.globalStats.totalSpecs = data.totalSpecs; + this.globalStats.totalDuration = data.totalDuration; + this.globalStats.totalPass = data.totalPass; + this.globalStats.totalFail = data.totalFail; + this.globalStats.totalError = data.totalError; + this.globalStats.totalSkipped = data.totalSkipped; + this.runCompleted = true; + this.isStopping = true; + this.stopTests(); + } ); + + this.eventSource.onerror = () => { + // onerror races with testRunEnd on normal server close — defer one tick + // so testRunEnd has a chance to set runCompleted/isStopping first. + setTimeout( () => { + if ( this.isStopping || this.runCompleted || !this.isRunning ) return; + this.globalError = "Connection to test runner lost."; + this.isStopping = true; + this.stopTests(); + }, 0 ); + }; + }, + + /** + * Manually closes the active SSE communication stream dropping the runner state, + * gracefully transitioning dangling/timeout instances to an explicit stopped status. + */ + stopTests() { + if ( this.eventSource ) { + // Detach handlers before closing to avoid close-related onerror noise. + this.eventSource.onerror = null; + this.eventSource.onmessage = null; + this.eventSource.onopen = null; + this.eventSource.close(); + this.eventSource = null; + } + this.isRunning = false; + this.activeBundlePath = null; + + // Mark any stuck 'running' states as 'error' or 'skipped' (optional) + this.bundles.forEach( b => { + if ( b.status === "running" ) b.status = "error"; + b.suites.forEach( s => { + if ( s.status === "running" ) s.status = "error"; + s.specs.forEach( sp => { + if ( sp.status === "running" ) sp.status = "skipped"; + } ); + } ); + b.specs.forEach( sp => { + if ( sp.status === "running" ) sp.status = "skipped"; + } ); + } ); + }, + + /** + * Performs an iterative traversal nested search to isolate a matching suite configuration by ID. + * + * @param {string} id - Active reference ID representing requested target. + * @returns {object|null} Resolving pair returning parent and matched item explicitly. + */ + findSuite( id, bundlePath = null ) { + for ( let b of this.bundles ) { + if ( bundlePath && b.path !== bundlePath ) continue; + for ( let s of b.suites ) { + if ( s.sourceId === id ) return { bundle: b, suite: s }; + } + } + return null; + }, + + /** + * Performs an iterative traversal nested search to isolate a corresponding spec node configuration by ID. + * Supports deep-diving internal suite-bound lists or standalone xUnit top-tier bounds respectively. + * + * @param {string} id - Extrapolated reference ID of lookup element. + * @returns {object|null} Extended trace collection grouping targeting tree elements implicitly. + */ + findSpec( id, bundlePath = null, suiteId = null ) { + for ( let b of this.bundles ) { + if ( bundlePath && b.path !== bundlePath ) continue; + for ( let s of b.suites ) { + if ( suiteId && s.sourceId !== suiteId ) continue; + for ( let sp of s.specs ) { + if ( sp.sourceId === id ) return { bundle: b, suite: s, spec: sp }; + } + } + // Top-level specs (xUnit style — no parent suite) + for ( let sp of b.specs ) { + if ( sp.sourceId === id ) return { bundle: b, suite: null, spec: sp }; + } + } + return null; + }, + + /** + * Resets the entire UI to initial discovery state, wiping all run results and + * re-fetching the test bundle structure via a fresh dry run. Stops any active run first. + */ + refreshTests() { + if ( this.isRunning ) { + this.stopTests(); + } + this.bundles = []; + this.runCompleted = false; + this.activeBundlePath = null; + this.specsCompleted = 0; + this.globalError = null; + this.globalErrorDetail = null; + this.globalStats = { + totalBundles: 0, totalSuites: 0, totalSpecs: 0, totalDuration: 0, + totalPass: 0, totalFail: 0, totalError: 0, totalSkipped: 0 + }; + this.fetchDryRun(); + }, + + /** + * Toggles all bundles and their suites between fully expanded and fully collapsed. + */ + toggleAllBundles() { + const anyExpanded = this.bundles.some( b => b.expanded ); + if ( anyExpanded ) { + this.collapseAll(); + } else { + this.expandAll(); + } + }, + + /** + * Expands all bundle cards in the test tree. + */ + expandAll() { + this.bundles.forEach( b => { + b.expanded = true; + b.suites.forEach( s => s.expanded = true ); + } ); + }, + + /** + * Registers global keyboard shortcuts for common actions. + * + * Shortcuts: + * Ctrl+K / ⌘K — Focus the search / filter input + * Escape — Clear the search query (when search input is focused) + * Ctrl+Enter — Run all tests + * Ctrl+. — Reload (re-fetch dry run without full page reload) + * Ctrl+, — Open the Settings modal + * Ctrl+H / ⌘H — Open the Help / About dialog + * Ctrl+B — Toggle expand / collapse all bundles + * Ctrl+D — Toggle dark / light theme + */ + initKeyboardShortcuts() { + document.addEventListener( "keydown", ( e ) => { + const tag = document.activeElement?.tagName?.toLowerCase(); + const inInput = tag === "input" || tag === "textarea" || tag === "select" || document.activeElement?.isContentEditable; + + // Ctrl+K / ⌘K → focus search + if ( ( e.ctrlKey || e.metaKey ) && e.key === "k" ) { + e.preventDefault(); + const searchEl = document.getElementById( "searchInput" ); + if ( document.activeElement === searchEl ) { + searchEl?.blur(); + } else { + searchEl?.focus(); + searchEl?.select(); + } + return; + } + + // Escape → clear search (only while search input has focus) + if ( e.key === "Escape" && document.activeElement?.id === "searchInput" ) { + this.searchQuery = ""; + document.activeElement.blur(); + return; + } + + // All remaining shortcuts require Ctrl/Meta and must not fire inside text inputs + if ( !( e.ctrlKey || e.metaKey ) || inInput ) return; + + switch ( e.key ) { + case "Enter": + // Ctrl+Enter → run all tests + e.preventDefault(); + if ( !this.isRunning ) this.runAllTests(); + break; + + case ".": + // Ctrl+. → reload (re-fetch dry run without full page reload) + e.preventDefault(); + if ( !this.isRunning ) this.fetchDryRun(); + break; + + case ",": + // Ctrl+, → open settings modal + e.preventDefault(); + bootstrap.Modal.getOrCreateInstance( document.getElementById( "settingsModal" ) ).show(); + break; + + case "h": + case "H": + // Ctrl+H / ⌘H → open About / Help modal + e.preventDefault(); + bootstrap.Modal.getOrCreateInstance( document.getElementById( "aboutModal" ) ).show(); + break; + + case "b": + case "B": + // Ctrl+B → toggle expand/collapse all bundles + e.preventDefault(); + this.toggleAllBundles(); + break; + + case "d": + case "D": + // Ctrl+D → toggle dark/light theme + e.preventDefault(); + this.toggleTheme(); + break; + } + } ); + }, + + /** + * Collapses all bundle cards in the test tree. + */ + collapseAll() { + this.bundles.forEach( b => { + b.expanded = false; + b.suites.forEach( s => s.expanded = false ); + } ); + }, + + /** + * Computes the overarching roll-up derivation status of dynamic container + * representations mapping test results progressively. + * + * @param {object} data - Reference map holding error, fail, or sequence metric states. + * @returns {string} Derived string representing logical test aggregate status. + */ + determineBundleStatus( data ) { + if ( data.totalError > 0 ) return "error"; + if ( data.totalFail > 0 ) return "failed"; + if ( data.totalPass > 0 ) return "passed"; + if ( data.totalSkipped > 0 ) return "skipped"; + return "pending"; // default + } + } ) ); + + // x-tooltip: wraps Bootstrap 5 Tooltip per element. + // Usage: x-tooltip (reads data-bs-title or title attr) | x-tooltip.bottom (placement modifier) + Alpine.directive( "tooltip", ( el, { modifiers }, { cleanup } ) => { + let instance = new bootstrap.Tooltip( el, { + title: () => el.getAttribute( "data-bs-title" ) || el.getAttribute( "title" ) || "", + trigger: "hover focus", + placement: modifiers[ 0 ] || "top" + } ); + // Hide immediately on click so tooltips don't linger after buttons are pressed + const hideOnClick = () => instance.hide(); + el.addEventListener( "click", hideOnClick ); + cleanup( () => { + el.removeEventListener( "click", hideOnClick ); + instance.dispose(); + } ); + } ); +} ); diff --git a/tests/assets/js/vendor/alpine-collapse.min.js b/tests/assets/js/vendor/alpine-collapse.min.js new file mode 100755 index 000000000..a8653437a --- /dev/null +++ b/tests/assets/js/vendor/alpine-collapse.min.js @@ -0,0 +1 @@ +(()=>{function g(n){n.directive("collapse",e),e.inline=(t,{modifiers:i})=>{i.includes("min")&&(t._x_doShow=()=>{},t._x_doHide=()=>{})};function e(t,{modifiers:i}){let r=l(i,"duration",250)/1e3,h=l(i,"min",0),u=!i.includes("min");t._x_isShown||(t.style.height=`${h}px`),!t._x_isShown&&u&&(t.hidden=!0),t._x_isShown||(t.style.overflow="hidden");let c=(d,s)=>{let o=n.setStyles(d,s);return s.height?()=>{}:o},f={transitionProperty:"height",transitionDuration:`${r}s`,transitionTimingFunction:"cubic-bezier(0.4, 0.0, 0.2, 1)"};t._x_transition={in(d=()=>{},s=()=>{}){u&&(t.hidden=!1),u&&(t.style.display=null);let o=t.getBoundingClientRect().height;t.style.height="auto";let a=t.getBoundingClientRect().height;o===a&&(o=h),n.transition(t,n.setStyles,{during:f,start:{height:o+"px"},end:{height:a+"px"}},()=>t._x_isShown=!0,()=>{Math.abs(t.getBoundingClientRect().height-a)<1&&(t.style.overflow=null)})},out(d=()=>{},s=()=>{}){let o=t.getBoundingClientRect().height;n.transition(t,c,{during:f,start:{height:o+"px"},end:{height:h+"px"}},()=>t.style.overflow="hidden",()=>{t._x_isShown=!1,t.style.height==`${h}px`&&u&&(t.style.display="none",t.hidden=!0)})}}}}function l(n,e,t){if(n.indexOf(e)===-1)return t;let i=n[n.indexOf(e)+1];if(!i)return t;if(e==="duration"){let r=i.match(/([0-9]+)ms/);if(r)return r[1]}if(e==="min"){let r=i.match(/([0-9]+)px/);if(r)return r[1]}return i}document.addEventListener("alpine:init",()=>{window.Alpine.plugin(g)});})(); diff --git a/tests/assets/js/vendor/alpine.min.js b/tests/assets/js/vendor/alpine.min.js new file mode 100755 index 000000000..b050ef14a --- /dev/null +++ b/tests/assets/js/vendor/alpine.min.js @@ -0,0 +1,5 @@ +(()=>{var nt=!1,it=!1,W=[],ot=-1;function Ut(e){Rn(e)}function Rn(e){W.includes(e)||W.push(e),Mn()}function Wt(e){let t=W.indexOf(e);t!==-1&&t>ot&&W.splice(t,1)}function Mn(){!it&&!nt&&(nt=!0,queueMicrotask(Nn))}function Nn(){nt=!1,it=!0;for(let e=0;ee.effect(t,{scheduler:r=>{st?Ut(r):r()}}),at=e.raw}function ct(e){N=e}function Yt(e){let t=()=>{};return[n=>{let i=N(n);return e._x_effects||(e._x_effects=new Set,e._x_runEffects=()=>{e._x_effects.forEach(o=>o())}),e._x_effects.add(i),t=()=>{i!==void 0&&(e._x_effects.delete(i),$(i))},i},()=>{t()}]}function Se(e,t){let r=!0,n,i=N(()=>{let o=e();JSON.stringify(o),r?n=o:queueMicrotask(()=>{t(o,n),n=o}),r=!1});return()=>$(i)}var Xt=[],Zt=[],Qt=[];function er(e){Qt.push(e)}function te(e,t){typeof t=="function"?(e._x_cleanups||(e._x_cleanups=[]),e._x_cleanups.push(t)):(t=e,Zt.push(t))}function Ae(e){Xt.push(e)}function Oe(e,t,r){e._x_attributeCleanups||(e._x_attributeCleanups={}),e._x_attributeCleanups[t]||(e._x_attributeCleanups[t]=[]),e._x_attributeCleanups[t].push(r)}function lt(e,t){e._x_attributeCleanups&&Object.entries(e._x_attributeCleanups).forEach(([r,n])=>{(t===void 0||t.includes(r))&&(n.forEach(i=>i()),delete e._x_attributeCleanups[r])})}function tr(e){for(e._x_effects?.forEach(Wt);e._x_cleanups?.length;)e._x_cleanups.pop()()}var ut=new MutationObserver(mt),ft=!1;function ue(){ut.observe(document,{subtree:!0,childList:!0,attributes:!0,attributeOldValue:!0}),ft=!0}function dt(){Dn(),ut.disconnect(),ft=!1}var le=[];function Dn(){let e=ut.takeRecords();le.push(()=>e.length>0&&mt(e));let t=le.length;queueMicrotask(()=>{if(le.length===t)for(;le.length>0;)le.shift()()})}function m(e){if(!ft)return e();dt();let t=e();return ue(),t}var pt=!1,ve=[];function rr(){pt=!0}function nr(){pt=!1,mt(ve),ve=[]}function mt(e){if(pt){ve=ve.concat(e);return}let t=new Set,r=new Set,n=new Map,i=new Map;for(let o=0;os.nodeType===1&&t.add(s)),e[o].removedNodes.forEach(s=>s.nodeType===1&&r.add(s))),e[o].type==="attributes")){let s=e[o].target,a=e[o].attributeName,c=e[o].oldValue,l=()=>{n.has(s)||n.set(s,[]),n.get(s).push({name:a,value:s.getAttribute(a)})},u=()=>{i.has(s)||i.set(s,[]),i.get(s).push(a)};s.hasAttribute(a)&&c===null?l():s.hasAttribute(a)?(u(),l()):u()}i.forEach((o,s)=>{lt(s,o)}),n.forEach((o,s)=>{Xt.forEach(a=>a(s,o))});for(let o of r)t.has(o)||Zt.forEach(s=>s(o));t.forEach(o=>{o._x_ignoreSelf=!0,o._x_ignore=!0});for(let o of t)r.has(o)||o.isConnected&&(delete o._x_ignoreSelf,delete o._x_ignore,Qt.forEach(s=>s(o)),o._x_ignore=!0,o._x_ignoreSelf=!0);t.forEach(o=>{delete o._x_ignoreSelf,delete o._x_ignore}),t=null,r=null,n=null,i=null}function Ce(e){return B(F(e))}function D(e,t,r){return e._x_dataStack=[t,...F(r||e)],()=>{e._x_dataStack=e._x_dataStack.filter(n=>n!==t)}}function F(e){return e._x_dataStack?e._x_dataStack:typeof ShadowRoot=="function"&&e instanceof ShadowRoot?F(e.host):e.parentNode?F(e.parentNode):[]}function B(e){return new Proxy({objects:e},Pn)}var Pn={ownKeys({objects:e}){return Array.from(new Set(e.flatMap(t=>Object.keys(t))))},has({objects:e},t){return t==Symbol.unscopables?!1:e.some(r=>Object.prototype.hasOwnProperty.call(r,t)||Reflect.has(r,t))},get({objects:e},t,r){return t=="toJSON"?In:Reflect.get(e.find(n=>Reflect.has(n,t))||{},t,r)},set({objects:e},t,r,n){let i=e.find(s=>Object.prototype.hasOwnProperty.call(s,t))||e[e.length-1],o=Object.getOwnPropertyDescriptor(i,t);return o?.set&&o?.get?o.set.call(n,r)||!0:Reflect.set(i,t,r)}};function In(){return Reflect.ownKeys(this).reduce((t,r)=>(t[r]=Reflect.get(this,r),t),{})}function Te(e){let t=n=>typeof n=="object"&&!Array.isArray(n)&&n!==null,r=(n,i="")=>{Object.entries(Object.getOwnPropertyDescriptors(n)).forEach(([o,{value:s,enumerable:a}])=>{if(a===!1||s===void 0||typeof s=="object"&&s!==null&&s.__v_skip)return;let c=i===""?o:`${i}.${o}`;typeof s=="object"&&s!==null&&s._x_interceptor?n[o]=s.initialize(e,c,o):t(s)&&s!==n&&!(s instanceof Element)&&r(s,c)})};return r(e)}function Re(e,t=()=>{}){let r={initialValue:void 0,_x_interceptor:!0,initialize(n,i,o){return e(this.initialValue,()=>kn(n,i),s=>ht(n,i,s),i,o)}};return t(r),n=>{if(typeof n=="object"&&n!==null&&n._x_interceptor){let i=r.initialize.bind(r);r.initialize=(o,s,a)=>{let c=n.initialize(o,s,a);return r.initialValue=c,i(o,s,a)}}else r.initialValue=n;return r}}function kn(e,t){return t.split(".").reduce((r,n)=>r[n],e)}function ht(e,t,r){if(typeof t=="string"&&(t=t.split(".")),t.length===1)e[t[0]]=r;else{if(t.length===0)throw error;return e[t[0]]||(e[t[0]]={}),ht(e[t[0]],t.slice(1),r)}}var ir={};function y(e,t){ir[e]=t}function fe(e,t){let r=Ln(t);return Object.entries(ir).forEach(([n,i])=>{Object.defineProperty(e,`$${n}`,{get(){return i(t,r)},enumerable:!1})}),e}function Ln(e){let[t,r]=_t(e),n={interceptor:Re,...t};return te(e,r),n}function or(e,t,r,...n){try{return r(...n)}catch(i){re(i,e,t)}}function re(e,t,r=void 0){e=Object.assign(e??{message:"No error message given."},{el:t,expression:r}),console.warn(`Alpine Expression Error: ${e.message} + +${r?'Expression: "'+r+`" + +`:""}`,t),setTimeout(()=>{throw e},0)}var Me=!0;function De(e){let t=Me;Me=!1;let r=e();return Me=t,r}function R(e,t,r={}){let n;return x(e,t)(i=>n=i,r),n}function x(...e){return sr(...e)}var sr=xt;function ar(e){sr=e}function xt(e,t){let r={};fe(r,e);let n=[r,...F(e)],i=typeof t=="function"?$n(n,t):Fn(n,t,e);return or.bind(null,e,t,i)}function $n(e,t){return(r=()=>{},{scope:n={},params:i=[]}={})=>{let o=t.apply(B([n,...e]),i);Ne(r,o)}}var gt={};function jn(e,t){if(gt[e])return gt[e];let r=Object.getPrototypeOf(async function(){}).constructor,n=/^[\n\s]*if.*\(.*\)/.test(e.trim())||/^(let|const)\s/.test(e.trim())?`(async()=>{ ${e} })()`:e,o=(()=>{try{let s=new r(["__self","scope"],`with (scope) { __self.result = ${n} }; __self.finished = true; return __self.result;`);return Object.defineProperty(s,"name",{value:`[Alpine] ${e}`}),s}catch(s){return re(s,t,e),Promise.resolve()}})();return gt[e]=o,o}function Fn(e,t,r){let n=jn(t,r);return(i=()=>{},{scope:o={},params:s=[]}={})=>{n.result=void 0,n.finished=!1;let a=B([o,...e]);if(typeof n=="function"){let c=n(n,a).catch(l=>re(l,r,t));n.finished?(Ne(i,n.result,a,s,r),n.result=void 0):c.then(l=>{Ne(i,l,a,s,r)}).catch(l=>re(l,r,t)).finally(()=>n.result=void 0)}}}function Ne(e,t,r,n,i){if(Me&&typeof t=="function"){let o=t.apply(r,n);o instanceof Promise?o.then(s=>Ne(e,s,r,n)).catch(s=>re(s,i,t)):e(o)}else typeof t=="object"&&t instanceof Promise?t.then(o=>e(o)):e(t)}var wt="x-";function C(e=""){return wt+e}function cr(e){wt=e}var Pe={};function d(e,t){return Pe[e]=t,{before(r){if(!Pe[r]){console.warn(String.raw`Cannot find directive \`${r}\`. \`${e}\` will use the default order of execution`);return}let n=G.indexOf(r);G.splice(n>=0?n:G.indexOf("DEFAULT"),0,e)}}}function lr(e){return Object.keys(Pe).includes(e)}function pe(e,t,r){if(t=Array.from(t),e._x_virtualDirectives){let o=Object.entries(e._x_virtualDirectives).map(([a,c])=>({name:a,value:c})),s=Et(o);o=o.map(a=>s.find(c=>c.name===a.name)?{name:`x-bind:${a.name}`,value:`"${a.value}"`}:a),t=t.concat(o)}let n={};return t.map(dr((o,s)=>n[o]=s)).filter(mr).map(zn(n,r)).sort(Kn).map(o=>Bn(e,o))}function Et(e){return Array.from(e).map(dr()).filter(t=>!mr(t))}var yt=!1,de=new Map,ur=Symbol();function fr(e){yt=!0;let t=Symbol();ur=t,de.set(t,[]);let r=()=>{for(;de.get(t).length;)de.get(t).shift()();de.delete(t)},n=()=>{yt=!1,r()};e(r),n()}function _t(e){let t=[],r=a=>t.push(a),[n,i]=Yt(e);return t.push(i),[{Alpine:z,effect:n,cleanup:r,evaluateLater:x.bind(x,e),evaluate:R.bind(R,e)},()=>t.forEach(a=>a())]}function Bn(e,t){let r=()=>{},n=Pe[t.type]||r,[i,o]=_t(e);Oe(e,t.original,o);let s=()=>{e._x_ignore||e._x_ignoreSelf||(n.inline&&n.inline(e,t,i),n=n.bind(n,e,t,i),yt?de.get(ur).push(n):n())};return s.runCleanups=o,s}var Ie=(e,t)=>({name:r,value:n})=>(r.startsWith(e)&&(r=r.replace(e,t)),{name:r,value:n}),ke=e=>e;function dr(e=()=>{}){return({name:t,value:r})=>{let{name:n,value:i}=pr.reduce((o,s)=>s(o),{name:t,value:r});return n!==t&&e(n,t),{name:n,value:i}}}var pr=[];function ne(e){pr.push(e)}function mr({name:e}){return hr().test(e)}var hr=()=>new RegExp(`^${wt}([^:^.]+)\\b`);function zn(e,t){return({name:r,value:n})=>{let i=r.match(hr()),o=r.match(/:([a-zA-Z0-9\-_:]+)/),s=r.match(/\.[^.\]]+(?=[^\]]*$)/g)||[],a=t||e[r]||r;return{type:i?i[1]:null,value:o?o[1]:null,modifiers:s.map(c=>c.replace(".","")),expression:n,original:a}}}var bt="DEFAULT",G=["ignore","ref","data","id","anchor","bind","init","for","model","modelable","transition","show","if",bt,"teleport"];function Kn(e,t){let r=G.indexOf(e.type)===-1?bt:e.type,n=G.indexOf(t.type)===-1?bt:t.type;return G.indexOf(r)-G.indexOf(n)}function J(e,t,r={}){e.dispatchEvent(new CustomEvent(t,{detail:r,bubbles:!0,composed:!0,cancelable:!0}))}function P(e,t){if(typeof ShadowRoot=="function"&&e instanceof ShadowRoot){Array.from(e.children).forEach(i=>P(i,t));return}let r=!1;if(t(e,()=>r=!0),r)return;let n=e.firstElementChild;for(;n;)P(n,t,!1),n=n.nextElementSibling}function E(e,...t){console.warn(`Alpine Warning: ${e}`,...t)}var _r=!1;function gr(){_r&&E("Alpine has already been initialized on this page. Calling Alpine.start() more than once can cause problems."),_r=!0,document.body||E("Unable to initialize. Trying to load Alpine before `` is available. Did you forget to add `defer` in Alpine's ` - #args.menuItem.getLabel()# + + +#args.menuItem.getLabel()# \ No newline at end of file diff --git a/modules/contentbox/models/menu/views/media/display.cfm b/modules/contentbox/models/menu/views/media/display.cfm index af80bb4e5..a0204719e 100644 --- a/modules/contentbox/models/menu/views/media/display.cfm +++ b/modules/contentbox/models/menu/views/media/display.cfm @@ -1,3 +1,3 @@ - #args.menuItem.getLabel()# + #args.menuItem.getLabel()# \ No newline at end of file diff --git a/modules/contentbox/models/menu/views/submenu/display.cfm b/modules/contentbox/models/menu/views/submenu/display.cfm index 276d00a55..d634fd14f 100644 --- a/modules/contentbox/models/menu/views/submenu/display.cfm +++ b/modules/contentbox/models/menu/views/submenu/display.cfm @@ -1,4 +1,7 @@ - #args.menuItem.getLabel()# - #cb.menu( slug="#args.data.menuSlug#", type="html", slugCache=args.slugCache )# + #args.menuItem.getLabel()##cb.menu( + slug = "#args.data.menuSlug#", + type = "html", + slugCache = args.slugCache + )# \ No newline at end of file diff --git a/modules/contentbox/models/menu/views/url/display.cfm b/modules/contentbox/models/menu/views/url/display.cfm index 7c5dbc1a3..3d66b7da5 100644 --- a/modules/contentbox/models/menu/views/url/display.cfm +++ b/modules/contentbox/models/menu/views/url/display.cfm @@ -1,3 +1,3 @@ - #args.menuItem.getLabel()# + #args.menuItem.getLabel()# \ No newline at end of file diff --git a/modules/contentbox/models/modules/Module.cfc b/modules/contentbox/models/modules/Module.cfc index 8f68daffa..629b48335 100644 --- a/modules/contentbox/models/modules/Module.cfc +++ b/modules/contentbox/models/modules/Module.cfc @@ -13,96 +13,84 @@ component cachename ="cbModule" cacheuse ="read-write" { - - /* ********************************************************************* - ** PROPERTIES - ********************************************************************* */ + /********************************************************************** + * ** PROPERTIES + **********************************************************************/ property - name ="moduleID" - column ="moduleID" + name="moduleID" + column="moduleID" fieldtype="id" generator="uuid" - length ="36" - ormtype ="string" - update ="false"; - + length="36" + ormtype="string" + update="false"; property - name ="name" - column ="name" + name="name" + column="name" notnull="true" - length ="255" - index ="idx_moduleName"; - + length="255" + index="idx_moduleName"; property - name ="title" - column ="title" + name="title" + column="title" notnull="false" - length ="255" + length="255" default=""; - property - name ="version" - column ="version" + name="version" + column="version" notnull="false" - length ="255" + length="255" default="0"; - property - name ="entryPoint" - column ="entryPoint" + name="entryPoint" + column="entryPoint" notnull="false" - length ="255" + length="255" default="" - index ="idx_entryPoint"; - + index="idx_entryPoint"; property - name ="author" - column ="author" + name="author" + column="author" notnull="false" - length ="255" + length="255" default=""; - property - name ="webURL" - column ="webURL" + name="webURL" + column="webURL" notnull="false" - length ="500" + length="500" default=""; - property - name ="forgeBoxSlug" - column ="forgeBoxSlug" + name="forgeBoxSlug" + column="forgeBoxSlug" notnull="false" - length ="255" + length="255" default=""; - property - name ="description" - column ="description" + name="description" + column="description" notnull="false" ormtype="text" - length ="8000" + length="8000" default=""; - property - name ="isActive" - column ="isActive" + name="isActive" + column="isActive" notnull="true" ormtype="boolean" default="false" - index ="idx_activeModule"; - + index="idx_activeModule"; property - name ="moduleType" - column ="moduleType" - default ="core" + name="moduleType" + column="moduleType" + default="core" dbdefault="'unknown'" - index ="idx_moduleType"; - - /* ********************************************************************* - ** PK + CONSTRAINTS - ********************************************************************* */ + index="idx_moduleType"; + /********************************************************************** + * ** PK + CONSTRAINTS + **********************************************************************/ this.pk = "moduleID"; @@ -119,7 +107,7 @@ component "version", "webURL" ], - defaultExcludes : [ "" ] + defaultExcludes : [ ""] }; this.constraints = { @@ -132,8 +120,8 @@ component "forgeBoxSlug" : { required : false, size : "1..255" } }; - function init(){ - variables.isActive = false; + function init() { + variables.isActive = false; variables.moduleType = "core"; super.init(); @@ -141,4 +129,4 @@ component return this; } -} +} \ No newline at end of file diff --git a/modules/contentbox/models/modules/ModuleService.cfc b/modules/contentbox/models/modules/ModuleService.cfc index 9c8b09c50..10d5df516 100644 --- a/modules/contentbox/models/modules/ModuleService.cfc +++ b/modules/contentbox/models/modules/ModuleService.cfc @@ -5,82 +5,69 @@ * --- * Manage ContentBox Modules */ -component - extends ="cborm.models.VirtualEntityService" - accessors="true" - singleton - threadsafe -{ - +component extends ="cborm.models.VirtualEntityService" accessors="true" singleton threadsafe { // DI property name="settingService" inject="settingService@contentbox"; property name="widgetService" inject="provider:widgetService@contentbox"; property name="contentBoxSettings" inject="coldbox:moduleConfig:contentbox"; - property name="customModuleSettings" inject="coldbox:moduleConfig:contentbox-custom"; + property + name="customModuleSettings" + inject="coldbox:moduleConfig:contentbox-custom"; property name="coldboxModuleService" inject="coldbox:moduleService"; property name="log" inject="logbox:logger:{this}"; - /** * The absolute path to custom module locations */ property name="customModulesPath" type="string"; - /** * The invocation path for custom modules */ property name="customModulesInvocationPath" type="string"; - /** * The absolute path to core module locations */ property name="coreModulesPath" type="string"; - /** * The invocation path for core modules */ property name="coreModulesInvocationPath" type="string"; - /** * The module widget cache */ property name="moduleWidgetCache" type="struct"; - /** * ColdBox Module Registry reference */ property name="moduleRegistry" type="struct"; - /** * ColdBox Module Config Cache reference */ property name="moduleConfigCache" type="struct"; - /** * Module Map References */ property name="moduleMap" type="struct"; - /** * Constructor */ - ModuleService function init(){ + ModuleService function init() { // init it super.init( entityName = "cbModule" ); - variables.customModulesPath = ""; + variables.customModulesPath = ""; variables.customModulesInvocationPath = ""; - variables.coreModulesPath = ""; - variables.coreModulesInvocationPath = ""; - variables.moduleWidgetCache = {}; - variables.moduleRegistry = {}; - variables.moduleConfigCache = {}; + variables.coreModulesPath = ""; + variables.coreModulesInvocationPath = ""; + variables.moduleWidgetCache = {}; + variables.moduleRegistry = {}; + variables.moduleConfigCache = {}; /** * Stores all module information when loaded: * { - * type : "The module type : core or custom", - * path : The absolute path, - * invocationPath : The invocation path + * type : "The module type : core or custom", + * path : The absolute path, + * invocationPath : The invocation path * } */ variables.moduleMap = {}; @@ -91,17 +78,17 @@ component /** * Executes after DI completion */ - function onDIComplete(){ + function onDIComplete() { // Setup Core Modules - variables.coreModulesPath = variables.contentBoxSettings.path & "/modules_user"; + variables.coreModulesPath = variables.contentBoxSettings.path & "/modules_user"; variables.coreModulesInvocationPath = variables.contentBoxSettings.invocationPath & ".modules_user"; // Setup Custom Modules - variables.customModulesPath = variables.customModuleSettings.path & "/_modules"; + variables.customModulesPath = variables.customModuleSettings.path & "/_modules"; variables.customModulesInvocationPath = variables.customModuleSettings.invocationPath & "._modules"; // Module Registry + Config Cache References - variables.moduleRegistry = variables.coldboxModuleService.getModuleRegistry(); + variables.moduleRegistry = variables.coldboxModuleService.getModuleRegistry(); variables.moduleConfigCache = variables.coldboxModuleService.getModuleConfigCache(); } @@ -113,7 +100,7 @@ component * * @return The module populated */ - any function populateModule( any module, any config ){ + any function populateModule( any module, any config ) { arguments.module.setTitle( arguments.config.title ?: "" ); arguments.module.setAuthor( arguments.config.author ?: "" ); arguments.module.setWebURL( arguments.config.webURL ?: "" ); @@ -131,9 +118,9 @@ component * * @return The persisted module or a new module object representing not found. */ - Module function findModuleByEntryPoint( required entryPoint ){ + Module function findModuleByEntryPoint( required entryPoint ) { var module = findWhere( { entryPoint : arguments.entryPoint } ); - return ( isNull( module ) ? new () : module ); + return ( isNull( module ) ? new() : module ); } /** @@ -144,12 +131,12 @@ component * * @return struct:{ count:numeric, modules:array of objects } */ - struct function findModules( isActive = "any", moduleType ){ - var results = {}; + struct function findModules( isActive = "any", moduleType ) { + var results = {}; var criteria = newCriteria(); // isApproved filter - if ( !isNull( arguments.isActive ) AND arguments.isActive NEQ "any" ) { + if ( !isNull( arguments.isActive ) && arguments.isActive NEQ "any" ) { criteria.isEq( "isActive", javacast( "Boolean", arguments.isActive ) ); } @@ -159,7 +146,7 @@ component } // run criteria query and projections count - results.count = criteria.count(); + results.count = criteria.count(); results.modules = criteria.list( sortOrder = "title", asQuery = false ); return results; @@ -172,14 +159,16 @@ component * * @return The invocation path or empty if not found */ - string function getModuleWidgetInvocationPath( required string widgetName ){ + string function getModuleWidgetInvocationPath( required string widgetName ) { var path = ""; // if widget name is in module widget cache, return its invocation path if ( structKeyExists( variables.moduleWidgetCache, arguments.widgetName ) ) { path = variables.moduleWidgetCache[ arguments.widgetName ].invocationPath; } else { - log.error( "Could not find #arguments.widgetname# widget in the module." ); + log.error( + "Could not find #arguments.widgetname# widget in the module." + ); } return path; } @@ -190,24 +179,27 @@ component * @name The name of the module to register * @type The type of module: core or custom */ - Module function registerNewModule( required name, required type ){ - var thisPath = variables[ arguments.type & "ModulesPath" ]; + Module function registerNewModule( required name, required type ) { + var thisPath = variables[ arguments.type & "ModulesPath" ]; var thisInvocationPath = variables[ arguments.type & "ModulesInvocationPath" ]; if ( fileExists( thisPath & "/#arguments.name#/ModuleConfig.cfc" ) ) { - var oConfig = createObject( "component", thisInvocationPath & ".#arguments.name#.ModuleConfig" ); - var oModule = new ( { name : arguments.name, moduleType : arguments.type } ); + var oConfig = createObject( + "component", + thisInvocationPath & ".#arguments.name#.ModuleConfig" + ); + var oModule = new( { name : arguments.name, moduleType : arguments.type } ); save( populateModule( oModule, oConfig ) ); return oModule; } else { log.error( - "Cannot register new module #arguments.name# as it is not a valid ContentBox Module. No ModuleConfig.cfc found." - ); + "Cannot register new module #arguments.name# as it is not a valid ContentBox Module. No ModuleConfig.cfc found." + ); } - return new ( { name : arguments.name } ); + return new( { name : arguments.name } ); } /** @@ -215,7 +207,7 @@ component * * @name The module to deactivate */ - ModuleService function deactivateModule( required name ){ + ModuleService function deactivateModule( required name ) { // Get Module Record var oModule = findWhere( { name : arguments.name } ); @@ -230,8 +222,12 @@ component if ( structKeyExists( config, "onDeactivate" ) ) { try { config.onDeactivate(); - } catch ( Any e ) { - log.error( "Error deactivating module: #arguments.name# with #e.message & e.detail#", e ); + } catch (Any e) { + log.error( + "Error deactivating module: #arguments.name# with #e.message & e.detail#", + e + ); + // dont' throw. just log and continue deactivating modules. } } @@ -250,26 +246,13 @@ component return this; } - /** - * Detach coldbox module configuration registrations - * - * @name The name of the module to detach - */ - private ModuleService function detachColdBoxModuleRegistration( required name ){ - // Verify in module registry - if ( structKeyExists( variables.moduleRegistry, arguments.name ) ) { - structDelete( variables.moduleRegistry, arguments.name ); - } - return this; - } - /** * Activate a module from ContentBox * * @name The name of the module to activate */ - ModuleService function activateModule( required name ){ - var oModule = findWhere( { name : arguments.name } ); + ModuleService function activateModule( required name ) { + var oModule = findWhere( { name : arguments.name } ); var sModuleMap = variables.moduleMap[ arguments.name ]; // Set module as active @@ -287,7 +270,7 @@ component if ( structKeyExists( oConfig, "onActivate" ) ) { try { oConfig.onActivate(); - } catch ( Any e ) { + } catch (Any e) { // deactivate module, it did not activate correctly oModule.setIsActive( false ); // deactivate from ColdBox as it did not activate correctly @@ -312,12 +295,15 @@ component * * @name The name of the module to delete */ - ModuleService function deleteModule( required name ){ + ModuleService function deleteModule( required name ) { var moduleEntry = variables.moduleMap[ arguments.name ]; // Try to do an onDelete() callback if it exists if ( fileExists( moduleEntry.path & "/#arguments.name#/ModuleConfig.cfc" ) ) { - var oConfig = createObject( "component", moduleEntry.invocationPath & ".#arguments.name#.ModuleConfig" ); + var oConfig = createObject( + "component", + moduleEntry.invocationPath & ".#arguments.name#.ModuleConfig" + ); if ( structKeyExists( oConfig, "onDelete" ) ) { oConfig.onDelete(); } @@ -335,7 +321,7 @@ component /** * Reset all modules by deactivating all of them and cleaning our db history */ - ModuleService function resetModules(){ + ModuleService function resetModules() { var aModules = list( asQuery = false ); transaction { @@ -356,22 +342,23 @@ component return this; } - - /** * Startup the service. This iterates through all modules on disk and if it finds a module that it has not been * registered, it will register it. If it loads a module that is registered and marked as active it will activate it. */ - ModuleService function startup(){ + ModuleService function startup() { // Get all modules var aModules = getAll(); // Register ColdBox Module Closure - var registerColdBoxModule = function( name, moduleType, invocationPath, path ){ + var registerColdBoxModule = function( name, moduleType, invocationPath, path ) { // var oModule = findWhere( { name : arguments.name } ); - var oModule = aModules.reduce( function( results, thisModule ){ - return ( thisModule.getName() == name ? thisModule : results ); - }, {} ); + var oModule = aModules.reduce( + function( results, thisModule ) { + return ( thisModule.getName() == name ? thisModule : results ); + }, + {} + ); // Register type lookup for faster finding, instead of querying the db. variables.moduleMap[ arguments.name ] = { @@ -388,12 +375,10 @@ component } // If we get here, the module is loaded in the database now - if ( oModule.getIsActive() AND !structKeyExists( variables.moduleRegistry, arguments.name ) ) { + if ( oModule.getIsActive() && !structKeyExists( variables.moduleRegistry, arguments.name ) ) { // Register with ColdBox now - variables.coldboxModuleService.registerModule( - moduleName = arguments.name, - invocationPath = arguments.invocationPath - ); + variables.coldboxModuleService.registerModule( moduleName = arguments.name, + invocationPath = arguments.invocationPath ); // Module registered, move to activation return true; } @@ -403,49 +388,68 @@ component }; // Register and Activate Core Modules First - getModulesOnDisk( variables.coreModulesPath ) - .filter( function( name ){ - return registerColdBoxModule( - arguments.name, - "core", - variables.coreModulesInvocationPath, - variables.coreModulesPath - ); - } ) - .each( function( name ){ - variables.coldboxModuleService.activateModule( arguments.name ); - } ); + getModulesOnDisk( variables.coreModulesPath ).filter( + function( name ) { + return registerColdBoxModule( + arguments.name, + "core", + variables.coreModulesInvocationPath, + variables.coreModulesPath + ); + } + ).each( + function( name ) { + variables.coldboxModuleService.activateModule( arguments.name ); + } + ); // Register and Activate Custom Modules - getModulesOnDisk( variables.customModulesPath ) - .filter( function( name ){ - return registerColdBoxModule( - arguments.name, - "custom", - variables.customModulesInvocationPath, - variables.customModulesPath - ); - } ) - .each( function( name ){ - variables.coldboxModuleService.activateModule( arguments.name ); - } ); + getModulesOnDisk( variables.customModulesPath ).filter( + function( name ) { + return registerColdBoxModule( + arguments.name, + "custom", + variables.customModulesInvocationPath, + variables.customModulesPath + ); + } + ).each( + function( name ) { + variables.coldboxModuleService.activateModule( arguments.name ); + } + ); // build widget cache buildModuleWidgetsCache( - aModules.filter( function( thisModule ){ - return arguments.thisModule.getIsActive(); - } ) + aModules.filter( + function( thisModule ) { + return arguments.thisModule.getIsActive(); + } + ) ); return this; } + /** + * Detach coldbox module configuration registrations + * + * @name The name of the module to detach + */ + private ModuleService function detachColdBoxModuleRegistration( required name ) { + // Verify in module registry + if ( structKeyExists( variables.moduleRegistry, arguments.name ) ) { + structDelete( variables.moduleRegistry, arguments.name ); + } + return this; + } + /** * Iterates over all registered, active modules and sets any found widgets into a cache in moduleservice * * @modules Optional passed array to use to build the module cache with, else query the database */ - private ModuleService function buildModuleWidgetsCache( array modules ){ + private ModuleService function buildModuleWidgetsCache( array modules ) { // Init Module Widget Cache variables.moduleWidgetCache = {}; @@ -455,41 +459,59 @@ component } // Process Module Widgets - arguments.modules - .filter( function( thisModule ){ - // Active and On Disk Module? - if ( variables.moduleMap.keyExists( thisModule.getName() ) ) { - return true; - } - - // Deactivate it, not fond in registry, might be an orphaned record - if ( log.canWarn() ) { - log.warn( "Orphaned module discovered: #thisModule.getName()#, deactiving it from the database" ); + arguments + .modules + .filter( + function( thisModule ) { + if ( variables.moduleMap.keyExists( thisModule.getName() ) ) { + return true; + } + + if ( log.canWarn() ) { + log.warn( + "Orphaned module discovered: #thisModule.getName()#, deactiving it from the database" + ); + } + deactivateModule( thisModule.getName() ); + return false; } - deactivateModule( thisModule.getName() ); - return false; - } ) - .each( function( thisModule ){ - // Module reference maps pointer - var moduleRecord = variables.moduleMap[ thisModule.getName() ]; - // Widgets path + local Reference - var thisWidgetsPath = moduleRecord.path & "/" & thisModule.getName() & "/widgets"; - - // check that module widgets folder exists on disk, if so, iterate and register - if ( directoryExists( thisWidgetsPath ) ) { - directoryList( thisWidgetsPath, false, "name", "*.cfc" ).each( function( thisWidget ){ - var widgetName = reReplaceNoCase( thisWidget, ".cfc", "", "all" ); - // Store Record - variables.moduleWidgetCache[ widgetName & "@" & thisModule.getName() ] = { - name : widgetName, - invocationPath : moduleRecord.invocationPath & ".#thisModule.getName()#.widgets.#widgetName#", - path : thisWidgetsPath & "/" & thisWidget, - module : thisModule.getName() - }; - } ); + ) + // Active and On Disk Module? + + // Deactivate it, not fond in registry, might be an orphaned record + + .each( + function( thisModule ) { + var moduleRecord = variables.moduleMap[ thisModule.getName() ]; + + var thisWidgetsPath = moduleRecord.path & "/" & thisModule.getName() & "/widgets"; + + if ( directoryExists( thisWidgetsPath ) ) { + directoryList( + thisWidgetsPath, + false, + "name", + "*.cfc" + ).each( + function( thisWidget ) { + var widgetName = reReplaceNoCase( + thisWidget, + ".cfc", + "", + "all" + ); + + variables.moduleWidgetCache[ widgetName & "@" & thisModule.getName() ] = { + name : widgetName, + invocationPath : moduleRecord.invocationPath & ".#thisModule.getName()#.widgets.#widgetName#", + path : thisWidgetsPath & "/" & thisWidget, + module : thisModule.getName() + }; + } + ); + } } - // end if widgets found - } ); + ); // Force Reload all widgets variables.widgetService.getWidgets( reload = true ); @@ -504,7 +526,7 @@ component * * @return An array of names found on disk */ - private array function getModulesOnDisk( required path ){ + private array function getModulesOnDisk( required path ) { // path, recurse, listInfo, filter, sort, type return directoryList( arguments.path, @@ -513,9 +535,11 @@ component "", "name asc", "dir" - ).filter( function( name ){ - return ( left( name, 1 ) neq "." ); - } ); + ).filter( + function( name ) { + return ( left( name, 1 ) NEQ "." ); + } + ); } -} +} \ No newline at end of file diff --git a/modules/contentbox/models/modules/custom/ModuleConfigBase.cfc b/modules/contentbox/models/modules/custom/ModuleConfigBase.cfc index 98ca6802b..6b61832ff 100644 --- a/modules/contentbox/models/modules/custom/ModuleConfigBase.cfc +++ b/modules/contentbox/models/modules/custom/ModuleConfigBase.cfc @@ -9,27 +9,26 @@ * - modules - custom modules */ component { - // Module Properties - this.title = "ContentBox Custom Module"; - this.author = "Ortus Solutions, Corp"; - this.webURL = "https://www.ortussolutions.com"; - this.description = "This module is where all custom user assets can be placed and version controlled"; - this.viewParentLookup = true; + this.title = "ContentBox Custom Module"; + this.author = "Ortus Solutions, Corp"; + this.webURL = "https://www.ortussolutions.com"; + this.description = "This module is where all custom user assets can be placed and version controlled"; + this.viewParentLookup = true; this.layoutParentLookup = true; // URL Entry Point - this.entryPoint = "cbCustom"; + this.entryPoint = "cbCustom"; // Model Namespace - this.modelNamespace = "contentbox-custom"; + this.modelNamespace = "contentbox-custom"; // CF Mapping - this.cfmapping = "contentbox-custom"; + this.cfmapping = "contentbox-custom"; // ContentBox must be loaded first - this.dependencies = [ "contentbox" ]; + this.dependencies = [ "contentbox"]; /** * Configure Module */ - function configure(){ + function configure() { // contentbox settings settings = {}; @@ -52,21 +51,24 @@ component { /** * Development tier */ - function development(){ + function development() { + } /** * Fired when the module is registered and activated. */ - function onLoad(){ + function onLoad() { + } /** * Fired when the module is unregistered and unloaded */ - function onUnload(){ + function onUnload() { + } - /************************************** PRIVATE *********************************************/ -} + /************************************** PRIVATE *********************************************/ +} \ No newline at end of file diff --git a/modules/contentbox/models/rss/RSSCacheCleanup.cfc b/modules/contentbox/models/rss/RSSCacheCleanup.cfc index 0483b28f0..97fc76f19 100644 --- a/modules/contentbox/models/rss/RSSCacheCleanup.cfc +++ b/modules/contentbox/models/rss/RSSCacheCleanup.cfc @@ -1,45 +1,47 @@ -/** -******************************************************************************** -ContentBox - A Modular Content Platform -Copyright 2012 by Luis Majano and Ortus Solutions, Corp -www.ortussolutions.com -******************************************************************************** -Apache License, Version 2.0 - -Copyright Since [2012] [Luis Majano and Ortus Solutions,Corp] - -Licensed under the Apache License, Version 2.0 (the "License" ); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -******************************************************************************** -* This interceptor monitors new comments and new entries to clear RSS cache entries -*/ +/** + * ******************************************************************************** + * ContentBox - A Modular Content Platform + * Copyright 2012 by Luis Majano and Ortus Solutions, Corp + * www.ortussolutions.com + * ******************************************************************************** + * Apache License, Version 2.0 + * + * Copyright Since [2012] [Luis Majano and Ortus Solutions,Corp] + * + * Licensed under the Apache License, Version 2.0 (the "License" ); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ******************************************************************************** + * This interceptor monitors new comments and new entries to clear RSS cache entries + */ component extends="coldbox.system.Interceptor" { // Listen when entries are saved - function cbadmin_postEntrySave( event, data ){ + function cbadmin_postEntrySave( event, data ) { getInstance( "rssService@contentbox" ).clearCaches(); } // Listen when entries are removed - function cbadmin_postEntryRemove( event, data ){ + function cbadmin_postEntryRemove( event, data ) { getInstance( "rssService@contentbox" ).clearCaches(); } // Listen when comments are made - function cbui_onCommentPost( event, data ){ - getInstance( "rssService@contentbox" ).clearCaches( - comments = true, - slug = arguments.data.comment.getRelatedContent().getSlug() - ); + function cbui_onCommentPost( event, data ) { + getInstance( "rssService@contentbox" ).clearCaches( comments = true, + slug = arguments + .data + .comment + .getRelatedContent() + .getSlug() ); } -} +} \ No newline at end of file diff --git a/modules/contentbox/models/rss/RSSService.cfc b/modules/contentbox/models/rss/RSSService.cfc index 96ceba785..443c92d57 100755 --- a/modules/contentbox/models/rss/RSSService.cfc +++ b/modules/contentbox/models/rss/RSSService.cfc @@ -1,4 +1,4 @@ -/** +/** * ContentBox - A Modular Content Platform * Copyright since 2012 by Ortus Solutions, Corp * www.ortussolutions.com/products/contentbox @@ -6,7 +6,6 @@ * RSS Services for this application */ component singleton { - // DI property name="entryService" inject="entryService@contentbox"; property name="pageService" inject="pageService@contentbox"; @@ -22,14 +21,14 @@ component singleton { * @settingService.inject id:settingService@contentbox * @cacheBox.inject cachebox */ - RSSService function init( required settingService, required cacheBox ){ + RSSService function init( required settingService, required cacheBox ) { // Dependencies variables.settingService = arguments.settingService; - variables.cacheBox = arguments.cacheBox; + variables.cacheBox = arguments.cacheBox; // Get all settings - var settings = settingService.getAllSettings(); + var settings = settingService.getAllSettings(); // setup the user selected cache provider - variables.cache = cacheBox.getCache( settings.cb_rss_cacheName ); + variables.cache = cacheBox.getCache( settings.cb_rss_cacheName ); return this; } @@ -42,7 +41,7 @@ component singleton { * @comments Clear comment caches or not, defaults to false * @slug The content slug to clear on */ - RSSService function clearCaches( boolean comments = false, string slug = "" ){ + RSSService function clearCaches( boolean comments = false, string slug = "" ) { var cacheKey = ""; // compose cache key @@ -58,8 +57,8 @@ component singleton { // log if ( variables.log.canInfo() ) { variables.log.info( - "Sent clear command using the following content key: #cacheKey# from provider: #variables.cache.getName()#" - ); + "Sent clear command using the following content key: #cacheKey# from provider: #variables.cache.getName()#" + ); } return this; @@ -68,7 +67,7 @@ component singleton { /** * Clean All RSS caches NOW BABY, NOW! */ - RSSService function clearAllCaches( boolean async = false ){ + RSSService function clearAllCaches( boolean async = false ) { variables.cache.clearByKeySnippet( keySnippet = "cb-feeds", async = arguments.async ); return this; } @@ -88,17 +87,16 @@ component singleton { category = "", contentType = "", string siteID = "" - ){ + ) { var settings = settingService.getAllSettings(); - var rssFeed = ""; + var rssFeed = ""; var cacheKey = ""; // Comments cache Key if ( arguments.comments ) { cacheKey = "cb-feeds-#cgi.HTTP_HOST#-content-comments-#arguments.slug#"; - } - // Entries cache Key - else { + } else // Entries cache Key + { cacheKey = "cb-feeds-#cgi.HTTP_HOST#-content-#hash( arguments.category & arguments.contentType )#"; } @@ -117,7 +115,7 @@ component singleton { // Building comment feed or content feed if ( arguments.comments ) { arguments.contentType = "Page"; - rssfeed = buildCommentFeed( argumentCollection = arguments ); + rssfeed = buildCommentFeed( argumentCollection = arguments ); } else { rssfeed = buildPageFeed( argumentCollection = arguments ); } @@ -128,7 +126,7 @@ component singleton { // Building comment feed or content feed if ( arguments.comments ) { arguments.contentType = "Entry"; - rssfeed = buildCommentFeed( argumentCollection = arguments ); + rssfeed = buildCommentFeed( argumentCollection = arguments ); } else { rssfeed = buildEntryFeed( argumentCollection = arguments ); } @@ -150,11 +148,11 @@ component singleton { // Cache it with settings if ( settings.cb_rss_caching ) { variables.cache.set( - cacheKey, - rssFeed, - settings.cb_rss_cachingTimeout, - settings.cb_rss_cachingTimeoutIdle - ); + cacheKey, + rssFeed, + settings.cb_rss_cachingTimeout, + settings.cb_rss_cachingTimeoutIdle + ); } return rssFeed; @@ -168,50 +166,78 @@ component singleton { * @category The category to filter on * @siteID The site to filter on */ - private function buildEntryFeed( string category = "", string siteID = "" ){ - var settings = settingService.getAllSettings(); + private function buildEntryFeed( string category = "", string siteID = "" ) { + var settings = settingService.getAllSettings(); var entryResults = entryService.findPublishedContent( - category: arguments.category, - max : settings.cb_rss_maxEntries, - siteID : arguments.siteID - ); - var myArray = []; + category = arguments.category, + max = settings.cb_rss_maxEntries, + siteID = arguments.siteID + ); + var myArray = []; var feedStruct = {}; - var columnMap = {}; - var qEntries = entityToQuery( entryResults.content ); + var columnMap = {}; + var qEntries = entityToQuery( entryResults.content ); // max checks - if ( settings.cb_rss_maxEntries lt entryResults.count ) { + if ( settings.cb_rss_maxEntries LT entryResults.count ) { entryResults.count = settings.cb_rss_maxEntries; } // Create the column maps - columnMap.title = "title"; - columnMap.description = "content"; - columnMap.pubDate = "publishedDate"; - columnMap.link = "link"; - columnMap.author = "author"; + columnMap.title = "title"; + columnMap.description = "content"; + columnMap.pubDate = "publishedDate"; + columnMap.link = "link"; + columnMap.author = "author"; columnMap.category_tag = "categories"; // Add necessary columns to query - queryAddColumn( qEntries, "link", myArray ); - queryAddColumn( qEntries, "linkComments", myArray ); - queryAddColumn( qEntries, "author", myArray ); - queryAddColumn( qEntries, "categories", myArray ); - queryAddColumn( qEntries, "content", myArray ); - queryAddColumn( qEntries, "guid_permalink", myArray ); - queryAddColumn( qEntries, "guid_string", myArray ); + queryAddColumn( + qEntries, + "link", + myArray + ); + queryAddColumn( + qEntries, + "linkComments", + myArray + ); + queryAddColumn( + qEntries, + "author", + myArray + ); + queryAddColumn( + qEntries, + "categories", + myArray + ); + queryAddColumn( + qEntries, + "content", + myArray + ); + queryAddColumn( + qEntries, + "guid_permalink", + myArray + ); + queryAddColumn( + qEntries, + "guid_string", + myArray + ); // Attach permalinks - for ( var i = 1; i lte entryResults.count; i++ ) { + for ( var i = 1; i LTE entryResults.count; i++ ) { // build URL to entry - qEntries.link[ i ] = variables.CBHelper.linkEntry( qEntries.slug[ i ] ); + qEntries.link[ i ] = variables.CBHelper.linkEntry( qEntries.slug[ i ] ); qEntries.guid_permalink[ i ] = false; - qEntries.guid_string[ i ] = variables.CBHelper.linkEntry( qEntries.slug[ i ] ); - ; - qEntries.author[ i ] = "#entryResults.content[ i ].getAuthorEmail()# (#entryResults.content[ i ].getAuthorName()#)"; + qEntries.guid_string[ i ] = variables.CBHelper.linkEntry( qEntries.slug[ i ] ); + + qEntries.author[ i ] = "#entryResults.content[ i ].getAuthorEmail()# (#entryResults.content[ i ].getAuthorName()#)"; qEntries.linkComments[ i ] = variables.CBHelper.linkComments( entryResults.content[ i ] ); - qEntries.categories[ i ] = entryResults.content[ i ].getCategoriesList(); + qEntries.categories[ i ] = entryResults.content[ i ].getCategoriesList(); if ( entryResults.content[ i ].hasExcerpt() ) { qEntries.content[ i ] = entryResults.content[ i ].renderExcerpt(); } else { @@ -221,15 +247,17 @@ component singleton { } // Generate feed items - feedStruct.title = "Blog " & settings.cb_rss_title; - feedStruct.generator = settings.cb_rss_generator; - feedStruct.copyright = settings.cb_rss_copyright; + feedStruct.title = "Blog " & settings.cb_rss_title; + feedStruct.generator = settings.cb_rss_generator; + feedStruct.copyright = settings.cb_rss_copyright; feedStruct.description = settings.cb_rss_description; - if ( len( settings.cb_rss_webmaster ) ) feedStruct.webmaster = settings.cb_rss_webmaster; - feedStruct.pubDate = now(); + if ( len( settings.cb_rss_webmaster ) ) { + feedStruct.webmaster = settings.cb_rss_webmaster; + } + feedStruct.pubDate = now(); feedStruct.lastbuilddate = now(); - feedStruct.link = variables.CBHelper.linkHome(); - feedStruct.items = qEntries; + feedStruct.link = variables.CBHelper.linkHome(); + feedStruct.items = qEntries; return variables.feedGenerator.createFeed( feedStruct, columnMap ); } @@ -240,49 +268,77 @@ component singleton { * @category The category to filter on * @siteID The site to filter on */ - private function buildPageFeed( string category = "", string siteID = "" ){ - var settings = settingService.getAllSettings(); + private function buildPageFeed( string category = "", string siteID = "" ) { + var settings = settingService.getAllSettings(); var pageResults = variables.pageService.findPublishedContent( - category: arguments.category, - max : settings.cb_rss_maxEntries, - siteID : arguments.siteID - ); - var myArray = []; + category = arguments.category, + max = settings.cb_rss_maxEntries, + siteID = arguments.siteID + ); + var myArray = []; var feedStruct = {}; - var columnMap = {}; - var qPages = entityToQuery( pageResults.content ); + var columnMap = {}; + var qPages = entityToQuery( pageResults.content ); // max checks - if ( settings.cb_rss_maxEntries lt pageResults.count ) { + if ( settings.cb_rss_maxEntries LT pageResults.count ) { pageResults.count = settings.cb_rss_maxEntries; } // Create the column maps - columnMap.title = "title"; - columnMap.description = "content"; - columnMap.pubDate = "publishedDate"; - columnMap.link = "link"; - columnMap.author = "author"; + columnMap.title = "title"; + columnMap.description = "content"; + columnMap.pubDate = "publishedDate"; + columnMap.link = "link"; + columnMap.author = "author"; columnMap.category_tag = "categories"; // Add necessary columns to query - queryAddColumn( qPages, "link", myArray ); - queryAddColumn( qPages, "linkComments", myArray ); - queryAddColumn( qPages, "author", myArray ); - queryAddColumn( qPages, "categories", myArray ); - queryAddColumn( qPages, "content", myArray ); - queryAddColumn( qPages, "guid_permalink", myArray ); - queryAddColumn( qPages, "guid_string", myArray ); + queryAddColumn( + qPages, + "link", + myArray + ); + queryAddColumn( + qPages, + "linkComments", + myArray + ); + queryAddColumn( + qPages, + "author", + myArray + ); + queryAddColumn( + qPages, + "categories", + myArray + ); + queryAddColumn( + qPages, + "content", + myArray + ); + queryAddColumn( + qPages, + "guid_permalink", + myArray + ); + queryAddColumn( + qPages, + "guid_string", + myArray + ); // Attach permalinks - for ( var i = 1; i lte pageResults.count; i++ ) { + for ( var i = 1; i LTE pageResults.count; i++ ) { // build URL to entry - qPages.link[ i ] = variables.CBHelper.linkPage( qPages.slug ); - qPages.author[ i ] = "#pageResults.content[ i ].getAuthorEmail()# (#pageResults.content[ i ].getAuthorName()#)"; - qPages.linkComments[ i ] = variables.CBHelper.linkComments( pageResults.content[ i ] ); - qPages.categories[ i ] = pageResults.content[ i ].getCategoriesList(); + qPages.link[ i ] = variables.CBHelper.linkPage( qPages.slug ); + qPages.author[ i ] = "#pageResults.content[ i ].getAuthorEmail()# (#pageResults.content[ i ].getAuthorName()#)"; + qPages.linkComments[ i ] = variables.CBHelper.linkComments( pageResults.content[ i ] ); + qPages.categories[ i ] = pageResults.content[ i ].getCategoriesList(); qPages.guid_permalink[ i ] = false; - qPages.guid_string[ i ] = variables.CBHelper.linkPage( qPages.slug ); + qPages.guid_string[ i ] = variables.CBHelper.linkPage( qPages.slug ); if ( pageResults.content[ i ].hasExcerpt() ) { qPages.content[ i ] = pageResults.content[ i ].renderExcerpt(); } else { @@ -292,15 +348,17 @@ component singleton { } // Generate feed items - feedStruct.title = "Page " & settings.cb_rss_title; - feedStruct.generator = settings.cb_rss_generator; - feedStruct.copyright = settings.cb_rss_copyright; + feedStruct.title = "Page " & settings.cb_rss_title; + feedStruct.generator = settings.cb_rss_generator; + feedStruct.copyright = settings.cb_rss_copyright; feedStruct.description = settings.cb_rss_description; - if ( len( settings.cb_rss_webmaster ) ) feedStruct.webmaster = settings.cb_rss_webmaster; - feedStruct.pubDate = now(); + if ( len( settings.cb_rss_webmaster ) ) { + feedStruct.webmaster = settings.cb_rss_webmaster; + } + feedStruct.pubDate = now(); feedStruct.lastbuilddate = now(); - feedStruct.link = CBHelper.linkHome(); - feedStruct.items = qPages; + feedStruct.link = CBHelper.linkHome(); + feedStruct.items = qPages; return variables.feedGenerator.createFeed( feedStruct, columnMap ); } @@ -311,66 +369,100 @@ component singleton { * @category The category to filter on * @siteID The site to filter on */ - private function buildContentFeed( string category = "", string siteID = "" ){ - var settings = variables.settingService.getAllSettings(); + private function buildContentFeed( string category = "", string siteID = "" ) { + var settings = variables.settingService.getAllSettings(); var contentResults = variables.contentService.findPublishedContent( - category: arguments.category, - max : settings.cb_rss_maxEntries, - siteID : arguments.siteID - ); - var myArray = []; + category = arguments.category, + max = settings.cb_rss_maxEntries, + siteID = arguments.siteID + ); + var myArray = []; var feedStruct = {}; - var columnMap = {}; - var qContent = entityToQuery( contentResults.content ); + var columnMap = {}; + var qContent = entityToQuery( contentResults.content ); // max checks - if ( settings.cb_rss_maxEntries lt contentResults.count ) { + if ( settings.cb_rss_maxEntries LT contentResults.count ) { contentResults.count = settings.cb_rss_maxEntries; } // Create the column maps - columnMap.title = "title"; - columnMap.description = "content"; - columnMap.pubDate = "publishedDate"; - columnMap.link = "link"; - columnMap.author = "author"; + columnMap.title = "title"; + columnMap.description = "content"; + columnMap.pubDate = "publishedDate"; + columnMap.link = "link"; + columnMap.author = "author"; columnMap.category_tag = "categories"; // Add necessary columns to query - queryAddColumn( qContent, "link", myArray ); - queryAddColumn( qContent, "linkComments", myArray ); - queryAddColumn( qContent, "author", myArray ); - queryAddColumn( qContent, "categories", myArray ); - queryAddColumn( qContent, "content", myArray ); - queryAddColumn( qContent, "guid_permalink", myArray ); - queryAddColumn( qContent, "guid_string", myArray ); + queryAddColumn( + qContent, + "link", + myArray + ); + queryAddColumn( + qContent, + "linkComments", + myArray + ); + queryAddColumn( + qContent, + "author", + myArray + ); + queryAddColumn( + qContent, + "categories", + myArray + ); + queryAddColumn( + qContent, + "content", + myArray + ); + queryAddColumn( + qContent, + "guid_permalink", + myArray + ); + queryAddColumn( + qContent, + "guid_string", + myArray + ); // Attach permalinks - for ( var i = 1; i lte contentResults.count; i++ ) { + for ( var i = 1; i LTE contentResults.count; i++ ) { // Check for empty authors, just in case. - var authorEmail = len( contentResults.content[ i ].getAuthorEmail() ) ? contentResults.content[ i ].getAuthorEmail() : "nobody@nobody.com"; - var authorName = len( contentResults.content[ i ].getAuthorName() ) ? contentResults.content[ i ].getAuthorName() : "nobody"; + var authorEmail = len( contentResults.content[ i ].getAuthorEmail() ) + ? contentResults.content[ i ].getAuthorEmail() + : "nobody@nobody.com"; + var authorName = len( contentResults.content[ i ].getAuthorName() ) + ? contentResults.content[ i ].getAuthorName() + : "nobody"; // build URL to entry - qContent.link[ i ] = CBHelper.linkContent( contentResults.content[ i ] ); - qContent.author[ i ] = "#authorEmail# (#authorName#)"; - qContent.linkComments[ i ] = CBHelper.linkComments( contentResults.content[ i ] ); - qContent.categories[ i ] = contentResults.content[ i ].getCategoriesList(); - qContent.content[ i ] = cleanupContent( contentResults.content[ i ].renderContent() ); + qContent.link[ i ] = CBHelper.linkContent( contentResults.content[ i ] ); + qContent.author[ i ] = "#authorEmail# (#authorName#)"; + qContent.linkComments[ i ] = CBHelper.linkComments( contentResults.content[ i ] ); + qContent.categories[ i ] = contentResults.content[ i ].getCategoriesList(); + qContent.content[ i ] = cleanupContent( contentResults.content[ i ].renderContent() ); qContent.guid_permalink[ i ] = false; - qContent.guid_string[ i ] = CBHelper.linkContent( contentResults.content[ i ] ); + qContent.guid_string[ i ] = CBHelper.linkContent( contentResults.content[ i ] ); } // Generate feed items - feedStruct.title = "Content " & settings.cb_rss_title; - feedStruct.generator = settings.cb_rss_generator; - feedStruct.copyright = settings.cb_rss_copyright; + feedStruct.title = "Content " & settings.cb_rss_title; + feedStruct.generator = settings.cb_rss_generator; + feedStruct.copyright = settings.cb_rss_copyright; feedStruct.description = settings.cb_rss_description; - if ( len( settings.cb_rss_webmaster ) ) feedStruct.webmaster = settings.cb_rss_webmaster; - feedStruct.pubDate = now(); + if ( len( settings.cb_rss_webmaster ) ) { + feedStruct.webmaster = settings.cb_rss_webmaster; + } + feedStruct.pubDate = now(); feedStruct.lastbuilddate = now(); - feedStruct.link = CBHelper.linkHome(); - feedStruct.items = qContent; + feedStruct.link = CBHelper.linkHome(); + feedStruct.items = qContent; return variables.feedGenerator.createFeed( feedStruct, columnMap ); } @@ -386,55 +478,77 @@ component singleton { string slug = "", string contentType = "", string siteID = "" - ){ - var settings = variables.settingService.getAllSettings(); + ) { + var settings = variables.settingService.getAllSettings(); var commentResults = variables.commentService.findAllApproved( - contentID : variables.contentService.getIDBySlug( arguments.slug ), - contentType: arguments.contentType, - max : settings.cb_rss_maxComments, - siteID : arguments.siteID - ); - var myArray = []; + contentID = variables.contentService.getIDBySlug( arguments.slug ), + contentType = arguments.contentType, + max = settings.cb_rss_maxComments, + siteID = arguments.siteID + ); + var myArray = []; var feedStruct = {}; - var columnMap = {}; - var qComments = entityToQuery( commentResults.comments ); + var columnMap = {}; + var qComments = entityToQuery( commentResults.comments ); // Create the column maps - columnMap.title = "title"; - columnMap.description = "content"; - columnMap.pubDate = "createddate"; - columnMap.link = "link"; - columnMap.author = "rssAuthor"; + columnMap.title = "title"; + columnMap.description = "content"; + columnMap.pubDate = "createddate"; + columnMap.link = "link"; + columnMap.author = "rssAuthor"; columnMap.category_tag = "categories"; // Add necessary columns to query - queryAddColumn( qComments, "title", myArray ); - queryAddColumn( qComments, "linkComments", myArray ); - queryAddColumn( qComments, "rssAuthor", myArray ); - queryAddColumn( qComments, "guid_permalink", myArray ); - queryAddColumn( qComments, "guid_string", myArray ); + queryAddColumn( + qComments, + "title", + myArray + ); + queryAddColumn( + qComments, + "linkComments", + myArray + ); + queryAddColumn( + qComments, + "rssAuthor", + myArray + ); + queryAddColumn( + qComments, + "guid_permalink", + myArray + ); + queryAddColumn( + qComments, + "guid_string", + myArray + ); // Attach permalinks - for ( var i = 1; i lte commentResults.count; i++ ) { + for ( var i = 1; i LTE commentResults.count; i++ ) { // build URL to entry - qComments.title[ i ] = "Comment by #qComments.author[ i ]# on #commentResults.comments[ i ].getParentTitle()#"; - qComments.rssAuthor[ i ] = "#qComments.authorEmail# (#qComments.author#)"; - qComments.linkComments[ i ] = CBHelper.linkComment( commentResults.comments[ i ] ); - qComments.content[ i ] = cleanupContent( qComments.content[ i ] ); + qComments.title[ i ] = "Comment by #qComments.author[ i ]# on #commentResults.comments[ i ].getParentTitle()#"; + qComments.rssAuthor[ i ] = "#qComments.authorEmail# (#qComments.author#)"; + qComments.linkComments[ i ] = CBHelper.linkComment( commentResults.comments[ i ] ); + qComments.content[ i ] = cleanupContent( qComments.content[ i ] ); qComments.guid_permalink[ i ] = false; - qComments.guid_string[ i ] = CBHelper.linkComment( commentResults.comments[ i ] ); + qComments.guid_string[ i ] = CBHelper.linkComment( commentResults.comments[ i ] ); } // Generate feed items - feedStruct.title = "Comments " & settings.cb_rss_title; - feedStruct.generator = settings.cb_rss_generator; - feedStruct.copyright = settings.cb_rss_copyright; + feedStruct.title = "Comments " & settings.cb_rss_title; + feedStruct.generator = settings.cb_rss_generator; + feedStruct.copyright = settings.cb_rss_copyright; feedStruct.description = settings.cb_rss_description; - if ( len( settings.cb_rss_webmaster ) ) feedStruct.webmaster = settings.cb_rss_webmaster; - feedStruct.pubDate = now(); + if ( len( settings.cb_rss_webmaster ) ) { + feedStruct.webmaster = settings.cb_rss_webmaster; + } + feedStruct.pubDate = now(); feedStruct.lastbuilddate = now(); - feedStruct.link = CBHelper.linkHome(); - feedStruct.items = qComments; + feedStruct.link = CBHelper.linkHome(); + feedStruct.items = qComments; return variables.feedGenerator.createFeed( feedStruct, columnMap ); } @@ -442,8 +556,13 @@ component singleton { /** * Cleanup HTML to normal strings to avoid parsing issues */ - private function cleanupContent( required content ){ - return reReplaceNoCase( arguments.content, "<[^>]*>", "", "all" ); + private function cleanupContent( required content ) { + return reReplaceNoCase( + arguments.content, + "<[^>]*>", + "", + "all" + ); } -} +} \ No newline at end of file diff --git a/modules/contentbox/models/search/DBSearch.cfc b/modules/contentbox/models/search/DBSearch.cfc index 7a275f72b..bc33cf4b4 100644 --- a/modules/contentbox/models/search/DBSearch.cfc +++ b/modules/contentbox/models/search/DBSearch.cfc @@ -8,7 +8,6 @@ * @see contentbox.models.search.ISearchAdapter */ component accessors="true" singleton { - // DI property name="contentService" inject="contentService@contentbox"; property name="cb" inject="cbHelper@contentbox"; @@ -17,7 +16,7 @@ component accessors="true" singleton { /** * Constructor */ - DBSearch function init(){ + DBSearch function init() { return this; } @@ -36,32 +35,36 @@ component accessors="true" singleton { numeric max = 0, numeric offset = 0, string siteID = "" - ){ + ) { // get new search results object var searchResults = variables.wirebox.getInstance( "SearchResults@contentbox" ); - var sTime = getTickCount(); + var sTime = getTickCount(); try { var results = variables.contentService.searchContent( - offset : arguments.offset, - max : arguments.max, - searchTerm : arguments.searchTerm, - showInSearch: true, - contentTypes: "Page,Entry", - siteID : arguments.siteID - ); + offset = arguments.offset, + max = arguments.max, + searchTerm = arguments.searchTerm, + showInSearch = true, + contentTypes = "Page,Entry", + siteID = arguments.siteID + ); // populate the search results - searchResults.populate( { - results : results.content, - total : results.count, - searchTime : getTickCount() - sTime, - searchTerm : arguments.searchTerm, - error : false - } ); - } catch ( Any e ) { + searchResults.populate( + { + results : results.content, + total : results.count, + searchTime : getTickCount() - sTime, + searchTerm : arguments.searchTerm, + error : false + } + ); + } catch (Any e) { searchResults.setError( true ); - searchResults.setErrorMessages( [ "Error executing content search: #e.detail# #e.message#" ] ); + searchResults.setErrorMessages( + [ "Error executing content search: #e.detail# #e.message#"] + ); } return searchResults; @@ -72,7 +75,8 @@ component accessors="true" singleton { * * @return contentbox.models.search.ISearchAdapter */ - DBSearch function refresh(){ + DBSearch function refresh() { + } /** @@ -84,7 +88,7 @@ component accessors="true" singleton { required string searchTerm, numeric max = 0, numeric offset = 0 - ){ + ) { var searchResults = search( argumentCollection = arguments ); return renderSearchWithResults( searchResults ); } @@ -94,40 +98,41 @@ component accessors="true" singleton { * * @searchResults The search results object */ - any function renderSearchWithResults( required SearchResults searchResults ){ - var results = ""; + any function renderSearchWithResults( required SearchResults searchResults ) { + var results = ""; var searchItems = arguments.searchResults.getResults(); - var total = arguments.searchResults.getTotal(); - var searchTerm = arguments.searchResults.getSearchTerm(); + var total = arguments.searchResults.getTotal(); + var searchTerm = arguments.searchResults.getSearchTerm(); // cfformat-ignore-start savecontent variable="results" { - // Render out the results or error if the results had errors - if( arguments.searchResults.getError() ){ - writeOutput( " + if ( arguments.searchResults.getError() ) { + writeOutput( + "

Error Running Search

- #arrayToList( - arguments.searchResults.getErrorMessages(), - "
" - )# + #arrayToList( arguments.searchResults.getErrorMessages(), "
" )#

- " ); + " + ); } else { - writeOutput( " + writeOutput( + "
Found #total# results in #arguments.searchResults.getSearchTime()#ms!
- " ); + " + ); } // Render out the items for ( var item in searchItems ) { - writeOutput(" + writeOutput( + "
#item.getTitle()# @@ -136,18 +141,21 @@ component accessors="true" singleton {

#highlightSearchTerm( searchTerm, stripHTML( item.renderContent() ) )#

#item.getContentType()# : #cb.linkContent( item )#
- "); + " + ); if ( item.hasCategories() ) { writeOutput( "
Categories: " ); - for ( var categoryItem in #item.getCategoriesList()# ) { - writeOutput( " #categoryItem#" ); + for ( var categoryItem in item.getCategoriesList() ) { + writeOutput( + " #categoryItem#" + ); } writeOutput( "
" ); } writeOutput( "
" ); - }; + } writeOutput( "
" ); } @@ -159,8 +167,13 @@ component accessors="true" singleton { /** * utility to strip HTML */ - private function stripHTML( stringTarget ){ - return reReplaceNoCase( arguments.stringTarget, "<[^>]*>", "", "ALL" ); + private function stripHTML( stringTarget ) { + return reReplaceNoCase( + arguments.stringTarget, + "<[^>]*>", + "", + "ALL" + ); } /** @@ -169,23 +182,27 @@ component accessors="true" singleton { * @term The search term * @content The content searched */ - private function highlightSearchTerm( required term, required content ){ - var match = findNoCase( arguments.term, arguments.content ); - var end = 0; + private function highlightSearchTerm( required term, required content ) { + var match = findNoCase( arguments.term, arguments.content ); + var end = 0; var excerpt = ""; - if ( match lte 250 ) { + if ( match LTE 250 ) { match = 1; } end = match + len( arguments.term ) + 500; - if ( len( arguments.content ) gt 500 ) { - if ( match gt 1 ) { - excerpt = "..." & mid( arguments.content, match - 250, end - match ); + if ( len( arguments.content ) GT 500 ) { + if ( match GT 1 ) { + excerpt = "..." & mid( + arguments.content, + match - 250, + end - match + ); } else { excerpt = left( arguments.content, end ); } - if ( len( arguments.content ) gt end ) { + if ( len( arguments.content ) GT end ) { excerpt = excerpt & "..."; } } else { @@ -199,7 +216,8 @@ component accessors="true" singleton { "\1", "all" ); - } catch ( Any e ) { + } catch (Any e) { + } // remove images @@ -210,4 +228,4 @@ component accessors="true" singleton { return excerpt; } -} +} \ No newline at end of file diff --git a/modules/contentbox/models/search/ISearchAdapter.cfc b/modules/contentbox/models/search/ISearchAdapter.cfc index fa951293d..936524a77 100644 --- a/modules/contentbox/models/search/ISearchAdapter.cfc +++ b/modules/contentbox/models/search/ISearchAdapter.cfc @@ -17,7 +17,7 @@ interface { * * @return SearchResults object with the results */ - contentbox.models.search.SearchResults function search( + default contentbox.models.search.SearchResults function search( required string searchTerm, numeric max = 0, numeric offset = 0, @@ -29,7 +29,7 @@ interface { * * @return Itself */ - contentbox.models.search.ISearchAdapter function refresh(); + default contentbox.models.search.ISearchAdapter function refresh(); /** * Render the search results according to the adapter and return HTML @@ -40,7 +40,7 @@ interface { * * @return The search html */ - any function renderSearch( + default any function renderSearch( required string searchTerm, numeric max = 0, numeric offset = 0 @@ -53,6 +53,7 @@ interface { * * @return The search html */ - any function renderSearchWithResults( required contentbox.models.search.SearchResults searchResults ); - -} + default any function renderSearchWithResults( + required contentbox.models.search.SearchResults searchResults + ); +} \ No newline at end of file diff --git a/modules/contentbox/models/search/SearchResults.cfc b/modules/contentbox/models/search/SearchResults.cfc index 19150d747..07edd3810 100644 --- a/modules/contentbox/models/search/SearchResults.cfc +++ b/modules/contentbox/models/search/SearchResults.cfc @@ -6,37 +6,27 @@ * The official ContentBox Search Results Object */ component accessors="true" { - property name="results" type="any" hint="The search results in query or array or whatever format"; - property name="total" type="numeric" hint="The total number of records found"; - property name="searchTime" type="numeric" hint="The amount of time it took for the search in milliseconds"; - - property - name="searchTerm" - type="string" - hint="The search term used"; - + property name="searchTerm" type="string" hint="The search term used"; property name="error" type="boolean" hint="Mark if the search results produce an error or not"; - property name="errorMessages" type="array" hint="An array of error messagse if any"; - property name="metadata" type="struct" @@ -45,14 +35,14 @@ component accessors="true" { /** * Constructor */ - function init(){ - variables.results = []; - variables.searchTime = 0; - variables.total = 0; - variables.metadata = {}; - variables.error = false; + function init() { + variables.results = []; + variables.searchTime = 0; + variables.total = 0; + variables.metadata = {}; + variables.error = false; variables.errorMessages = []; - variables.searchTerm = ""; + variables.searchTerm = ""; return this; } @@ -62,7 +52,7 @@ component accessors="true" { * * @return SearchResults */ - any function populate( required struct memento ){ + any function populate( required struct memento ) { for ( var key in arguments.memento ) { if ( structKeyExists( variables, key ) ) { variables[ key ] = arguments.memento[ key ]; @@ -76,7 +66,7 @@ component accessors="true" { * * @return struct of { "results","searchTime","total","metadata","error","errorMessages","searchTerm" } */ - struct function getMemento(){ + struct function getMemento() { return { "results" : results, "searchTime" : searchTime, @@ -88,4 +78,4 @@ component accessors="true" { }; } -} +} \ No newline at end of file diff --git a/modules/contentbox/models/search/SearchService.cfc b/modules/contentbox/models/search/SearchService.cfc index fa64dc5cd..e5d54adfc 100644 --- a/modules/contentbox/models/search/SearchService.cfc +++ b/modules/contentbox/models/search/SearchService.cfc @@ -6,7 +6,6 @@ * The official ContentBox Search Service */ component accessors="true" { - // DI property name="wirebox" inject="wirebox"; property name="settingService" inject="settingService@contentbox"; @@ -14,7 +13,7 @@ component accessors="true" { /** * Constructor */ - SearchService function init(){ + SearchService function init() { return this; } @@ -22,8 +21,8 @@ component accessors="true" { * Return the actual ContentBox configured search adapter that should meet * the following interface: contentbox.models.search.ISearchAdapter */ - any function getSearchAdapter(){ + any function getSearchAdapter() { return variables.wirebox.getInstance( variables.settingService.getSetting( "cb_search_adapter" ) ); } -} +} \ No newline at end of file diff --git a/modules/contentbox/models/security/Author.cfc b/modules/contentbox/models/security/Author.cfc index e373f57be..1e296fbb2 100644 --- a/modules/contentbox/models/security/Author.cfc +++ b/modules/contentbox/models/security/Author.cfc @@ -1,4 +1,4 @@ -/** +/** * ContentBox - A Modular Content Platform * Copyright since 2012 by Ortus Solutions, Corp * www.ortussolutions.com/products/contentbox @@ -14,208 +14,186 @@ component cachename ="cbAuthor" cacheuse ="read-write" { - - /* ********************************************************************* - ** DI - ********************************************************************* */ + /********************************************************************** + * ** DI + **********************************************************************/ property - name ="authorService" - inject ="provider:authorService@contentbox" + name="authorService" + inject="provider:authorService@contentbox" persistent="false"; - property - name ="avatar" - inject ="provider:Avatar@contentbox" + name="avatar" + inject="provider:Avatar@contentbox" persistent="false"; - - /* ********************************************************************* - ** PROPERTIES - ********************************************************************* */ + /********************************************************************** + * ** PROPERTIES + **********************************************************************/ property - name ="authorID" - column ="authorID" + name="authorID" + column="authorID" fieldtype="id" generator="uuid" - length ="36" - ormtype ="string" - update ="false"; - + length="36" + ormtype="string" + update="false"; property - name ="firstName" - column ="firstName" - length ="100" + name="firstName" + column="firstName" + length="100" notnull="true" default=""; - property - name ="lastName" - column ="lastName" - length ="100" + name="lastName" + column="lastName" + length="100" notnull="true" default=""; - property - name ="email" - column ="email" - length ="255" + name="email" + column="email" + length="255" notnull="true" - index ="idx_email" + index="idx_email" default=""; - property - name ="username" - column ="username" - length ="100" + name="username" + column="username" + length="100" notnull="true" - index ="idx_login" - unique ="true" + index="idx_login" + unique="true" default=""; - property - name ="password" - column ="password" - length ="100" + name="password" + column="password" + length="100" notnull="true" - index ="idx_login" + index="idx_login" default=""; - property - name ="isActive" - column ="isActive" + name="isActive" + column="isActive" ormtype="boolean" notnull="true" default="false" - index ="idx_login,idx_activeAuthor"; - + index="idx_login,idx_activeAuthor"; property - name ="lastLogin" - column ="lastLogin" + name="lastLogin" + column="lastLogin" ormtype="timestamp" notnull="false"; - property - name ="biography" - column ="biography" + name="biography" + column="biography" ormtype="text" notnull="false" - length ="8000" + length="8000" default=""; - property - name ="preferences" - column ="preferences" + name="preferences" + column="preferences" ormtype="text" notnull="false" - length ="8000" + length="8000" default=""; - property - name ="isPasswordReset" - column ="isPasswordReset" + name="isPasswordReset" + column="isPasswordReset" ormtype="boolean" notnull="true" default="false" - index ="idx_passwordReset"; - + index="idx_passwordReset"; property - name ="is2FactorAuth" - column ="is2FactorAuth" + name="is2FactorAuth" + column="is2FactorAuth" ormtype="boolean" notnull="true" default="false" - index ="idx_2factorauth"; - - /* ********************************************************************* - ** RELATIONSHIPS - ********************************************************************* */ + index="idx_2factorauth"; + /********************************************************************** + * ** RELATIONSHIPS + **********************************************************************/ // O2M -> Entries property - name ="entries" + name="entries" singularName="entry" - type ="array" - fieldtype ="one-to-many" - cfc ="contentbox.models.content.Entry" - fkcolumn ="FK_authorID" - inverse ="true" - lazy ="true" - cascade ="save-update" - batchsize ="10" - orderby ="publishedDate DESC"; - + type="array" + fieldtype="one-to-many" + cfc="contentbox.models.content.Entry" + fkcolumn="FK_authorID" + inverse="true" + lazy="true" + cascade="save-update" + batchsize="10" + orderby="publishedDate DESC"; // O2M -> Pages property - name ="pages" + name="pages" singularName="page" - type ="array" - fieldtype ="one-to-many" - cfc ="contentbox.models.content.Page" - fkcolumn ="FK_authorID" - inverse ="true" - lazy ="true" - cascade ="save-update" - batchsize ="10" - orderby ="publishedDate DESC"; - + type="array" + fieldtype="one-to-many" + cfc="contentbox.models.content.Page" + fkcolumn="FK_authorID" + inverse="true" + lazy="true" + cascade="save-update" + batchsize="10" + orderby="publishedDate DESC"; // M20 -> Role property - name ="role" - notnull ="true" + name="role" + notnull="true" fieldtype="many-to-one" - cfc ="contentbox.models.security.Role" - fkcolumn ="FK_roleID" - lazy ="true"; - + cfc="contentbox.models.security.Role" + fkcolumn="FK_roleID" + lazy="true"; // M2M -> A-la-carte Author Permissions property - name ="permissions" - fieldtype ="many-to-many" - cascade ="save-update" - type ="array" - lazy ="true" - cfc ="contentbox.models.security.Permission" - fkcolumn ="FK_authorID" - linktable ="cb_authorPermissions" + name="permissions" + fieldtype="many-to-many" + cascade="save-update" + type="array" + lazy="true" + cfc="contentbox.models.security.Permission" + fkcolumn="FK_authorID" + linktable="cb_authorPermissions" inversejoincolumn="FK_permissionID" - orderby ="permission"; - + orderby="permission"; // M2M -> A-la-carte Author Permission Groups property - name ="permissionGroups" - singularName ="permissionGroup" - fieldtype ="many-to-many" - type ="array" - lazy ="true" - inverse ="true" - cfc ="contentbox.models.security.PermissionGroup" - cascade ="all" - fkcolumn ="FK_authorID" - linktable ="cb_authorPermissionGroups" + name="permissionGroups" + singularName="permissionGroup" + fieldtype="many-to-many" + type="array" + lazy="true" + inverse="true" + cfc="contentbox.models.security.PermissionGroup" + cascade="all" + fkcolumn="FK_authorID" + linktable="cb_authorPermissionGroups" inversejoincolumn="FK_permissionGroupID" - orderby ="name"; - - /* ********************************************************************* - ** CALCULATED FIELDS - ********************************************************************* */ + orderby="name"; + /********************************************************************** + * ** CALCULATED FIELDS + **********************************************************************/ - /* ********************************************************************* - ** NON PERSISTED PROPERTIES - ********************************************************************* */ + /********************************************************************** + * ** NON PERSISTED PROPERTIES + **********************************************************************/ // Non-persisted properties property - name ="loggedIn" + name="loggedIn" persistent="false" - default ="false" - type ="boolean"; - + default="false" + type="boolean"; property name="permissionList" persistent="false"; - - /* ********************************************************************* - ** PK + CONSTRAINTS + MEMENTO - ********************************************************************* */ + /********************************************************************** + * ** PK + CONSTRAINTS + MEMENTO + **********************************************************************/ this.pk = "authorID"; @@ -247,7 +225,7 @@ component "isPasswordReset", "is2FactorAuth" ], - neverInclude : [ "password" ], + neverInclude : [ "password"], // Defaults defaults : { "role" : {} } }; @@ -270,20 +248,20 @@ component "role" : { required : true } }; - /* ********************************************************************* - ** PUBLIC FUNCTIONS - ********************************************************************* */ + /********************************************************************** + * ** PUBLIC FUNCTIONS + **********************************************************************/ /** * Constructor */ - function init(){ - variables.permissionList = []; - variables.loggedIn = false; - variables.isActive = true; + function init() { + variables.permissionList = []; + variables.loggedIn = false; + variables.isActive = true; variables.permissionGroups = []; - variables.isPasswordReset = false; - variables.is2FactorAuth = false; + variables.isPasswordReset = false; + variables.is2FactorAuth = false; // Setup empty preferences setPreferences( {} ); @@ -296,28 +274,28 @@ component /** * Get the total number of content items this author has created */ - numeric function getNumberOfContent(){ + numeric function getNumberOfContent() { return ( isLoaded() ? variables.authorService.getTotalContent( getAuthorId() ) : 0 ); } /** * Get the total number of entries this author has created */ - numeric function getNumberOfEntries(){ + numeric function getNumberOfEntries() { return ( isLoaded() ? variables.authorService.getTotalEntries( getAuthorId() ) : 0 ); } /** * Get the total number of pages this author has created */ - numeric function getNumberOfPages(){ + numeric function getNumberOfPages() { return ( isLoaded() ? variables.authorService.getTotalPages( getAuthorId() ) : 0 ); } /** * Get the total number of content store items this author has created */ - numeric function getNumberOfContentStore(){ + numeric function getNumberOfContentStore() { return ( isLoaded() ? variables.authorService.getTotalContentStoreItems( getAuthorId() ) : 0 ); } @@ -326,9 +304,9 @@ component * * @permission One or a list of permissions to check for access */ - boolean function hasPermission( required permission ){ + boolean function hasPermission( required permission ) { // cache deconstructed permissions in case it's called many times during a request. - if ( !arrayLen( variables.permissionList ) AND hasPermissions() ) { + if ( !arrayLen( variables.permissionList ) && hasPermissions() ) { variables.permissionList = arrayReduce( getPermissions(), ( result, item ) => { @@ -340,11 +318,9 @@ component // checks via role, then group permissions and then local permissions if ( - ( hasRole() && getRole().hasPermission( arguments.permission ) ) - OR - checkGroupPermissions( arguments.permission ) - OR - inPermissionList( arguments.permission ) + ( hasRole() && getRole().hasPermission( arguments.permission ) ) || + checkGroupPermissions( arguments.permission ) || + inPermissionList( arguments.permission ) ) { return true; } @@ -357,7 +333,7 @@ component * * @slug The slug to check */ - boolean function checkGroupPermissions( required slug ){ + boolean function checkGroupPermissions( required slug ) { // If no groups, just return false if ( !hasPermissionGroup() ) { return false; @@ -378,16 +354,16 @@ component * * @permission A list or array of permission to check */ - public function inPermissionList( required permission ){ - return arrayWrap( arguments.permission ) - .filter( ( item ) => variables.permissionList.findNoCase( arguments.item ) ) - .len(); + public function inPermissionList( required permission ) { + return arrayWrap( arguments.permission ).filter( + ( item ) => variables.permissionList.findNoCase( arguments.item ) + ).len(); } /** * Remove all permissions */ - Author function clearPermissions(){ + Author function clearPermissions() { if ( hasPermissions() ) { variables.permissions.clear(); } else { @@ -400,7 +376,7 @@ component /** * Remove all permission groups */ - Author function clearPermissionGroups(){ + Author function clearPermissionGroups() { if ( hasPermissionGroup() ) { variables.permissionGroups.clear(); } else { @@ -414,7 +390,7 @@ component * * @permissions The permissions array to override */ - Author function setPermissions( required array permissions ){ + Author function setPermissions( required array permissions ) { if ( hasPermissions() ) { variables.permissions.clear(); variables.permissions.addAll( arguments.permissions ); @@ -427,7 +403,7 @@ component /** * Shortcut Utlity function to get a list of all the permission groups this user belongs to. */ - string function getPermissionGroupsList( delimiter = "," ){ + string function getPermissionGroupsList( delimiter = "," ) { if ( hasPermissionGroup() ) { return arrayReduce( variables.permissionGroups, @@ -443,7 +419,7 @@ component * * @group The permission group to add */ - Author function addPermissionGroup( required group ){ + Author function addPermissionGroup( required group ) { // Only add if not already there. if ( !hasPermissionGroup( arguments.group ) ) { arrayAppend( variables.permissionGroups, arguments.group ); @@ -457,7 +433,7 @@ component * * @group The permission group to add */ - Author function removePermissionGroup( required group ){ + Author function removePermissionGroup( required group ) { // Only add if not already there. if ( hasPermissionGroup( arguments.group ) ) { arrayDelete( variables.permissionGroups, arguments.group ); @@ -470,17 +446,20 @@ component * Utility method to verify if an author has been logged in to the system or not. * This method does not account for permissions. Only for logged in status. */ - function isLoggedIn(){ + function isLoggedIn() { return getLoggedIn(); } /** * Get formatted lastLogin */ - string function getDisplayLastLogin( dateFormat = this.DATE_FORMAt, timeFormat = this.TIME_FORMAT ){ + string function getDisplayLastLogin( + dateFormat = this.DATE_FORMAt, + timeFormat = this.TIME_FORMAT + ) { var lastLogin = getLastLogin(); - if ( NOT isNull( lastLogin ) ) { + if ( !isNull( lastLogin ) ) { return dateFormat( lastLogin, arguments.dateFormat ) & " " & timeFormat( lastLogin, arguments.timeFormat ); } @@ -494,8 +473,13 @@ component * * @preferences.hint A struct of data or a JSON packet to store */ - Author function setPreferences( required any preferences ){ - lock name="user.#getAuthorID()#.preferences" type="exclusive" throwontimeout="true" timeout="5" { + Author function setPreferences( required any preferences ) { + lock + name ="user.#getAuthorID()#.preferences" + type ="exclusive" + throwontimeout="true" + timeout ="5" + { if ( isStruct( arguments.preferences ) ) { arguments.preferences = serializeJSON( arguments.preferences ); } @@ -508,18 +492,28 @@ component /** * Get all user preferences in inflated format */ - struct function getAllPreferences(){ - lock name="user.#getAuthorID()#.preferences" type="readonly" throwontimeout="true" timeout="5" { - return ( !isNull( preferences ) AND isJSON( preferences ) ? deserializeJSON( preferences ) : structNew() ); + struct function getAllPreferences() { + lock + name ="user.#getAuthorID()#.preferences" + type ="readonly" + throwontimeout="true" + timeout ="5" + { + return ( !isNull( preferences ) && isJSON( preferences ) ? deserializeJSON( preferences ) : structNew() ); } } /** * Get a preference, you can pass a default value if preference does not exist */ - any function getPreference( required name, defaultValue ){ + any function getPreference( required name, defaultValue ) { // get preference - lock name="user.#getAuthorID()#.preferences" type="readonly" throwontimeout="true" timeout="5" { + lock + name ="user.#getAuthorID()#.preferences" + type ="readonly" + throwontimeout="true" + timeout ="5" + { var allPreferences = getAllPreferences(); if ( structKeyExists( allPreferences, arguments.name ) ) { return allPreferences[ arguments.name ]; @@ -540,8 +534,8 @@ component /** * Set a preference in the user preferences */ - Author function setPreference( required name, required value ){ - var allPreferences = getAllPreferences(); + Author function setPreference( required name, required value ) { + var allPreferences = getAllPreferences(); allPreferences[ arguments.name ] = arguments.value; // store in lock mode return setPreferences( allPreferences ); @@ -552,14 +546,14 @@ component /** * A struct of custom claims to add to the JWT token */ - struct function getJwtCustomClaims(){ + struct function getJwtCustomClaims() { return {}; } /** * This function returns an array of all the scopes that should be attached to the JWT token that will be used for authorization. */ - array function getJwtScopes(){ + array function getJwtScopes() { return []; } @@ -568,14 +562,14 @@ component /** * Get the user's role name */ - string function getRoleName(){ + string function getRoleName() { return ( hasRole() ? getRole().getRole() : "" ); } /** * Retrieve full name */ - string function getFullName(){ + string function getFullName() { return getFirstname() & " " & getLastName(); } @@ -584,14 +578,14 @@ component * * @size The size of the avatar, defaults to 40 */ - string function getAvatarLink( numeric size = 40 ){ + string function getAvatarLink( numeric size = 40 ) { return ( isNull( getEmail() ) ? "" : variables.avatar.generateLink( getEmail(), arguments.size ) ); } /** * Utility method to get a snapshot of the user information */ - struct function getInfoSnapshot(){ + struct function getInfoSnapshot() { if ( isLoaded() ) { return { "authorID" : getAuthorID(), @@ -604,4 +598,4 @@ component return {}; } -} +} \ No newline at end of file diff --git a/modules/contentbox/models/security/AuthorService.cfc b/modules/contentbox/models/security/AuthorService.cfc index 14df4f3a5..d8f91ef21 100755 --- a/modules/contentbox/models/security/AuthorService.cfc +++ b/modules/contentbox/models/security/AuthorService.cfc @@ -1,23 +1,20 @@ -/** +/** * ContentBox - A Modular Content Platform * Copyright since 2012 by Ortus Solutions, Corp * www.ortussolutions.com/products/contentbox * --- * Service to handle user operations. */ -component - extends ="cborm.models.VirtualEntityService" - accessors="true" - singleton -{ - +component extends ="cborm.models.VirtualEntityService" accessors="true" singleton { // DI property name="bCrypt" inject="BCrypt@BCrypt"; property name="dateUtil" inject="DateUtil@contentbox"; property name="CBHelper" inject="CBHelper@contentbox"; property name="mailService" inject="mailService@cbmailservices"; property name="permissionService" inject="permissionService@contentbox"; - property name="permissionGroupService" inject="permissionGroupService@contentbox"; + property + name="permissionGroupService" + inject="permissionGroupService@contentbox"; property name="renderer" inject="coldbox:renderer"; property name="requestService" inject="coldbox:requestService"; property name="roleService" inject="roleService@contentbox"; @@ -28,7 +25,7 @@ component /** * Constructor */ - AuthorService function init(){ + AuthorService function init() { // init it super.init( entityName = "cbAuthor" ); @@ -40,16 +37,16 @@ component * * @authorId The author id to report on */ - numeric function getTotalPages( required authorId ){ + numeric function getTotalPages( required authorId ) { return executeQuery( - query: " + query = " SELECT count(*) FROM cbContent content join content.creator creator WHERE creator.authorID = :authorId AND content.contentType = 'Page' ", - params: { "authorId" : arguments.authorId }, - unique: true + params = { "authorId" : arguments.authorId }, + unique = true ); } @@ -58,16 +55,16 @@ component * * @authorId The author id to report on */ - numeric function getTotalEntries( required authorId ){ + numeric function getTotalEntries( required authorId ) { return executeQuery( - query: " + query = " SELECT count(*) FROM cbContent content join content.creator creator WHERE creator.authorID = :authorId AND content.contentType = 'Entry' ", - params: { "authorId" : arguments.authorId }, - unique: true + params = { "authorId" : arguments.authorId }, + unique = true ); } @@ -76,16 +73,16 @@ component * * @authorId The author id to report on */ - numeric function getTotalContentStoreItems( required authorId ){ + numeric function getTotalContentStoreItems( required authorId ) { return executeQuery( - query: " + query = " SELECT count(*) FROM cbContent content join content.creator creator WHERE creator.authorID = :authorId AND content.contentType = 'ContentStore' ", - params: { "authorId" : arguments.authorId }, - unique: true + params = { "authorId" : arguments.authorId }, + unique = true ); } @@ -94,23 +91,23 @@ component * * @authorId The author id to report on */ - numeric function getTotalContent( required authorId ){ + numeric function getTotalContent( required authorId ) { return executeQuery( - query: " + query = " SELECT count(*) FROM cbContent content join content.creator creator WHERE creator.authorID = :authorId ", - params: { "authorId" : arguments.authorId }, - unique: true + params = { "authorId" : arguments.authorId }, + unique = true ); } /** * Get a status report of authors in the system. */ - function getStatusReport(){ - var c = newCriteria(); + function getStatusReport() { + var c = newCriteria(); var results = { "active" : 0, "deactivated" : 0, @@ -119,7 +116,7 @@ component }; var statusReport = c - .withProjections( count: "isActive:authors", groupProperty: "isActive" ) + .withProjections( count = "isActive:authors", groupProperty = "isActive" ) .asStruct() .list(); @@ -132,7 +129,10 @@ component } var twoFactorAuthReport = c - .withProjections( count: "is2FactorAuth:authors", groupProperty: "is2FactorAuth" ) + .withProjections( + count = "is2FactorAuth:authors", + groupProperty = "is2FactorAuth" + ) .asStruct() .list(); @@ -152,7 +152,7 @@ component * * @author The author to delete */ - AuthorService function delete( required author ){ + AuthorService function delete( required author ) { transaction { // Clear out relationships arguments.author.clearPermissions(); @@ -172,7 +172,7 @@ component * * @return true if they match */ - boolean function isSameHash( required incoming, required target ){ + boolean function isSameHash( required incoming, required target ) { return variables.bcrypt.checkPassword( arguments.incoming, arguments.target ); } @@ -183,7 +183,7 @@ component * * @return The created author */ - Author function createNewAuthor( required author ){ + Author function createNewAuthor( required author ) { transaction { // Save it this.save( arguments.author ); @@ -192,9 +192,9 @@ component var mailResults = sendNewUserEmail( arguments.author ); if ( mailResults.error ) { variables.logger.error( - "Error sending author created email for #arguments.author.getFullName()#", - mailResults.messages - ); + "Error sending author created email for #arguments.author.getFullName()#", + mailResults.messages + ); } } @@ -209,9 +209,9 @@ component * * @return Author */ - Author function save( required author, boolean passwordChange = false ){ + Author function save( required author, boolean passwordChange = false ) { // bcrypt password if new author - if ( !arguments.author.isLoaded() OR arguments.passwordChange ) { + if ( !arguments.author.isLoaded() || arguments.passwordChange ) { // bcrypt the incoming password arguments.author.setPassword( variables.bcrypt.hashPassword( arguments.author.getPassword() ) ); } @@ -239,58 +239,60 @@ component string searchTerm = "", string isActive, string role, - numeric max = 0, - numeric offset = 0, - boolean asQuery = false, - string sortOrder = "lastName", + numeric max = 0, + numeric offset = 0, + boolean asQuery = false, + string sortOrder = "lastName", string permissionGroups, string twoFactorAuth - ){ + ) { var results = { "count" : 0, "authors" : [] }; - var c = newCriteria(); + var c = newCriteria(); // Search if ( len( arguments.searchTerm ) ) { c.$or( - c.restrictions.like( "firstName", "%#arguments.searchTerm#%" ), - c.restrictions.like( "lastName", "%#arguments.searchTerm#%" ), - c.restrictions.like( "email", "%#arguments.searchTerm#%" ) - ); + c.restrictions.like( "firstName", "%#arguments.searchTerm#%" ), + c.restrictions.like( "lastName", "%#arguments.searchTerm#%" ), + c.restrictions.like( "email", "%#arguments.searchTerm#%" ) + ); } // isActive filter - if ( structKeyExists( arguments, "isActive" ) AND arguments.isActive NEQ "any" ) { + if ( structKeyExists( arguments, "isActive" ) && arguments.isActive NEQ "any" ) { c.isEq( "isActive", javacast( "boolean", arguments.isActive ) ); } // twoFactorAuth filter - if ( structKeyExists( arguments, "twoFactorAuth" ) AND arguments.twoFactorAuth NEQ "any" ) { - c.isEq( "is2FactorAuth", javacast( "boolean", arguments.twoFactorAuth ) ); + if ( structKeyExists( arguments, "twoFactorAuth" ) && arguments.twoFactorAuth NEQ "any" ) { + c.isEq( + "is2FactorAuth", + javacast( "boolean", arguments.twoFactorAuth ) + ); } // role filter - if ( structKeyExists( arguments, "role" ) AND arguments.role NEQ "any" ) { + if ( structKeyExists( arguments, "role" ) && arguments.role NEQ "any" ) { c.createAlias( "role", "role" ).isEq( "role.roleID", arguments.role ); } // permission groups filter - if ( structKeyExists( arguments, "permissionGroups" ) AND arguments.permissionGroups NEQ "any" ) { - c.createAlias( "permissionGroups", "permissionGroups" ) - .isIn( "permissionGroups.permissionGroupID", listToArray( arguments.permissionGroups ) ); + if ( structKeyExists( arguments, "permissionGroups" ) && arguments.permissionGroups NEQ "any" ) { + c.createAlias( "permissionGroups", "permissionGroups" ).isIn( + "permissionGroups.permissionGroupID", + listToArray( arguments.permissionGroups ) + ); } // run criteria query and projections count - results.count = c.count( "authorID" ); - results.authors = c - .resultTransformer( c.DISTINCT_ROOT_ENTITY ) - .list( + results.count = c.count( "authorID" ); + results.authors = c.resultTransformer( c.DISTINCT_ROOT_ENTITY ).list( offset = arguments.offset, max = arguments.max, sortOrder = arguments.sortOrder, asQuery = arguments.asQuery ); - return results; } @@ -301,7 +303,7 @@ component * * @throws EntityNotFound */ - Author function retrieveUserByUsername( required username ){ + Author function retrieveUserByUsername( required username ) { var oAuthor = newCriteria() .isEq( "username", arguments.username ) .isTrue( "isActive" ) @@ -324,7 +326,7 @@ component * * @throws EntityNotFound */ - Author function retrieveUserById( required id ){ + Author function retrieveUserById( required id ) { var oAuthor = newCriteria() .isEq( "authorID", arguments.id ) .isTrue( "isActive" ) @@ -332,7 +334,10 @@ component .get(); if ( isNull( oAuthor ) ) { - throw( type = "EntityNotFound", message = "Author not found with id (#encodeForHTML( arguments.id )#)" ); + throw( + type = "EntityNotFound", + message = "Author not found with id (#encodeForHTML( arguments.id )#)" + ); } return oAuthor; } @@ -342,7 +347,7 @@ component * * @username The username to check if it exists already */ - boolean function usernameFound( required username ){ + boolean function usernameFound( required username ) { var args = { "username" : arguments.username }; return ( countWhere( argumentCollection = args ) GT 0 ); } @@ -352,7 +357,7 @@ component * * @email The email to check if it exists already */ - boolean function emailFound( required email ){ + boolean function emailFound( required email ) { var args = { "email" : arguments.email }; return ( countWhere( argumentCollection = args ) GT 0 ); } @@ -360,12 +365,14 @@ component /** * Get all data prepared for export */ - array function getAllForExport(){ - return getAll().map( function( thisItem ){ - return arguments.thisItem.getMemento( - includes: "permissions,permissionGroups,isPasswordReset,is2FactorAuth" + array function getAllForExport() { + return getAll().map( + function( thisItem ) { + return arguments.thisItem.getMemento( + includes = "permissions,permissionGroups,isPasswordReset,is2FactorAuth" + ); + } ); - } ); } /** @@ -378,14 +385,17 @@ component * * @throws InvalidImportFormat */ - string function importFromFile( required importFile, boolean override = false ){ - var data = fileRead( arguments.importFile ); + string function importFromFile( required importFile, boolean override = false ) { + var data = fileRead( arguments.importFile ); var importLog = createObject( "java", "java.lang.StringBuilder" ).init( - "Starting import with override = #arguments.override#...
" - ); + "Starting import with override = #arguments.override#...
" + ); if ( !isJSON( data ) ) { - throw( message = "Cannot import file as the contents is not JSON", type = "InvalidImportFormat" ); + throw( + message = "Cannot import file as the contents is not JSON", + type = "InvalidImportFormat" + ); } // deserialize packet: Should be array of { settingID, name, value } @@ -411,12 +421,12 @@ component required importData, boolean override = false, importLog - ){ + ) { var allUsers = []; // if struct, inflate into an array if ( isStruct( arguments.importData ) ) { - arguments.importData = [ arguments.importData ]; + arguments.importData = [ arguments.importData]; } transaction { @@ -424,15 +434,15 @@ component for ( var thisUser in arguments.importData ) { // Get new or persisted var oUser = this.findByUsername( thisUser.username ); - oUser = ( isNull( oUser ) ? new () : oUser ); + oUser = ( isNull( oUser ) ? new() : oUser ); // populate content from data getBeanPopulator().populateFromStruct( - target = oUser, - memento = thisUser, - exclude = "role,authorID,permissions,permissionGroups", - composeRelationships = false - ); + target = oUser, + memento = thisUser, + exclude = "role,authorID,permissions,permissionGroups", + composeRelationships = false + ); // A-LA-CARTE PERMISSIONS if ( arrayLen( thisUser.permissions ) ) { @@ -440,12 +450,14 @@ component var allPermissions = []; for ( var thisPermission in thisUser.permissions ) { var oPerm = variables.permissionService.findByPermission( thisPermission.permission ); - oPerm = ( - isNull( oPerm ) ? getBeanPopulator().populateFromStruct( - target = variables.permissionService.new(), - memento = thisPermission, - exclude = "permissionID" - ) : oPerm + oPerm = ( + isNull( oPerm ) + ? getBeanPopulator().populateFromStruct( + target = variables.permissionService.new(), + memento = thisPermission, + exclude = "permissionID" + ) + : oPerm ); // save oPerm if new only if ( !oPerm.isLoaded() ) { @@ -461,12 +473,14 @@ component // Create group permissions that don't exist first for ( var thisGroup in thisUser.permissionGroups ) { var oGroup = variables.permissionGroupService.findByName( thisGroup.name ); - oGroup = ( - isNull( oGroup ) ? getBeanPopulator().populateFromStruct( - target = variables.permissionGroupService.new(), - memento = thisGroup, - exclude = "permissionGroupID,permissions" - ) : oGroup + oGroup = ( + isNull( oGroup ) + ? getBeanPopulator().populateFromStruct( + target = variables.permissionGroupService.new(), + memento = thisGroup, + exclude = "permissionGroupID,permissions" + ) + : oGroup ); // save oGroup if new only if ( !oGroup.isLoaded() ) { @@ -483,8 +497,8 @@ component arguments.importLog.append( "User role found and linked: #thisUser.role.role#
" ); } else { throw( - message: "The role to import (#encodeForHTML( thisUser.role.role )#) does not exist in the system. Create it or import it first.", - type : "RoleNotFoundException" + message = "The role to import (#encodeForHTML( thisUser.role.role )#) does not exist in the system. Create it or import it first.", + type = "RoleNotFoundException" ); } @@ -492,7 +506,7 @@ component if ( !oUser.isLoaded() ) { arguments.importLog.append( "New user imported: #thisUser.username#
" ); arrayAppend( allUsers, oUser ); - } else if ( oUser.isLoaded() and arguments.override ) { + } else if ( oUser.isLoaded() && arguments.override ) { arguments.importLog.append( "Persisted user overriden: #thisUser.username#
" ); arrayAppend( allUsers, oUser ); } else { @@ -507,8 +521,8 @@ component arguments.importLog.append( "Saved all imported and overriden users!" ); } else { arguments.importLog.append( - "No users imported as none where found or able to be overriden from the import file." - ); + "No users imported as none where found or able to be overriden from the import file." + ); } } // end of transaction @@ -523,11 +537,14 @@ component * * @return struct of { error:boolean, messages:array } */ - struct function sendNewUserEmail( required Author author ){ - var token = variables.securityService.generateResetToken( arguments.author ); - var settings = variables.settingService.getAllSettings(); + struct function sendNewUserEmail( required Author author ) { + var token = variables.securityService.generateResetToken( arguments.author ); + var settings = variables.settingService.getAllSettings(); var defaultSite = variables.siteService.getDefaultSite(); - var adminUrl = variables.requestService.getContext().buildLink( to: "/cbadmin", ssl: settings.cb_admin_ssl ); + var adminUrl = variables + .requestService + .getContext() + .buildLink( to = "/cbadmin", ssl = settings.cb_admin_ssl ); // get mail payload var bodyTokens = { @@ -544,28 +561,31 @@ component // Build email out var mail = variables.mailservice.newMail( - to = arguments.author.getEmail(), - from = settings.cb_site_outgoingEmail, - subject = "#defaultSite.getName()# Account was created for you", - bodyTokens = bodyTokens, - type = "html", - server = settings.cb_site_mail_server, - username = settings.cb_site_mail_username, - password = settings.cb_site_mail_password, - port = settings.cb_site_mail_smtp, - useTLS = settings.cb_site_mail_tls, - useSSL = settings.cb_site_mail_ssl - ); + to = arguments.author.getEmail(), + from = settings.cb_site_outgoingEmail, + subject = "#defaultSite.getName()# Account was created for you", + bodyTokens = bodyTokens, + type = "html", + server = settings.cb_site_mail_server, + username = settings.cb_site_mail_username, + password = settings.cb_site_mail_password, + port = settings.cb_site_mail_smtp, + useTLS = settings.cb_site_mail_tls, + useSSL = settings.cb_site_mail_ssl + ); mail.setBody( - variables.renderer.layout( - layout = "/contentbox/email_templates/layouts/email", - view = "contentbox/email_templates/author_welcome" - ) - ); + variables.renderer.layout( + layout = "/contentbox/email_templates/layouts/email", + view = "contentbox/email_templates/author_welcome" + ) + ); // send it out - return variables.mailService.send( mail ).getResults(); + return variables + .mailService + .send( mail ) + .getResults(); } -} +} \ No newline at end of file diff --git a/modules/contentbox/models/security/ISecurityService.cfc b/modules/contentbox/models/security/ISecurityService.cfc index 6791fe312..bc3c95598 100755 --- a/modules/contentbox/models/security/ISecurityService.cfc +++ b/modules/contentbox/models/security/ISecurityService.cfc @@ -1,4 +1,4 @@ -/** +/** * ContentBox - A Modular Content Platform * Copyright since 2012 by Ortus Solutions, Corp * www.ortussolutions.com/products/contentbox @@ -13,22 +13,22 @@ interface { * @rule The security rule being tested for * @controller The ColdBox controller calling the validation */ - boolean function validateSecurity( struct rule, securedValue, any controller ); + default boolean function validateSecurity( struct rule, securedValue, any controller ); /** * Get an author from session, or returns a new empty author entity */ - Author function getAuthorSession(); + default Author function getAuthorSession(); /** * Logs in a user into persistence storages for tracking purposes */ - ISecurityService function login( required Author author ); + default ISecurityService function login( required Author author ); /** * Delete author session */ - ISecurityService function logout(); + default ISecurityService function logout(); /** * Authenticate an author via ContentBox credentials. If the user is not valid an InvalidCredentials is thrown. Required for JWT services @@ -46,7 +46,7 @@ interface { * * @throws InvalidCredentials */ - Author function authenticate( + default Author function authenticate( required username, required password, boolean logThemIn = false @@ -55,7 +55,7 @@ interface { /** * Send password reminder for an author */ - struct function sendPasswordReminder( + default struct function sendPasswordReminder( required Author author, boolean adminIssued = false, Author issuer @@ -66,7 +66,7 @@ interface { * * @return {error:boolean, messages:string} */ - struct function resetUserPassword( + default struct function resetUserPassword( required token, required Author author, required password @@ -75,11 +75,10 @@ interface { /** * Check to authorize a user to view a content entry or page */ - boolean function authorizeContent( required content, required password ); + default boolean function authorizeContent( required content, required password ); /** * Checks Whether a content entry or page is protected and user has credentials for it */ - boolean function isContentViewable( required content ); - -} + default boolean function isContentViewable( required content ); +} \ No newline at end of file diff --git a/modules/contentbox/models/security/LoginAttempt.cfc b/modules/contentbox/models/security/LoginAttempt.cfc index df6c119b4..7fef35399 100644 --- a/modules/contentbox/models/security/LoginAttempt.cfc +++ b/modules/contentbox/models/security/LoginAttempt.cfc @@ -13,39 +13,37 @@ component cachename ="loginAttempt" cacheuse ="read-write" { - - /* ********************************************************************* - ** PROPERTIES - ********************************************************************* */ + /********************************************************************** + * ** PROPERTIES + **********************************************************************/ /** * Primary key */ property - name ="loginAttemptsID" - column ="loginAttemptsID" + name="loginAttemptsID" + column="loginAttemptsID" fieldtype="id" generator="uuid" - length ="36" - ormtype ="string" - update ="false"; - + length="36" + ormtype="string" + update="false"; /** * The username attempt value */ property - name ="value" - column ="value" + name="value" + column="value" notnull="true" default="" - length ="255" - index ="idx_values"; + length="255" + index="idx_values"; /** * How many attempts in the system */ property - name ="attempts" - column ="attempts" + name="attempts" + column="attempts" ormtype="integer" notnull="true" default="0"; @@ -53,51 +51,45 @@ component * Tracks the last successful login IP address */ property - name ="lastLoginSuccessIP" - column ="lastLoginSuccessIP" + name="lastLoginSuccessIP" + column="lastLoginSuccessIP" notnull="false" - length ="100"; + length="100"; /** * Verifies if tracking is blocked or not */ property - name ="isBlocked" - column ="isBlocked" + name="isBlocked" + column="isBlocked" persistent="false" - default ="false" - type ="boolean"; - - /* ********************************************************************* - ** PK+CONSTRAINTS - ********************************************************************* */ + default="false" + type="boolean"; + /********************************************************************** + * ** PK+CONSTRAINTS + **********************************************************************/ this.pk = "loginAttemptsID"; this.memento = { - defaultIncludes : [ - "value", - "attempts", - "lastLoginSuccessIP", - "isBlocked" - ], - defaultExcludes : [ "" ] + defaultIncludes : [ "value", "attempts", "lastLoginSuccessIP", "isBlocked"], + defaultExcludes : [ ""] }; - /* ********************************************************************* - ** PUBLIC FUNCTIONS - ********************************************************************* */ + /********************************************************************** + * ** PUBLIC FUNCTIONS + **********************************************************************/ /** * Constructor */ - function init(){ + function init() { variables.createdDate = now(); - variables.attempts = 0; - variables.isBlocked = false; + variables.attempts = 0; + variables.isBlocked = false; super.init(); return this; } -} +} \ No newline at end of file diff --git a/modules/contentbox/models/security/LoginTracker.cfc b/modules/contentbox/models/security/LoginTracker.cfc index 8ba2f8b93..b2c347bff 100644 --- a/modules/contentbox/models/security/LoginTracker.cfc +++ b/modules/contentbox/models/security/LoginTracker.cfc @@ -6,7 +6,6 @@ * Listens to login attempts to keep track of them via the Login Tracker System */ component extends="coldbox.system.Interceptor" { - // DI property name="settingService" inject="id:settingService@contentbox"; property name="securityService" inject="id:securityService@contentbox"; @@ -18,7 +17,7 @@ component extends="coldbox.system.Interceptor" { /** * Configure interceptor */ - function configure(){ + function configure() { return this; } @@ -26,7 +25,7 @@ component extends="coldbox.system.Interceptor" { * Before login check if user has been blocked. It will verify login attempts * by username and IP address and block accordingly. */ - function cbadmin_preLogin( event, data, buffer ){ + function cbadmin_preLogin( event, data, buffer ) { // if disabled, we do not track logins if ( !settingService.getSetting( "cb_security_login_blocker" ) ) { return; @@ -35,28 +34,28 @@ component extends="coldbox.system.Interceptor" { loginTrackerService.reset(); // prepare collections - var prc = event.getCollection( private = true ); - var realIP = variables.securityService.getRealIP(); + var prc = event.getCollection( private = true ); + var realIP = variables.securityService.getRealIP(); var realUsername = event.getValue( "username", "" ); // Try to find by username or IPs being blocked - var aBlockIPs = loginTrackerService.findAllByValue( realIP ); + var aBlockIPs = loginTrackerService.findAllByValue( realIP ); var aBlockUsernames = loginTrackerService.findAllByValue( realUsername ); - prc.oBlockByIP = ( arrayLen( aBlockIps ) ? aBlockIps[ 1 ] : loginTrackerService.new() ); + prc.oBlockByIP = ( arrayLen( aBlockIps ) ? aBlockIps[ 1 ] : loginTrackerService.new() ); prc.oBlockByUsername = ( arrayLen( aBlockUsernames ) ? aBlockUsernames[ 1 ] : loginTrackerService.new() ); // do checks to prevent login var isBlocked = false; // which reason? - var byIP = false; + var byIP = false; // do checks by username and IP - if ( !isNull( prc.oBlockByUsername ) and loginTrackerService.isBlocked( prc.oBlockByUsername ) ) { + if ( !isNull( prc.oBlockByUsername ) && loginTrackerService.isBlocked( prc.oBlockByUsername ) ) { isBlocked = true; } - if ( !isNull( prc.oBlockByIP ) and loginTrackerService.isBlocked( prc.oBlockByIP ) ) { + if ( !isNull( prc.oBlockByIP ) && loginTrackerService.isBlocked( prc.oBlockByIP ) ) { isBlocked = true; - byIP = true; + byIP = true; } // If blocked, relocate @@ -67,7 +66,9 @@ component extends="coldbox.system.Interceptor" { messagebox.warn( cb.r( "messages.user_blocked@security" ) ); } // Log it - log.warn( "Request blocked (#realIP#;#realUsername#) via login tracker" ); + log.warn( + "Request blocked (#realIP#;#realUsername#) via login tracker" + ); // Relocate relocate( "#prc.cbAdminEntryPoint#.security.login" ); } @@ -76,21 +77,23 @@ component extends="coldbox.system.Interceptor" { /** * Listen to successful logins */ - function cbadmin_onLogin( event, data, buffer ){ + function cbadmin_onLogin( event, data, buffer ) { // if disabled, we do not track logins if ( !settingService.getSetting( "cb_security_login_blocker" ) ) { return; } // get prc - var prc = event.getCollection( private = true ); + var prc = event.getCollection( private = true ); // get logged in user - var oUser = securityService.getAuthorSession(); + var oUser = securityService.getAuthorSession(); // Build entry to log - var oEntry = loginTrackerService.new( { - lastLoginSuccessIP : variables.securityService.getRealIP(), - attempts : 0, - value : oUser.getUsername() - } ); + var oEntry = loginTrackerService.new( + { + lastLoginSuccessIP : variables.securityService.getRealIP(), + attempts : 0, + value : oUser.getUsername() + } + ); // If blocked username get's it right, then log it and clear attempts if ( !isNull( prc.oBlockByUsername ) ) { @@ -112,14 +115,14 @@ component extends="coldbox.system.Interceptor" { * so we can verify later if they will be blocked by username or ip misuses * the blockByIp and blockByUsername entities are prepared on pre-login */ - void function cbadmin_onBadLogin( event, data, buffer ){ + void function cbadmin_onBadLogin( event, data, buffer ) { // if disabled, we do not track logins if ( !settingService.getSetting( "cb_security_login_blocker" ) ) { return; } // prepare collections - var prc = event.getCollection( private = true ); - var realIP = variables.securityService.getRealIP(); + var prc = event.getCollection( private = true ); + var realIP = variables.securityService.getRealIP(); var realUsername = event.getValue( "username", "" ); // make or update entry for IP @@ -143,4 +146,4 @@ component extends="coldbox.system.Interceptor" { loginTrackerService.save( prc.oBlockByUsername ); } -} +} \ No newline at end of file diff --git a/modules/contentbox/models/security/LoginTrackerService.cfc b/modules/contentbox/models/security/LoginTrackerService.cfc index 7f1953b49..92262f08a 100644 --- a/modules/contentbox/models/security/LoginTrackerService.cfc +++ b/modules/contentbox/models/security/LoginTrackerService.cfc @@ -6,7 +6,6 @@ * This Service tracks logins into the ContentBox System */ component extends="cborm.models.VirtualEntityService" singleton { - // DI property name="settingService" inject="id:settingService@contentbox"; property name="cb" inject="cbhelper@contentbox"; @@ -15,7 +14,7 @@ component extends="cborm.models.VirtualEntityService" singleton { /** * Constructor */ - function init(){ + function init() { // init it super.init( entityName = "cbLoginAttempt" ); return this; @@ -28,13 +27,20 @@ component extends="cborm.models.VirtualEntityService" singleton { * * @return If the attempt was blocked or not */ - boolean function isBlocked( LoginAttempt attempt ){ - var max_attempts = variables.settingService.getSetting( "cb_security_max_attempts" ); + boolean function isBlocked( LoginAttempt attempt ) { + var max_attempts = variables.settingService.getSetting( "cb_security_max_attempts" ); var max_blockTime = variables.settingService.getSetting( "cb_security_blocktime" ); if ( - arguments.attempt.getAttempts() gte max_attempts AND - ( dateDiff( "n", arguments.attempt.getCreatedDate(), now() ) lte max_blockTime ) + arguments.attempt.getAttempts() GTE max_attempts && + ( + dateDiff( + "n", + arguments.attempt.getCreatedDate(), + now() + ) LTE + max_blockTime + ) ) { return true; } @@ -47,7 +53,7 @@ component extends="cborm.models.VirtualEntityService" singleton { * * @sortOrder The sorting columns. */ - public array function getAll( sortOrder = "attempts" ){ + public array function getAll( sortOrder = "attempts" ) { var allEntries = list( sortOrder = arguments.sortOrder ); for ( var e in allEntries ) { @@ -64,10 +70,8 @@ component extends="cborm.models.VirtualEntityService" singleton { * * @max How many to retrieve */ - function getLastLogins( required numeric max ){ - return newCriteria() - .isNotNull( "lastLoginSuccessIP" ) - .list( + function getLastLogins( required numeric max ) { + return newCriteria().isNotNull( "lastLoginSuccessIP" ).list( asQuery = false, max = arguments.max, sortOrder = "createdDate DESC" @@ -77,7 +81,7 @@ component extends="cborm.models.VirtualEntityService" singleton { /** * Truncate the entire auth logs */ - LoginTrackerService function truncate(){ + LoginTrackerService function truncate() { queryExecute( "TRUNCATE TABLE cb_loginAttempts" ); return this; } @@ -86,22 +90,25 @@ component extends="cborm.models.VirtualEntityService" singleton { * Rotate auth logs * Usually called by the {@code LoginTracker} Interceptor asynchronously */ - LoginTrackerService function rotate(){ + LoginTrackerService function rotate() { // if disabled, we do not track logins if ( !settingService.getSetting( "cb_security_login_blocker" ) ) { - log.debug( "Rotation not enabled since the security login blocker is disabled" ); + log.debug( + "Rotation not enabled since the security login blocker is disabled" + ); return this; } - var maxLogs = variables.settingService.getSetting( "cb_security_max_auth_logs" ); - var maxLogs = 2; + var maxLogs = variables.settingService.getSetting( "cb_security_max_auth_logs" ); + var maxLogs = 2; var totalLogs = count(); // only if we have a max logs and we have gone above max logs, let's truncate if ( len( maxLogs ) && isNumeric( maxLogs ) && totalLogs > maxLogs ) { - var aToDelete = newCriteria() - .withProjections( property = "loginAttemptsID" ) - .list( max = ( totalLogs - maxLogs ), sortOrder = "createdDate ASC" ); + var aToDelete = newCriteria().withProjections( property = "loginAttemptsID" ).list( + max = ( totalLogs - maxLogs ), + sortOrder = "createdDate ASC" + ); var hql = " DELETE FROM cbLoginAttempt @@ -110,9 +117,9 @@ component extends="cborm.models.VirtualEntityService" singleton { // run it var results = executeQuery( - query : hql, - params : { "idsToDelete" : aToDelete }, - asQuery: false + query = hql, + params = { "idsToDelete" : aToDelete }, + asQuery = false ); // log it @@ -127,7 +134,7 @@ component extends="cborm.models.VirtualEntityService" singleton { /** * Reset login attempts if the time limit is reached */ - LoginTrackerService function reset(){ + LoginTrackerService function reset() { var limit = dateAdd( "n", variables.settingService.getSetting( "cb_security_blocktime" ) * -1, @@ -144,9 +151,13 @@ component extends="cborm.models.VirtualEntityService" singleton { var params = { "limit" : limit }; // run it - executeQuery( query = hql, params = params, asQuery = false ); + executeQuery( + query = hql, + params = params, + asQuery = false + ); return this; } -} +} \ No newline at end of file diff --git a/modules/contentbox/models/security/Permission.cfc b/modules/contentbox/models/security/Permission.cfc index 78e05849c..c79d62a06 100644 --- a/modules/contentbox/models/security/Permission.cfc +++ b/modules/contentbox/models/security/Permission.cfc @@ -13,65 +13,58 @@ component cachename ="cbPermission" cacheuse ="read-write" { - - /* ********************************************************************* - ** PROPERTIES - ********************************************************************* */ + /********************************************************************** + * ** PROPERTIES + **********************************************************************/ property - name ="permissionID" - column ="permissionID" + name="permissionID" + column="permissionID" fieldtype="id" generator="uuid" - length ="36" - ormtype ="string" - update ="false"; - + length="36" + ormtype="string" + update="false"; property - name ="permission" - column ="permission" + name="permission" + column="permission" ormtype="string" notnull="true" - length ="255" - unique ="true" + length="255" + unique="true" default="" - index ="idx_permissionName"; - + index="idx_permissionName"; property - name ="description" - column ="description" + name="description" + column="description" ormtype="string" notnull="false" default="" - length ="500"; - - /* ********************************************************************* - ** CALCULATED FIELDS - ********************************************************************* */ + length="500"; + /********************************************************************** + * ** CALCULATED FIELDS + **********************************************************************/ property - name ="numberOfPermissionGroups" + name="numberOfPermissionGroups" formula="select count(*) from cb_groupPermissions as gp where gp.FK_permissionID=permissionID"; - property - name ="numberOfRoles" + name="numberOfRoles" formula="select count(*) from cb_rolePermissions as rolePermissions where rolePermissions.FK_permissionID=permissionID"; - property - name ="numberOfGroups" + name="numberOfGroups" formula="select count(*) from cb_groupPermissions as groupPermissions where groupPermissions.FK_permissionID=permissionID"; - - /* ********************************************************************* - ** PK + CONSTRAINTS + MEMENTO - ********************************************************************* */ + /********************************************************************** + * ** PK + CONSTRAINTS + MEMENTO + **********************************************************************/ this.pk = "permissionID"; this.memento = { - defaultIncludes : [ "permission", "description" ], - defaultExcludes : [ "" ] + defaultIncludes : [ "permission", "description"], + defaultExcludes : [ ""] }; this.constraints = { @@ -83,16 +76,16 @@ component "description" : { required : false, size : "1..500" } }; - /* ********************************************************************* - ** PUBLIC FUNCITONS - ********************************************************************* */ + /********************************************************************** + * ** PUBLIC FUNCITONS + **********************************************************************/ /** * Constructor */ - function init(){ + function init() { super.init(); return this; } -} +} \ No newline at end of file diff --git a/modules/contentbox/models/security/PermissionGroup.cfc b/modules/contentbox/models/security/PermissionGroup.cfc index 73e4e8c42..27ff60437 100644 --- a/modules/contentbox/models/security/PermissionGroup.cfc +++ b/modules/contentbox/models/security/PermissionGroup.cfc @@ -13,102 +13,91 @@ component cachename ="cbPermissionGroup" cacheuse ="read-write" { + /********************************************************************** + * ** DI + **********************************************************************/ - /* ********************************************************************* - ** DI - ********************************************************************* */ - - - - /* ********************************************************************* - ** PROPERTIES - ********************************************************************* */ + /********************************************************************** + * ** PROPERTIES + **********************************************************************/ property - name ="permissionGroupID" - column ="permissionGroupID" + name="permissionGroupID" + column="permissionGroupID" fieldtype="id" generator="uuid" - length ="36" - update ="false"; - + length="36" + update="false"; property - name ="name" - column ="name" + name="name" + column="name" ormtype="string" notnull="true" - length ="255" - unique ="true" + length="255" + unique="true" default="" - index ="idx_permissionGroupName"; - + index="idx_permissionGroupName"; property - name ="description" - column ="description" + name="description" + column="description" ormtype="string" notnull="false" default="" - length ="500"; - - /* ********************************************************************* - ** RELATIONSHIPS - ********************************************************************* */ + length="500"; + /********************************************************************** + * ** RELATIONSHIPS + **********************************************************************/ // M2M -> Permissions property - name ="permissions" - fieldtype ="many-to-many" - type ="array" - lazy ="true" - orderby ="permission" - cascade ="save-update" - cacheuse ="read-write" - cfc ="contentbox.models.security.Permission" - fkcolumn ="FK_permissionGroupID" - linktable ="cb_groupPermissions" + name="permissions" + fieldtype="many-to-many" + type="array" + lazy="true" + orderby="permission" + cascade="save-update" + cacheuse="read-write" + cfc="contentbox.models.security.Permission" + fkcolumn="FK_permissionGroupID" + linktable="cb_groupPermissions" inversejoincolumn="FK_permissionID"; - // M2M -> Authors property - name ="authors" - singularName ="author" - fieldtype ="many-to-many" - type ="array" - lazy ="true" - cacheuse ="read-write" - cfc ="contentbox.models.security.Author" - fkcolumn ="FK_permissionGroupID" - linktable ="cb_authorPermissionGroups" + name="authors" + singularName="author" + fieldtype="many-to-many" + type="array" + lazy="true" + cacheuse="read-write" + cfc="contentbox.models.security.Author" + fkcolumn="FK_permissionGroupID" + linktable="cb_authorPermissionGroups" inversejoincolumn="FK_authorID"; - - /* ********************************************************************* - ** CALCULATED FIELDS - ********************************************************************* */ + /********************************************************************** + * ** CALCULATED FIELDS + **********************************************************************/ property - name ="numberOfPermissions" + name="numberOfPermissions" formula="select count(*) from cb_groupPermissions as groupPermissions where groupPermissions.FK_permissionGroupID = permissionGroupID"; - property - name ="numberOfAuthors" + name="numberOfAuthors" formula="select count(*) from cb_authorPermissionGroups as pg where pg.FK_permissionGroupID = permissionGroupID"; - - /* ********************************************************************* - ** NON PERSISTED PROPERTIES - ********************************************************************* */ + /********************************************************************** + * ** NON PERSISTED PROPERTIES + **********************************************************************/ property name="permissionList" persistent="false"; - - /* ********************************************************************* - ** PK + CONSTRAINTS + MEMENTO - ********************************************************************* */ + /********************************************************************** + * ** PK + CONSTRAINTS + MEMENTO + **********************************************************************/ this.pk = "permissionGroupID"; this.memento = { - defaultIncludes : [ "*" ], - defaultExcludes : [ "authors" ] + defaultIncludes : [ "*"], + defaultExcludes : [ "authors"] }; this.constraints = { @@ -120,16 +109,16 @@ component "description" : { required : false, size : "1..500" } }; - /* ********************************************************************* - ** PUBLIC FUNCTIONS - ********************************************************************* */ + /********************************************************************** + * ** PUBLIC FUNCTIONS + **********************************************************************/ /** * Constructor */ - function init(){ - variables.permissions = []; - variables.authors = []; + function init() { + variables.permissions = []; + variables.authors = []; variables.permissionList = []; super.init(); @@ -141,9 +130,9 @@ component * * @permission One or a list of permissions to verify */ - boolean function hasPermission( required permission ){ + boolean function hasPermission( required permission ) { // cache deconstructed permissions in case it's called many times during a request. - if ( !arrayLen( variables.permissionList ) AND hasPermissions() ) { + if ( !arrayLen( variables.permissionList ) && hasPermissions() ) { variables.permissionList = arrayReduce( getPermissions(), ( result, item ) => { @@ -154,16 +143,16 @@ component } // verify - return arrayWrap( arguments.permission ) - .filter( ( item ) => variables.permissionList.findNoCase( arguments.item ) ) - .len(); + return arrayWrap( arguments.permission ).filter( + ( item ) => variables.permissionList.findNoCase( arguments.item ) + ).len(); } /** * Clear all permissions */ - PermissionGroup function clearPermissions(){ - variables.permissions = []; + PermissionGroup function clearPermissions() { + variables.permissions = []; variables.permissionList = []; return this; } @@ -171,7 +160,7 @@ component /** * Clear all authors */ - PermissionGroup function clearAuthors(){ + PermissionGroup function clearAuthors() { variables.authors = []; return this; } @@ -181,7 +170,7 @@ component * * @permissions The permissions array */ - PermissionGroup function setPermissions( required array permissions ){ + PermissionGroup function setPermissions( required array permissions ) { if ( hasPermissions() ) { variables.permissions.clear(); variables.permissions.addAll( arguments.permissions ); @@ -196,7 +185,7 @@ component * * @authors The permissions array */ - PermissionGroup function setAuthors( required array authors ){ + PermissionGroup function setAuthors( required array authors ) { if ( hasAuthor() ) { variables.authors.clear(); variables.authors.addAll( arguments.authors ); @@ -206,4 +195,4 @@ component return this; } -} +} \ No newline at end of file diff --git a/modules/contentbox/models/security/PermissionGroupService.cfc b/modules/contentbox/models/security/PermissionGroupService.cfc index 6a4b6b64d..f5e481ad8 100755 --- a/modules/contentbox/models/security/PermissionGroupService.cfc +++ b/modules/contentbox/models/security/PermissionGroupService.cfc @@ -1,4 +1,4 @@ -/** +/** * ContentBox - A Modular Content Platform * Copyright since 2012 by Ortus Solutions, Corp * www.ortussolutions.com/products/contentbox @@ -6,7 +6,6 @@ * Permission Group Service for contentbox */ component extends="cborm.models.VirtualEntityService" singleton { - // DI property name="permissionService" inject="permissionService@contentbox"; property name="dateUtil" inject="DateUtil@contentbox"; @@ -14,7 +13,7 @@ component extends="cborm.models.VirtualEntityService" singleton { /** * Constructor */ - PermissionGroupService function init(){ + PermissionGroupService function init() { // init it super.init( entityName = "cbPermissionGroup" ); @@ -24,10 +23,12 @@ component extends="cborm.models.VirtualEntityService" singleton { /** * Get all data prepared for export */ - array function getAllForExport(){ - return getAll().map( function( thisItem ){ - return thisItem.getMemento(); - } ); + array function getAllForExport() { + return getAll().map( + function( thisItem ) { + return thisItem.getMemento(); + } + ); } /** @@ -40,14 +41,17 @@ component extends="cborm.models.VirtualEntityService" singleton { * * @throws InvalidImportFormat */ - string function importFromFile( required importFile, boolean override = false ){ - var data = fileRead( arguments.importFile ); + string function importFromFile( required importFile, boolean override = false ) { + var data = fileRead( arguments.importFile ); var importLog = createObject( "java", "java.lang.StringBuilder" ).init( - "Starting import with override = #arguments.override#...
" - ); + "Starting import with override = #arguments.override#...
" + ); if ( !isJSON( data ) ) { - throw( message = "Cannot import file as the contents is not JSON", type = "InvalidImportFormat" ); + throw( + message = "Cannot import file as the contents is not JSON", + type = "InvalidImportFormat" + ); } // deserialize packet: Should be array of { settingID, name, value } @@ -71,12 +75,12 @@ component extends="cborm.models.VirtualEntityService" singleton { required importData, boolean override = false, importLog - ){ + ) { var allGroups = []; // if struct, inflate into an array if ( isStruct( arguments.importData ) ) { - arguments.importData = [ arguments.importData ]; + arguments.importData = [ arguments.importData]; } transaction { @@ -84,15 +88,15 @@ component extends="cborm.models.VirtualEntityService" singleton { for ( var thisGroup in arguments.importData ) { // Get new or persisted var oGroup = this.findByName( thisGroup.name ); - oGroup = ( isNull( oGroup ) ? new () : oGroup ); + oGroup = ( isNull( oGroup ) ? new() : oGroup ); // populate content from data getBeanPopulator().populateFromStruct( - target = oGroup, - memento = thisGroup, - exclude = "permissionGroupID,permissions", - composeRelationships = false - ); + target = oGroup, + memento = thisGroup, + exclude = "permissionGroupID,permissions", + composeRelationships = false + ); // PERMISSIONS if ( arrayLen( thisGroup.permissions ) ) { @@ -100,12 +104,14 @@ component extends="cborm.models.VirtualEntityService" singleton { var allPermissions = []; for ( var thisPermission in thisGroup.permissions ) { var oPerm = variables.permissionService.findByPermission( thisPermission.permission ); - oPerm = ( - isNull( oPerm ) ? getBeanPopulator().populateFromStruct( - target = variables.permissionService.new(), - memento = thisPermission, - exclude = "permissionID" - ) : oPerm + oPerm = ( + isNull( oPerm ) + ? getBeanPopulator().populateFromStruct( + target = variables.permissionService.new(), + memento = thisPermission, + exclude = "permissionID" + ) + : oPerm ); // save oPerm if new only if ( !oPerm.isLoaded() ) { @@ -122,8 +128,10 @@ component extends="cborm.models.VirtualEntityService" singleton { if ( !oGroup.isLoaded() ) { arguments.importLog.append( "New permission group imported: #thisGroup.name#
" ); arrayAppend( allGroups, oGroup ); - } else if ( oGroup.isLoaded() and arguments.override ) { - arguments.importLog.append( "Persisted permission group overriden: #thisGroup.name#
" ); + } else if ( oGroup.isLoaded() && arguments.override ) { + arguments.importLog.append( + "Persisted permission group overriden: #thisGroup.name#
" + ); arrayAppend( allGroups, oGroup ); } else { arguments.importLog.append( "Skipping permission group: #thisGroup.name#
" ); @@ -137,8 +145,8 @@ component extends="cborm.models.VirtualEntityService" singleton { arguments.importLog.append( "Saved all imported and overriden permission groups!" ); } else { arguments.importLog.append( - "No permission groups imported as none where found or able to be overriden from the import file." - ); + "No permission groups imported as none where found or able to be overriden from the import file." + ); } } // end of transaction @@ -146,4 +154,4 @@ component extends="cborm.models.VirtualEntityService" singleton { return arguments.importLog.toString(); } -} +} \ No newline at end of file diff --git a/modules/contentbox/models/security/PermissionService.cfc b/modules/contentbox/models/security/PermissionService.cfc index 404b8b6cb..daa75657e 100755 --- a/modules/contentbox/models/security/PermissionService.cfc +++ b/modules/contentbox/models/security/PermissionService.cfc @@ -1,4 +1,4 @@ -/** +/** * ContentBox - A Modular Content Platform * Copyright since 2012 by Ortus Solutions, Corp * www.ortussolutions.com/products/contentbox @@ -6,14 +6,13 @@ * Permissions service for contentbox */ component extends="cborm.models.VirtualEntityService" singleton { - // DI property name="dateUtil" inject="DateUtil@contentbox"; /** * Constructor */ - PermissionService function init(){ + PermissionService function init() { // init it super.init( entityName = "cbPermission" ); @@ -25,7 +24,7 @@ component extends="cborm.models.VirtualEntityService" singleton { * * @permissionID The permission ID to remove */ - boolean function deletePermission( required permissionID ){ + boolean function deletePermission( required permissionID ) { transaction { // We do SQL deletions as those relationships are not bi-directional @@ -58,17 +57,29 @@ component extends="cborm.models.VirtualEntityService" singleton { * * @return Array of struct permission data */ - array function getAllForExport(){ + array function getAllForExport() { return newCriteria() - .withProjections( property = "permissionID,permission,description,createdDate,modifiedDate,isDeleted" ) + .withProjections( + property = "permissionID,permission,description,createdDate,modifiedDate,isDeleted" + ) .asStruct() .list( sortOrder = "permission" ) // output conversions - .map( function( item ){ - item[ "createdDate" ] = variables.dateUtil.toUTC( item[ "createdDate" ], "", "UTC" ); - item[ "modifiedDate" ] = variables.dateUtil.toUTC( item[ "modifiedDate" ], "", "UTC" ); - return item; - } ); + .map( + function( item ) { + item[ "createdDate" ] = variables.dateUtil.toUTC( + item[ "createdDate" ], + "", + "UTC" + ); + item[ "modifiedDate" ] = variables.dateUtil.toUTC( + item[ "modifiedDate" ], + "", + "UTC" + ); + return item; + } + ); } /** @@ -81,14 +92,17 @@ component extends="cborm.models.VirtualEntityService" singleton { * * @throws InvalidImportFormat */ - string function importFromFile( required importFile, boolean override = false ){ - var data = fileRead( arguments.importFile ); + string function importFromFile( required importFile, boolean override = false ) { + var data = fileRead( arguments.importFile ); var importLog = createObject( "java", "java.lang.StringBuilder" ).init( - "Starting import with override = #arguments.override#...
" - ); + "Starting import with override = #arguments.override#...
" + ); if ( !isJSON( data ) ) { - throw( message = "Cannot import file as the contents is not JSON", type = "InvalidImportFormat" ); + throw( + message = "Cannot import file as the contents is not JSON", + type = "InvalidImportFormat" + ); } // deserialize packet: Should be array of { settingID, name, value } @@ -114,12 +128,12 @@ component extends="cborm.models.VirtualEntityService" singleton { required importData, boolean override = false, importLog - ){ + ) { var allPermissions = []; // if struct, inflate into an array if ( isStruct( arguments.importData ) ) { - arguments.importData = [ arguments.importData ]; + arguments.importData = [ arguments.importData]; } transaction { @@ -127,21 +141,23 @@ component extends="cborm.models.VirtualEntityService" singleton { for ( var thisPermission in arguments.importData ) { // Get new or persisted var oPermission = this.findByPermission( thisPermission.permission ); - oPermission = ( isNull( oPermission ) ? new () : oPermission ); + oPermission = ( isNull( oPermission ) ? new() : oPermission ); // populate content from data getBeanPopulator().populateFromStruct( - target = oPermission, - memento = thisPermission, - exclude = "permissionID", - composeRelationships = false - ); + target = oPermission, + memento = thisPermission, + exclude = "permissionID", + composeRelationships = false + ); // if new or persisted with override then save. if ( !oPermission.isLoaded() ) { - arguments.importLog.append( "New permission imported: #thisPermission.permission#
" ); + arguments.importLog.append( + "New permission imported: #thisPermission.permission#
" + ); arrayAppend( allPermissions, oPermission ); - } else if ( oPermission.isLoaded() and arguments.override ) { + } else if ( oPermission.isLoaded() && arguments.override ) { arguments.importLog.append( "Permission overriden: #thisPermission.permission#
" ); arrayAppend( allPermissions, oPermission ); } else { @@ -156,8 +172,8 @@ component extends="cborm.models.VirtualEntityService" singleton { arguments.importLog.append( "Saved all imported and overriden permissions!" ); } else { arguments.importLog.append( - "No permissions imported as none where found or able to be overriden from the import file." - ); + "No permissions imported as none where found or able to be overriden from the import file." + ); } } // end of transaction @@ -165,4 +181,4 @@ component extends="cborm.models.VirtualEntityService" singleton { return arguments.importLog.toString(); } -} +} \ No newline at end of file diff --git a/modules/contentbox/models/security/RateLimiter.cfc b/modules/contentbox/models/security/RateLimiter.cfc index ea01388ee..4d40bc3a1 100755 --- a/modules/contentbox/models/security/RateLimiter.cfc +++ b/modules/contentbox/models/security/RateLimiter.cfc @@ -6,7 +6,6 @@ * Limits and prevents DOS attacks */ component extends="coldbox.system.Interceptor" { - // DI property name="settingService" inject="id:settingService@contentbox"; property name="securityService" inject="id:securityService@contentbox"; @@ -16,16 +15,18 @@ component extends="coldbox.system.Interceptor" { * onRequestCapture * fires before any event caching or processing */ - function onRequestCapture( event, data, buffer ){ + function onRequestCapture( event, data, buffer ) { var allSettings = variables.settingService.getAllSettings(); // If turned off, just exist - if ( !structKeyExists( allSettings, "cb_security_rate_limiter" ) || allSettings.cb_security_rate_limiter == false ) { + if ( + !structKeyExists( allSettings, "cb_security_rate_limiter" ) || allSettings.cb_security_rate_limiter == false + ) { return false; } // do we limit bot OR normal requests as well? - if ( !len( cgi.http_cookie ) OR !allSettings.cb_security_rate_limiter_bots_only ) { + if ( !len( cgi.http_cookie ) || !allSettings.cb_security_rate_limiter_bots_only ) { // limit it now. limiter( count = allSettings[ "cb_security_rate_limiter_count" ], @@ -46,15 +47,18 @@ component extends="coldbox.system.Interceptor" { * @event The request context object * @settings The settings structure */ - private function limiter( count, duration, event, settings ){ + private function limiter( count, duration, event, settings ) { // Get real IP address of requester - var realIP = variables.securityService.getRealIP(); - var cache = cachebox.getDefaultCache(); + var realIP = variables.securityService.getRealIP(); + var cache = cachebox.getDefaultCache(); var cacheKey = "limiter" & realIP; - var targetData = cache.getOrSet( cacheKey, function(){ - return { attempts : 0, lastAttempt : now() } - } ); + var targetData = cache.getOrSet( + cacheKey, + function() { + return { attempts : 0, lastAttempt : now() }; + } + ); // on first visit no further processing if ( targetData.attempts == 0 ) { @@ -68,12 +72,22 @@ component extends="coldbox.system.Interceptor" { // log.info( "Within Duration " & dateDiff( "s", targetData.lastAttempt, Now() ) LT arguments.duration ); // Are we executing another request withing our duration in seconds? Ex: Has X seconds passed before last attempt - if ( dateDiff( "s", targetData.lastAttempt, now() ) LT arguments.duration ) { + if ( + dateDiff( + "s", + targetData.lastAttempt, + now() + ) LT + arguments.duration + ) { // Limit by count? if ( targetData.attempts GT arguments.count ) { if ( settings.cb_security_rate_limiter_logging && log.canInfo() ) { // Log it to app logs - log.info( "'limiter invoked for:','#realIP#',#targetData.attempts#,#cgi.request_method#,'#cgi.SCRIPT_NAME#', '#cgi.QUERY_STRING#','#cgi.http_user_agent#','#targetData.lastAttempt#',#listLen( cgi.http_cookie, ";" )#" ); + log.info( + "'limiter invoked for:','#realIP#',#targetData.attempts#,#cgi.request_method#,'#cgi.SCRIPT_NAME#', '#cgi.QUERY_STRING#','#cgi.http_user_agent#','#targetData.lastAttempt#',#listLen( cgi.http_cookie, + ";" )#" + ); } // Log attempt @@ -100,7 +114,8 @@ component extends="coldbox.system.Interceptor" { "all" ) ); - arguments.event + arguments + .event .setHTTPHeader( statusCode = "503" ) .setHTTPHeader( name = "Retry-After", value = arguments.duration ); @@ -116,7 +131,7 @@ component extends="coldbox.system.Interceptor" { } } else { // Reset attempts counter, since we are in the safe zone, just log the last attempt timestamp - targetData.attempts = 0; + targetData.attempts = 0; targetData.lastAttempt = now(); } @@ -124,4 +139,4 @@ component extends="coldbox.system.Interceptor" { return this; } -} +} \ No newline at end of file diff --git a/modules/contentbox/models/security/Role.cfc b/modules/contentbox/models/security/Role.cfc index 130edcdef..4692b1496 100644 --- a/modules/contentbox/models/security/Role.cfc +++ b/modules/contentbox/models/security/Role.cfc @@ -13,90 +13,83 @@ component cachename ="cbRole" cacheuse ="read-write" { - - /* ********************************************************************* - ** DI - ********************************************************************* */ + /********************************************************************** + * ** DI + **********************************************************************/ property - name ="permissionService" - inject ="provider:permissionService@contentbox" + name="permissionService" + inject="provider:permissionService@contentbox" persistent="false"; - - /* ********************************************************************* - ** PROPERTIES - ********************************************************************* */ + /********************************************************************** + * ** PROPERTIES + **********************************************************************/ property - name ="roleID" - column ="roleID" + name="roleID" + column="roleID" fieldtype="id" generator="uuid" - length ="36" - ormtype ="string" - update ="false"; - + length="36" + ormtype="string" + update="false"; property - name ="role" - column ="role" + name="role" + column="role" ormtype="string" notnull="true" - length ="255" - unique ="true" + length="255" + unique="true" default="" - index ="idx_roleName"; - + index="idx_roleName"; property - name ="description" - column ="description" + name="description" + column="description" ormtype="string" notnull="false" default="" - length ="500"; - - /* ********************************************************************* - ** RELATIONSHIPS - ********************************************************************* */ + length="500"; + /********************************************************************** + * ** RELATIONSHIPS + **********************************************************************/ // M2M -> Permissions property - name ="permissions" - fieldtype ="many-to-many" - type ="array" - lazy ="true" - orderby ="permission" - cascade ="save-update" - cacheuse ="read-write" - cfc ="contentbox.models.security.Permission" - fkcolumn ="FK_roleID" - linktable ="cb_rolePermissions" + name="permissions" + fieldtype="many-to-many" + type="array" + lazy="true" + orderby="permission" + cascade="save-update" + cacheuse="read-write" + cfc="contentbox.models.security.Permission" + fkcolumn="FK_roleID" + linktable="cb_rolePermissions" inversejoincolumn="FK_permissionID"; - - /* ********************************************************************* - ** CALUCLATED FIELDS - ********************************************************************* */ + /********************************************************************** + * ** CALUCLATED FIELDS + **********************************************************************/ property - name ="numberOfPermissions" + name="numberOfPermissions" formula="select count(*) from cb_rolePermissions as rolePermissions where rolePermissions.FK_roleID=roleID"; - - property name="numberOfAuthors" formula="select count(*) from cb_author as author where author.FK_roleID=roleID"; - - /* ********************************************************************* - ** NON PERSISTED PROPERTIES - ********************************************************************* */ + property + name="numberOfAuthors" + formula="select count(*) from cb_author as author where author.FK_roleID=roleID"; + /********************************************************************** + * ** NON PERSISTED PROPERTIES + **********************************************************************/ property name="permissionList" persistent="false"; - - /* ********************************************************************* - ** PK + CONSTRAINTS + MEMENTO - ********************************************************************* */ + /********************************************************************** + * ** PK + CONSTRAINTS + MEMENTO + **********************************************************************/ this.pk = "roleID"; this.memento = { - defaultIncludes : [ "role", "description" ], - defaultExcludes : [ "permissionList" ] + defaultIncludes : [ "role", "description"], + defaultExcludes : [ "permissionList"] }; this.constraints = { @@ -108,13 +101,13 @@ component "description" : { required : false, size : "1..500" } }; - /* ********************************************************************* - ** PUBLIC FUNCTIONS - ********************************************************************* */ + /********************************************************************** + * ** PUBLIC FUNCTIONS + **********************************************************************/ // Constructor - function init(){ - variables.permissions = []; + function init() { + variables.permissions = []; variables.permissionList = []; super.init(); @@ -124,7 +117,7 @@ component /** * Get the role name, same as getRole() */ - string function getName(){ + string function getName() { return variables.role; } @@ -133,9 +126,9 @@ component * * @permission One or a list of permissions to verify */ - boolean function hasPermission( required permission ){ + boolean function hasPermission( required permission ) { // cache deconstructed permissions in case it's called many times during a request. - if ( !arrayLen( variables.permissionList ) AND hasPermissions() ) { + if ( !arrayLen( variables.permissionList ) && hasPermissions() ) { variables.permissionList = arrayReduce( getPermissions(), ( result, item ) => { @@ -145,16 +138,16 @@ component ); } // verify - return arrayWrap( arguments.permission ) - .filter( ( item ) => variables.permissionList.findNoCase( arguments.item ) ) - .len(); + return arrayWrap( arguments.permission ).filter( + ( item ) => variables.permissionList.findNoCase( arguments.item ) + ).len(); } /** * Clear all permissions */ - Role function clearPermissions(){ - variables.permissions = []; + Role function clearPermissions() { + variables.permissions = []; variables.permissionList = []; return this; } @@ -162,7 +155,7 @@ component /** * Override the setPermissions */ - Role function setPermissions( required array permissions ){ + Role function setPermissions( required array permissions ) { if ( hasPermissions() ) { variables.permissions.clear(); variables.permissions.addAll( arguments.permissions ); @@ -172,4 +165,4 @@ component return this; } -} +} \ No newline at end of file diff --git a/modules/contentbox/models/security/RoleService.cfc b/modules/contentbox/models/security/RoleService.cfc index 3e509b096..4dc8ad935 100755 --- a/modules/contentbox/models/security/RoleService.cfc +++ b/modules/contentbox/models/security/RoleService.cfc @@ -1,4 +1,4 @@ -/** +/** * ContentBox - A Modular Content Platform * Copyright since 2012 by Ortus Solutions, Corp * www.ortussolutions.com/products/contentbox @@ -6,7 +6,6 @@ * Roles service for contentbox */ component extends="cborm.models.VirtualEntityService" singleton { - // DI property name="permissionService" inject="permissionService@contentbox"; property name="dateUtil" inject="DateUtil@contentbox"; @@ -14,7 +13,7 @@ component extends="cborm.models.VirtualEntityService" singleton { /** * Constructor */ - RoleService function init(){ + RoleService function init() { // init it super.init( entityName = "cbRole" ); @@ -24,10 +23,12 @@ component extends="cborm.models.VirtualEntityService" singleton { /** * Get all data prepared for export */ - array function getAllForExport(){ - return getAll().map( function( thisItem ){ - return thisItem.getMemento( includes = "permissions" ); - } ); + array function getAllForExport() { + return getAll().map( + function( thisItem ) { + return thisItem.getMemento( includes = "permissions" ); + } + ); } /** @@ -40,14 +41,17 @@ component extends="cborm.models.VirtualEntityService" singleton { * * @throws InvalidImportFormat */ - string function importFromFile( required importFile, boolean override = false ){ - var data = fileRead( arguments.importFile ); + string function importFromFile( required importFile, boolean override = false ) { + var data = fileRead( arguments.importFile ); var importLog = createObject( "java", "java.lang.StringBuilder" ).init( - "Starting import with override = #arguments.override#...
" - ); + "Starting import with override = #arguments.override#...
" + ); if ( !isJSON( data ) ) { - throw( message = "Cannot import file as the contents is not JSON", type = "InvalidImportFormat" ); + throw( + message = "Cannot import file as the contents is not JSON", + type = "InvalidImportFormat" + ); } // deserialize packet: Should be array of { settingID, name, value } @@ -73,12 +77,12 @@ component extends="cborm.models.VirtualEntityService" singleton { required importData, boolean override = false, importLog - ){ + ) { var allRoles = []; // if struct, inflate into an array if ( isStruct( arguments.importData ) ) { - arguments.importData = [ arguments.importData ]; + arguments.importData = [ arguments.importData]; } transaction { @@ -86,15 +90,15 @@ component extends="cborm.models.VirtualEntityService" singleton { for ( var thisRole in arguments.importData ) { // Get new or persisted var oRole = this.findByRole( thisRole.role ); - oRole = ( isNull( oRole ) ? new () : oRole ); + oRole = ( isNull( oRole ) ? new() : oRole ); // populate content from data getBeanPopulator().populateFromStruct( - target = oRole, - memento = thisRole, - exclude = "roleID,permissions", - composeRelationships = false - ); + target = oRole, + memento = thisRole, + exclude = "roleID,permissions", + composeRelationships = false + ); // PERMISSIONS if ( arrayLen( thisRole.permissions ) ) { @@ -102,12 +106,14 @@ component extends="cborm.models.VirtualEntityService" singleton { var allPermissions = []; for ( var thisPermission in thisRole.permissions ) { var oPerm = variables.permissionService.findByPermission( thisPermission.permission ); - oPerm = ( - isNull( oPerm ) ? getBeanPopulator().populateFromStruct( - target = variables.permissionService.new(), - memento = thisPermission, - exclude = "permissionID" - ) : oPerm + oPerm = ( + isNull( oPerm ) + ? getBeanPopulator().populateFromStruct( + target = variables.permissionService.new(), + memento = thisPermission, + exclude = "permissionID" + ) + : oPerm ); // save oPerm if new only if ( !oPerm.isLoaded() ) { @@ -124,7 +130,7 @@ component extends="cborm.models.VirtualEntityService" singleton { if ( !oRole.isLoaded() ) { arguments.importLog.append( "New role imported: #thisRole.role#
" ); arrayAppend( allRoles, oRole ); - } else if ( oRole.isLoaded() and arguments.override ) { + } else if ( oRole.isLoaded() && arguments.override ) { arguments.importLog.append( "Persisted role overriden: #thisRole.role#
" ); arrayAppend( allRoles, oRole ); } else { @@ -139,8 +145,8 @@ component extends="cborm.models.VirtualEntityService" singleton { arguments.importLog.append( "Saved all imported and overriden roles!" ); } else { arguments.importLog.append( - "No roles imported as none where found or able to be overriden from the import file." - ); + "No roles imported as none where found or able to be overriden from the import file." + ); } } // end transaction @@ -148,4 +154,4 @@ component extends="cborm.models.VirtualEntityService" singleton { return arguments.importLog.toString(); } -} +} \ No newline at end of file diff --git a/modules/contentbox/models/security/SecurityRule.cfc b/modules/contentbox/models/security/SecurityRule.cfc index 50c8d2a76..97d0ac600 100644 --- a/modules/contentbox/models/security/SecurityRule.cfc +++ b/modules/contentbox/models/security/SecurityRule.cfc @@ -13,146 +13,129 @@ component cachename ="cbSecurityRule" cacheuse ="read-write" { - - /* ********************************************************************* - ** PROPERTIES - ********************************************************************* */ + /********************************************************************** + * ** PROPERTIES + **********************************************************************/ property - name ="ruleID" - column ="ruleID" + name="ruleID" + column="ruleID" fieldtype="id" generator="uuid" - length ="36" - ormtype ="string" - update ="false"; - + length="36" + ormtype="string" + update="false"; property - name ="whitelist" - column ="whitelist" + name="whitelist" + column="whitelist" ormtype="string" notnull="false" default="" - length ="255"; - + length="255"; property - name ="securelist" - column ="securelist" + name="securelist" + column="securelist" ormtype="string" notnull="true" default="" - length ="255"; - + length="255"; property - name ="match" - column ="match" - ormtype ="string" - notnull ="false" - default ="event" + name="match" + column="match" + ormtype="string" + notnull="false" + default="event" dbdefault="'event'" - length ="50"; - + length="50"; property - name ="roles" - column ="roles" + name="roles" + column="roles" ormtype="string" notnull="false" default="" - length ="255"; - + length="255"; property - name ="permissions" - column ="permissions" + name="permissions" + column="permissions" ormtype="string" notnull="false" default="" - length ="500"; - + length="500"; property - name ="redirect" - column ="redirect" + name="redirect" + column="redirect" ormtype="string" notnull="false" default="" - length ="500"; - + length="500"; property - name ="overrideEvent" - column ="overrideEvent" + name="overrideEvent" + column="overrideEvent" ormtype="string" notnull="false" default="" - length ="500"; - + length="500"; property - name ="useSSL" - column ="useSSL" + name="useSSL" + column="useSSL" ormtype="boolean" notnull="false" default="false"; - property - name ="action" - column ="action" - ormtype ="string" - notnull ="false" - default ="redirect" + name="action" + column="action" + ormtype="string" + notnull="false" + default="redirect" dbdefault="'redirect'" - length ="50"; - + length="50"; property - name ="module" - column ="module" + name="module" + column="module" ormtype="string" notnull="false" default="" - length ="500"; - + length="500"; property - name ="httpMethods" - column ="httpMethods" - ormtype ="string" - notnull ="true" - default ="*" + name="httpMethods" + column="httpMethods" + ormtype="string" + notnull="true" + default="*" dbdefault="'*'" - length ="255"; - + length="255"; property - name ="allowedIPs" - column ="allowedIPs" - ormtype ="string" - notnull ="true" - default ="*" + name="allowedIPs" + column="allowedIPs" + ormtype="string" + notnull="true" + default="*" dbdefault="'*'" - length ="255"; - + length="255"; property - name ="order" - column ="order" + name="order" + column="order" ormtype="integer" notnull="true" default="0"; - property - name ="message" - column ="message" + name="message" + column="message" ormtype="string" notnull="false" default="" - length ="255"; - + length="255"; property - name ="messageType" - column ="messageType" - ormtype ="string" - notnull ="false" - default ="info" + name="messageType" + column="messageType" + ormtype="string" + notnull="false" + default="info" dbdefault="'info'" - length ="50"; - - /* ********************************************************************* - ** PK + CONSTRAINTS - ********************************************************************* */ + length="50"; + /********************************************************************** + * ** PK + CONSTRAINTS + **********************************************************************/ this.pk = "ruleID"; @@ -174,7 +157,7 @@ component "useSSL", "whitelist" ], - defaultExcludes : [ "" ] + defaultExcludes : [ ""] }; this.constraints = { @@ -201,25 +184,25 @@ component "messageType" : { required : false, size : "1..50" } }; - /* ********************************************************************* - ** PUBLIC FUNCTIONS - ********************************************************************* */ + /********************************************************************** + * ** PUBLIC FUNCTIONS + **********************************************************************/ // Constructor - function init(){ - variables.match = "event"; - variables.action = "redirect"; - variables.useSSL = false; - variables.order = 0; - variables.messageType = "info"; + function init() { + variables.match = "event"; + variables.action = "redirect"; + variables.useSSL = false; + variables.order = 0; + variables.messageType = "info"; variables.overrideEvent = ""; - variables.module = ""; - variables.httpMethods = "*"; - variables.allowedIPs = "*"; + variables.module = ""; + variables.httpMethods = "*"; + variables.allowedIPs = "*"; super.init(); return this; } -} +} \ No newline at end of file diff --git a/modules/contentbox/models/security/SecurityRuleService.cfc b/modules/contentbox/models/security/SecurityRuleService.cfc index d19a72d73..6a51ac39f 100755 --- a/modules/contentbox/models/security/SecurityRuleService.cfc +++ b/modules/contentbox/models/security/SecurityRuleService.cfc @@ -1,4 +1,4 @@ -/** +/** * ContentBox - A Modular Content Platform * Copyright since 2012 by Ortus Solutions, Corp * www.ortussolutions.com/products/contentbox @@ -6,7 +6,6 @@ * Security rules manager */ component extends="cborm.models.VirtualEntityService" singleton { - // DI property name="populator" inject="wirebox:populator"; property name="dateUtil" inject="DateUtil@contentbox"; @@ -14,7 +13,7 @@ component extends="cborm.models.VirtualEntityService" singleton { /** * Constructor */ - SecurityRuleService function init(){ + SecurityRuleService function init() { // init it super.init( entityName = "cbSecurityRule" ); @@ -24,8 +23,11 @@ component extends="cborm.models.VirtualEntityService" singleton { /** * Get the maximum used order */ - numeric function getMaxOrder(){ - var q = executeQuery( query = "select max( sr.order ) from cbSecurityRule as sr", asQuery = false ); + numeric function getMaxOrder() { + var q = executeQuery( + query = "select max( sr.order ) from cbSecurityRule as sr", + asQuery = false + ); if ( arrayIsDefined( q, 1 ) ) { return q[ 1 ]; } @@ -35,7 +37,7 @@ component extends="cborm.models.VirtualEntityService" singleton { /** * Get the next maximum used order */ - numeric function getNextMaxOrder(){ + numeric function getNextMaxOrder() { return getMaxOrder() + 1; } @@ -47,7 +49,7 @@ component extends="cborm.models.VirtualEntityService" singleton { boolean forceInsert = false, boolean flush = false, boolean transactional = getUseTransactions() - ){ + ) { // determine new or not if ( !arguments.entity.isLoaded() ) { // new, so add next max order if not default @@ -62,14 +64,14 @@ component extends="cborm.models.VirtualEntityService" singleton { /** * Get all rules in firing order */ - query function getSecurityRules(){ + query function getSecurityRules() { return list( sortOrder = "order asc", asQuery = true ); } /** * Reset rules to factory shipping standards, this will remove all rules also */ - any function resetRules(){ + any function resetRules() { transaction { // Get rules path var rulesPath = getDirectoryFromPath( getMetadata( this ).path ) & "data/securityRules.json"; @@ -83,7 +85,7 @@ component extends="cborm.models.VirtualEntityService" singleton { if ( structKeyExists( thisRule, "ruleID" ) ) { structDelete( thisRule, "ruleID" ); } - var oRule = new ( properties = thisRule ); + var oRule = new( properties = thisRule ); save( entity = oRule, transactional = false ); } } @@ -94,10 +96,12 @@ component extends="cborm.models.VirtualEntityService" singleton { /** * Get all data prepared for export */ - array function getAllForExport(){ - return getAll().map( function( thisItem ){ - return thisItem.getMemento( profile: "export" ); - } ); + array function getAllForExport() { + return getAll().map( + function( thisItem ) { + return thisItem.getMemento( profile = "export" ); + } + ); } /** @@ -110,14 +114,17 @@ component extends="cborm.models.VirtualEntityService" singleton { * * @throws InvalidImportFormat */ - string function importFromFile( required importFile, boolean override = false ){ - var data = fileRead( arguments.importFile ); + string function importFromFile( required importFile, boolean override = false ) { + var data = fileRead( arguments.importFile ); var importLog = createObject( "java", "java.lang.StringBuilder" ).init( - "Starting import with override = #arguments.override#...
" - ); + "Starting import with override = #arguments.override#...
" + ); if ( !isJSON( data ) ) { - throw( message = "Cannot import file as the contents is not JSON", type = "InvalidImportFormat" ); + throw( + message = "Cannot import file as the contents is not JSON", + type = "InvalidImportFormat" + ); } // deserialize packet: Should be array of { settingID, name, value } @@ -141,12 +148,12 @@ component extends="cborm.models.VirtualEntityService" singleton { required importData, boolean override = false, importLog - ){ + ) { var allRules = []; // if struct, inflate into an array if ( isStruct( arguments.importData ) ) { - arguments.importData = [ arguments.importData ]; + arguments.importData = [ arguments.importData]; } transaction { @@ -154,34 +161,38 @@ component extends="cborm.models.VirtualEntityService" singleton { for ( var thisRule in arguments.importData ) { // Get new or persisted with enough info to match var oRule = this.findWhere( - criteria = { - match : thisRule.match, - whitelist : thisRule.whitelist, - securelist : thisRule.securelist, - redirect : thisRule.redirect, - roles : thisRule.roles, - permissions : thisRule.permissions - } - ); - oRule = ( isNull( oRule ) ? new () : oRule ); + criteria = { + match : thisRule.match, + whitelist : thisRule.whitelist, + securelist : thisRule.securelist, + redirect : thisRule.redirect, + roles : thisRule.roles, + permissions : thisRule.permissions + } + ); + oRule = ( isNull( oRule ) ? new() : oRule ); // populate content from data getBeanPopulator().populateFromStruct( - target = oRule, - memento = thisRule, - exclude = "ruleID", - composeRelationships = false - ); + target = oRule, + memento = thisRule, + exclude = "ruleID", + composeRelationships = false + ); // if new or persisted with override then save. if ( !oRule.isLoaded() ) { arguments.importLog.append( "New security rule imported: #thisRule.toString()#
" ); arrayAppend( allRules, oRule ); - } else if ( oRule.isLoaded() and arguments.override ) { - arguments.importLog.append( "Persisted security rule overriden: #thisRule.toString()#
" ); + } else if ( oRule.isLoaded() && arguments.override ) { + arguments.importLog.append( + "Persisted security rule overriden: #thisRule.toString()#
" + ); arrayAppend( allRules, oRule ); } else { - arguments.importLog.append( "Skipping persisted security rule: #thisRule.toString()#
" ); + arguments.importLog.append( + "Skipping persisted security rule: #thisRule.toString()#
" + ); } } // end import loop @@ -192,8 +203,8 @@ component extends="cborm.models.VirtualEntityService" singleton { arguments.importLog.append( "Saved all imported and overriden security rules!" ); } else { arguments.importLog.append( - "No security rules imported as none where found or able to be overriden from the import file." - ); + "No security rules imported as none where found or able to be overriden from the import file." + ); } } // end transaction @@ -201,4 +212,4 @@ component extends="cborm.models.VirtualEntityService" singleton { return arguments.importLog.toString(); } -} +} \ No newline at end of file diff --git a/modules/contentbox/models/security/SecurityService.cfc b/modules/contentbox/models/security/SecurityService.cfc index dbf3f625e..ebb261768 100755 --- a/modules/contentbox/models/security/SecurityService.cfc +++ b/modules/contentbox/models/security/SecurityService.cfc @@ -6,7 +6,6 @@ * Our contentbox security service must match our interface: ISecurityService */ component singleton { - // Dependencies property name="authorService" inject="authorService@contentbox"; property name="settingService" inject="settingService@contentbox"; @@ -21,17 +20,15 @@ component singleton { property name="cache" inject="cachebox:template"; property name="bCrypt" inject="BCrypt@BCrypt"; property name="cbCSRF" inject="@cbcsrf"; - // Properties property name="encryptionKey"; - // Static key to identify an author in a request: more for api interaction than web. variables.AUTHOR_KEY = "contentbox_author"; /** * Constructor */ - SecurityService function init(){ + SecurityService function init() { variables.encryptionKey = ""; return this; } @@ -41,7 +38,7 @@ component singleton { * * @author The author object */ - Author function updateAuthorLoginTimestamp( required author ){ + Author function updateAuthorLoginTimestamp( required author ) { arguments.author.setLastLogin( now() ); variables.authorService.save( arguments.author ); return arguments.author; @@ -50,7 +47,7 @@ component singleton { /** * Alias to getAuthorSession() created to satisfy JWT Services */ - function getUser(){ + function getUser() { return this.getAuthorSession(); } @@ -64,7 +61,7 @@ component singleton { * * @return Logged in or new author object */ - Author function getAuthorSession(){ + Author function getAuthorSession() { // Check if valid author id in session or request respectively var oAuthor = variables.requestStorage.get( variables.AUTHOR_KEY, variables.authorService.new() ); if ( oAuthor.isLoggedIn() ) { @@ -81,7 +78,9 @@ component singleton { // If we found an authorID, load it up and check it if ( len( authorID ) ) { // try to get it with that ID - var author = variables.authorService.findWhere( { authorID : authorID, isActive : true, isDeleted : false } ); + var author = variables.authorService.findWhere( + { authorID : authorID, isActive : true, isDeleted : false } + ); // If user found? Inflate them back if ( !isNull( author ) ) { return login( author ); @@ -95,7 +94,7 @@ component singleton { /** * Verifies if a user is logged in or not. Required for JWT Services */ - boolean function isLoggedIn(){ + boolean function isLoggedIn() { var oAuthor = this.getAuthorSession(); return oAuthor.isLoaded() && oAuthor.isLoggedIn(); } @@ -105,7 +104,7 @@ component singleton { * * @author The author to log in */ - Author function login( required author ){ + Author function login( required author ) { // Mark as logged in arguments.author.setLoggedIn( true ); // Store for the duration of the request, API mode @@ -120,7 +119,7 @@ component singleton { * * @return SecurityService */ - SecurityService function logout(){ + SecurityService function logout() { variables.requestStorage.delete( variables.AUTHOR_KEY ); variables.cacheStorage.clearAll(); variables.cookieStorage.delete( "contentbox_keep_logged_in" ); @@ -149,32 +148,38 @@ component singleton { required username, required password, boolean logThemIn = false - ){ + ) { // Find them try { var oAuthor = variables.authorService.retrieveUserByUsername( arguments.username ); - } catch ( EntityNotFound e ) { + } catch (EntityNotFound e) { variables.log.warn( - "Invalid username authentication from #getRealIP()# for username: #arguments.username#", - getHTTPRequestData( false ) + "Invalid username authentication from #getRealIP()# for username: #arguments.username#", + getHTTPRequestData( false ) + ); + throw( + type = "InvalidCredentials", + message = "Incorrect Credentials Entered" ); - throw( type = "InvalidCredentials", message = "Incorrect Credentials Entered" ); } // Validate password using bcrypt try { var isSamePassword = variables.bcrypt.checkPassword( arguments.password, oAuthor.getPassword() ); - } catch ( any e ) { + } catch (any e) { var isSamePassword = false; } // Verify Password if ( !isSamePassword ) { variables.log.warn( - "Invalid password authentication from #getRealIP()# for username: #arguments.username#", - getHTTPRequestData( false ) + "Invalid password authentication from #getRealIP()# for username: #arguments.username#", + getHTTPRequestData( false ) + ); + throw( + type = "InvalidCredentials", + message = "Incorrect Credentials Entered" ); - throw( type = "InvalidCredentials", message = "Incorrect Credentials Entered" ); } // Set last login date @@ -193,7 +198,7 @@ component singleton { * * @string The string to bcrypt */ - string function encryptString( required string ){ + string function encryptString( required string ) { return variables.bCrypt.hashPassword( arguments.string ); } @@ -202,16 +207,18 @@ component singleton { * * @author The author to create the reset token for. */ - string function generateResetToken( required Author author ){ + string function generateResetToken( required Author author ) { var tokenTimeout = variables.settingService.getSetting( "cb_security_password_reset_expiration" ); // Store Security Token For X minutes - var token = hash( arguments.author.getEmail() & arguments.author.getAuthorID() & now() ); - cache.set( - "reset-token-#token#", - arguments.author.getAuthorID(), - tokenTimeout, - tokenTimeout + var token = hash( + arguments.author.getEmail() & arguments.author.getAuthorID() & now() ); + cache.set( + "reset-token-#token#", + arguments.author.getAuthorID(), + tokenTimeout, + tokenTimeout + ); return token; } @@ -229,12 +236,12 @@ component singleton { required Author author, boolean adminIssued = false, Author issuer - ){ + ) { // Generate security token var token = generateResetToken( arguments.author ); // get settings - var settings = variables.settingService.getAllSettings(); + var settings = variables.settingService.getAllSettings(); var defaultSite = variables.siteService.getDefaultSite(); // get mail payload @@ -243,31 +250,32 @@ component singleton { ip : getRealIP(), linkTimeout : settings.cb_security_password_reset_expiration, siteName : defaultSite.getName(), - linkToken : CBHelper.linkAdmin( event = "security.verifyReset", ssl = settings.cb_admin_ssl ) & "?token=#token#", + linkToken : CBHelper.linkAdmin( event = "security.verifyReset", + ssl = settings.cb_admin_ssl ) & "?token=#token#", issuedBy : "", issuedEmail : "" }; // Check if an issuer was passed if ( !isNull( arguments.issuer ) ) { - bodyTokens.issuedBy = arguments.issuer.getFullName(); + bodyTokens.issuedBy = arguments.issuer.getFullName(); bodyTokens.issuedEmail = arguments.issuer.getEmail(); } // Build email out var mail = variables.mailservice.newMail( - to = arguments.author.getEmail(), - from = settings.cb_site_outgoingEmail, - subject = "#defaultSite.getName()# Password Reset Verification", - bodyTokens = bodyTokens, - type = "html", - server = settings.cb_site_mail_server, - username = settings.cb_site_mail_username, - password = settings.cb_site_mail_password, - port = settings.cb_site_mail_smtp, - useTLS = settings.cb_site_mail_tls, - useSSL = settings.cb_site_mail_ssl - ); + to = arguments.author.getEmail(), + from = settings.cb_site_outgoingEmail, + subject = "#defaultSite.getName()# Password Reset Verification", + bodyTokens = bodyTokens, + type = "html", + server = settings.cb_site_mail_server, + username = settings.cb_site_mail_username, + password = settings.cb_site_mail_password, + port = settings.cb_site_mail_smtp, + useTLS = settings.cb_site_mail_tls, + useSSL = settings.cb_site_mail_ssl + ); // Decide template depending if issued by user or admin var emailTemplate = "password_verification"; @@ -276,11 +284,11 @@ component singleton { } mail.setBody( - renderer.layout( - view = "/contentbox/email_templates/#emailTemplate#", - layout = "/contentbox/email_templates/layouts/email" - ) - ); + renderer.layout( + view = "/contentbox/email_templates/#emailTemplate#", + layout = "/contentbox/email_templates/layouts/email" + ) + ); // send it out return mailService.send( mail ).getResults(); @@ -294,8 +302,8 @@ component singleton { * * @return {error, author} */ - struct function validateResetToken( required token ){ - var results = { "error" : false, "author" : "" }; + struct function validateResetToken( required token ) { + var results = { "error" : false, "author" : "" }; var cacheKey = "reset-token-#arguments.token#"; var authorID = cache.get( cacheKey ); @@ -303,14 +311,14 @@ component singleton { if ( isNull( authorID ) ) { results.error = true; return results; - }; + } // Verify the author of the token results.author = variables.authorService.get( authorID ); if ( isNull( results.author ) ) { results.error = true; return results; - }; + } return results; } @@ -328,30 +336,30 @@ component singleton { required token, required Author author, required password - ){ - var results = { "error" : false, "messages" : "" }; + ) { + var results = { "error" : false, "messages" : "" }; var cacheKey = "reset-token-#arguments.token#"; var authorID = cache.get( cacheKey ); // If token not found, don't reset and return back if ( isNull( authorID ) ) { - results.error = true; + results.error = true; results.messages = "Token does not exist or has expired"; return results; - }; + } // Verify the author of the token - if ( arguments.author.getAuthorID() neq authorID ) { - results.error = true; + if ( arguments.author.getAuthorID() NEQ authorID ) { + results.error = true; results.messages = "Author reset token mismatch"; return results; - }; + } // Remove token now that we have the data and it has been validated cache.clear( cacheKey ); // get settings - var settings = settingService.getAllSettings(); + var settings = settingService.getAllSettings(); var defaultSite = variables.siteService.getDefaultSite(); // set it in the user and save reset password @@ -368,25 +376,25 @@ component singleton { adminEmail : settings.cb_site_email }; var mail = mailservice.newMail( - to = arguments.author.getEmail(), - from = settings.cb_site_outgoingEmail, - subject = "#defaultSite.getName()# Password Reset Completed", - bodyTokens = bodyTokens, - type = "html", - server = settings.cb_site_mail_server, - username = settings.cb_site_mail_username, - password = settings.cb_site_mail_password, - port = settings.cb_site_mail_smtp, - useTLS = settings.cb_site_mail_tls, - useSSL = settings.cb_site_mail_ssl - ); + to = arguments.author.getEmail(), + from = settings.cb_site_outgoingEmail, + subject = "#defaultSite.getName()# Password Reset Completed", + bodyTokens = bodyTokens, + type = "html", + server = settings.cb_site_mail_server, + username = settings.cb_site_mail_username, + password = settings.cb_site_mail_password, + port = settings.cb_site_mail_smtp, + useTLS = settings.cb_site_mail_tls, + useSSL = settings.cb_site_mail_ssl + ); // ,body=renderer.$get().externalView(view="/contentbox/email_templates/password_reminder" ) mail.setBody( - renderer.layout( - view = "/contentbox/email_templates/password_reset", - layout = "/contentbox/email_templates/layouts/email" - ) - ); + renderer.layout( + view = "/contentbox/email_templates/password_reset", + layout = "/contentbox/email_templates/layouts/email" + ) + ); // send it out mailService.send( mail ); @@ -399,14 +407,14 @@ component singleton { * @content The content object * @password The password to check */ - boolean function authorizeContent( required content, required password ){ + boolean function authorizeContent( required content, required password ) { // Validate Password - if ( compare( arguments.content.getPasswordProtection(), arguments.password ) eq 0 ) { + if ( compare( arguments.content.getPasswordProtection(), arguments.password ) EQ 0 ) { // Set simple validation cacheStorage.set( - "protection-#hash( arguments.content.getSlug() )#", - getContentProtectedHash( arguments.content ) - ); + "protection-#hash( arguments.content.getSlug() )#", + getContentProtectedHash( arguments.content ) + ); return true; } @@ -418,52 +426,54 @@ component singleton { * * @content The content object to check */ - boolean function isContentViewable( required content ){ + boolean function isContentViewable( required content ) { var protectedHash = cacheStorage.get( "protection-#hash( arguments.content.getSlug() )#", "" ); // check hash against validated content - if ( compare( protectedHash, getContentProtectedHash( arguments.content ) ) EQ 0 ) { + if ( + compare( + protectedHash, + getContentProtectedHash( arguments.content ) + ) EQ + 0 + ) { return true; } return false; } - /** - * Get password content protected salt - * - * @content The content object - */ - private string function getContentProtectedHash( required content ){ - return hash( arguments.content.getSlug() & arguments.content.getPasswordProtection(), "SHA-256" ); - } - /** * Get remember me cookie */ - any function getRememberMe(){ + any function getRememberMe() { var cookieValue = cookieStorage.get( "contentbox_remember_me", "" ); try { return decryptIt( cookieValue ); - } catch ( Any e ) { + } catch (Any e) { // Errors on decryption - log.error( "Error decrypting remember me key: #e.message# #e.detail#", cookieValue ); + log.error( + "Error decrypting remember me key: #e.message# #e.detail#", + cookieValue + ); cookieStorage.delete( "contentbox_remember_me" ); return ""; } } - /** * Get keep me logged in cookie */ - any function getKeepMeLoggedIn(){ + any function getKeepMeLoggedIn() { var cookieValue = cookieStorage.get( "contentbox_keep_logged_in", "" ); try { return decryptIt( cookieValue ); - } catch ( Any e ) { + } catch (Any e) { // Errors on decryption - log.error( "Error decrypting Keep Me Logged in key: #e.message# #e.detail#", cookieValue ); + log.error( + "Error decrypting Keep Me Logged in key: #e.message# #e.detail#", + cookieValue + ); cookieStorage.delete( "contentbox_keep_logged_in" ); return ""; } @@ -475,7 +485,7 @@ component singleton { * @username The username to store * @days The days to store */ - SecurityService function setRememberMe( required username, required numeric days = 0 ){ + SecurityService function setRememberMe( required username, required numeric days = 0 ) { // If the user now only wants to be remembered for this session, remove any existing cookies. if ( !arguments.days ) { variables.cookieStorage.delete( "contentbox_remember_me" ); @@ -485,24 +495,26 @@ component singleton { // Save the username to pre-populate the login field after their login expires for up to a year. variables.cookieStorage.set( - name = "contentbox_remember_me", - value = encryptIt( arguments.username ), - expires = 365 - ); + name = "contentbox_remember_me", + value = encryptIt( arguments.username ), + expires = 365 + ); // Look up the user ID and store for the duration specified - var author = variables.authorService.findWhere( { - username : arguments.username, - isActive : true, - isDeleted : false - } ); + var author = variables.authorService.findWhere( + { + username : arguments.username, + isActive : true, + isDeleted : false + } + ); if ( !isNull( author ) ) { // The user will be auto-logged in as long as this cookie exists variables.cookieStorage.set( - name = "contentbox_keep_logged_in", - value = encryptIt( author.getAuthorID() ), - expires = arguments.days - ); + name = "contentbox_keep_logged_in", + value = encryptIt( author.getAuthorID() ), + expires = arguments.days + ); } return this; @@ -513,7 +525,7 @@ component singleton { * * @encValue value to encrypt */ - string function encryptIt( required encValue ){ + string function encryptIt( required encValue ) { // if empty just return it if ( !len( arguments.encValue ) ) { return arguments.encValue; @@ -531,7 +543,7 @@ component singleton { * * @decValue value to decrypt */ - string function decryptIt( required decValue ){ + string function decryptIt( required decValue ) { if ( !len( arguments.decValue ) ) { return arguments.decValue; } @@ -547,7 +559,7 @@ component singleton { * Verifies we have a salt in our installation * if not, it will generate a new cb_enc_key */ - string function getEncryptionKey(){ + string function getEncryptionKey() { // Is the encryption key loaded? if ( len( variables.encryptionKey ) ) { return variables.encryptionKey; @@ -558,10 +570,12 @@ component singleton { // if no key, then create it for this ContentBox installation if ( isNull( oSetting ) ) { - oSetting = settingService.new( { - name : "cb_enc_key", - value : generateSecretKey( "BLOWFISH" ) - } ); + oSetting = settingService.new( + { + name : "cb_enc_key", + value : generateSecretKey( "BLOWFISH" ) + } + ); settingService.save( entity = oSetting ); log.info( "Registered new cookie encryption key" ); } @@ -576,7 +590,7 @@ component singleton { /** * Get Real IP, by looking at clustered, proxy headers and locally. */ - function getRealIP(){ + function getRealIP() { var headers = getHTTPRequestData( false ).headers; // When going through a proxy, the IP can be a delimtied list, thus we take the last one in the list @@ -591,4 +605,16 @@ component singleton { return len( cgi.remote_addr ) ? trim( listFirst( cgi.remote_addr ) ) : "127.0.0.1"; } -} + /** + * Get password content protected salt + * + * @content The content object + */ + private string function getContentProtectedHash( required content ) { + return hash( + arguments.content.getSlug() & arguments.content.getPasswordProtection(), + "SHA-256" + ); + } + +} \ No newline at end of file diff --git a/modules/contentbox/models/security/SecurityValidator.cfc b/modules/contentbox/models/security/SecurityValidator.cfc index 98a8cd007..5a1782cb9 100644 --- a/modules/contentbox/models/security/SecurityValidator.cfc +++ b/modules/contentbox/models/security/SecurityValidator.cfc @@ -6,12 +6,7 @@ * This is our security validator that works with CBSecurity in order to validate * rules and annotations. */ -component - singleton - threadsafe - extends="cbsecurity.models.validators.AuthValidator" -{ - +component singleton threadsafe extends="cbsecurity.models.validators.AuthValidator" { // Dependencies property name="securityService" inject="securityService@contentbox"; @@ -27,11 +22,11 @@ component * * @return { allow:boolean, type:string(authentication|authorization), messages:string } */ - struct function ruleValidator( required rule, required controller ){ + struct function ruleValidator( required rule, required controller ) { return validateSecurity( - rule : arguments.rule, - securedValue: arguments.rule.permissions, - controller : arguments.controller + rule = arguments.rule, + securedValue = arguments.rule.permissions, + controller = arguments.controller ); } @@ -46,8 +41,8 @@ component * * @return { allow:boolean, type:string(authentication|authorization), messages:string } */ - struct function annotationValidator( required securedValue, required controller ){ - return validateSecurity( securedValue: arguments.securedValue, controller: arguments.controller ); + struct function annotationValidator( required securedValue, required controller ) { + return validateSecurity( securedValue = arguments.securedValue, controller = arguments.controller ); } /** @@ -59,7 +54,7 @@ component * * @return Validation struct of: { allow:boolean, type:(authentication|authorization), messages } */ - struct function validateSecurity( struct rule, securedValue = "", any controller ){ + struct function validateSecurity( struct rule, securedValue = "", any controller ) { var results = super.validateSecurity( arguments.securedValue ); // If we are allowing, then continue to test the ContentBox roles @@ -70,9 +65,12 @@ component // Check if the rule requires roles if ( !isNull( arguments.rule ) && len( arguments.rule.roles ) ) { results.allow = false; - results.type = "authorization"; - for ( var thisRole in arguments.rule.roles.listToArray() ) { - if ( oAuthor.getRoleName() eq thisRole ) { + results.type = "authorization"; + for ( var thisRole in arguments + .rule + .roles + .listToArray() ) { + if ( oAuthor.getRoleName() EQ thisRole ) { results.allow = true; break; } @@ -82,21 +80,22 @@ component // If the rule has a message, then set a messagebox if ( - !results.allow && - !isNull( arguments.rule ) && - !isNull( arguments.rule.message ) && - len( arguments.rule.message ) + !results.allow && !isNull( arguments.rule ) && !isNull( arguments.rule.message ) && + len( arguments.rule.message ) ) { - arguments.controller + arguments + .controller .getWireBox() .getInstance( "messagebox@cbmessagebox" ) .setMessage( - type : ( structKeyExists( rule, "messageType" ) && len( rule.messageType ) ? rule.messageType : "info" ), - message: rule.message + type = ( + structKeyExists( rule, "messageType" ) && len( rule.messageType ) ? rule.messageType : "info" + ), + message = rule.message ); } return results; } -} +} \ No newline at end of file diff --git a/modules/contentbox/models/security/twofactor/BaseTwoFactorProvider.cfc b/modules/contentbox/models/security/twofactor/BaseTwoFactorProvider.cfc index 62d31f4ba..c331a7dc9 100644 --- a/modules/contentbox/models/security/twofactor/BaseTwoFactorProvider.cfc +++ b/modules/contentbox/models/security/twofactor/BaseTwoFactorProvider.cfc @@ -13,7 +13,6 @@ * - CBHelper */ component { - // DI property name="log" inject="logbox:logger:{this}"; property name="settingService" inject="settingService@contentbox"; @@ -25,14 +24,14 @@ component { /** * Constructor */ - function init(){ + function init() { return this; } /** * Get all system settings */ - struct function getAllSettings(){ + struct function getAllSettings() { return variables.settingService.getAllSettings(); } @@ -41,7 +40,7 @@ component { * * @return Site */ - function getDefaultSite(){ + function getDefaultSite() { return variables.siteService.getDefaultSite(); } @@ -50,8 +49,8 @@ component { * * @return Site */ - function discoverSite(){ + function discoverSite() { return variables.siteService.discoverSite(); } -} +} \ No newline at end of file diff --git a/modules/contentbox/models/security/twofactor/ITwoFactorProvider.cfc b/modules/contentbox/models/security/twofactor/ITwoFactorProvider.cfc index 84457a057..771c58fb1 100644 --- a/modules/contentbox/models/security/twofactor/ITwoFactorProvider.cfc +++ b/modules/contentbox/models/security/twofactor/ITwoFactorProvider.cfc @@ -10,33 +10,33 @@ interface { /** * Get the internal name of a provider, used for registration, internal naming and more. */ - function getName(); + default function getName(); /** * Get the display name for the provider. Used in all UI screens */ - function getDisplayName(); + default function getDisplayName(); /** * Returns html to display to the user for required two-factor fields */ - function getAuthorSetupForm( required author ); + default function getAuthorSetupForm( required author ); /** * Get the display help for the provider. Used in the UI setup screens for the author */ - function getAuthorSetupHelp( required author ); + default function getAuthorSetupHelp( required author ); /** * Get the verification help for the provider. Used in the UI verification screen. */ - function getVerificationHelp(); + default function getVerificationHelp(); /** * If true, then ContentBox will set a tracking cookie for the authentication provider user browser. * If the user, logs in and the device is within the trusted timespan, then no two-factor authentication validation will occur. */ - boolean function allowTrustedDevice(); + default boolean function allowTrustedDevice(); /** * Send a challenge via the 2 factor auth implementation. @@ -46,7 +46,7 @@ interface { * * @return struct:{ error:boolean, messages=string } */ - struct function sendChallenge( required author ); + default struct function sendChallenge( required author ); /** * Leverage the default provider to verify a challenge for the specific user. @@ -57,7 +57,7 @@ interface { * * @return struct:{ error:boolean, messages:string } */ - struct function verifyChallenge( required string code, required author ); + default struct function verifyChallenge( required string code, required author ); /** * This method is called once a two factor challenge is accepted and valid. @@ -66,6 +66,5 @@ interface { * @code The verification code * @author The author to verify challenge */ - function finalize( required string code, required author ); - -} + default function finalize( required string code, required author ); +} \ No newline at end of file diff --git a/modules/contentbox/models/security/twofactor/TwoFactorService.cfc b/modules/contentbox/models/security/twofactor/TwoFactorService.cfc index 9e68425d9..e4e74df24 100644 --- a/modules/contentbox/models/security/twofactor/TwoFactorService.cfc +++ b/modules/contentbox/models/security/twofactor/TwoFactorService.cfc @@ -6,19 +6,16 @@ * Manages Two Factor Authenticators */ component accessors="true" threadSafe singleton { - // DI property name="settingService" inject="settingService@contentbox"; property name="securityService" inject="securityService@contentbox"; property name="authorService" inject="authorService@contentbox"; property name="cookieStorage" inject="cookieStorage@cbStorages"; property name="log" inject="logbox:logger:{this}"; - /** * Providers registry */ property name="providers" type="struct"; - // Static Properties variables.TRUSTED_DEVICE_COOKIE = "contentbox_2factor_device"; @@ -28,7 +25,7 @@ component accessors="true" threadSafe singleton { * * @wirebox.inject wirebox */ - TwoFactorService function init( required wirebox ){ + TwoFactorService function init( required wirebox ) { // init providers variables.providers = {}; @@ -41,7 +38,7 @@ component accessors="true" threadSafe singleton { /** * Are we forcing global two factor authentication */ - boolean function isForceTwoFactorAuth(){ + boolean function isForceTwoFactorAuth() { // Doing !! to force a boolean return !!settingService.getSetting( "cb_security_2factorAuth_force" ); } @@ -49,21 +46,21 @@ component accessors="true" threadSafe singleton { /** * Get the default system provider name */ - string function getDefaultProvider(){ + string function getDefaultProvider() { return settingService.getSetting( "cb_security_2factorAuth_provider" ); } /** * Get the default system provider object */ - contentbox.models.security.twofactor.ITwoFactorProvider function getDefaultProviderObject(){ + contentbox.models.security.twofactor.ITwoFactorProvider function getDefaultProviderObject() { return getProvider( getDefaultProvider() ); } /** * Get the default system trusted device timespan */ - numeric function getTrustedDeviceTimespan(){ + numeric function getTrustedDeviceTimespan() { return settingService.getSetting( "cb_security_2factorAuth_trusted_days" ); } @@ -74,7 +71,7 @@ component accessors="true" threadSafe singleton { */ TwoFactorService function registerProvider( required contentbox.models.security.twofactor.ITwoFactorProvider provider - ){ + ) { variables.providers[ arguments.provider.getName() ] = arguments.provider; return this; } @@ -84,7 +81,7 @@ component accessors="true" threadSafe singleton { * * @name The name of the provider to unregister */ - TwoFactorService function unRegisterProvider( required name ){ + TwoFactorService function unRegisterProvider( required name ) { structDelete( variables.providers, arguments.name ); return this; } @@ -92,16 +89,18 @@ component accessors="true" threadSafe singleton { /** * Get an array of registered provider names in alphabetical order */ - array function getRegisteredProviders(){ - return listToArray( listSort( structKeyList( variables.providers ), "textnocase" ) ); + array function getRegisteredProviders() { + return listToArray( + listSort( structKeyList( variables.providers ), "textnocase" ) + ); } /** * Get an array of registered provider names in alphabetical order with their display names */ - array function getRegisteredProvidersMap(){ + array function getRegisteredProvidersMap() { var aProviders = getRegisteredProviders(); - var result = []; + var result = []; for ( var thisProvider in aProviders ) { arrayAppend( result, @@ -119,7 +118,7 @@ component accessors="true" threadSafe singleton { * * @name The name of the provider */ - contentbox.models.security.twofactor.ITwoFactorProvider function getProvider( required name ){ + contentbox.models.security.twofactor.ITwoFactorProvider function getProvider( required name ) { return variables.providers[ arguments.name ]; } @@ -128,7 +127,7 @@ component accessors="true" threadSafe singleton { * * @name The name of the provider */ - boolean function hasProvider( required name ){ + boolean function hasProvider( required name ) { return structKeyExists( variables.providers, arguments.name ); } @@ -137,12 +136,12 @@ component accessors="true" threadSafe singleton { * * @trustedID The trusted ID to track in the tracking cookie */ - TwoFactorService function setTrustedDevice( required trustedID ){ + TwoFactorService function setTrustedDevice( required trustedID ) { cookieStorage.set( - name = variables.TRUSTED_DEVICE_COOKIE, - value = securityService.encryptIt( arguments.trustedID ), - expires = getTrustedDeviceTimespan() - ); + name = variables.TRUSTED_DEVICE_COOKIE, + value = securityService.encryptIt( arguments.trustedID ), + expires = getTrustedDeviceTimespan() + ); return this; } @@ -151,21 +150,24 @@ component accessors="true" threadSafe singleton { * * @trustedID The trusted ID to verify */ - boolean function isTrustedDevice( required trustedID ){ + boolean function isTrustedDevice( required trustedID ) { var cookieValue = cookieStorage.get( name = variables.TRUSTED_DEVICE_COOKIE, defaultValue = "" ); try { // decrypt the target id var targetTrustedID = securityService.decryptIt( cookieValue ); // Verify they are the same - if ( targetTrustedID neq arguments.trustedID ) { + if ( targetTrustedID NEQ arguments.trustedID ) { cookieStorage.delete( variables.TRUSTED_DEVICE_COOKIE ); return false; } return true; - } catch ( Any e ) { + } catch (Any e) { // Errors on decryption - log.error( "Error decrypting trusted id cookie: #e.message# #e.detail#", cookieValue ); + log.error( + "Error decrypting trusted id cookie: #e.message# #e.detail#", + cookieValue + ); cookieStorage.delete( variables.TRUSTED_DEVICE_COOKIE ); return false; } @@ -176,18 +178,16 @@ component accessors="true" threadSafe singleton { * * @author The author to challenge or not */ - boolean function canChallenge( required author ){ + boolean function canChallenge( required author ) { var oProvider = getProvider( getDefaultProvider() ); - var results = false; + var results = false; if ( - // Verify if global force is enabled - isForceTwoFactorAuth() - OR - // Verify if user has two factor auth enabled - arguments.author.getIs2FactorAuth() + isForceTwoFactorAuth() || + arguments.author// Verify if user has two factor auth enabled + .getIs2FactorAuth() ) { // Verify if using trusted device options and if device is trusted - if ( oProvider.allowTrustedDevice() AND isTrustedDevice( arguments.author.getAuthorID() ) ) { + if ( oProvider.allowTrustedDevice() && isTrustedDevice( arguments.author.getAuthorID() ) ) { results = false; } else { results = true; @@ -206,7 +206,7 @@ component accessors="true" threadSafe singleton { * * @return struct:{ error:boolean, messages:string } */ - struct function sendChallenge( required author ){ + struct function sendChallenge( required author ) { return getProvider( getDefaultProvider() ).sendChallenge( arguments.author ); } @@ -219,8 +219,8 @@ component accessors="true" threadSafe singleton { * * @return struct:{ error:boolean, messages:string } */ - struct function verifyChallenge( required string code, required author ){ + struct function verifyChallenge( required string code, required author ) { return getProvider( getDefaultProvider() ).verifyChallenge( arguments.code, arguments.author ); } -} +} \ No newline at end of file diff --git a/modules/contentbox/models/subscriptions/BaseSubscription.cfc b/modules/contentbox/models/subscriptions/BaseSubscription.cfc index 0b58cfa28..b49368017 100644 --- a/modules/contentbox/models/subscriptions/BaseSubscription.cfc +++ b/modules/contentbox/models/subscriptions/BaseSubscription.cfc @@ -13,92 +13,76 @@ component cachename ="cbSubscription" cacheuse ="read-write" { - - /* ********************************************************************* - ** PROPERTIES again due to ACF Bug - ********************************************************************* */ + /********************************************************************** + * ** PROPERTIES again due to ACF Bug + **********************************************************************/ property - name ="createdDate" - column ="createdDate" - type ="date" + name="createdDate" + column="createdDate" + type="date" ormtype="timestamp" notnull="true" - update ="false"; - + update="false"; property - name ="modifiedDate" - column ="modifiedDate" - type ="date" + name="modifiedDate" + column="modifiedDate" + type="date" ormtype="timestamp" notnull="true"; - property - name ="isDeleted" - column ="isDeleted" + name="isDeleted" + column="isDeleted" ormtype="boolean" notnull="true" default="false"; - - /* ********************************************************************* - ** PROPERTIES - ********************************************************************* */ + /********************************************************************** + * ** PROPERTIES + **********************************************************************/ property - name ="subscriptionID" - column ="subscriptionID" + name="subscriptionID" + column="subscriptionID" fieldtype="id" generator="uuid" - length ="36" - ormtype ="string" - update ="false"; - + length="36" + ormtype="string" + update="false"; /** * This token identifies subscribers (emails) to appropriate subscriptions */ property - name ="subscriptionToken" - column ="subscriptionToken" + name="subscriptionToken" + column="subscriptionToken" ormtype="string" - length ="255" + length="255" notnull="true"; - /** * The type of subscriptions. Available subscriptions are : comment */ - property - name ="type" - column ="type" - ormtype="string" - notnull="true"; - - /* ********************************************************************* - ** RELATIONSHIPS - ********************************************************************* */ + property name="type" column="type" ormtype="string" notnull="true"; + /********************************************************************** + * ** RELATIONSHIPS + **********************************************************************/ // M20 -> Content loaded as a proxy property - name ="subscriber" - notnull ="true" - cfc ="contentbox.models.subscriptions.Subscriber" + name="subscriber" + notnull="true" + cfc="contentbox.models.subscriptions.Subscriber" fieldtype="many-to-one" - fkcolumn ="FK_subscriberID" - lazy ="true" - inverse ="true" - orderBy ="subscriberEmail"; - - /* ********************************************************************* - ** PK + CONSTRAINTS - ********************************************************************* */ + fkcolumn="FK_subscriberID" + lazy="true" + inverse="true" + orderBy="subscriberEmail"; + /********************************************************************** + * ** PK + CONSTRAINTS + **********************************************************************/ this.pk = "subscriptionID"; this.memento = { - defaultIncludes : [ - "subscriptionToken", - "type", - "subscriberSnapshot:subscriber" - ], + defaultIncludes : [ "subscriptionToken", "type", "subscriberSnapshot:subscriber"], defaultExcludes : [] }; @@ -107,11 +91,11 @@ component "type" : { required : true, size : "1..255", regex : "^(comment)$" } }; - /* ********************************************************************* - ** METHODS - ********************************************************************* */ + /********************************************************************** + * ** METHODS + **********************************************************************/ - function init(){ + function init() { super.init(); return this; } @@ -119,11 +103,11 @@ component /** * Build a snapshot of the subscriber */ - struct function getSubscriberSnapshot(){ + struct function getSubscriberSnapshot() { if ( hasSubscriber() ) { return getSubscriber().getInfoSnapshot(); } return {}; } -} +} \ No newline at end of file diff --git a/modules/contentbox/models/subscriptions/CommentSubscription.cfc b/modules/contentbox/models/subscriptions/CommentSubscription.cfc index 0a49b185d..a56648a0b 100644 --- a/modules/contentbox/models/subscriptions/CommentSubscription.cfc +++ b/modules/contentbox/models/subscriptions/CommentSubscription.cfc @@ -14,44 +14,45 @@ component cachename ="cbCommentSubscription" cacheuse ="read-write" { - - /* ********************************************************************* - ** DI - ********************************************************************* */ + /********************************************************************** + * ** DI + **********************************************************************/ property - name ="commentSubscriptionService" - inject ="provider:commentSubscriptionService@contentbox" + name="commentSubscriptionService" + inject="provider:commentSubscriptionService@contentbox" persistent="false"; - - /* ********************************************************************* - ** RELATIONSHIPS - ********************************************************************* */ + /********************************************************************** + * ** RELATIONSHIPS + **********************************************************************/ // M20 -> Content loaded as a proxy property - name ="relatedContent" - notnull ="true" - cfc ="contentbox.models.content.BaseContent" + name="relatedContent" + notnull="true" + cfc="contentbox.models.content.BaseContent" fieldtype="many-to-one" - fkcolumn ="FK_contentID" - lazy ="true" - orderBy ="Title ASC"; + fkcolumn="FK_contentID" + lazy="true" + orderBy="Title ASC"; - /* ********************************************************************* - ** METHODS - ********************************************************************* */ + /********************************************************************** + * ** METHODS + **********************************************************************/ - function init(){ + function init() { super.init(); - appendToMemento( [ "relatedContentSnapshot:relatedContent" ], "defaultIncludes" ); + appendToMemento( + [ "relatedContentSnapshot:relatedContent"], + "defaultIncludes" + ); return this; } /** * Build a snapshot of the related content */ - struct function getRelatedContentSnapshot(){ + struct function getRelatedContentSnapshot() { if ( hasRelatedContent() ) { return getRelatedContent().getInfoSnapshot(); } @@ -61,7 +62,7 @@ component /** * Before insertion verify if we have a token, else generate one. */ - public void function preInsert(){ + public void function preInsert() { super.preInsert(); if ( isNull( getSubscriptionToken() ) ) { @@ -70,12 +71,14 @@ component } } - public boolean function isExtantSubscription(){ - var extantSubscription = variables.commentSubscriptionService.findWhere( { - relatedContent : getRelatedContent(), - subscriber : getSubscriber() - } ); + public boolean function isExtantSubscription() { + var extantSubscription = variables.commentSubscriptionService.findWhere( + { + relatedContent : getRelatedContent(), + subscriber : getSubscriber() + } + ); return isNull( extantSubscription ) ? false : true; } -} +} \ No newline at end of file diff --git a/modules/contentbox/models/subscriptions/CommentSubscriptionService.cfc b/modules/contentbox/models/subscriptions/CommentSubscriptionService.cfc index 8f8f98ff0..c7544c64e 100644 --- a/modules/contentbox/models/subscriptions/CommentSubscriptionService.cfc +++ b/modules/contentbox/models/subscriptions/CommentSubscriptionService.cfc @@ -7,8 +7,11 @@ */ component extends="SubscriptionService" singleton { - function init(){ - super.init( entityName = "cbCommentSubscription", useQueryCaching = true ); + function init() { + super.init( + entityName = "cbCommentSubscription", + useQueryCaching = true + ); return this; } @@ -19,20 +22,23 @@ component extends="SubscriptionService" singleton { * * @return Array of properties: title, relatedContent, subscriberCount or label, value */ - array function getGroupedSubscriptions( required numeric max = 0 ){ + array function getGroupedSubscriptions( required numeric max = 0 ) { if ( !arguments.max ) { - var sortOrder = "title ASC"; + var sortOrder = "title ASC"; var groupProperty = "content.title:title,relatedContent"; - var countAlias = "subscriberCount"; + var countAlias = "subscriberCount"; } else { - var sortOrder = "value DESC"; + var sortOrder = "value DESC"; var groupProperty = "content.title:label"; - var countAlias = "value"; + var countAlias = "value"; } return newCriteria() .joinTo( "relatedContent", "content" ) - .withProjections( groupProperty = groupProperty, count = "subscriber:#countAlias#" ) + .withProjections( + groupProperty = groupProperty, + count = "subscriber:#countAlias#" + ) .asStruct() .list( sortOrder = sortOrder, max = arguments.max ); } @@ -40,8 +46,8 @@ component extends="SubscriptionService" singleton { /** * Get the count of total comment subscriptions */ - public numeric function getGroupedSubscriptionCount(){ + public numeric function getGroupedSubscriptionCount() { return newCriteria().count(); } -} +} \ No newline at end of file diff --git a/modules/contentbox/models/subscriptions/Subscriber.cfc b/modules/contentbox/models/subscriptions/Subscriber.cfc index 13cb4623c..1dde3a5c4 100644 --- a/modules/contentbox/models/subscriptions/Subscriber.cfc +++ b/modules/contentbox/models/subscriptions/Subscriber.cfc @@ -13,63 +13,54 @@ component cachename ="cbSubscriber" cacheuse ="read-write" { - - /* ********************************************************************* - ** PROPERTIES - ********************************************************************* */ + /********************************************************************** + * ** PROPERTIES + **********************************************************************/ property - name ="subscriberID" - column ="subscriberID" + name="subscriberID" + column="subscriberID" fieldtype="id" generator="uuid" - length ="36" - ormtype ="string" - update ="false"; - + length="36" + ormtype="string" + update="false"; property - name ="subscriberEmail" - column ="subscriberEmail" + name="subscriberEmail" + column="subscriberEmail" notnull="true" - length ="255" - index ="idx_subscriberEmail"; - + length="255" + index="idx_subscriberEmail"; property - name ="subscriberToken" - column ="subscriberToken" + name="subscriberToken" + column="subscriberToken" ormtype="string" - length ="255" + length="255" notnull="true"; - - /* ********************************************************************* - ** RELATIONSHIPS - ********************************************************************* */ + /********************************************************************** + * ** RELATIONSHIPS + **********************************************************************/ property - name ="subscriptions" + name="subscriptions" singularName="subscription" - fieldtype ="one-to-many" - type ="array" - lazy ="true" - batchsize ="25" - orderby ="createdDate" - cfc ="contentbox.models.subscriptions.BaseSubscription" - fkcolumn ="FK_subscriberID" - inverse ="true" - cascade ="all-delete-orphan"; - - /* ********************************************************************* - ** PK + CONSTRAINTS - ********************************************************************* */ + fieldtype="one-to-many" + type="array" + lazy="true" + batchsize="25" + orderby="createdDate" + cfc="contentbox.models.subscriptions.BaseSubscription" + fkcolumn="FK_subscriberID" + inverse="true" + cascade="all-delete-orphan"; + /********************************************************************** + * ** PK + CONSTRAINTS + **********************************************************************/ this.pk = "subscriberID"; this.memento = { - defaultIncludes : [ - "subscriberEmail", - "subscriptionToken", - "subscriptionsSnapshot:subscriptions" - ], + defaultIncludes : [ "subscriberEmail", "subscriptionToken", "subscriptionsSnapshot:subscriptions"], defaultExcludes : [] }; @@ -78,11 +69,11 @@ component "subscriberEmail" : { required : true, size : "1..255", type : "email" } }; - /* ********************************************************************* - ** METHODS - ********************************************************************* */ + /********************************************************************** + * ** METHODS + **********************************************************************/ - function init(){ + function init() { super.init(); return this; } @@ -90,7 +81,7 @@ component /** * Utility method to get a snapshot of this object */ - struct function getInfoSnapshot(){ + struct function getInfoSnapshot() { if ( isLoaded() ) { return { "subscriberID" : getSubscriberID(), @@ -104,11 +95,13 @@ component /** * Build a snapshot of subscriptions this subscriber has */ - array function getSubscriptionsSnapshot(){ + array function getSubscriptionsSnapshot() { if ( hasSubscriptions() ) { - return getSubscriptions().map( function( thisItem ){ - return arguments.thisItem.getMemento( excludes = "subscriber" ); - } ); + return getSubscriptions().map( + function( thisItem ) { + return arguments.thisItem.getMemento( excludes = "subscriber" ); + } + ); } return {}; } @@ -116,7 +109,7 @@ component /** * Before insert create the user subscription token */ - public void function preInsert(){ + public void function preInsert() { super.preInsert(); if ( isNull( getSubscriberToken() ) ) { var tkn = getSubscriberEmail() & getCreatedDate(); @@ -127,7 +120,7 @@ component /** * Returns a slim representation of subscriptions by type */ - public struct function getSubscriptionsByContentType(){ + public struct function getSubscriptionsByContentType() { var subs = {}; for ( var subscription in getSubscriptions() ) { var contentType = subscription.getType(); @@ -145,4 +138,4 @@ component return subs; } -} +} \ No newline at end of file diff --git a/modules/contentbox/models/subscriptions/SubscriberService.cfc b/modules/contentbox/models/subscriptions/SubscriberService.cfc index 51f41517d..bd7981888 100644 --- a/modules/contentbox/models/subscriptions/SubscriberService.cfc +++ b/modules/contentbox/models/subscriptions/SubscriberService.cfc @@ -7,15 +7,15 @@ */ component extends="cborm.models.VirtualEntityService" singleton { - SubscriberService function init(){ + SubscriberService function init() { // init it super.init( entityName = "cbSubscriber", useQueryCaching = true ); return this; } - numeric function getUniqueSubscriberCount(){ + numeric function getUniqueSubscriberCount() { return newCriteria().withProjections( countDistinct = "email" ).count(); } -} +} \ No newline at end of file diff --git a/modules/contentbox/models/subscriptions/SubscriptionListener.cfc b/modules/contentbox/models/subscriptions/SubscriptionListener.cfc index 5945f4409..0e917715e 100644 --- a/modules/contentbox/models/subscriptions/SubscriptionListener.cfc +++ b/modules/contentbox/models/subscriptions/SubscriptionListener.cfc @@ -5,19 +5,21 @@ * --- * Comment Notification interceptor */ -component extends="coldbox.system.Interceptor" accessors="true" { - +component extends ="coldbox.system.Interceptor" accessors="true" { // DI property name="commentService" inject="id:commentService@contentbox"; property name="subscriberService" inject="id:subscriberService@contentbox"; - property name="commentSubscriptionService" inject="id:commentSubscriptionService@contentbox"; + property + name="commentSubscriptionService" + inject="id:commentSubscriptionService@contentbox"; + + function configure() { - function configure(){ } - public void function cbui_onCommentPost( required any event, required struct data ){ - var content = data.content; - var comment = data.comment; + public void function cbui_onCommentPost( required any event, required struct data ) { + var content = data.content; + var comment = data.comment; var subscribe = data.subscribe; var moderated = data.moderationResults.moderated; @@ -28,9 +30,9 @@ component extends="coldbox.system.Interceptor" accessors="true" { // if author has elected to subscribe to comments, do it if ( subscribe ) { - var criteria = { subscriberEmail : comment.getAuthorEmail() }; + var criteria = { subscriberEmail : comment.getAuthorEmail() }; var subscriber = variables.subscriberService.findWhere( criteria = criteria ); - var exists = false; + var exists = false; if ( isNull( subscriber ) ) { subscriber = variables.subscriberService.new( criteria ); @@ -52,7 +54,7 @@ component extends="coldbox.system.Interceptor" accessors="true" { } } - public void function cbadmin_onCommentStatusUpdate( required any event, required struct data ){ + public void function cbadmin_onCommentStatusUpdate( required any event, required struct data ) { var commentIds = listToArray( arguments.data.commentID ); for ( var commentId in commentIds ) { var comment = commentService.get( commentId ); @@ -62,4 +64,4 @@ component extends="coldbox.system.Interceptor" accessors="true" { } } -} +} \ No newline at end of file diff --git a/modules/contentbox/models/subscriptions/SubscriptionService.cfc b/modules/contentbox/models/subscriptions/SubscriptionService.cfc index 1c243c3e7..0967663c3 100644 --- a/modules/contentbox/models/subscriptions/SubscriptionService.cfc +++ b/modules/contentbox/models/subscriptions/SubscriptionService.cfc @@ -7,9 +7,9 @@ */ component extends="cborm.models.VirtualEntityService" singleton { - SubscriptionService function init( entityName = "cbSubscription" ){ + SubscriptionService function init( entityName = "cbSubscription" ) { super.init( entityName = arguments.entityName, useQueryCaching = true ); return this; } -} +} \ No newline at end of file diff --git a/modules/contentbox/models/system/CBHelper.cfc b/modules/contentbox/models/system/CBHelper.cfc index 58235a45b..88ce3924b 100644 --- a/modules/contentbox/models/system/CBHelper.cfc +++ b/modules/contentbox/models/system/CBHelper.cfc @@ -6,7 +6,6 @@ * This is the ContentBox UI helper class that is injected by the CBRequest interceptor */ component accessors="true" singleton threadSafe { - // DI property name="categoryService" inject="categoryService@contentbox"; property name="settingService" inject="settingService@contentbox"; @@ -33,7 +32,7 @@ component accessors="true" singleton threadSafe { /** * Constructor */ - function init(){ + function init() { return this; } @@ -44,7 +43,7 @@ component accessors="true" singleton threadSafe { * * @return coldbox.system.web.context.RequestContext */ - function getRequestContext(){ + function getRequestContext() { return variables.requestService.getContext(); } @@ -53,14 +52,14 @@ component accessors="true" singleton threadSafe { * * @private The boolean bit that says give me the RC by default or true for the private collection (PRC) */ - struct function getRequestCollection( boolean private = false ){ + struct function getRequestCollection( boolean private = false ) { return getRequestContext().getCollection( private = arguments.private ); } /** * Get the private request collection */ - struct function getPrivateRequestCollection(){ + struct function getPrivateRequestCollection() { return getRequestContext().getCollection( private = true ); } @@ -69,7 +68,7 @@ component accessors="true" singleton threadSafe { * * @module The module to retrieve the configuration settings from */ - struct function getModuleSettings( required module ){ + struct function getModuleSettings( required module ) { return getModuleConfig( arguments.module ).settings; } @@ -78,15 +77,15 @@ component accessors="true" singleton threadSafe { * * @module The module to retrieve the configuration structure from */ - struct function getModuleConfig( required module ){ + struct function getModuleConfig( required module ) { var mConfig = controller.getSetting( "modules" ); if ( structKeyExists( mConfig, arguments.module ) ) { return mConfig[ arguments.module ]; } throw( - message: "The module you passed #arguments.module# is invalid.", - detail : "The loaded modules are #structKeyList( mConfig )#", - type : "FrameworkSuperType.InvalidModuleException" + message = "The module you passed #arguments.module# is invalid.", + detail = "The loaded modules are #structKeyList( mConfig )#", + type = "FrameworkSuperType.InvalidModuleException" ); } @@ -99,7 +98,7 @@ component accessors="true" singleton threadSafe { * @defaultValue The default value to return if not found * @throw InvalidSettingException */ - any function setting( required key, defaultValue ){ + any function setting( required key, defaultValue ) { var prc = getPrivateRequestCollection(); // See if all the settings are loaded, else lazy load them @@ -117,9 +116,9 @@ component accessors="true" singleton threadSafe { } // else throw exception throw( - message: "Setting requested: #arguments.key# not found", - detail : "Settings keys are #structKeyList( prc.cbSettings )#", - type : "InvalidSettingException" + message = "Setting requested: #arguments.key# not found", + detail = "Settings keys are #structKeyList( prc.cbSettings )#", + type = "InvalidSettingException" ); } @@ -131,7 +130,7 @@ component accessors="true" singleton threadSafe { * * @throws InvalidSettingException */ - any function siteSetting( required key, defaultValue ){ + any function siteSetting( required key, defaultValue ) { var prc = getPrivateRequestCollection(); // See if all the settings are loaded, else lazy load them @@ -149,44 +148,44 @@ component accessors="true" singleton threadSafe { } // else throw exception throw( - message: "Site Setting requested: #arguments.key# not found", - detail : "Site Settings keys are #structKeyList( prc.cbSiteSettings )#", - type : "InvalidSettingException" + message = "Site Setting requested: #arguments.key# not found", + detail = "Site Settings keys are #structKeyList( prc.cbSiteSettings )#", + type = "InvalidSettingException" ); } /** * The ContentBox version */ - function getContentBoxVersion(){ + function getContentBoxVersion() { return getModuleConfig( "contentbox" ).version; } /** * The ContentBox Codename */ - function getContentBoxCodeName(){ + function getContentBoxCodeName() { return getModuleSettings( "contentbox" ).codename; } /** * The ContentBox codename Uri */ - function getContentBoxCodeNameURL(){ + function getContentBoxCodeNameURL() { return getModuleSettings( "contentbox" ).codenameLink; } /** * Get the blog entry point as specified in the global settings */ - function getBlogEntryPoint(){ + function getBlogEntryPoint() { return setting( "cb_site_blog_entrypoint", "blog" ); } /** * Get the maintenance message from the ContentBox settings in rendering condition. */ - function getMaintenanceMessage(){ + function getMaintenanceMessage() { return variables.markdown.toHTML( setting( "cb_site_maintenance_message", "" ) ); } @@ -201,8 +200,8 @@ component accessors="true" singleton threadSafe { required slug, defaultValue = "", string siteID = site().getsiteID() - ){ - var content = variables.contentStoreService.findBySlug( slug: arguments.slug, siteID: arguments.siteID ); + ) { + var content = variables.contentStoreService.findBySlug( slug = arguments.slug, siteID = arguments.siteID ); // Render if the object is loaded return ( !content.isLoaded() ? arguments.defaultValue : content.renderContent() ); @@ -215,12 +214,12 @@ component accessors="true" singleton threadSafe { * * @return The content object or a new empty content object */ - function contentStoreObject( required slug, string siteID = site().getsiteID() ){ + function contentStoreObject( required slug, string siteID = site().getsiteID() ) { return contentStoreService.findBySlug( - slug : arguments.slug, - showUnpublished: true, - siteID : arguments.siteID - ); + slug = arguments.slug, + showUnpublished = true, + siteID = arguments.siteID + ); } /************************************** root and pathing methods *********************************************/ @@ -228,28 +227,28 @@ component accessors="true" singleton threadSafe { /** * Verifies if the admin module is loaded */ - function isAdminLoaded(){ + function isAdminLoaded() { return structKeyExists( controller.getSetting( "modules" ), "contentbox-admin" ); } /** * Get the admin site root location using the configured module's entry point */ - function adminRoot(){ + function adminRoot() { return getPrivateRequestCollection().cbAdminEntryPoint; } /** * Get the name of the current set and active theme */ - function themeName(){ + function themeName() { return getPrivateRequestCollection().cbTheme; } /** * Get the theme record of the current set and active theme */ - function themeRecord(){ + function themeRecord() { return getPrivateRequestCollection().cbThemeRecord; } @@ -259,7 +258,7 @@ component accessors="true" singleton threadSafe { * @key The name of the theme setting * @defaultValue The default value if the layout setting does not exist */ - function themeSetting( required key, defaultValue ){ + function themeSetting( required key, defaultValue ) { arguments.key = "cb_theme_#themeName()#_#arguments.key#"; return siteSetting( argumentCollection = arguments ); } @@ -275,14 +274,17 @@ component accessors="true" singleton threadSafe { * * @siteID The site id to get the root from, by default we use the current site you are on */ - function site( string siteID = "" ){ + function site( string siteID = "" ) { // Verify incoming override/lookup if ( len( arguments.siteID ) ) { // Do a request storage lookup cache for optmizations when requesting the same site // over and over again in the same request. - return variables.requestStorage.getOrSet( "cbhelper-request-site-#arguments.siteID#", function(){ - return variables.siteService.getOrFail( siteID ); - } ); + return variables.requestStorage.getOrSet( + "cbhelper-request-site-#arguments.siteID#", + function() { + return variables.siteService.getOrFail( siteID ); + } + ); } // Verify PRC @@ -298,7 +300,7 @@ component accessors="true" singleton threadSafe { /** * Get the location of your currently defined theme in the application, great for assets, cfincludes, etc */ - function themeRoot(){ + function themeRoot() { return getPrivateRequestCollection().cbThemeRoot; } @@ -307,7 +309,7 @@ component accessors="true" singleton threadSafe { * * @siteID The site id to get the root from, by default we use the current site you are on */ - function siteRoot( string siteID = "" ){ + function siteRoot( string siteID = "" ) { // Return the appropriate site Uri return this.site( arguments.siteID ).getSiteRoot() & getModuleConfig( "contentbox-ui" ).entryPoint; } @@ -317,7 +319,7 @@ component accessors="true" singleton threadSafe { * * @siteID The site id to get the root from, by default we use the current site you are on */ - function siteBaseURL( string siteID = "" ){ + function siteBaseURL( string siteID = "" ) { return reReplaceNoCase( this.siteRoot( arguments.siteID ), "index.cfm\/?", @@ -328,42 +330,42 @@ component accessors="true" singleton threadSafe { /** * Retrieve the site name */ - function siteName(){ + function siteName() { return site().getName(); } /** * Retrieve the site tagline */ - function siteTagLine(){ + function siteTagLine() { return site().getTagLine(); } /** * Retrieve the site description */ - function siteDescription(){ + function siteDescription() { return site().getDescription(); } /** * Retrieve the site keywords */ - function siteKeywords(){ + function siteKeywords() { return site().getKeywords(); } /** * Retrieve the site administrator email */ - function siteEmail(){ + function siteEmail() { return site().getNotificationEmails(); } /** * Retrieve the site outgoing email */ - function siteOutgoingEmail(){ + function siteOutgoingEmail() { return setting( "cb_site_outgoingEmail" ); } @@ -372,9 +374,9 @@ component accessors="true" singleton threadSafe { * * @content The entry or page content to validate comments also with */ - boolean function isCommentsEnabled( content ){ + boolean function isCommentsEnabled( content ) { if ( !isNull( arguments.content ) ) { - return ( arguments.content.getAllowComments() AND siteSetting( "cb_comments_enabled" ) ); + return ( arguments.content.getAllowComments() && siteSetting( "cb_comments_enabled" ) ); } return ( siteSetting( "cb_comments_enabled" ) ); } @@ -382,14 +384,14 @@ component accessors="true" singleton threadSafe { /** * Determines if a comment form error has ocurred */ - boolean function isCommentFormError(){ + boolean function isCommentFormError() { return getFlash().exists( "commentErrors" ); } /** * Determine if you are in printing or exporting format */ - boolean function isPrintFormat(){ + boolean function isPrintFormat() { var currentFormat = getRequestContext().getValue( "format", "contentbox" ); return ( listFindNoCase( "contentbox,html", currentFormat ) ? false : true ); } @@ -399,7 +401,7 @@ component accessors="true" singleton threadSafe { * * @return The errors array or an empty array */ - array function getCommentErrors(){ + array function getCommentErrors() { return getFlash().get( "commentErrors", [] ); } @@ -420,10 +422,10 @@ component accessors="true" singleton threadSafe { string title, string description, string keywords - ){ + ) { var event = getRequestContext(); - var prc = getPrivateRequestCollection(); - var rc = getRequestCollection(); + var prc = getPrivateRequestCollection(); + var rc = getRequestCollection(); // Request Metadata if ( structKeyExists( arguments, "title" ) ) { @@ -437,7 +439,7 @@ component accessors="true" singleton threadSafe { } // store UI module root - prc.cbRoot = getContextRoot() & event.getModuleRoot( "contentbox" ); + prc.cbRoot = getContextRoot() & event.getModuleRoot( "contentbox" ); // store module entry point prc.cbEntryPoint = getModuleConfig( "contentbox-ui" ).entryPoint; // store site entry point @@ -447,29 +449,29 @@ component accessors="true" singleton threadSafe { prc.cbAdminEntryPoint = ""; } // Place site on request - prc.oCurrentSite = variables.siteService.discoverSite(); + prc.oCurrentSite = variables.siteService.discoverSite(); // Place global cb options on scope - prc.cbSettings = variables.settingService.getAllSettings(); + prc.cbSettings = variables.settingService.getAllSettings(); prc.cbSiteSettings = variables.settingService.getAllSiteSettings( prc.oCurrentSite.getSlug() ); // Place the default layout on scope - prc.cbTheme = prc.oCurrentSite.getActiveTheme(); - prc.cbThemeRecord = variables.themeService.getThemeRecord( prc.cbTheme ); + prc.cbTheme = prc.oCurrentSite.getActiveTheme(); + prc.cbThemeRecord = variables.themeService.getThemeRecord( prc.cbTheme ); // Place layout root location - prc.cbthemeRoot = prc.cbThemeRecord.includePath; + prc.cbthemeRoot = prc.cbThemeRecord.includePath; // Place widgets root location - prc.cbWidgetRoot = prc.cbRoot & "/widgets"; + prc.cbWidgetRoot = prc.cbRoot & "/widgets"; // announce event this.event( "cbui_preRequest" ); // Do we set a layout for them already? if ( structKeyExists( arguments, "layout" ) ) { - event.setLayout( name = "#prc.cbThemeRecord.includePath#/layouts/" & arguments.layout ); + event.setLayout( name = "#prc.cbThemeRecord.includePath#/layouts/#arguments.layout#" ); } /************************************** FORCE SITE WIDE SSL *********************************************/ - if ( prc.oCurrentSite.getIsSSL() and !event.isSSL() ) { + if ( prc.oCurrentSite.getIsSSL() && !event.isSSL() ) { controller.relocate( event = event.getCurrentRoutedURL(), ssl = true ); } @@ -483,54 +485,56 @@ component accessors="true" singleton threadSafe { } // Determine if you have a category filter - boolean function categoryFilterExists(){ + boolean function categoryFilterExists() { var rc = getRequestCollection(); - return ( structKeyExists( rc, "category" ) AND len( rc.category ) ); + return ( structKeyExists( rc, "category" ) && len( rc.category ) ); } + // Get Category Filter - function getCategoryFilter(){ + function getCategoryFilter() { return getRequestContext().getValue( "category", "" ); } // Get Year Filter - function getYearFilter(){ + function getYearFilter() { return getRequestContext().getValue( "year", "0" ); } + // Get Month Filter - function getMonthFilter(){ + function getMonthFilter() { return getRequestContext().getValue( "month", "0" ); } + // Get Day Filter - function getDayFilter(){ + function getDayFilter() { return getRequestContext().getValue( "day", "0" ); } // Determine if you are in the blog - boolean function isBlogView(){ - if ( isIndexView() OR isEntryView() OR isArchivesView() ) { + boolean function isBlogView() { + if ( isIndexView() || isEntryView() || isArchivesView() ) { return true; } return false; } + // Determine if you are in the archives view - boolean function isArchivesView(){ + boolean function isArchivesView() { var event = getRequestContext(); - return ( event.getCurrentEvent() eq "contentbox-ui:blog.archives" ); + return ( event.getCurrentEvent() EQ "contentbox-ui:blog.archives" ); } + // Determine if you are in the index view - boolean function isIndexView(){ + boolean function isIndexView() { var event = getRequestContext(); - return ( event.getCurrentEvent() eq "contentbox-ui:blog.index" ); + return ( event.getCurrentEvent() EQ "contentbox-ui:blog.index" ); } + // Determine if you are in the entry view - boolean function isEntryView(){ + boolean function isEntryView() { var event = getRequestContext(); return ( - // If in static export, then mark as yes - event.getPrivateValue( "staticExport", false ) - OR - // In executing view - event.getCurrentEvent() eq "contentbox-ui:blog.entry" + event.getPrivateValue( "staticExport", false ) || event.getCurrentEvent() EQ "contentbox-ui:blog.entry" ); } @@ -539,20 +543,18 @@ component accessors="true" singleton threadSafe { * * @page Optional page slug to determine if you are in that page or not. */ - boolean function isPageView( page = "" ){ + boolean function isPageView( page = "" ) { var event = getRequestContext(); if ( - // If in static export, then mark as yes - event.getPrivateValue( "staticExport", false ) OR - ( - // Check if in page event - findNoCase( "contentbox-ui:page", event.getCurrentEvent() ) AND - // And page Exists - event.valueExists( "page", true ) - ) + event.getPrivateValue( "staticExport", false ) || + ( + findNoCase( "contentbox-ui:page", event.getCurrentEvent() ) && + event// And page Exists + .valueExists( "page", true ) + ) ) { // slug check - if ( len( arguments.page ) AND getCurrentPage().getSlug() eq arguments.page ) { + if ( len( arguments.page ) && getCurrentPage().getSlug() EQ arguments.page ) { return true; } else if ( !len( arguments.page ) ) { return true; @@ -565,7 +567,7 @@ component accessors="true" singleton threadSafe { /** * Determine if you're in a "preview" mode or not */ - boolean function isPreview(){ + boolean function isPreview() { return reFindNoCase( "contentbox-ui:.*preview", getRequestContext().getCurrentEvent() ) ? true : false; } @@ -576,7 +578,7 @@ component accessors="true" singleton threadSafe { * * @throws ContentBox.CBHelper.InvalidEntriesContext */ - any function getCurrentEntries(){ + any function getCurrentEntries() { var prc = getRequestCollection( private = true ); if ( structKeyExists( prc, "entries" ) ) { return prc.entries; @@ -593,7 +595,7 @@ component accessors="true" singleton threadSafe { * * @throws ContentBox.CBHelper.InvalidEntriesContext */ - numeric function getCurrentEntriesCount(){ + numeric function getCurrentEntriesCount() { var prc = getRequestCollection( private = true ); if ( structKeyExists( prc, "entriesCount" ) ) { return prc.entriesCount; @@ -612,11 +614,11 @@ component accessors="true" singleton threadSafe { * * @return array of category */ - any function getCurrentCategories( isPublic = true ){ + any function getCurrentCategories( isPublic = true ) { return variables.categoryService.search( - isPublic: ( isNull( arguments.isPublic ) ? javacast( "null", "" ) : arguments.isPublic ), - siteId : site().getSiteId() - ).categories; + isPublic = ( isNull( arguments.isPublic ) ? javacast( "null", "" ) : arguments.isPublic ), + siteId = site().getSiteId() + ).categories; } /** @@ -626,7 +628,7 @@ component accessors="true" singleton threadSafe { * * @throws ContentBox.CBHelper.InvalidEntryContext */ - any function getCurrentEntry(){ + any function getCurrentEntry() { var prc = getRequestCollection( private = true ); if ( structKeyExists( prc, "entry" ) ) { return prc.entry; @@ -645,7 +647,7 @@ component accessors="true" singleton threadSafe { * * @throws ContentBox.CBHelper.InvalidPageContext */ - any function getCurrentPage(){ + any function getCurrentPage() { var prc = getRequestCollection( private = true ); if ( structKeyExists( prc, "page" ) ) { return prc.page; @@ -664,7 +666,7 @@ component accessors="true" singleton threadSafe { * * @throws ContentBox.CBHelper.InvalidCommentContext */ - any function getCurrentComments(){ + any function getCurrentComments() { var prc = getRequestCollection( private = true ); if ( structKeyExists( prc, "comments" ) ) { return prc.comments; @@ -685,7 +687,7 @@ component accessors="true" singleton threadSafe { * * @throws ContentBox.CBHelper.InvalidCommentContext */ - numeric function getCurrentCommentsCount(){ + numeric function getCurrentCommentsCount() { var prc = getRequestCollection( private = true ); if ( structKeyExists( prc, "commentsCount" ) ) { return prc.commentsCount; @@ -700,12 +702,12 @@ component accessors="true" singleton threadSafe { /** * Get the missing page, if any, usually used in a page not found context */ - any function getMissingPage(){ + any function getMissingPage() { return getRequestContext().getValue( - name = "missingPage", - private = "true", - defaultValue = "" - ); + name = "missingPage", + private = "true", + defaultValue = "" + ); } /** @@ -713,14 +715,14 @@ component accessors="true" singleton threadSafe { * * @siteID The site id to get it from */ - any function getHomePage( string siteID = "" ){ + any function getHomePage( string siteID = "" ) { return site( arguments.siteID ).getHomepage(); } /** * Checks if the currently viewed page is the homepage. */ - boolean function isHomePage(){ + boolean function isHomePage() { var prc = getPrivateRequestCollection(); // Check if page exists if ( !structKeyExists( prc, "page" ) ) { @@ -734,7 +736,7 @@ component accessors="true" singleton threadSafe { * * @return array */ - any function getCurrentRelatedContent(){ + any function getCurrentRelatedContent() { var relatedContent = []; if ( isPageView() && getCurrentPage().hasRelatedContent() ) { relatedContent = getCurrentPage().getRelatedContent(); @@ -747,7 +749,7 @@ component accessors="true" singleton threadSafe { /** * Get the current page's or blog entries custom fields as a struct */ - struct function getCurrentCustomFields(){ + struct function getCurrentCustomFields() { if ( isPageView() ) { return getCurrentPage().getCustomFieldsAsStruct(); } else { @@ -761,7 +763,7 @@ component accessors="true" singleton threadSafe { * @key The custom field key * @defaultValue The default value to return if not found */ - any function getCustomField( required key, defaultValue ){ + any function getCustomField( required key, defaultValue ) { var fields = getCurrentCustomFields(); if ( structKeyExists( fields, arguments.key ) ) { return fields[ arguments.key ]; @@ -781,7 +783,7 @@ component accessors="true" singleton threadSafe { /** * Check the Meta struct exists in the PRC */ - function checkMetaStruct(){ + function checkMetaStruct() { var prc = getPrivateRequestCollection(); if ( !structKeyExists( prc, "meta" ) ) { prc.meta = {}; @@ -793,7 +795,7 @@ component accessors="true" singleton threadSafe { * * @title - The new title */ - function setMetaTitle( required string title ){ + function setMetaTitle( required string title ) { var prc = getPrivateRequestCollection(); checkMetaStruct(); prc.meta.title = arguments.title; @@ -802,7 +804,7 @@ component accessors="true" singleton threadSafe { /** * Get the Meta Title for the request */ - function getMetaTitle(){ + function getMetaTitle() { var prc = getPrivateRequestCollection(); checkMetaStruct(); if ( structKeyExists( prc.meta, "title" ) ) { @@ -817,7 +819,7 @@ component accessors="true" singleton threadSafe { * * @description - The new Description */ - function setMetaDescription( required string description ){ + function setMetaDescription( required string description ) { var prc = getPrivateRequestCollection(); checkMetaStruct(); prc.meta.description = arguments.description; @@ -826,7 +828,7 @@ component accessors="true" singleton threadSafe { /** * Get the Meta description for the request */ - function getMetaDescription(){ + function getMetaDescription() { var prc = getPrivateRequestCollection(); checkMetaStruct(); if ( structKeyExists( prc.meta, "description" ) ) { @@ -841,7 +843,7 @@ component accessors="true" singleton threadSafe { * * @keywords - The new Keywords */ - function setMetaKeywords( required string keywords ){ + function setMetaKeywords( required string keywords ) { var prc = getPrivateRequestCollection(); checkMetaStruct(); prc.meta.keywords = arguments.keywords; @@ -850,7 +852,7 @@ component accessors="true" singleton threadSafe { /** * Get the Meta keywords for the request */ - function getMetaKeywords(){ + function getMetaKeywords() { var prc = getPrivateRequestCollection(); checkMetaStruct(); if ( structKeyExists( prc.meta, "keywords" ) ) { @@ -863,9 +865,9 @@ component accessors="true" singleton threadSafe { /** * Get the current content metadata title according to SEO Discovery Rules */ - function getContentTitle(){ + function getContentTitle() { var oCurrentContent = ""; - var metaTitle = getMetaTitle(); + var metaTitle = getMetaTitle(); // If Meta Title is set Manually, return it if ( len( metaTitle ) ) { @@ -896,7 +898,7 @@ component accessors="true" singleton threadSafe { /** * Get the current content metadata description according to SEO discovery rules */ - function getContentDescription(){ + function getContentDescription() { var oCurrentContent = ""; var metaDescription = getMetaDescription(); @@ -917,9 +919,9 @@ component accessors="true" singleton threadSafe { // - Site Description if ( isHomePage() ) { metaDescription = oCurrentContent.getHTMLDescription(); - return len( metaDescription ) ? encodeForHTMLAttribute( metaDescription ) : encodeForHTMLAttribute( - siteDescription() - ); + return len( metaDescription ) + ? encodeForHTMLAttribute( metaDescription ) + : encodeForHTMLAttribute( siteDescription() ); } // Page/Blog Rules @@ -947,9 +949,9 @@ component accessors="true" singleton threadSafe { /** * Get the current content metadata keywords according to SEO discovery rules */ - function getContentKeywords(){ + function getContentKeywords() { var oCurrentContent = ""; - var metaKeywords = getMetaKeywords(); + var metaKeywords = getMetaKeywords(); // If Meta Keywords is set Manually, return it if ( len( metaKeywords ) ) { @@ -968,13 +970,13 @@ component accessors="true" singleton threadSafe { // - Site if ( isHomePage() ) { metaKeywords = stripWhitespace( oCurrentContent.getHTMLKeywords() ); - return len( metaKeywords ) ? encodeForHTMLAttribute( metaKeywords ) : encodeForHTMLAttribute( - siteKeywords() - ); + return len( metaKeywords ) + ? encodeForHTMLAttribute( metaKeywords ) + : encodeForHTMLAttribute( siteKeywords() ); } // in context view or global - if ( isObject( oCurrentContent ) AND len( oCurrentContent.getHTMLKeywords() ) ) { + if ( isObject( oCurrentContent ) && len( oCurrentContent.getHTMLKeywords() ) ) { return encodeForHTMLAttribute( stripWhitespace( oCurrentContent.getHTMLKeywords() ) ); } @@ -982,13 +984,12 @@ component accessors="true" singleton threadSafe { return encodeForHTMLAttribute( siteKeywords() ); } - /** * Set the Meta Canonical URL for the request * * @url - The new url */ - function setMetaURL( required string url ){ + function setMetaURL( required string url ) { var prc = getPrivateRequestCollection(); checkMetaStruct(); prc.meta.url = arguments.url; @@ -997,7 +998,7 @@ component accessors="true" singleton threadSafe { /** * Get the Meta Canonical URL for the request */ - function getMetaURL(){ + function getMetaURL() { var prc = getPrivateRequestCollection(); checkMetaStruct(); if ( structKeyExists( prc.meta, "url" ) ) { @@ -1007,14 +1008,13 @@ component accessors="true" singleton threadSafe { } } - /** * Get the Canonical URL based on content type */ - function getContentURL(){ - var oCurrentContent = ""; + function getContentURL() { + var oCurrentContent = ""; var oCurrentEntryPoint = ""; - var metaURL = getMetaURL(); + var metaURL = getMetaURL(); if ( len( metaURL ) ) { return metaURL; @@ -1022,15 +1022,15 @@ component accessors="true" singleton threadSafe { // Check if in page view or entry view if ( isPageView() ) { - oCurrentContent = getCurrentPage(); + oCurrentContent = getCurrentPage(); oCurrentEntryPoint = ""; } else if ( isEntryView() ) { - oCurrentContent = getCurrentEntry(); + oCurrentContent = getCurrentEntry(); oCurrentEntryPoint = setting( "cb_site_blog_entrypoint" ) & "/"; } // in context view or global - if ( isObject( oCurrentContent ) AND len( oCurrentContent.getslug() ) ) { + if ( isObject( oCurrentContent ) && len( oCurrentContent.getslug() ) ) { return siteBaseURL() & oCurrentEntryPoint & oCurrentContent.getslug(); } @@ -1042,7 +1042,7 @@ component accessors="true" singleton threadSafe { * * @imageURL - The new imageURL */ - function setMetaImageURL( required string imageURL ){ + function setMetaImageURL( required string imageURL ) { var prc = getPrivateRequestCollection(); checkMetaStruct(); prc.meta.imageURL = arguments.imageURL; @@ -1051,7 +1051,7 @@ component accessors="true" singleton threadSafe { /** * Get the Meta ImageURL for the request */ - function getMetaImageURL(){ + function getMetaImageURL() { var prc = getPrivateRequestCollection(); checkMetaStruct(); if ( structKeyExists( prc.meta, "imageURL" ) ) { @@ -1064,9 +1064,9 @@ component accessors="true" singleton threadSafe { /** * Get the Content Image URL based on content type */ - function getContentImageURL(){ + function getContentImageURL() { var oCurrentContent = ""; - var metaImageUrl = getMetaImageURL(); + var metaImageUrl = getMetaImageURL(); if ( len( metaImageURL ) ) { return metaImageURL; @@ -1080,7 +1080,7 @@ component accessors="true" singleton threadSafe { } // in context view or global - if ( isObject( oCurrentContent ) AND len( oCurrentContent.getFeaturedImageURL() ) ) { + if ( isObject( oCurrentContent ) && len( oCurrentContent.getFeaturedImageURL() ) ) { return siteBaseURL() & oCurrentContent.getFeaturedImageURL(); } @@ -1092,7 +1092,7 @@ component accessors="true" singleton threadSafe { * * @OGType - The new OGType */ - function setMetaOGType( required string OGType ){ + function setMetaOGType( required string OGType ) { var prc = getPrivateRequestCollection(); checkMetaStruct(); prc.meta.OGType = arguments.OGType; @@ -1101,7 +1101,7 @@ component accessors="true" singleton threadSafe { /** * Get the Meta OGType for the request */ - function getMetaOGType(){ + function getMetaOGType() { var prc = getPrivateRequestCollection(); checkMetaStruct(); if ( structKeyExists( prc.meta, "OGType" ) ) { @@ -1114,7 +1114,7 @@ component accessors="true" singleton threadSafe { /** * Get the Content Open Graph Type based on content type */ - function getContentOGType(){ + function getContentOGType() { var metaOGType = getMetaOGType(); if ( len( metaOGType ) ) { @@ -1136,20 +1136,30 @@ component accessors="true" singleton threadSafe { * More information: https://developers.facebook.com/docs/reference/opengraph * Best Practices: https://developers.facebook.com/docs/sharing/best-practices */ - function getOpenGraphMeta(){ - var content = ""; + function getOpenGraphMeta() { + var content = ""; // cfformat-ignore-start savecontent variable="content" { - writeOutput( "#chr( 10 )#" ); - writeOutput( "#chr( 10 )#" ); - writeOutput( "#chr( 10 )#" ); + writeOutput( + "#chr( 10 )#" + ); + writeOutput( + "#chr( 10 )#" + ); + writeOutput( + "#chr( 10 )#" + ); if ( len( getContentURL() ) ) { - writeOutput( "#chr( 10 )#" ); + writeOutput( + "#chr( 10 )#" + ); } if ( len( getContentImageURL() ) ) { - writeOutput( "#chr( 10 )#" ); + writeOutput( + "#chr( 10 )#" + ); } } // cfformat-ignore-end @@ -1157,28 +1167,27 @@ component accessors="true" singleton threadSafe { return content; } - /************************************** search *********************************************/ // Determine if you are in the search view - boolean function isSearchView(){ + boolean function isSearchView() { var event = getRequestContext(); - return ( event.getCurrentEvent() eq "contentbox-ui:page.search" ); + return ( event.getCurrentEvent() EQ "contentbox-ui:page.search" ); } /** * quickSearchForm will build a standard ContentBox Content Search Form according to the SearchForm widget */ - function quickSearchForm(){ + function quickSearchForm() { return widget( "SearchForm", { type : "content" } ); } /** * Render out paging for search content */ - function quickSearchPaging(){ + function quickSearchPaging() { var prc = getRequestCollection( private = true ); - if ( NOT structKeyExists( prc, "oPaging" ) ) { + if ( !structKeyExists( prc, "oPaging" ) ) { throw( message = "Paging object is not in the collection", detail = "This probably means you are trying to use the paging outside of the search results page and that is a No No", @@ -1186,10 +1195,10 @@ component accessors="true" singleton threadSafe { ); } return prc.oPaging.renderit( - foundRows = getSearchResults().getTotal(), - link = prc.pagingLink, - pagingMaxRows = setting( "cb_search_maxResults" ) - ); + foundRows = getSearchResults().getTotal(), + link = prc.pagingLink, + pagingMaxRows = setting( "cb_search_maxResults" ) + ); } /** @@ -1197,46 +1206,46 @@ component accessors="true" singleton threadSafe { * * @return contentbox.models.search.SearchResults */ - function getSearchResults(){ + function getSearchResults() { var event = getRequestContext(); return event.getValue( - name = "searchResults", - private = "true", - default = "" - ); + name = "searchResults", + private = "true", + default = "" + ); } /** * get the curent search results HTML content */ - any function getSearchResultsContent(){ + any function getSearchResultsContent() { var event = getRequestContext(); return event.getValue( - name = "searchResultsContent", - private = "true", - default = "" - ); + name = "searchResultsContent", + private = "true", + default = "" + ); } /** * Determine if you have a search term */ - boolean function searchTermExists(){ + boolean function searchTermExists() { var rc = getRequestCollection(); - return ( structKeyExists( rc, "q" ) AND len( rc.q ) ); + return ( structKeyExists( rc, "q" ) && len( rc.q ) ); } /** * Get Search Term used */ - function getSearchTerm(){ + function getSearchTerm() { return getRequestContext().getValue( "q", "" ); } /************************************** events *********************************************/ // event announcements, funky for whitespace reasons - function event( required state, struct data = structNew() ) output="true"{ + function event( required state, struct data = structNew() ) output="true" { controller.getInterceptorService().announce( arguments.state, arguments.data ); } @@ -1255,16 +1264,16 @@ component accessors="true" singleton threadSafe { required string to, queryString = "", boolean ssl = getRequestContext().isSSL() - ){ + ) { // Remove by 6.x if ( !isNull( arguments.linkTo ) ) { arguments.to = arguments.linkTo; } return getRequestContext().buildLink( - to : adminRoot() & ".module.#arguments.module#.#arguments.to#", - queryString: arguments.queryString, - ssl : arguments.ssl - ); + to = adminRoot() & ".module.#arguments.module#.#arguments.to#", + queryString = arguments.queryString, + ssl = arguments.ssl + ); } /** @@ -1277,13 +1286,12 @@ component accessors="true" singleton threadSafe { string URI, string queryString = "", persist, - struct persistStruct - boolean addToken, + struct persistStruct boolean addToken, boolean ssl, baseURL, boolean postProcessExempt, numeric statusCode - ){ + ) { return moduleRelocate( argumentCollection = arguments ); } @@ -1310,13 +1318,12 @@ component accessors="true" singleton threadSafe { string URI, string queryString = "", persist, - struct persistStruct - boolean addToken, + struct persistStruct boolean addToken, boolean ssl, baseURL, boolean postProcessExempt, numeric statusCode - ){ + ) { arguments.event = adminRoot() & ".module.#arguments.module#.#arguments.event#"; return controller.relocate( argumentCollection = arguments ); } @@ -1327,7 +1334,7 @@ component accessors="true" singleton threadSafe { * @event An optional event to link to * @ssl Use SSL or not, defaults to event context. */ - function linkAdmin( event = "", boolean ssl = getRequestContext().isSSL() ){ + function linkAdmin( event = "", boolean ssl = getRequestContext().isSSL() ) { return getRequestContext().buildLink( to = adminRoot() & ".#arguments.event#", ssl = arguments.ssl ); } @@ -1336,7 +1343,7 @@ component accessors="true" singleton threadSafe { * * @ssl Use SSL or not, defaults to event context. */ - function linkAdminLogout( boolean ssl = getRequestContext().isSSL() ){ + function linkAdminLogout( boolean ssl = getRequestContext().isSSL() ) { return getRequestContext().buildLink( to = adminRoot() & "/security/doLogout", ssl = arguments.ssl ); } @@ -1345,7 +1352,7 @@ component accessors="true" singleton threadSafe { * * @ssl Use SSL or not, defaults to event context. */ - function linkAdminLogin( boolean ssl = getRequestContext().isSSL() ){ + function linkAdminLogin( boolean ssl = getRequestContext().isSSL() ) { return getRequestContext().buildLink( to = adminRoot() & "/security/login", ssl = arguments.ssl ); } @@ -1354,7 +1361,7 @@ component accessors="true" singleton threadSafe { * * @siteID The site id to link to or use the default */ - function linkHome( string siteID = "" ){ + function linkHome( string siteID = "" ) { return siteRoot( arguments.siteID ); } @@ -1363,24 +1370,17 @@ component accessors="true" singleton threadSafe { * * @siteID The site id to link to or use the default */ - function linkBlog( string siteID = "" ){ + function linkBlog( string siteID = "" ) { return "#siteRoot( arguments.siteID )#/#getBlogEntryPoint()#"; } /** * Create a link to the current page/entry you are on */ - function linkSelf(){ + function linkSelf() { return "#siteRoot()##sep()##cgi.script_name##cgi.path_info#"; } - /** - * Get the site URL separator depending if you have an entry point or not - */ - private function sep(){ - return "/"; - } - /** * Link to RSS feeds that ContentBox generates, by default it is the recent updates feed * @@ -1391,10 +1391,10 @@ component accessors="true" singleton threadSafe { */ function linkRSS( category, - comments = false, + comments = false, entry, boolean ssl = getRequestContext().isSSL() - ){ + ) { var xehRSS = linkBlog() & "/rss"; // do we have a category? @@ -1428,8 +1428,8 @@ component accessors="true" singleton threadSafe { any category, boolean comments = false, string slug, - boolean ssl = getRequestContext().isSSL() - ){ + boolean ssl = getRequestContext().isSSL() + ) { var xehRSS = siteRoot() & sep() & "__rss"; // do we have a category? @@ -1466,8 +1466,8 @@ component accessors="true" singleton threadSafe { any category, boolean comments = false, page, - boolean ssl = getRequestContext().isSSL() - ){ + boolean ssl = getRequestContext().isSSL() + ) { var xehRSS = siteRoot() & sep() & "__rss/pages"; // do we have a category? @@ -1498,7 +1498,7 @@ component accessors="true" singleton threadSafe { * @category The category object or slug to link to * @ssl Use SSL or not, defaults to false. */ - function linkCategory( required any category, boolean ssl = getRequestContext().isSSL() ){ + function linkCategory( required any category, boolean ssl = getRequestContext().isSSL() ) { var categorySlug = ""; if ( isSimpleValue( arguments.category ) ) { categorySlug = arguments.category; @@ -1515,7 +1515,10 @@ component accessors="true" singleton threadSafe { * @categorySlug The category slug as a string to link to * @ssl Use SSL or not, defaults to false. */ - function linkCategoryWithSlug( required string categorySlug, boolean ssl = getRequestContext().isSSL() ){ + function linkCategoryWithSlug( + required string categorySlug, + boolean ssl = getRequestContext().isSSL() + ) { return linkBlog() & "/category/#arguments.categorySlug#"; } @@ -1527,12 +1530,7 @@ component accessors="true" singleton threadSafe { * @day The day of the archive * @ssl Use SSL or not, defaults to false. */ - function linkArchive( - year, - month, - day, - boolean ssl = getRequestContext().isSSL() - ){ + function linkArchive( year, month, day, boolean ssl = getRequestContext().isSSL() ) { var xeh = linkBlog() & "/archives"; if ( structKeyExists( arguments, "year" ) ) { @@ -1553,7 +1551,7 @@ component accessors="true" singleton threadSafe { * * @ssl Use SSL or not, defaults to false. */ - function linkSearch( boolean ssl = getRequestContext().isSSL() ){ + function linkSearch( boolean ssl = getRequestContext().isSSL() ) { return linkBlog() & "/search"; } @@ -1562,7 +1560,7 @@ component accessors="true" singleton threadSafe { * * @ssl Use SSL or not, defaults to false. */ - function linkContentSearch( boolean ssl = getRequestContext().isSSL() ){ + function linkContentSearch( boolean ssl = getRequestContext().isSSL() ) { return siteRoot() & sep() & "__search"; } @@ -1571,7 +1569,7 @@ component accessors="true" singleton threadSafe { * * @ssl Use SSL or not, defaults to false. */ - function linkContentSubscription( boolean ssl = getRequestContext().isSSL() ){ + function linkContentSubscription( boolean ssl = getRequestContext().isSSL() ) { return siteRoot() & sep() & "__subscribe"; } @@ -1580,7 +1578,7 @@ component accessors="true" singleton threadSafe { * * @token The token to use for unsubscribing */ - function linkContentUnsubscribe( required string token, boolean ssl = getRequestContext().isSSL() ){ + function linkContentUnsubscribe( required string token, boolean ssl = getRequestContext().isSSL() ) { return siteRoot() & sep() & "__unsubscribe/#arguments.token#"; } @@ -1595,9 +1593,9 @@ component accessors="true" singleton threadSafe { required entry, boolean ssl = getRequestContext().isSSL(), format = "html" - ){ + ) { // format? - var outputFormat = ( arguments.format neq "html" ? ".#arguments.format#" : "" ); + var outputFormat = ( arguments.format NEQ "html" ? ".#arguments.format#" : "" ); if ( isSimpleValue( arguments.entry ) ) { return linkEntrywithslug( @@ -1621,11 +1619,15 @@ component accessors="true" singleton threadSafe { required slug, boolean ssl = getRequestContext().isSSL(), format = "html" - ){ + ) { // format? - var outputFormat = ( arguments.format neq "html" ? ".#arguments.format#" : "" ); + var outputFormat = ( arguments.format NEQ "html" ? ".#arguments.format#" : "" ); // Cleanup - arguments.slug = reReplace( arguments.slug, "^/", "" ); + arguments.slug = reReplace( + arguments.slug, + "^/", + "" + ); // link return linkBlog() & sep() & arguments.slug & outputFormat; } @@ -1641,15 +1643,15 @@ component accessors="true" singleton threadSafe { required content, boolean ssl = getRequestContext().isSSL(), format = "html" - ){ - if ( arguments.content.getContentType() eq "entry" ) { + ) { + if ( arguments.content.getContentType() EQ "entry" ) { return linkEntry( arguments.content, arguments.ssl, arguments.format ); } - if ( arguments.content.getContentType() eq "page" ) { + if ( arguments.content.getContentType() EQ "page" ) { return linkPage( arguments.content, arguments.ssl, @@ -1671,9 +1673,9 @@ component accessors="true" singleton threadSafe { required page, boolean ssl = getRequestContext().isSSL(), format = "html" - ){ + ) { // format extension? - var outputFormat = ( arguments.format neq "html" ? ".#arguments.format#" : "" ); + var outputFormat = ( arguments.format NEQ "html" ? ".#arguments.format#" : "" ); // link directly or with slug if ( isSimpleValue( arguments.page ) ) { @@ -1699,11 +1701,15 @@ component accessors="true" singleton threadSafe { required slug, boolean ssl = getRequestContext().isSSL(), format = "html" - ){ + ) { // format? - var outputFormat = ( arguments.format neq "html" ? ".#arguments.format#" : "" ); + var outputFormat = ( arguments.format NEQ "html" ? ".#arguments.format#" : "" ); // cleanup - arguments.slug = reReplace( arguments.slug, "^/", "" ); + arguments.slug = reReplace( + arguments.slug, + "^/", + "" + ); // Return the formed link return siteRoot() & sep() & arguments.slug & outputFormat; } @@ -1714,9 +1720,15 @@ component accessors="true" singleton threadSafe { * @comment The comment to link to * @ssl Use SSL or not, defaults to false. */ - function linkComment( required comment, boolean ssl = getRequestContext().isSSL() ){ + function linkComment( required comment, boolean ssl = getRequestContext().isSSL() ) { var xeh = ""; - if ( arguments.comment.getRelatedContent().getContentType() eq "page" ) { + if ( + arguments + .comment + .getRelatedContent() + .getContentType() EQ + "page" + ) { xeh = linkPage( arguments.comment.getRelatedContent(), arguments.ssl ); } else { xeh = linkEntry( arguments.comment.getRelatedContent(), arguments.ssl ); @@ -1732,9 +1744,9 @@ component accessors="true" singleton threadSafe { * @content The entry or page to link to its comments * @ssl Use SSL or not, defaults to false. */ - function linkComments( required content, boolean ssl = getRequestContext().isSSL() ){ + function linkComments( required content, boolean ssl = getRequestContext().isSSL() ) { var xeh = ""; - if ( arguments.content.getContentType() eq "page" ) { + if ( arguments.content.getContentType() EQ "page" ) { xeh = linkPage( arguments.content, arguments.ssl ); } else { xeh = linkEntry( arguments.content, arguments.ssl ); @@ -1751,8 +1763,8 @@ component accessors="true" singleton threadSafe { * * @return The URL to submit to. */ - function linkCommentPost( required content, boolean ssl = getRequestContext().isSSL() ){ - if ( arguments.content.getContentType() eq "page" ) { + function linkCommentPost( required content, boolean ssl = getRequestContext().isSSL() ) { + if ( arguments.content.getContentType() EQ "page" ) { return siteRoot() & sep() & "__pageCommentPost"; } @@ -1764,7 +1776,7 @@ component accessors="true" singleton threadSafe { * * @lang The iso language code */ - function linkLanguageChange( string lang = "en_US" ){ + function linkLanguageChange( string lang = "en_US" ) { return getRequestContext().buildLink( "__changeLang/" & arguments.lang ); } @@ -1776,7 +1788,7 @@ component accessors="true" singleton threadSafe { * @name The name of the installed widget to execute * @args The argument collection to pass to the widget's renderIt() method */ - function widget( required name, struct args = structNew() ){ + function widget( required name, struct args = structNew() ) { return getWidget( arguments.name ).renderit( argumentCollection = arguments.args ); } @@ -1788,7 +1800,7 @@ component accessors="true" singleton threadSafe { * * @name The name of the installed widget to return */ - function getWidget( required name ){ + function getWidget( required name ) { return widgetService.getWidgetByDiscovery( arguments.name ); } @@ -1798,33 +1810,38 @@ component accessors="true" singleton threadSafe { * Create entry category links to be display usually on an entry or entry list. * @entry The entry to use to build its category links list. */ - function quickCategoryLinks( required entry ){ + function quickCategoryLinks( required entry ) { var e = arguments.entry; // check if it has categories - if ( NOT e.hasCategories() ) { + if ( !e.hasCategories() ) { return ""; } - var cats = e.getCategories(); + var cats = e.getCategories(); var catList = []; // iterate and create links - for ( var x = 1; x lte arrayLen( cats ); x++ ) { + for ( var x = 1; x LTE arrayLen( cats ); x++ ) { var link = "#cats[ x ].getCategory()#"; arrayAppend( catList, link ); } // return list of links - return replace( arrayToList( catList ), ",", ", ", "all" ); + return replace( + arrayToList( catList ), + ",", + ", ", + "all" + ); } /** * Render out paging for blog entries only */ - function quickPaging(){ + function quickPaging() { var prc = getRequestCollection( private = true ); - if ( NOT structKeyExists( prc, "oPaging" ) ) { + if ( !structKeyExists( prc, "oPaging" ) ) { throw( message = "Paging object is not in the collection", detail = "This probably means you are trying to use the paging outside of the main entries index page and that is a No No", @@ -1832,11 +1849,11 @@ component accessors="true" singleton threadSafe { ); } return prc.oPaging.renderit( - foundRows = prc.entriesCount, - link = prc.pagingLink, - aslist = true, - pagingMaxRows = setting( "cb_paging_maxentries" ) - ); + foundRows = prc.entriesCount, + link = prc.pagingLink, + aslist = true, + pagingMaxRows = setting( "cb_paging_maxentries" ) + ); } /** @@ -1850,11 +1867,9 @@ component accessors="true" singleton threadSafe { string template = "entry", string collectionAs = "entry", struct args = structNew() - ){ + ) { var entries = getCurrentEntries(); - return controller - .getRenderer() - .view( + return controller.getRenderer().view( view = "#themeName()#/templates/#arguments.template#", collection = entries, collectionAs = arguments.collectionAs, @@ -1873,11 +1888,9 @@ component accessors="true" singleton threadSafe { string template = "entry", string collectionAs = "entry", struct args = structNew() - ){ - var entries = [ getCurrentEntry() ]; - return controller - .getRenderer() - .view( + ) { + var entries = [ getCurrentEntry()]; + return controller.getRenderer().view( view = "#themeName()#/templates/#arguments.template#", collection = entries, collectionAs = arguments.collectionAs, @@ -1898,11 +1911,9 @@ component accessors="true" singleton threadSafe { string collectionAs = "category", struct args = structNew(), isPublic = true - ){ + ) { var categories = getCurrentCategories( argumentCollection = arguments ); - return controller - .getRenderer() - .view( + return controller.getRenderer().view( view = "#themeName()#/templates/#arguments.template#", collection = categories, collectionAs = arguments.collectionAs, @@ -1921,11 +1932,9 @@ component accessors="true" singleton threadSafe { string template = "relatedContent", string collectionAs = "relatedContent", struct args = structNew() - ){ + ) { var relatedContent = getCurrentRelatedContent(); - return controller - .getRenderer() - .view( + return controller.getRenderer().view( view = "#themeName()#/templates/#arguments.template#", collection = relatedContent, collectionAs = arguments.collectionAs, @@ -1936,14 +1945,16 @@ component accessors="true" singleton threadSafe { /** * Render out custom fields for the current content */ - function quickCustomFields(){ + function quickCustomFields() { var customFields = getCurrentCustomFields(); - var content = ""; + var content = ""; savecontent variable="content" { writeOutput( "
    " ); for ( var thisField in customFields ) { - writeOutput( "
  • #thisField#: #customFields[ thisField ]#
  • " ); + writeOutput( + "
  • #thisField#: #customFields[ thisField ]#
  • " + ); } writeOutput( "
" ); } @@ -1962,11 +1973,9 @@ component accessors="true" singleton threadSafe { string template = "comment", string collectionAs = "comment", struct args = structNew() - ){ + ) { var comments = getCurrentComments(); - return controller - .getRenderer() - .view( + return controller.getRenderer().view( view = "#themeName()#/templates/#arguments.template#", collection = comments, collectionAs = arguments.collectionAs, @@ -1984,16 +1993,14 @@ component accessors="true" singleton threadSafe { required author, numeric size = 25, string class = "gravatar" - ){ + ) { var targetEmail = arguments.author; // check if simple or not - if ( NOT isSimpleValue( arguments.author ) ) { + if ( !isSimpleValue( arguments.author ) ) { targetEmail = arguments.author.getEmail(); } - return wirebox - .getInstance( "Avatar@contentbox" ) - .renderAvatar( + return wirebox.getInstance( "Avatar@contentbox" ).renderAvatar( email = targetEmail, size = arguments.size, class = arguments.class @@ -2021,12 +2028,12 @@ component accessors="true" singleton threadSafe { */ function quickView( required view, - cache = false, + cache = false, cacheTimeout, cacheLastAccessTimeout, cacheSuffix, - cacheProvider = "template", - module = "contentbox", + cacheProvider = "template", + module = "contentbox", struct args, collection, collectionAs = "", @@ -2034,8 +2041,8 @@ component accessors="true" singleton threadSafe { numeric collectionMaxRows = 0, collectionDelim = "", boolean prepostExempt = false - ){ - arguments.view = "#themeName()#/views/#arguments.view#"; + ) { + arguments.view = "#themeName()#/views/#arguments.view#"; arguments.module = themeRecord().module; return controller.getRenderer().view( argumentCollection = arguments ); } @@ -2058,7 +2065,7 @@ component accessors="true" singleton threadSafe { args = structNew(), viewModule = "", boolean prePostExempt = false - ){ + ) { arguments.layout = "#themeName()#/layouts/#arguments.layout#"; return controller.getRenderer().layout( argumentCollection = arguments ); } @@ -2068,7 +2075,7 @@ component accessors="true" singleton threadSafe { * * @content The content this comment form will be linked to, page or entry */ - function quickCommentForm( required content ){ + function quickCommentForm( required content ) { return widget( "CommentForm", { content : arguments.content } ); } @@ -2077,7 +2084,7 @@ component accessors="true" singleton threadSafe { * * @args */ - function mainView( struct args = structNew() ){ + function mainView( struct args = structNew() ) { return controller.getRenderer().view( view = "", args = arguments.args ); } @@ -2096,7 +2103,7 @@ component accessors="true" singleton threadSafe { required string slug, required type = "html", required array slugCache = [] - ){ + ) { var menu = variables.menuService.findBySlug( arguments.slug, site().getsiteID() ); if ( menu.isLoaded() ) { @@ -2117,13 +2124,16 @@ component accessors="true" singleton threadSafe { * @menu.doc_generic contentbox.models.menu.Menu * @slugCache The cache of menu slugs already used in this request */ - public string function buildProviderMenu( required any menu, required array slugCache = [] ){ - var listType = arguments.menu.getListType(); + public string function buildProviderMenu( + required any menu, + required array slugCache = [] + ) { + var listType = arguments.menu.getListType(); // arguments.listType = !reFindNoCase( "^(ul|ol)$", arguments.listType ) ? "
    " : arguments.listType; // set start var menuString = "<#listType# class='#arguments.menu.getMenuClass()#'>"; // now get root items - var items = arguments.menu.getRootMenuItems(); + var items = arguments.menu.getRootMenuItems(); // create cache of slugs to prevent infinite recursions arrayAppend( arguments.slugCache, menu.getSlug() ); // build out this top level @@ -2138,49 +2148,6 @@ component accessors="true" singleton threadSafe { return menuString; } - /** - * Builds out a level of a custom menu - * - * @items An array of menu items for this level - * @listType The type of list to create (derived from owning menu) - * @slugCache The cache of menu slugs already used in this request - */ - private string function buildProviderMenuLevel( - required array items, - required string listType = "ul", - required array slugCache = [], - string listClass = "" - ){ - var menuString = ""; - // loop over items to build out level - for ( var item in arguments.items ) { - var extras = { - slugCache : arguments.slugCache, - listType : arguments.listType - }; - // check that item can be added - if ( item.canDisplay( options = extras ) ) { - // get template from provider - menuString &= "
  • " & item - .getProvider() - .getDisplayTemplate( item, extras ); - // if this menu item has children... - if ( item.hasChild() ) { - // recurse, recurse, recurse! - menuString &= "<#arguments.listType# class='#arguments.listClass#'>" & - buildProviderMenuLevel( - items = item.getChildren(), - listType = arguments.listType, - slugCache = arguments.slugCache - ) & - ""; - } - menuString &= "
  • "; - } - } - return menuString; - } - /** * Render out a quick menu for root level pages * @@ -2202,16 +2169,16 @@ component accessors="true" singleton threadSafe { elementClass = "", parentClass = "parent", activeClass = "active" - ){ - arguments.showNone = false; + ) { + arguments.showNone = false; // get root pages arguments.pageRecords = pageService.findPublishedContent( - parent : "", - showInMenu: true, - siteID : site().getsiteID(), - properties: "contentID,slug,title,numberOfChildren", - sortOrder : "order ASC, publishedDate ASC" - ); + parent = "", + showInMenu = true, + siteID = site().getsiteID(), + properties = "contentID,slug,title,numberOfChildren", + sortOrder = "order ASC, publishedDate ASC" + ); // build it out return buildMenu( argumentCollection = arguments ); } @@ -2243,7 +2210,7 @@ component accessors="true" singleton threadSafe { parentClass = "parent", activeClass = "active", activeShowChildren = false - ){ + ) { // If page not passed, or empty, then use the current page if ( isNull( arguments.page ) || ( isSimpleValue( arguments.page ) && !len( arguments.page ) ) ) { arguments.page = getCurrentPage(); @@ -2257,12 +2224,12 @@ component accessors="true" singleton threadSafe { // get child pages arguments.pageRecords = pageService.findPublishedContent( - parent : page.getContentID(), - showInMenu: true, - siteID : site().getsiteID(), - properties: "contentID,slug,title,numberOfChildren", - sortOrder : "order ASC, publishedDate ASC" - ); + parent = page.getContentID(), + showInMenu = true, + siteID = site().getsiteID(), + properties = "contentID,slug,title,numberOfChildren", + sortOrder = "order ASC, publishedDate ASC" + ); // build it out return buildMenu( argumentCollection = arguments ); @@ -2274,7 +2241,7 @@ component accessors="true" singleton threadSafe { * @page Optional page to create link for, else look for current page * @text The optional text to use for the link, else it uses the page's title */ - function linkToParentPage( page, text = "" ){ + function linkToParentPage( page, text = "" ) { // verify incoming page if ( !structKeyExists( arguments, "page" ) ) { arguments.page = getCurrentPage(); @@ -2282,7 +2249,10 @@ component accessors="true" singleton threadSafe { // link if parent found. if ( arguments.page.hasParent() ) { if ( !len( arguments.text ) ) { - arguments.text = arguments.page.getParent().getTitle(); + arguments.text = arguments + .page + .getParent() + .getTitle(); } // build link return "#arguments.text#"; @@ -2296,12 +2266,13 @@ component accessors="true" singleton threadSafe { * @page Optional page to create link for, else look for current page * @separator Breadcrumb separator, defaults to '>' */ - function breadCrumbs( any page, string separator = ">" ){ + function breadCrumbs( any page, string separator = ">" ) { // verify incoming page if ( !structKeyExists( arguments, "page" ) ) { arguments.page = getCurrentPage(); } - return wirebox.getInstance( "PageBreadcrumbVisitor@contentbox-ui" ).visit( arguments.page, arguments.separator ); + return wirebox.getInstance( "PageBreadcrumbVisitor@contentbox-ui" ).visit( arguments.page, + arguments.separator ); } /************************************** UTILITIES *********************************************/ @@ -2309,14 +2280,14 @@ component accessors="true" singleton threadSafe { /** * Detects if the incoming request is from a mobile device or NOT. */ - boolean function isMobile(){ + boolean function isMobile() { return mobileDetector.isMobile(); } /** * Return the current system flash scope */ - any function getFlash(){ + any function getFlash() { return controller.getRequestService().getFlashScope(); } @@ -2335,10 +2306,10 @@ component accessors="true" singleton threadSafe { string locale, any values, string bundle - ){ + ) { // check for resource@bundle convention: if ( find( "@", arguments.resource ) ) { - arguments.bundle = listLast( arguments.resource, "@" ); + arguments.bundle = listLast( arguments.resource, "@" ); arguments.resource = listFirst( arguments.resource, "@" ); } return variables.cbResourceService.getResource( argumentCollection = arguments ); @@ -2347,16 +2318,80 @@ component accessors="true" singleton threadSafe { /** * utility to strip HTML */ - function stripHTML( required stringTarget ){ - return encodeForHTML( reReplaceNoCase( arguments.stringTarget, "<[^>]*>", "", "ALL" ) ); + function stripHTML( required stringTarget ) { + return encodeForHTML( + reReplaceNoCase( + arguments.stringTarget, + "<[^>]*>", + "", + "ALL" + ) + ); } /** * removes CR LF TAB double spaces from string */ - function stripWhitespace( required stringTarget ){ - arguments.stringTarget = reReplace( arguments.stringTarget, "\s", " ", "ALL" ); - return trim( reReplace( arguments.stringTarget, "\s{2,}", " ", "ALL" ) ); + function stripWhitespace( required stringTarget ) { + arguments.stringTarget = reReplace( + arguments.stringTarget, + "\s", + " ", + "ALL" + ); + return trim( reReplace( + arguments.stringTarget, + "\s{2,}", + " ", + "ALL" + ) ); + } + + /** + * Get the site URL separator depending if you have an entry point or not + */ + private function sep() { + return "/"; + } + + /** + * Builds out a level of a custom menu + * + * @items An array of menu items for this level + * @listType The type of list to create (derived from owning menu) + * @slugCache The cache of menu slugs already used in this request + */ + private string function buildProviderMenuLevel( + required array items, + required string listType = "ul", + required array slugCache = [], + string listClass = "" + ) { + var menuString = ""; + // loop over items to build out level + for ( var item in arguments.items ) { + var extras = { + slugCache : arguments.slugCache, + listType : arguments.listType + }; + // check that item can be added + if ( item.canDisplay( options = extras ) ) { + // get template from provider + menuString &= "
  • #item.getProvider().getDisplayTemplate( item, + extras )#"; + // if this menu item has children... + if ( item.hasChild() ) { + // recurse, recurse, recurse! + menuString &= "<#arguments.listType# class='#arguments.listClass#'>#buildProviderMenuLevel( + items = item.getChildren(), + listType = arguments.listType, + slugCache = arguments.slugCache + )##""#"; + } + menuString &= "
  • "; + } + } + return menuString; } /************************************** PRIVATE *********************************************/ @@ -2374,34 +2409,34 @@ component accessors="true" singleton threadSafe { parentClass = "parent", activeClass = "active", activeShowChildren = false - ){ + ) { // Levels = *, then create big enough integer - if ( arguments.levels eq "*" ) { + if ( arguments.levels EQ "*" ) { arguments.levels = "999999"; } // check type? if ( !reFindNoCase( "^(ul|ol|li|data|none)$", arguments.type ) ) { arguments.type = "ul"; } - var pageResults = arguments.pageRecords; + var pageResults = arguments.pageRecords; // buffer - var b = createObject( "java", "java.lang.StringBuilder" ).init( "" ); + var b = createObject( "java", "java.lang.StringBuilder" ).init( "" ); // current page? - var prc = getRequestCollection( private = true ); + var prc = getRequestCollection( private = true ); var pageAncestorContentIDs = ""; - var locPage = ""; + var locPage = ""; // class text - var classtext = []; - var currentPageID = 0; + var classtext = []; + var currentPageID = 0; // Get contentID - if ( structKeyExists( prc, "page" ) and prc.page.isLoaded() ) { - locPage = getCurrentPage(); - currentPageID = locPage.getContentID(); + if ( structKeyExists( prc, "page" ) && prc.page.isLoaded() ) { + locPage = getCurrentPage(); + currentPageID = locPage.getContentID(); pageAncestorContentIDs = locPage.getContentID(); // If this is subnav, add ancestry trail while ( locPage.hasParent() ) { - locPage = locPage.getParent(); + locPage = locPage.getParent(); pageAncestorContentIDs = listAppend( pageAncestorContentIDs, locPage.getContentID() ); } } @@ -2411,25 +2446,27 @@ component accessors="true" singleton threadSafe { b.append( "<#arguments.type# class=""#arguments.typeClass#"">" ); } // data setup - if ( arguments.type eq "data" ) { + if ( arguments.type EQ "data" ) { var dataMenu = []; } // Iterate through pages and create sub menus - for ( var x = 1; x lte pageResults.count; x++ ) { + for ( var x = 1; x LTE pageResults.count; x++ ) { // Build up the element class into the current classText array list if ( isSimpleValue( arguments.elementClass ) ) { arguments.elementClass = listToArray( arguments.elementClass ); } classText = duplicate( arguments.elementClass ); - if ( !len( arguments.excludes ) OR !listFindNoCase( arguments.excludes, pageResults.content[ x ][ "title" ] ) ) { + if ( + !len( arguments.excludes ) || !listFindNoCase( arguments.excludes, pageResults.content[ x ][ "title" ] ) + ) { // Do we need to nest? var doNesting = ( - arguments.currentLevel lt arguments.levels AND pageResults.content[ x ][ "numberOfChildren" ] > 0 + arguments.currentLevel LT arguments.levels && pageResults.content[ x ][ "numberOfChildren" ] > 0 ); // Is element active (or one of its decendants) - var isElementActive = currentPageID eq pageResults.content[ x ][ "contentID" ]; + var isElementActive = currentPageID EQ pageResults.content[ x ][ "contentID" ]; var isElementActiveAncestor = ( listFindNoCase( pageAncestorContentIDs, pageResults.content[ x ][ "contentID" ] ) ); @@ -2439,89 +2476,92 @@ component accessors="true" singleton threadSafe { } // list - if ( arguments.type neq "none" and arguments.type neq "data" ) { + if ( arguments.type NEQ "none" && arguments.type NEQ "data" ) { // Nested Levels? if ( doNesting ) { // Setup Parent class, we are going down the wormhole arrayAppend( classText, arguments.parentClass ); // Start Embedded List b.append( - "
  • #pageResults.content[ x ][ "title" ]#" - ); + "
  • #pageResults.content[ x ][ "title" ]#" + ); // If type is "li" then guess to do a nested ul list b.append( - buildMenu( - pageRecords = pageService.findPublishedContent( - parent : pageResults.content[ x ][ "contentID" ], - showInMenu: true, - siteID : site().getsiteID(), - properties: "contentID,slug,title,numberOfChildren", - sortOrder : "order ASC, publishedDate ASC" - ), - excludes = arguments.excludes, - type = ( arguments.type eq "li" ? "ul" : arguments.type ), - typeClass = arguments.typeClass, - elementClass = arguments.elementClass, - showNone = arguments.showNone, - levels = arguments.levels, - currentLevel = arguments.currentLevel + 1, - activeShowChildren = arguments.activeShowChildren - ) - ); - } - // Do we nest active and activeShowChildren flag is activated? - else if ( - activeShowChildren AND ( isElementActive OR isElementActiveAncestor ) AND pageResults.content[ x ].hasChild() + buildMenu( + pageRecords = pageService.findPublishedContent( + parent = pageResults.content[ x ][ "contentID" ], + showInMenu = true, + siteID = site().getsiteID(), + properties = "contentID,slug,title,numberOfChildren", + sortOrder = "order ASC, publishedDate ASC" + ), + excludes = arguments.excludes, + type = ( arguments.type EQ "li" ? "ul" : arguments.type ), + typeClass = arguments.typeClass, + elementClass = arguments.elementClass, + showNone = arguments.showNone, + levels = arguments.levels, + currentLevel = arguments.currentLevel + 1, + activeShowChildren = arguments.activeShowChildren + ) + ); + } else // Do we nest active and activeShowChildren flag is activated? + if ( + activeShowChildren && ( isElementActive || isElementActiveAncestor ) && + pageResults.content[ x ].hasChild() ) { // Setup Parent class, we are going down the wormhole arrayAppend( classText, arguments.parentClass ); // Start Embedded List b.append( - "
  • #pageResults.content[ x ][ "title" ]#" - ); + "
  • #pageResults.content[ x ][ "title" ]#" + ); // If type is "li" then guess to do a nested ul list b.append( - buildMenu( - pageRecords = pageService.findPublishedContent( - parent : pageResults.content[ x ][ "contentID" ], - showInMenu: true, - siteID : site().getsiteID(), - properties: "contentID,slug,title,numberOfChildren", - sortOrder : "order ASC, publishedDate ASC" - ), - excludes = arguments.excludes, - type = ( arguments.type eq "li" ? "ul" : arguments.type ), - typeClass = arguments.typeClass, - showNone = arguments.showNone, - levels = 1, - elementClass = arguments.elementClass, - currentLevel = arguments.currentLevel + 1, - activeShowChildren = activeShowChildren - ) - ); + buildMenu( + pageRecords = pageService.findPublishedContent( + parent = pageResults.content[ x ][ "contentID" ], + showInMenu = true, + siteID = site().getsiteID(), + properties = "contentID,slug,title,numberOfChildren", + sortOrder = "order ASC, publishedDate ASC" + ), + excludes = arguments.excludes, + type = ( arguments.type EQ "li" ? "ul" : arguments.type ), + typeClass = arguments.typeClass, + showNone = arguments.showNone, + levels = 1, + elementClass = arguments.elementClass, + currentLevel = arguments.currentLevel + 1, + activeShowChildren = activeShowChildren + ) + ); } else { // Start Embedded List b.append( - "
  • #pageResults.content[ x ][ "title" ]#" - ); + "
  • #pageResults.content[ x ][ "title" ]#" + ); } // Close it b.append( "
  • " ); - } else if ( arguments.type eq "data" ) { + } else if ( arguments.type EQ "data" ) { var pageData = { title : pageResults.content[ x ][ "title" ], link : linkPageWithSlug( pageResults.content[ x ][ "slug" ] ) }; if ( doNesting ) { pageData.subPageMenu = buildMenu( - pageRecords = pageService.findPublishedContent( - parent : pageResults.content[ x ][ "contentID" ], - showInMenu: true, - siteID : site().getsiteID(), - properties: "contentID,slug,title,numberOfChildren", - sortOrder : "order ASC, publishedDate ASC" - ), + pageRecords = pageService.findPublishedContent( + parent = pageResults.content[ x ][ "contentID" ], + showInMenu = true, + siteID = site().getsiteID(), + properties = "contentID,slug,title,numberOfChildren", + sortOrder = "order ASC, publishedDate ASC" + ), excludes = arguments.excludes, type = arguments.type, typeClass = arguments.typeClass, @@ -2531,19 +2571,16 @@ component accessors="true" singleton threadSafe { currentLevel = arguments.currentLevel + 1, activeShowChildren = arguments.activeShowChildren ); - } - // Do we nest active and activeShowChildren flag is activated? - else if ( - activeShowChildren AND isElementActive AND pageResults.content[ x ][ "numberOfChildren" ] > 0 - ) { + } else // Do we nest active and activeShowChildren flag is activated? + if ( activeShowChildren && isElementActive && pageResults.content[ x ][ "numberOfChildren" ] > 0 ) { pageData.subPageMenu = buildMenu( - pageRecords = pageService.findPublishedContent( - parent : pageResults.content[ x ][ "contentID" ], - showInMenu: true, - siteID : site().getsiteID(), - properties: "contentID,slug,title,numberOfChildren", - sortOrder : "order ASC, publishedDate ASC" - ), + pageRecords = pageService.findPublishedContent( + parent = pageResults.content[ x ][ "contentID" ], + showInMenu = true, + siteID = site().getsiteID(), + properties = "contentID,slug,title,numberOfChildren", + sortOrder = "order ASC, publishedDate ASC" + ), excludes = arguments.excludes, type = arguments.type, typeClass = arguments.typeClass, @@ -2557,18 +2594,19 @@ component accessors="true" singleton threadSafe { arrayAppend( dataMenu, pageData ); } else { b.append( - "#pageResults.content[ x ][ "title" ]##arguments.separator#" - ); + "#pageResults.content[ x ][ "title" ]##arguments.separator#" + ); } } } - if ( arguments.type eq "data" ) { + if ( arguments.type EQ "data" ) { return dataMenu; } // None? - if ( pageResults.count eq 0 and arguments.showNone ) { + if ( pageResults.count EQ 0 && arguments.showNone ) { b.append( "
  • No Sub Pages
  • " ); } @@ -2581,4 +2619,4 @@ component accessors="true" singleton threadSafe { return b.toString(); } -} +} \ No newline at end of file diff --git a/modules/contentbox/models/system/EmailTemplateHelper.cfc b/modules/contentbox/models/system/EmailTemplateHelper.cfc index d7c101cce..4198b597e 100644 --- a/modules/contentbox/models/system/EmailTemplateHelper.cfc +++ b/modules/contentbox/models/system/EmailTemplateHelper.cfc @@ -6,20 +6,19 @@ * A helper to create standard email template sections */ component singleton { - // DI property name="avatar" inject="Avatar@contentbox"; /** - * Constructor + * Constructor */ - EmailTemplateHelper function init(){ + EmailTemplateHelper function init() { // Return instance return this; } - public string function author( required string email, required string content ){ - var gravatar = variables.avatar.renderAvatar( email = arguments.email, size = 40 ); + public string function author( required string email, required string content ) { + var gravatar = variables.avatar.renderAvatar( email = arguments.email, size = 40 ); savecontent variable="authorContent" { writeOutput( text( @@ -56,18 +55,19 @@ component singleton { required string borderColor = "DDDDDD", required string borderStyle = "solid", required numeric borderWidth = 1 - ){ + ) { // cfformat-ignore-start - savecontent variable="dividerContent" { - writeoutput(' - - + savecontent variable="dividerContent" { + writeoutput( + " +
    + -
    - +
    + - @@ -77,10 +77,11 @@ component singleton {
    +
    - '); - } + " + ); + } // cfformat-ignore-end -return dividerContent; + return dividerContent; } /** @@ -96,19 +97,19 @@ return dividerContent; required string level = "h2", required string color = "777", required numeric fontSize = 20 - ){ + ) { // cfformat-ignore-start - savecontent variable="headingContent" { - writeoutput(' - <#arguments.level# style="color: ###arguments.color#; display: block; font-family: Helvetica; font-size: #arguments.fontSize#px; font-style: normal; font-weight: bold; line-height: 110%; letter-spacing: normal; margin: 0; margin-bottom: 9px; text-align: left"> + savecontent variable="headingContent" { + writeoutput( + " + <#arguments.level# style=""color: ###arguments.color#; display: block; font-family: Helvetica; font-size: #arguments.fontSize#px; font-style: normal; font-weight: bold; line-height: 110%; letter-spacing: normal; margin: 0; margin-bottom: 9px; text-align: left""> #arguments.content# - '); - } + " + ); + } // cfformat-ignore-end -return text( - headingContent - ); + return text( headingContent ); } /** @@ -117,29 +118,37 @@ return text( * @content.hint Content for the text area * @callout.hint If true, will wrap content in a callout box */ - public string function text( required string content, required boolean callout = false ){ + public string function text( + required string content, + required boolean callout = false + ) { // cfformat-ignore-start - savecontent variable="textContent" { - writeoutput(' - - + savecontent variable="textContent" { + writeoutput( + " +
    + -
    - +
    + - @@ -148,10 +157,11 @@ return text(
    - '); - if( arguments.callout ) { - writeoutput( ' -
    - '); - } - writeoutput( '#arguments.content#' ); - if( arguments.callout ) { - writeoutput( '
    ' ); - } - writeoutput(' +
    + " + ); + if ( arguments.callout ) { + writeoutput( + " +
    + " + ); + } + writeoutput( "#arguments.content#" ); + if ( arguments.callout ) { + writeoutput( "
    " ); + } + writeoutput( + "
    - '); - } + " + ); + } // cfformat-ignore-end -return textContent; + return textContent; } /** @@ -159,52 +169,62 @@ return textContent; * * @buttons.hint Array of buttons to add to the button bar */ - public string function buttonBar( required Array buttons ){ + public string function buttonBar( required Array buttons ) { // cfformat-ignore-start - savecontent variable="buttonBarContent" { - writeoutput(' - #divider( 18, 18, 5, 18 )# - - + savecontent variable="buttonBarContent" { + writeoutput( + " + #divider( + 18, + 18, + 5, + 18 + )# +
    + -
    - +
    + -
    - +
    + -
    - '); - // loop over buttons - for( var button in arguments.buttons ) { - writeoutput(' - + @@ -217,11 +237,17 @@ return textContent;
    + " + ); + // loop over buttons + for ( var button in arguments.buttons ) { + writeoutput( + " + - -
    - #button.text# + +
    - #button.text# + + #button.text#
    - +
    - +
      
    - '); - } - // close off bar - writeoutput(' + " + ); + } + // close off bar + writeoutput( + "
    - #divider( 0, 18, 18, 18 )# - '); - } + #divider( + 0, + 18, + 18, + 18 + )# + " + ); + } // cfformat-ignore-end -return buttonBarContent; + return buttonBarContent; } -} +} \ No newline at end of file diff --git a/modules/contentbox/models/system/EmailTemplateService.cfc b/modules/contentbox/models/system/EmailTemplateService.cfc index 73399e461..831dbd3bd 100644 --- a/modules/contentbox/models/system/EmailTemplateService.cfc +++ b/modules/contentbox/models/system/EmailTemplateService.cfc @@ -1,37 +1,35 @@ -/** -******************************************************************************** -ContentBox - A Modular Content Platform -Copyright 2012 by Luis Majano and Ortus Solutions, Corp -www.ortussolutions.com -******************************************************************************** -Apache License, Version 2.0 - -Copyright Since [2012] [Luis Majano and Ortus Solutions,Corp] - -Licensed under the Apache License, Version 2.0 (the "License" ); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -******************************************************************************** -* Manage the system's email templates for admin purposes -*/ +/** + * ******************************************************************************** + * ContentBox - A Modular Content Platform + * Copyright 2012 by Luis Majano and Ortus Solutions, Corp + * www.ortussolutions.com + * ******************************************************************************** + * Apache License, Version 2.0 + * + * Copyright Since [2012] [Luis Majano and Ortus Solutions,Corp] + * + * Licensed under the Apache License, Version 2.0 (the "License" ); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ******************************************************************************** + * Manage the system's email templates for admin purposes + */ component accessors="true" singleton { - // Dependecnies property name="moduleSettings" inject="coldbox:setting:modules"; property name="log" inject="logbox:logger:{this}"; - // Local properties property name="templatesPath" type="string"; - EmailTemplateService function init(){ + EmailTemplateService function init() { templatesPath = ""; return this; } @@ -39,21 +37,21 @@ component accessors="true" singleton { /** * onDIComplete */ - function onDIComplete(){ + function onDIComplete() { setTemplatesPath( moduleSettings[ "contentbox" ].path & "/email_templates" ); } /** * Check if template exists */ - boolean function templateExists( required string name ){ + boolean function templateExists( required string name ) { return fileExists( getTemplatesPath() & "/#arguments.name#.cfm" ); } /** * Get installed templates */ - query function getTemplates(){ + query function getTemplates() { var templates = directoryList( getTemplatesPath(), false, @@ -64,4 +62,4 @@ component accessors="true" singleton { return templates; } -} +} \ No newline at end of file diff --git a/modules/contentbox/models/system/EventHandler.cfc b/modules/contentbox/models/system/EventHandler.cfc index daee5cfc0..e0ce9cc11 100644 --- a/modules/contentbox/models/system/EventHandler.cfc +++ b/modules/contentbox/models/system/EventHandler.cfc @@ -1,37 +1,36 @@ /** -******************************************************************************** -ContentBox - A Modular Content Platform -Copyright 2012 by Luis Majano and Ortus Solutions, Corp -www.ortussolutions.com -******************************************************************************** -Apache License, Version 2.0 - -Copyright Since [2012] [Luis Majano and Ortus Solutions,Corp] - -Licensed under the Apache License, Version 2.0 (the "License" ); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -******************************************************************************** -******************************************************************************** -Copyright Since 2005 ColdBox Framework by Luis Majano and Ortus Solutions, Corp -www.coldbox.org | www.luismajano.com | www.ortussolutions.com -******************************************************************************** - -Author : Luis Majano -Date : 10/16/2007 -Description : - A ColdBox Enabled Hibernate Event Handler that ties to the ColdBox proxy for ColdBox Operations. - -*/ + * ******************************************************************************** + * ContentBox - A Modular Content Platform + * Copyright 2012 by Luis Majano and Ortus Solutions, Corp + * www.ortussolutions.com + * ******************************************************************************** + * Apache License, Version 2.0 + * + * Copyright Since [2012] [Luis Majano and Ortus Solutions,Corp] + * + * Licensed under the Apache License, Version 2.0 (the "License" ); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ******************************************************************************** + * ******************************************************************************** + * Copyright Since 2005 ColdBox Framework by Luis Majano and Ortus Solutions, Corp + * www.coldbox.org | www.luismajano.com | www.ortussolutions.com + * ******************************************************************************** + * + * Author : Luis Majano + * Date : 10/16/2007 + * Description : + * A ColdBox Enabled Hibernate Event Handler that ties to the ColdBox proxy for ColdBox Operations. + * + */ component extends="cborm.models.EventHandler" { - -} +} \ No newline at end of file diff --git a/modules/contentbox/models/system/NotificationService.cfc b/modules/contentbox/models/system/NotificationService.cfc index 4e0bff9b0..e3731c005 100755 --- a/modules/contentbox/models/system/NotificationService.cfc +++ b/modules/contentbox/models/system/NotificationService.cfc @@ -1,4 +1,4 @@ -/** +/** * ContentBox - A Modular Content Platform * Copyright since 2012 by Ortus Solutions, Corp * www.ortussolutions.com/products/contentbox @@ -10,8 +10,7 @@ * - Page Saving and removals * - ContentStore saving and removals */ -component extends="coldbox.system.Interceptor" accessors="true" { - +component extends ="coldbox.system.Interceptor" accessors="true" { // DI property name="settingService" inject="settingService@contentbox"; property name="siteService" inject="siteService@contentbox"; @@ -23,7 +22,8 @@ component extends="coldbox.system.Interceptor" accessors="true" { /** * Configure the Service */ - function configure(){ + function configure() { + } /** @@ -33,18 +33,18 @@ component extends="coldbox.system.Interceptor" accessors="true" { * @data Intercept Data * @buffer Output buffer */ - function cbadmin_postNewAuthorSave( event, data, buffer ){ - var author = arguments.data.author; + function cbadmin_postNewAuthorSave( event, data, buffer ) { + var author = arguments.data.author; var settings = variables.settingService.getAllSettings(); // Only new authors are announced, not updates, and also verify author notifications are online. - if ( NOT settings.cb_notify_author ) { + if ( !settings.cb_notify_author ) { return; } // get current logged in author performing the action var currentAuthor = variables.securityService.getAuthorSession(); - var defaultSite = variables.siteService.getDefaultSite(); + var defaultSite = variables.siteService.getDefaultSite(); // get mail payload var bodyTokens = { @@ -53,34 +53,34 @@ component extends="coldbox.system.Interceptor" accessors="true" { permissionGroups : author.getPermissionGroupsList(), authorEmail : author.getEmail(), authorURL : CBHelper.linkAdmin( - event = "authors.editor.authorID.#author.getAuthorID()#", - ssl = settings.cb_admin_ssl - ), + event = "authors.editor.authorID.#author.getAuthorID()#", + ssl = settings.cb_admin_ssl + ), currentAuthor : currentauthor.getFullName(), currentAuthorEmail : currentAuthor.getEmail() }; var mail = variables.mailservice.newMail( - to = settings.cb_site_email, - from = settings.cb_site_outgoingEmail, - subject = "#defaultSite.getName()# - Author Created - #bodyTokens.authorName#", - bodyTokens = bodyTokens, - type = "html", - server = settings.cb_site_mail_server, - username = settings.cb_site_mail_username, - password = settings.cb_site_mail_password, - port = settings.cb_site_mail_smtp, - useTLS = settings.cb_site_mail_tls, - useSSL = settings.cb_site_mail_ssl - ); + to = settings.cb_site_email, + from = settings.cb_site_outgoingEmail, + subject = "#defaultSite.getName()# - Author Created - #bodyTokens.authorName#", + bodyTokens = bodyTokens, + type = "html", + server = settings.cb_site_mail_server, + username = settings.cb_site_mail_username, + password = settings.cb_site_mail_password, + port = settings.cb_site_mail_smtp, + useTLS = settings.cb_site_mail_tls, + useSSL = settings.cb_site_mail_ssl + ); // generate content for email from template mail.setBody( - renderer.layout( - view = "/contentbox/email_templates/author_new", - layout = "/contentbox/email_templates/layouts/email", - args = { gravatarEmail : currentAuthor.getEmail() } - ) - ); + renderer.layout( + view = "/contentbox/email_templates/author_new", + layout = "/contentbox/email_templates/layouts/email", + args = { gravatarEmail : currentAuthor.getEmail() } + ) + ); // send it out variables.mailService.send( mail ); @@ -93,19 +93,19 @@ component extends="coldbox.system.Interceptor" accessors="true" { * @data Intercept Data * @buffer Output buffer */ - function cbadmin_preAuthorRemove( event, data, buffer ){ - var author = arguments.data.author; + function cbadmin_preAuthorRemove( event, data, buffer ) { + var author = arguments.data.author; // Get settings var settings = variables.settingService.getAllSettings(); // Only notify when enabled. - if ( NOT settings.cb_notify_author ) { + if ( !settings.cb_notify_author ) { return; } // get current logged in author performing the action var currentAuthor = variables.securityService.getAuthorSession(); - var defaultSite = variables.siteService.getDefaultSite(); + var defaultSite = variables.siteService.getDefaultSite(); // get mail payload var bodyTokens = { @@ -116,27 +116,27 @@ component extends="coldbox.system.Interceptor" accessors="true" { currentAuthorEmail : currentAuthor.getEmail() }; var mail = variables.mailservice.newMail( - to = settings.cb_site_email, - from = settings.cb_site_outgoingEmail, - subject = "#defaultSite.getName()# - Author Removed - #bodyTokens.authorName#", - bodyTokens = bodyTokens, - type = "html", - server = settings.cb_site_mail_server, - username = settings.cb_site_mail_username, - password = settings.cb_site_mail_password, - port = settings.cb_site_mail_smtp, - useTLS = settings.cb_site_mail_tls, - useSSL = settings.cb_site_mail_ssl - ); + to = settings.cb_site_email, + from = settings.cb_site_outgoingEmail, + subject = "#defaultSite.getName()# - Author Removed - #bodyTokens.authorName#", + bodyTokens = bodyTokens, + type = "html", + server = settings.cb_site_mail_server, + username = settings.cb_site_mail_username, + password = settings.cb_site_mail_password, + port = settings.cb_site_mail_smtp, + useTLS = settings.cb_site_mail_tls, + useSSL = settings.cb_site_mail_ssl + ); // generate content for email from template mail.setBody( - renderer.layout( - view = "/contentbox/email_templates/author_remove", - layout = "/contentbox/email_templates/layouts/email", - args = { gravatarEmail : currentAuthor.getEmail() } - ) - ); + renderer.layout( + view = "/contentbox/email_templates/author_remove", + layout = "/contentbox/email_templates/layouts/email", + args = { gravatarEmail : currentAuthor.getEmail() } + ) + ); // send it out variables.mailService.send( mail ); } @@ -148,15 +148,15 @@ component extends="coldbox.system.Interceptor" accessors="true" { * @data Intercept Data * @buffer Output buffer */ - function cbadmin_postEntrySave( event, data, buffer ){ + function cbadmin_postEntrySave( event, data, buffer ) { // Only new entries are announced, not updates, and also verify entry notifications are online. - if ( NOT arguments.data.isNew ) { + if ( !arguments.data.isNew ) { return; } // Setup the entry + site - var entry = arguments.data.content; - var site = entry.getSite(); + var entry = arguments.data.content; + var site = entry.getSite(); var settings = variables.settingService.getAllSettings(); // Only notify when enabled. @@ -186,27 +186,27 @@ component extends="coldbox.system.Interceptor" accessors="true" { } var mail = variables.mailservice.newMail( - to : getNotifiers( site, settings ), - from : settings.cb_site_outgoingEmail, - subject : "#site.getName()# - Blog Entry Created - #bodyTokens.entryTitle#", - bodyTokens: bodyTokens, - type : "html", - server : settings.cb_site_mail_server, - username : settings.cb_site_mail_username, - password : settings.cb_site_mail_password, - port : settings.cb_site_mail_smtp, - useTLS : settings.cb_site_mail_tls, - useSSL : settings.cb_site_mail_ssl - ); + to = getNotifiers( site, settings ), + from = settings.cb_site_outgoingEmail, + subject = "#site.getName()# - Blog Entry Created - #bodyTokens.entryTitle#", + bodyTokens = bodyTokens, + type = "html", + server = settings.cb_site_mail_server, + username = settings.cb_site_mail_username, + password = settings.cb_site_mail_password, + port = settings.cb_site_mail_smtp, + useTLS = settings.cb_site_mail_tls, + useSSL = settings.cb_site_mail_ssl + ); // generate content for email from template mail.setBody( - renderer.layout( - view = "/contentbox/email_templates/entry_new", - layout = "/contentbox/email_templates/layouts/email", - args = { gravatarEmail : currentAuthor.getEmail() } - ) - ); + renderer.layout( + view = "/contentbox/email_templates/entry_new", + layout = "/contentbox/email_templates/layouts/email", + args = { gravatarEmail : currentAuthor.getEmail() } + ) + ); // send it out variables.mailService.send( mail ); @@ -219,9 +219,9 @@ component extends="coldbox.system.Interceptor" accessors="true" { * @data Intercept Data * @buffer Output buffer */ - function cbadmin_preEntryRemove( event, data, buffer ){ - var entry = arguments.data.content; - var site = entry.getSite(); + function cbadmin_preEntryRemove( event, data, buffer ) { + var entry = arguments.data.content; + var site = entry.getSite(); // Get settings var settings = variables.settingService.getAllSettings(); @@ -248,27 +248,27 @@ component extends="coldbox.system.Interceptor" accessors="true" { } var mail = variables.mailservice.newMail( - to = getNotifiers( site, settings ), - from = settings.cb_site_outgoingEmail, - subject = "#site.getName()# - Entry Removed - #bodyTokens.entryTitle#", - bodyTokens = bodyTokens, - type = "html", - server = settings.cb_site_mail_server, - username = settings.cb_site_mail_username, - password = settings.cb_site_mail_password, - port = settings.cb_site_mail_smtp, - useTLS = settings.cb_site_mail_tls, - useSSL = settings.cb_site_mail_ssl - ); + to = getNotifiers( site, settings ), + from = settings.cb_site_outgoingEmail, + subject = "#site.getName()# - Entry Removed - #bodyTokens.entryTitle#", + bodyTokens = bodyTokens, + type = "html", + server = settings.cb_site_mail_server, + username = settings.cb_site_mail_username, + password = settings.cb_site_mail_password, + port = settings.cb_site_mail_smtp, + useTLS = settings.cb_site_mail_tls, + useSSL = settings.cb_site_mail_ssl + ); // generate content for email from template mail.setBody( - renderer.layout( - view = "/contentbox/email_templates/entry_remove", - layout = "/contentbox/email_templates/layouts/email", - args = { gravatarEmail : currentAuthor.getEmail() } - ) - ); + renderer.layout( + view = "/contentbox/email_templates/entry_remove", + layout = "/contentbox/email_templates/layouts/email", + args = { gravatarEmail : currentAuthor.getEmail() } + ) + ); // send it out variables.mailService.send( mail ); } @@ -280,14 +280,14 @@ component extends="coldbox.system.Interceptor" accessors="true" { * @data Intercept Data * @buffer Output buffer */ - function cbadmin_postPageSave( event, data, buffer ){ + function cbadmin_postPageSave( event, data, buffer ) { // Only new pages are announced, not updates, and also verify page notifications are online. - if ( NOT arguments.data.isNew ) { + if ( !arguments.data.isNew ) { return; } - var page = arguments.data.content; - var site = page.getSite(); + var page = arguments.data.content; + var site = page.getSite(); var settings = variables.settingService.getAllSettings(); // Only notify when enabled. @@ -316,27 +316,27 @@ component extends="coldbox.system.Interceptor" accessors="true" { } var mail = variables.mailservice.newMail( - to = getNotifiers( site, settings ), - from = settings.cb_site_outgoingEmail, - subject = "#site.getName()# - Page Created - #bodyTokens.pageTitle#", - bodyTokens = bodyTokens, - type = "html", - server = settings.cb_site_mail_server, - username = settings.cb_site_mail_username, - password = settings.cb_site_mail_password, - port = settings.cb_site_mail_smtp, - useTLS = settings.cb_site_mail_tls, - useSSL = settings.cb_site_mail_ssl - ); + to = getNotifiers( site, settings ), + from = settings.cb_site_outgoingEmail, + subject = "#site.getName()# - Page Created - #bodyTokens.pageTitle#", + bodyTokens = bodyTokens, + type = "html", + server = settings.cb_site_mail_server, + username = settings.cb_site_mail_username, + password = settings.cb_site_mail_password, + port = settings.cb_site_mail_smtp, + useTLS = settings.cb_site_mail_tls, + useSSL = settings.cb_site_mail_ssl + ); // generate content for email from template mail.setBody( - renderer.layout( - view = "/contentbox/email_templates/page_new", - layout = "/contentbox/email_templates/layouts/email", - args = { gravatarEmail : currentAuthor.getEmail() } - ) - ); + renderer.layout( + view = "/contentbox/email_templates/page_new", + layout = "/contentbox/email_templates/layouts/email", + args = { gravatarEmail : currentAuthor.getEmail() } + ) + ); // send it out variables.mailService.send( mail ); @@ -349,9 +349,9 @@ component extends="coldbox.system.Interceptor" accessors="true" { * @data Intercept Data * @buffer Output buffer */ - function cbadmin_prePageRemove( event, data, buffer ){ - var page = arguments.data.content; - var site = page.getSite(); + function cbadmin_prePageRemove( event, data, buffer ) { + var page = arguments.data.content; + var site = page.getSite(); // Get settings var settings = variables.settingService.getAllSettings(); @@ -378,27 +378,27 @@ component extends="coldbox.system.Interceptor" accessors="true" { } var mail = variables.mailservice.newMail( - to = getNotifiers( site, settings ), - from = settings.cb_site_outgoingEmail, - subject = "#site.getName()# - Page Removed - #bodyTokens.pageTitle#", - bodyTokens = bodyTokens, - type = "html", - server = settings.cb_site_mail_server, - username = settings.cb_site_mail_username, - password = settings.cb_site_mail_password, - port = settings.cb_site_mail_smtp, - useTLS = settings.cb_site_mail_tls, - useSSL = settings.cb_site_mail_ssl - ); + to = getNotifiers( site, settings ), + from = settings.cb_site_outgoingEmail, + subject = "#site.getName()# - Page Removed - #bodyTokens.pageTitle#", + bodyTokens = bodyTokens, + type = "html", + server = settings.cb_site_mail_server, + username = settings.cb_site_mail_username, + password = settings.cb_site_mail_password, + port = settings.cb_site_mail_smtp, + useTLS = settings.cb_site_mail_tls, + useSSL = settings.cb_site_mail_ssl + ); // generate content for email from template mail.setBody( - renderer.layout( - view = "/contentbox/email_templates/page_remove", - layout = "/contentbox/email_templates/layouts/email", - args = { gravatarEmail : currentAuthor.getEmail() } - ) - ); + renderer.layout( + view = "/contentbox/email_templates/page_remove", + layout = "/contentbox/email_templates/layouts/email", + args = { gravatarEmail : currentAuthor.getEmail() } + ) + ); // send it out variables.mailService.send( mail ); @@ -411,14 +411,14 @@ component extends="coldbox.system.Interceptor" accessors="true" { * @data Intercept Data * @buffer Output buffer */ - function cbadmin_postContentStoreSave( event, data, buffer ){ + function cbadmin_postContentStoreSave( event, data, buffer ) { // Only new pages are announced, not updates - if ( NOT arguments.data.isNew ) { + if ( !arguments.data.isNew ) { return; } - var content = arguments.data.content; - var site = content.getSite(); + var content = arguments.data.content; + var site = content.getSite(); var settings = variables.settingService.getAllSettings(); // Only notify when enabled. @@ -439,34 +439,34 @@ component extends="coldbox.system.Interceptor" accessors="true" { contentPublishedDate : content.getDisplayPublishedDate(), contentExpireDate : content.getDisplayExpireDate(), contentURL : arguments.event.buildLink( - to = "#CBHelper.adminRoot()#.contentStore.export/contentID/#content.getContentID()#", - ssl = settings.cb_admin_ssl - ), + to = "#CBHelper.adminRoot()#.contentStore.export/contentID/#content.getContentID()#", + ssl = settings.cb_admin_ssl + ), contentExcerpt : content.renderContentSilent( content.getActiveContent().getContent() ) }; var mail = variables.mailservice.newMail( - to = getNotifiers( site, settings ), - from = settings.cb_site_outgoingEmail, - subject = "#site.getName()# - ContentStore Created - #bodyTokens.contentTitle#", - bodyTokens = bodyTokens, - type = "html", - server = settings.cb_site_mail_server, - username = settings.cb_site_mail_username, - password = settings.cb_site_mail_password, - port = settings.cb_site_mail_smtp, - useTLS = settings.cb_site_mail_tls, - useSSL = settings.cb_site_mail_ssl - ); + to = getNotifiers( site, settings ), + from = settings.cb_site_outgoingEmail, + subject = "#site.getName()# - ContentStore Created - #bodyTokens.contentTitle#", + bodyTokens = bodyTokens, + type = "html", + server = settings.cb_site_mail_server, + username = settings.cb_site_mail_username, + password = settings.cb_site_mail_password, + port = settings.cb_site_mail_smtp, + useTLS = settings.cb_site_mail_tls, + useSSL = settings.cb_site_mail_ssl + ); // generate content for email from template mail.setBody( - renderer.layout( - view = "/contentbox/email_templates/contentstore_new", - layout = "/contentbox/email_templates/layouts/email", - args = { gravatarEmail : currentAuthor.getEmail() } - ) - ); + renderer.layout( + view = "/contentbox/email_templates/contentstore_new", + layout = "/contentbox/email_templates/layouts/email", + args = { gravatarEmail : currentAuthor.getEmail() } + ) + ); // send it out variables.mailService.send( mail ); @@ -479,9 +479,9 @@ component extends="coldbox.system.Interceptor" accessors="true" { * @data Intercept Data * @buffer Output buffer */ - function cbadmin_preContentStoreRemove( event, data, buffer ){ - var content = arguments.data.content; - var site = content.getSite(); + function cbadmin_preContentStoreRemove( event, data, buffer ) { + var content = arguments.data.content; + var site = content.getSite(); // Get settings var settings = variables.settingService.getAllSettings(); @@ -503,34 +503,34 @@ component extends="coldbox.system.Interceptor" accessors="true" { contentPublishedDate : content.getDisplayPublishedDate(), contentExpireDate : content.getDisplayExpireDate(), contentURL : arguments.event.buildLink( - to = "#CBHelper.adminRoot()#.contentStore.export/contentID/#content.getContentID()#", - ssl = settings.cb_admin_ssl - ), + to = "#CBHelper.adminRoot()#.contentStore.export/contentID/#content.getContentID()#", + ssl = settings.cb_admin_ssl + ), contentExcerpt : content.renderContentSilent( content.getActiveContent().getContent() ) }; var mail = variables.mailservice.newMail( - to = getNotifiers( site, settings ), - from = settings.cb_site_outgoingEmail, - subject = "#site.getName()# - ContentStore Removed - #bodyTokens.contentTitle#", - bodyTokens = bodyTokens, - type = "html", - server = settings.cb_site_mail_server, - username = settings.cb_site_mail_username, - password = settings.cb_site_mail_password, - port = settings.cb_site_mail_smtp, - useTLS = settings.cb_site_mail_tls, - useSSL = settings.cb_site_mail_ssl - ); + to = getNotifiers( site, settings ), + from = settings.cb_site_outgoingEmail, + subject = "#site.getName()# - ContentStore Removed - #bodyTokens.contentTitle#", + bodyTokens = bodyTokens, + type = "html", + server = settings.cb_site_mail_server, + username = settings.cb_site_mail_username, + password = settings.cb_site_mail_password, + port = settings.cb_site_mail_smtp, + useTLS = settings.cb_site_mail_tls, + useSSL = settings.cb_site_mail_ssl + ); // generate content for email from template mail.setBody( - renderer.layout( - view = "/contentbox/email_templates/contentstore_remove", - layout = "/contentbox/email_templates/layouts/email", - args = { gravatarEmail : currentAuthor.getEmail() } - ) - ); + renderer.layout( + view = "/contentbox/email_templates/contentstore_remove", + layout = "/contentbox/email_templates/layouts/email", + args = { gravatarEmail : currentAuthor.getEmail() } + ) + ); // send it out variables.mailService.send( mail ); @@ -542,7 +542,7 @@ component extends="coldbox.system.Interceptor" accessors="true" { * @site The site the notification is from * @settings The global settings */ - private function getNotifiers( required site, required settings ){ + private function getNotifiers( required site, required settings ) { var notifiers = ""; // Site Notifiers if ( arguments.site.getNotifyOnEntries() ) { @@ -555,4 +555,4 @@ component extends="coldbox.system.Interceptor" accessors="true" { return notifiers; } -} +} \ No newline at end of file diff --git a/modules/contentbox/models/system/Setting.cfc b/modules/contentbox/models/system/Setting.cfc index 8a18a948e..c780b5591 100755 --- a/modules/contentbox/models/system/Setting.cfc +++ b/modules/contentbox/models/system/Setting.cfc @@ -1,4 +1,4 @@ -/** +/** * ContentBox - A Modular Content Platform * Copyright since 2012 by Ortus Solutions, Corp * www.ortussolutions.com/products/contentbox @@ -15,56 +15,42 @@ component cachename ="cbSetting" cacheuse ="read-write" { - - /* ********************************************************************* - ** PROPERTIES - ********************************************************************* */ + /********************************************************************** + * ** PROPERTIES + **********************************************************************/ property - name ="settingID" - column ="settingID" + name="settingID" + column="settingID" fieldtype="id" generator="uuid" - length ="36" - ormtype ="string" - update ="false"; - - property - name ="name" - column ="name" - notnull="true" - length ="100"; - + length="36" + ormtype="string" + update="false"; + property name="name" column="name" notnull="true" length="100"; + property name="value" column="value" notnull="true" ormtype="text"; property - name ="value" - column ="value" - notnull="true" - ormtype="text"; - - property - name ="isCore" - column ="isCore" + name="isCore" + column="isCore" ormtype="boolean" notnull="true" default="false" - index ="idx_core"; - - /* ********************************************************************* - ** RELATIONSHIPS - ********************************************************************* */ + index="idx_core"; + /********************************************************************** + * ** RELATIONSHIPS + **********************************************************************/ // M20 -> The site this setting belongs to property - name ="site" - notnull ="false" - cfc ="contentbox.models.system.Site" + name="site" + notnull="false" + cfc="contentbox.models.system.Site" fieldtype="many-to-one" - fkcolumn ="FK_siteID" - lazy ="true"; - - /* ********************************************************************* - ** PK + CONSTRAINTS + Memento - ********************************************************************* */ + fkcolumn="FK_siteID" + lazy="true"; + /********************************************************************** + * ** PK + CONSTRAINTS + Memento + **********************************************************************/ this.pk = "settingID"; @@ -74,18 +60,18 @@ component }; this.memento = { - defaultIncludes : [ "name", "value", "isCore", "siteSnapshot:site" ], - defaultExcludes : [ "site" ] + defaultIncludes : [ "name", "value", "isCore", "siteSnapshot:site"], + defaultExcludes : [ "site"] }; - /* ********************************************************************* - ** PUBLIC METHODS - ********************************************************************* */ + /********************************************************************** + * ** PUBLIC METHODS + **********************************************************************/ /** * Constructor */ - function init(){ + function init() { variables.isCore = false; super.init(); @@ -96,7 +82,7 @@ component /** * Build a site snapshot */ - struct function getSiteSnapshot(){ + struct function getSiteSnapshot() { return ( hasSite() ? getSite().getInfoSnapshot() : {} ); } @@ -105,11 +91,11 @@ component * * @return The associated site id or empty if none */ - function getSiteID(){ + function getSiteID() { if ( hasSite() ) { return getSite().getsiteID(); } return ""; } -} +} \ No newline at end of file diff --git a/modules/contentbox/models/system/SettingService.cfc b/modules/contentbox/models/system/SettingService.cfc index 00760f57b..cf9ff3a0a 100755 --- a/modules/contentbox/models/system/SettingService.cfc +++ b/modules/contentbox/models/system/SettingService.cfc @@ -7,16 +7,10 @@ * All settings are cached as a struct constructed using the following format: * - global : { name : value } // global settings * - sites : { - * slug : { name : value } + * slug : { name : value } * } */ -component - extends ="cborm.models.VirtualEntityService" - accessors="true" - threadsafe - singleton -{ - +component extends ="cborm.models.VirtualEntityService" accessors="true" threadsafe singleton { // DI properties property name="siteService" inject="siteService@contentbox"; property name="cachebox" inject="cachebox"; @@ -24,21 +18,15 @@ component property name="appName" inject="coldbox:setting:appName"; property name="contentboxSettings" inject="coldbox:moduleSettings:contentbox"; property name="log" inject="logbox:logger:{this}"; - /** * The cache provider name to use for settings caching. Defaults to 'template' cache. * This can also be set in the global ContentBox settings page to any CacheBox cache. */ property name="cacheProviderName" default="template"; - /** * Bit that detects if CB has been installed or not */ - property - name ="CBReadyFlag" - default="false" - type ="boolean"; - + property name="CBReadyFlag" default="false" type="boolean"; // Global Setting Defaults this.DEFAULTS = { // ContentBox Global Version @@ -223,9 +211,9 @@ component /** * Constructor */ - SettingService function init(){ - variables.oSystem = createObject( "java", "java.lang.System" ); - variables.CBReadyFlag = false; + SettingService function init() { + variables.oSystem = createObject( "java", "java.lang.System" ); + variables.CBReadyFlag = false; variables.cacheProviderName = "template"; // init it @@ -238,17 +226,18 @@ component * This method will go over all system settings and make sure that there are no missing default core settings. * If they are, we will create the core settings with the appropriate defaults: this.DEFAULTS */ - SettingService function preFlightCheck(){ + SettingService function preFlightCheck() { // Log it variables.log.info( "> Running ContentBox pre flight checks..." ); var sTime = getTickCount(); // Iterate over default core settings and check they exist lock - name ="contentbox-pre-flight", - timeout = "10" + name ="contentbox-pre-flight" + timeout ="10" throwOnTimeout="true" - type ="exclusive" { + type ="exclusive" + { // Check what's missing for the global settings transaction { // Get all core, non-deleted settings @@ -256,68 +245,90 @@ component .isNull( "site" ) .isFalse( "isDeleted" ) .isTrue( "isCore" ) - .withProjections( property: "name" ) + .withProjections( property = "name" ) .list( sortOrder = "name" ); // Verify defaults exist - this.DEFAULTS + this + .DEFAULTS + .filter( + function( key, value ) { + return !arrayContainsNoCase( loadedSettings, arguments.key ); + } + ) // only process defaults that do not exist in the database - .filter( function( key, value ){ - return !arrayContainsNoCase( loadedSettings, arguments.key ); - } ) + // Create the missing global setting - .each( function( key, value ){ - variables.log.info( "- Missing core setting (#arguments.key#) found in pre-flight, adding it!" ); - entitySave( - this.new( { - name : arguments.key, - value : trim( arguments.value ), - isCore : true - } ) - ); - } ); + .each( + function( key, value ) { + variables.log.info( + "- Missing core setting (#arguments.key#) found in pre-flight, adding it!" + ); + entitySave( + this.new( + { + name : arguments.key, + value : trim( arguments.value ), + isCore : true + } + ) + ); + } + ); // Get all site core settings in the database var dbSiteSettings = newCriteria() .isFalse( "isDeleted" ) .isTrue( "isCore" ) .joinTo( "site", "site" ) - .withProjections( property: "name,site.slug:siteSlug" ) + .withProjections( property = "name,site.slug:siteSlug" ) .asStruct() .list( sortOrder = "site.slug,name" ); // Get all Sites and process them for core settings - variables.siteService + variables + .siteService .getAll() - .each( function( site ){ - var targetSite = arguments.site; - // Ensure Site Media Folder - variables.siteService.ensureSiteMediaFolder( targetSite ); - // Setup Site Settings - this.SITE_DEFAULTS - // only process defaults that do not exist in the database - .filter( function( key, value ){ - return arrayFilter( dbSiteSettings, function( item ){ - return ( - arguments.item[ "name" ] == key && arguments.item[ "siteSlug" ] == targetSite.getSlug() - ); - } ).isEmpty(); - } ) - // Create the missing site setting - .each( function( key, value ){ - variables.log.info( - "- Site (#targetSite.getSlug()#) missing setting (#arguments.key#), adding it!" - ); - entitySave( - this.new( { - name : arguments.key, - value : trim( arguments.value ), - isCore : true, - site : targetSite - } ) + .each( + function( site ) { + var targetSite = arguments.site; + + variables.siteService.ensureSiteMediaFolder( targetSite ); + + this + .SITE_DEFAULTS + .filter( + function( key, value ) { + return arrayFilter( + dbSiteSettings, + function( item ) { + return ( + arguments.item[ "name" ] == key && + arguments.item[ "siteSlug" ] == targetSite.getSlug() + ); + } + ).isEmpty(); + } + ) + .each( + function( key, value ) { + variables.log.info( + "- Site (#targetSite.getSlug()#) missing setting (#arguments.key#), adding it!" + ); + entitySave( + this.new( + { + name : arguments.key, + value : trim( arguments.value ), + isCore : true, + site : targetSite + } + ) + ); + } ); - } ); - } ); + } + ); } // end transaction @@ -325,7 +336,9 @@ component loadCacheProviderName(); } - variables.log.info( "√ ContentBox pre flight checks done in (#getTickCount() - sTime#)ms!" ); + variables.log.info( + "√ ContentBox pre flight checks done in (#getTickCount() - sTime#)ms!" + ); return this; } @@ -333,24 +346,29 @@ component /** * Get a collection (struct) of settings that represent the defaults for a site in ContentBox */ - struct function getSiteSettingDefaults(){ + struct function getSiteSettingDefaults() { return this.SITE_DEFAULTS; } /** * The static key used for caching all the settings of this installation */ - string function getSettingsCacheKey(){ - return "cb-settings-container-#reReplace( variables.appName, "\s", "-", "all" )#"; + string function getSettingsCacheKey() { + return "cb-settings-container-#reReplace( + variables.appName, + "\s", + "-", + "all" + )#"; } /** * Check if the installer is present */ - boolean function isInstallationPresent(){ + boolean function isInstallationPresent() { if ( - structKeyExists( variables.moduleSettings, "contentbox-installer" ) AND - directoryExists( variables.moduleSettings[ "contentbox-installer" ].path ) + structKeyExists( variables.moduleSettings, "contentbox-installer" ) && + directoryExists( variables.moduleSettings[ "contentbox-installer" ].path ) ) { return true; } @@ -361,10 +379,10 @@ component /** * Delete the installer module */ - boolean function deleteInstaller(){ + boolean function deleteInstaller() { if ( - structKeyExists( variables.moduleSettings, "contentbox-installer" ) AND - directoryExists( variables.moduleSettings[ "contentbox-installer" ].path ) + structKeyExists( variables.moduleSettings, "contentbox-installer" ) && + directoryExists( variables.moduleSettings[ "contentbox-installer" ].path ) ) { directoryDelete( variables.moduleSettings[ "contentbox-installer" ].path, true ); return true; @@ -376,7 +394,7 @@ component * Check if contentbox has been installed by checking if there are no settings and no cb_active ONLY * If the query comes back with active, it will not run it again. */ - boolean function isCBReady(){ + boolean function isCBReady() { // Short circuit caching if ( variables.CBReadyFlag ) { return true; @@ -396,7 +414,7 @@ component /** * Mark cb as ready to serve */ - SettingService function activateCB(){ + SettingService function activateCB() { save( this.new( { name : "cb_active", value : "true" } ) ); return this; } @@ -411,7 +429,7 @@ component * * @throws SettingNotFoundException */ - function getSetting( required name, defaultValue ){ + function getSetting( required name, defaultValue ) { var allSettings = getAllSettings(); // verify it exists @@ -426,9 +444,9 @@ component // nothing we can do throw( - message: "Setting #arguments.name# not found in settings collection", - detail : "Registered settings are: #structKeyList( allSettings )#", - type : "SettingNotFoundException" + message = "Setting #arguments.name# not found in settings collection", + detail = "Registered settings are: #structKeyList( allSettings )#", + type = "SettingNotFoundException" ); } @@ -443,7 +461,7 @@ component * * @throws SettingNotFoundException */ - function getSiteSetting( required siteSlug, required name, defaultValue ){ + function getSiteSetting( required siteSlug, required name, defaultValue ) { var allSettings = getAllSiteSettings( arguments.siteSlug ); // verify it exists @@ -458,9 +476,9 @@ component // nothing we can do throw( - message: "Site setting #arguments.name# not found in site collection", - detail : "Registered settings are: #structKeyList( allSettings )#", - type : "SettingNotFoundException" + message = "Site setting #arguments.name# not found in site collection", + detail = "Registered settings are: #structKeyList( allSettings )#", + type = "SettingNotFoundException" ); } @@ -469,7 +487,7 @@ component * * @force To force clear the cache */ - struct function getAllSettings( boolean force = false ){ + struct function getAllSettings( boolean force = false ) { return getSettingsContainer( arguments.force ).global; } @@ -479,7 +497,7 @@ component * @slug The site slug to use to retrieve the settings * @force To force clear the cache */ - struct function getAllSiteSettings( required siteSlug, boolean force = false ){ + struct function getAllSiteSettings( required siteSlug, boolean force = false ) { return getSettingsContainer( arguments.force ).sites[ arguments.siteSlug ]; } @@ -488,7 +506,7 @@ component * * @force To force clear the cache */ - struct function getDefaultSiteSettings( boolean force = false ){ + struct function getDefaultSiteSettings( boolean force = false ) { return getSettingsContainer( arguments.force ).sites[ "default" ]; } @@ -497,7 +515,7 @@ component * * @force Force build */ - struct function getSettingsContainer( boolean force = false ){ + struct function getSettingsContainer( boolean force = false ) { // Force Clear if ( arguments.force ) { flushSettingsCache(); @@ -505,13 +523,13 @@ component // Get or set return getSettingsCacheProvider().getOrSet( - getSettingsCacheKey(), - function(){ - log.info( "Settings container not cached, rebuilding from DB!" ); - return buildSettingsContainer(); - }, - 7200 - ); + getSettingsCacheKey(), + function() { + log.info( "Settings container not cached, rebuilding from DB!" ); + return buildSettingsContainer(); + }, + 7200 + ); } /** @@ -522,7 +540,7 @@ component * * @return The setting object or null */ - function findSiteSetting( required site, required name ){ + function findSiteSetting( required site, required name ) { return newCriteria() .isEq( "site", arguments.site ) .isEq( "name", arguments.name ) @@ -534,14 +552,14 @@ component * * @return struct of { global : {}, sites : { slug : {} } } */ - struct function buildSettingsContainer(){ - var allSites = variables.siteService.getAllFlat(); + struct function buildSettingsContainer() { + var allSites = variables.siteService.getAllFlat(); var container = { "global" : {}, "sites" : {} }; // Initialize site setting containers from defaults container.sites = arrayReduce( allSites, - function( result, item ){ + function( result, item ) { result[ item[ "slug" ] ] = duplicate( this.SITE_DEFAULTS ); return result; }, @@ -552,14 +570,15 @@ component newCriteria() .isFalse( "isDeleted" ) .list( sortOrder = "site,name" ) - .each( function( item ){ - if ( item.hasSite() ) { - // Store the setting - container.sites[ item.getSite().getSlug() ][ item.getName() ] = item.getValue(); - } else { - container.global[ item.getName() ] = item.getValue(); + .each( + function( item ) { + if ( item.hasSite() ) { + container.sites[ item.getSite().getSlug() ][ item.getName() ] = item.getValue(); + } else { + container.global[ item.getName() ] = item.getValue(); + } } - } ); + ); return container; } @@ -568,20 +587,20 @@ component * This will store the incoming structure as the settings in cache. * Usually this method is used for major overrides. */ - SettingService function storeSettings( struct settings ){ + SettingService function storeSettings( struct settings ) { // cache them for 5 days, usually app timeout getSettingsCacheProvider().set( - getSettingsCacheKey(), - arguments.settings, - 7200 - ); + getSettingsCacheKey(), + arguments.settings, + 7200 + ); return this; } /** * flush settings cache for current multi-tenant host */ - SettingService function flushSettingsCache(){ + SettingService function flushSettingsCache() { // Info variables.log.info( "Settings Flush Executed!" ); // Clear out the settings cache @@ -604,39 +623,43 @@ component * * @return SettingService */ - SettingService function bulkSave( struct memento, site ){ - var settings = isNull( arguments.site ) ? getAllSettings() : getAllSiteSettings( arguments.site.getSlug() ); + SettingService function bulkSave( struct memento, site ) { + var settings = isNull( arguments.site ) ? getAllSettings() : getAllSiteSettings( arguments.site.getSlug() ); var newSettings = []; - arguments.memento + arguments + .memento + .filter( + function( key, value ) { + return settings.keyExists( key ); + } + ) // Only save, saveable keys - .filter( function( key, value ){ - return settings.keyExists( key ); - } ) + // Build out array of settings to save - .each( function( key, value ){ - var thisSetting = ""; - // Find the setting globally or by site, depending on the context - if ( isNull( site ) ) { - thisSetting = findWhere( { name : key } ); - } else { - thisSetting = findSiteSetting( site, key ); - } + .each( + function( key, value ) { + var thisSetting = ""; - // Maybe it's a new setting :) - if ( isNull( thisSetting ) ) { - thisSetting = new ( { name : key } ); - } + if ( isNull( site ) ) { + thisSetting = findWhere( { name : key } ); + } else { + thisSetting = findSiteSetting( site, key ); + } - thisSetting.setValue( toString( value ) ); + if ( isNull( thisSetting ) ) { + thisSetting = new( { name : key } ); + } - // Site mapping - if ( !isNull( site ) ) { - thisSetting.setSite( site ); - } + thisSetting.setValue( toString( value ) ); - newSettings.append( thisSetting ); - } ); + if ( !isNull( site ) ) { + thisSetting.setSite( site ); + } + + newSettings.append( thisSetting ); + } + ); // save new settings and flush cache saveAll( newSettings ); @@ -650,9 +673,9 @@ component * * @return struct */ - struct function buildFileBrowserSettings(){ + struct function buildFileBrowserSettings() { var cbSettings = getAllSettings(); - var settings = { + var settings = { directoryRoot : expandPath( cbSettings.cb_media_directoryRoot ), createFolders : cbSettings.cb_media_createFolders, deleteStuff : cbSettings.cb_media_allowDelete, @@ -669,12 +692,12 @@ component }; // Base MediaPath - var mediaPath = ""; + var mediaPath = ""; // add the entry point var entryPoint = moduleSettings[ "contentbox-ui" ].entryPoint; mediaPath &= ( len( entryPoint ) ? "#entryPoint#/" : "" ) & "__media"; // Store it - mediaPath = ( left( mediaPath, 1 ) == "/" ? mediaPath : "/" & mediaPath ); + mediaPath = ( left( mediaPath, 1 ) == "/" ? mediaPath : "/" & mediaPath ); settings.mediaPath = mediaPath; return settings; @@ -697,25 +720,30 @@ component offset = 0, sortOrder = "name asc", siteID = "" - ){ + ) { var results = { "count" : 0, "settings" : [] }; - var c = newCriteria() - // Search Criteria - .when( len( arguments.search ), function( c ){ - c.like( "name", "%#search#%" ); - } ) + var c = newCriteria().when( + len( arguments.search ), + function( c ) { + c.like( "name", "%#search#%" ); + } + )// Search Criteria + // Site Filter - .when( len( arguments.siteID ), function( c ){ - c.isEq( "site.siteID", siteID ); - } ); + .when( + len( arguments.siteID ), + function( c ) { + c.isEq( "site.siteID", siteID ); + } + ); // run criteria query and projections count - results.count = c.count( "settingID" ); + results.count = c.count( "settingID" ); results.settings = c.list( - offset : arguments.offset, - max : arguments.max, - sortOrder: arguments.sortOrder - ); + offset = arguments.offset, + max = arguments.max, + sortOrder = arguments.sortOrder + ); return results; } @@ -723,10 +751,12 @@ component /** * Get all data prepared for export */ - array function getAllForExport(){ - return getAll().map( function( thisItem ){ - return thisItem.getMemento( profile: "export" ); - } ); + array function getAllForExport() { + return getAll().map( + function( thisItem ) { + return thisItem.getMemento( profile = "export" ); + } + ); } /** @@ -739,14 +769,17 @@ component * * @throws InvalidImportFormat */ - string function importFromFile( required importFile, boolean override = false ){ - var data = fileRead( arguments.importFile ); + string function importFromFile( required importFile, boolean override = false ) { + var data = fileRead( arguments.importFile ); var importLog = createObject( "java", "java.lang.StringBuilder" ).init( - "Starting import with override = #arguments.override#...
    " - ); + "Starting import with override = #arguments.override#...
    " + ); if ( !isJSON( data ) ) { - throw( message = "Cannot import file as the contents is not JSON", type = "InvalidImportFormat" ); + throw( + message = "Cannot import file as the contents is not JSON", + type = "InvalidImportFormat" + ); } // deserialize packet: Should be array of { settingID, name, value } @@ -772,26 +805,28 @@ component required importData, boolean override = false, importLog - ){ + ) { var allSettings = []; var siteService = getWireBox().getInstance( "siteService@contentbox" ); // if struct, inflate into an array if ( isStruct( arguments.importData ) ) { - arguments.importData = [ arguments.importData ]; + arguments.importData = [ arguments.importData]; } transaction { // iterate and import for ( var thisSetting in arguments.importData ) { // Site Setting or Global Setting - var oSetting = findWhere( { - name : thisSetting.name, - site : isNull( thisSetting.site.siteID ) ? javacast( "null", "" ) : siteService.get( - thisSetting.site.siteID - ) - } ); - oSetting = ( isNull( oSetting ) ? new () : oSetting ); + var oSetting = findWhere( + { + name : thisSetting.name, + site : isNull( thisSetting.site.siteID ) + ? javacast( "null", "" ) + : siteService.get( thisSetting.site.siteID ) + } + ); + oSetting = ( isNull( oSetting ) ? new() : oSetting ); // Check for boolean values if ( isSimpleValue( thisSetting.value ) && isBoolean( thisSetting.value ) ) { @@ -800,11 +835,11 @@ component // populate content from data getBeanPopulator().populateFromStruct( - target : oSetting, - memento : thisSetting, - exclude : "settingID,site", - composeRelationships: false - ); + target = oSetting, + memento = thisSetting, + exclude = "settingID,site", + composeRelationships = false + ); // Link the site if it exists if ( !isNull( thisSetting.site.slug ) ) { @@ -815,7 +850,7 @@ component if ( !oSetting.isLoaded() ) { arguments.importLog.append( "New setting imported: #thisSetting.name#
    " ); arrayAppend( allSettings, oSetting ); - } else if ( oSetting.isLoaded() and arguments.override ) { + } else if ( oSetting.isLoaded() && arguments.override ) { arguments.importLog.append( "Persisted setting overriden: #thisSetting.name#
    " ); arrayAppend( allSettings, oSetting ); } else { @@ -830,8 +865,8 @@ component arguments.importLog.append( "Saved all imported and overriden settings!" ); } else { arguments.importLog.append( - "No settings imported as none where found or able to be overriden from the import file." - ); + "No settings imported as none where found or able to be overriden from the import file." + ); } } // end of transaction @@ -844,7 +879,7 @@ component * * @return coldbox.system.cache.ICacheProvider */ - function getSettingsCacheProvider(){ + function getSettingsCacheProvider() { // Return the cache to use return cacheBox.getCache( variables.cacheProviderName ); } @@ -852,19 +887,21 @@ component /** * Load up config overrides */ - function loadConfigOverrides(){ + function loadConfigOverrides() { // Global Settings if ( - structKeyExists( variables.contentboxSettings, "settings" ) - && - structKeyExists( variables.contentboxSettings.settings, "global" ) + structKeyExists( variables.contentboxSettings, "settings" ) && + structKeyExists( variables.contentboxSettings.settings, "global" ) ) { var settingsContainer = getSettingsContainer(); // Append and override structAppend( settingsContainer.global, - variables.contentboxSettings.settings.global, + variables + .contentboxSettings + .settings + .global, true ); @@ -872,12 +909,14 @@ component storeSettings( settingsContainer ); // Log it - variables.log.info( - "ContentBox global config overrides loaded.", - variables.contentboxSettings.settings.global - ); + variables.log.info( "ContentBox global config overrides loaded.", + variables + .contentboxSettings + .settings + .global ); } + // TODO: Site Settings } @@ -886,14 +925,18 @@ component * The pattern to look is `contentbox.{site}.{setting}` * Example: contentbox.default.cb_media_directoryRoot */ - function loadEnvironmentOverrides(){ + function loadEnvironmentOverrides() { var environmentSettings = variables.oSystem.getEnv(); - var overrides = {}; + var overrides = {}; // iterate and override for ( var thisKey in environmentSettings ) { if ( reFindNoCase( "^contentbox\_", thisKey ) ) { - overrides[ reReplaceNoCase( thisKey, "^contentbox\_", "" ) ] = environmentSettings[ thisKey ]; + overrides[ reReplaceNoCase( + thisKey, + "^contentbox\_", + "" + ) ] = environmentSettings[ thisKey ]; } } @@ -906,7 +949,11 @@ component var settingsContainer = getSettingsContainer(); // Append and override - structAppend( settingsContainer.global, overrides, true ); + structAppend( + settingsContainer.global, + overrides, + true + ); // Store them back in storeSettings( settingsContainer ); @@ -920,7 +967,7 @@ component /** * Load the cache provider name from DB or default value */ - private SettingService function loadCacheProviderName(){ + private SettingService function loadCacheProviderName() { // query db for cache name var oProvider = newCriteria().isEq( "name", "cb_site_settings_cache" ).get(); // Check if setting in DB already, else default it @@ -933,4 +980,4 @@ component return this; } -} +} \ No newline at end of file diff --git a/modules/contentbox/models/system/Site.cfc b/modules/contentbox/models/system/Site.cfc index 25703e9f3..3f6f1b89e 100644 --- a/modules/contentbox/models/system/Site.cfc +++ b/modules/contentbox/models/system/Site.cfc @@ -13,311 +13,276 @@ component cachename ="cbSite" cacheuse ="read-write" { - - /* ********************************************************************* - ** DI - ********************************************************************* */ + /********************************************************************** + * ** DI + **********************************************************************/ property - name ="contentService" - inject ="provider:contentService@contentbox" + name="contentService" + inject="provider:contentService@contentbox" persistent="false"; - - /* ********************************************************************* - ** PROPERTIES - ********************************************************************* */ + /********************************************************************** + * ** PROPERTIES + **********************************************************************/ property - name ="siteID" - column ="siteID" + name="siteID" + column="siteID" fieldtype="id" generator="uuid" - length ="36" - ormtype ="string" - update ="false"; - + length="36" + ormtype="string" + update="false"; property - name ="name" - column ="name" + name="name" + column="name" ormtype="string" notnull="true" - length ="255" + length="255" default=""; - property - name ="slug" - column ="slug" + name="slug" + column="slug" ormtype="string" notnull="true" - length ="255" - unique ="true" + length="255" + unique="true" default="" - index ="idx_siteSlug"; - + index="idx_siteSlug"; property - name ="description" - column ="description" + name="description" + column="description" ormtype="string" notnull="false" default="" - length ="500"; - + length="500"; property - name ="keywords" - column ="keywords" + name="keywords" + column="keywords" ormtype="string" notnull="false" default="" - length ="255"; - + length="255"; property - name ="domain" - column ="domain" + name="domain" + column="domain" ormtype="string" notnull="false" default="" - length ="255"; - + length="255"; property - name ="domainRegex" - column ="domainRegex" + name="domainRegex" + column="domainRegex" ormtype="string" notnull="false" default="" - length ="255"; - + length="255"; property - name ="domainAliases" - column ="domainAliases" + name="domainAliases" + column="domainAliases" ormtype="text" notnull="false" - length ="8000" + length="8000" default=""; - property - name ="tagline" - column ="tagline" + name="tagline" + column="tagline" ormtype="string" notnull="false" default="" - length ="255"; - + length="255"; property - name ="homepage" - column ="homepage" + name="homepage" + column="homepage" ormtype="string" notnull="false" default="cbBlog" - length ="255"; - + length="255"; property - name ="isBlogEnabled" - column ="isBlogEnabled" + name="isBlogEnabled" + column="isBlogEnabled" ormtype="boolean" notnull="true" default="true"; - property - name ="isSitemapEnabled" - column ="isSitemapEnabled" + name="isSitemapEnabled" + column="isSitemapEnabled" ormtype="boolean" notnull="true" default="true"; - property - name ="poweredByHeader" - column ="poweredByHeader" + name="poweredByHeader" + column="poweredByHeader" ormtype="boolean" notnull="true" default="true"; - property - name ="adminBar" - column ="adminBar" + name="adminBar" + column="adminBar" ormtype="boolean" notnull="true" default="true"; - property - name ="isSSL" - column ="isSSL" + name="isSSL" + column="isSSL" ormtype="boolean" notnull="true" default="false"; - property - name ="isActive" - column ="isActive" + name="isActive" + column="isActive" ormtype="boolean" notnull="true" default="true"; - property - name ="activeTheme" - column ="activeTheme" + name="activeTheme" + column="activeTheme" ormtype="string" notnull="false" default="" - length ="255"; - + length="255"; property - name ="notificationEmails" - column ="notificationEmails" + name="notificationEmails" + column="notificationEmails" ormtype="string" notnull="false" default="" - length ="500"; - + length="500"; property - name ="notifyOnEntries" - column ="notifyOnEntries" + name="notifyOnEntries" + column="notifyOnEntries" ormtype="boolean" notnull="true" default="true"; - property - name ="notifyOnPages" - column ="notifyOnPages" + name="notifyOnPages" + column="notifyOnPages" ormtype="boolean" notnull="true" default="true"; - property - name ="notifyOnContentStore" - column ="notifyOnContentStore" + name="notifyOnContentStore" + column="notifyOnContentStore" ormtype="boolean" notnull="true" default="true"; - property - name ="mediaDisk" - column ="mediaDisk" + name="mediaDisk" + column="mediaDisk" ormtype="string" notnull="false" default="contentbox" - length ="50"; - - /* ********************************************************************* - ** RELATIONSHIPS - ********************************************************************* */ + length="50"; + /********************************************************************** + * ** RELATIONSHIPS + **********************************************************************/ // O2M -> Settings property - name ="settings" + name="settings" singularName="setting" - fieldtype ="one-to-many" - type ="array" - lazy ="true" - orderby ="name" - cfc ="contentbox.models.system.Setting" - fkcolumn ="FK_siteID" - inverse ="true" - cascade ="all-delete-orphan"; - + fieldtype="one-to-many" + type="array" + lazy="true" + orderby="name" + cfc="contentbox.models.system.Setting" + fkcolumn="FK_siteID" + inverse="true" + cascade="all-delete-orphan"; // O2M -> Categories property - name ="categories" + name="categories" singularName="category" - fieldtype ="one-to-many" - type ="array" - lazy ="true" - orderby ="slug" - cfc ="contentbox.models.content.Category" - fkcolumn ="FK_siteID" - inverse ="true" - cascade ="all-delete-orphan"; - + fieldtype="one-to-many" + type="array" + lazy="true" + orderby="slug" + cfc="contentbox.models.content.Category" + fkcolumn="FK_siteID" + inverse="true" + cascade="all-delete-orphan"; // O2M -> Entries property - name ="entries" + name="entries" singularName="entry" - fieldtype ="one-to-many" - type ="array" - lazy ="true" - orderby ="createdDate desc" - cfc ="contentbox.models.content.Entry" - fkcolumn ="FK_siteID" - inverse ="true" - cascade ="all-delete-orphan"; - + fieldtype="one-to-many" + type="array" + lazy="true" + orderby="createdDate desc" + cfc="contentbox.models.content.Entry" + fkcolumn="FK_siteID" + inverse="true" + cascade="all-delete-orphan"; // O2M -> Pages property - name ="pages" + name="pages" singularName="page" - fieldtype ="one-to-many" - type ="array" - lazy ="true" - orderby ="parent" - cfc ="contentbox.models.content.Page" - fkcolumn ="FK_siteID" - inverse ="true" - cascade ="all-delete-orphan"; - + fieldtype="one-to-many" + type="array" + lazy="true" + orderby="parent" + cfc="contentbox.models.content.Page" + fkcolumn="FK_siteID" + inverse="true" + cascade="all-delete-orphan"; // O2M -> ContentStore property - name ="contentStore" + name="contentStore" fieldtype="one-to-many" - type ="array" - lazy ="true" - orderby ="parent" - cfc ="contentbox.models.content.ContentStore" - fkcolumn ="FK_siteID" - inverse ="true" - cascade ="all-delete-orphan"; - + type="array" + lazy="true" + orderby="parent" + cfc="contentbox.models.content.ContentStore" + fkcolumn="FK_siteID" + inverse="true" + cascade="all-delete-orphan"; // O2M -> menus property - name ="menus" + name="menus" singularName="menu" - fieldtype ="one-to-many" - type ="array" - lazy ="true" - orderby ="createdDate desc" - cfc ="contentbox.models.menu.Menu" - fkcolumn ="FK_siteID" - inverse ="true" - cascade ="all-delete-orphan"; - - /* ********************************************************************* - ** CALUCLATED FIELDS - ********************************************************************* */ - - property - name ="numberOfEntries" + fieldtype="one-to-many" + type="array" + lazy="true" + orderby="createdDate desc" + cfc="contentbox.models.menu.Menu" + fkcolumn="FK_siteID" + inverse="true" + cascade="all-delete-orphan"; + /********************************************************************** + * ** CALUCLATED FIELDS + **********************************************************************/ + + property + name="numberOfEntries" formula="select count(*) from cb_entry as entry, cb_content as content where entry.contentID=content.contentID and content.FK_siteID = siteID"; - property - name ="numberOfPages" + name="numberOfPages" formula="select count(*) from cb_page as page, cb_content as content where page.contentID=content.contentID and content.FK_siteID = siteID"; - property - name ="numberOfContentStore" + name="numberOfContentStore" formula="select count(*) from cb_contentStore as contentStore, cb_content as content where contentStore.contentID=content.contentID and content.FK_siteID = siteID"; - property - name ="numberOfMenus" + name="numberOfMenus" formula="select count(*) from cb_menu as menu where menu.FK_siteID = siteID"; - property - name ="numberOfCategories" + name="numberOfCategories" formula="select count(*) from cb_category as category where category.FK_siteID = siteID"; - - /* ********************************************************************* - ** PK + CONSTRAINTS + MEMENTO - ********************************************************************* */ + /********************************************************************** + * ** PK + CONSTRAINTS + MEMENTO + **********************************************************************/ this.pk = "siteID"; @@ -367,10 +332,7 @@ component "pages", "settings" ], - defaultExcludes : [ - "settings.siteSnapshot", - "menus.siteSnapshot:site" - ] + defaultExcludes : [ "settings.siteSnapshot", "menus.siteSnapshot:site"] } } }; @@ -398,25 +360,30 @@ component "activeTheme" : { required : true, size : "0..255" } }; - /* ********************************************************************* - ** PUBLIC METHODS - ********************************************************************* */ + /********************************************************************** + * ** PUBLIC METHODS + **********************************************************************/ /** * Constructor */ - function init(){ - variables.settings = []; - variables.categories = []; - variables.entries = []; - variables.pages = []; - variables.contentStore = []; + function init() { + variables.settings = []; + variables.categories = []; + variables.entries = []; + variables.pages = []; + variables.contentStore = []; variables.domainAliases = "[]"; super.init(); // Incorporate all includes to the export profile - this.memento.profiles.export.defaultIncludes.append( this.memento.defaultIncludes, true ); + this + .memento + .profiles + .export + .defaultIncludes + .append( this.memento.defaultIncludes, true ); return this; } @@ -424,14 +391,14 @@ component /** * Get the total number of content items in this site */ - numeric function getNumberOfContent(){ + numeric function getNumberOfContent() { return variables.contentService.getTotalContentCount( getsiteID() ); } /** * I remove all setting associations */ - Site function removeAllSettings(){ + Site function removeAllSettings() { if ( hasSetting() ) { variables.settings.clear(); } else { @@ -443,7 +410,7 @@ component /** * I remove all category associations */ - Site function removeAllCategories(){ + Site function removeAllCategories() { if ( hasCategory() ) { variables.categories.clear(); } else { @@ -455,7 +422,7 @@ component /** * I remove all entry associations */ - Site function removeAllEntries(){ + Site function removeAllEntries() { if ( hasEntry() ) { variables.entries.clear(); } else { @@ -467,7 +434,7 @@ component /** * I remove all menu associations */ - Site function removeAllMenus(){ + Site function removeAllMenus() { if ( hasMenu() ) { variables.menus.clear(); } else { @@ -479,7 +446,7 @@ component /** * I remove all page associations */ - Site function removeAllPages(){ + Site function removeAllPages() { if ( hasPage() ) { variables.pages.clear(); } else { @@ -491,7 +458,7 @@ component /** * I remove all contentStore associations */ - Site function removeAllContentStore(){ + Site function removeAllContentStore() { if ( hasContentStore() ) { variables.contentStore.clear(); } else { @@ -503,14 +470,17 @@ component /** * Get the site root URL as defined per the settings */ - String function getSiteRoot(){ + string function getSiteRoot() { var serverPort = getServerPort(); var domainName = this.getDomain(); // Return the appropriate site alias Uri from the domain aliases // If the domain alias matches the incoming server host name if ( - getDomainAliases().some( ( alias ) => alias.keyExists( "domainRegex" ) && reFindNoCase( alias.domainRegex, this.getServerName() ) ) + getDomainAliases().some( + ( alias ) => alias.keyExists( "domainRegex" ) && + reFindNoCase( alias.domainRegex, this.getServerName() ) + ) ) { domainName = this.getServerName(); } @@ -528,36 +498,10 @@ component ); } - /** - * Get the server port according to lookup order - * 1. x-forwarded-port header - * 2. cgi.server_port - */ - private function getServerPort(){ - var headers = getHTTPRequestData( false ).headers; - if ( structKeyExists( headers, "x-forwarded-port" ) && len( headers[ "x-forwarded-port" ] ) ) { - return headers[ "x-forwarded-port" ]; - } - return cgi.server_port; - } - - /** - * Get the server host according to lookup order - * 1. x-forwarded-host header - * 2. cgi.server_host - */ - private function getServerName(){ - var headers = getHTTPRequestData( false ).headers; - if ( structKeyExists( headers, "x-forwarded-host" ) && len( headers[ "x-forwarded-host" ] ) ) { - return headers[ "x-forwarded-host" ]; - } - return cgi.server_name; - } - /** * A nice snapshot of this entity used for mementifications */ - struct function getInfoSnapshot(){ + struct function getInfoSnapshot() { if ( isLoaded() ) { return { "siteID" : getId(), @@ -573,11 +517,13 @@ component * * @return The category object or null if not found */ - function getCategory( required slug ){ + function getCategory( required slug ) { if ( hasCategory() ) { - var aFound = variables.categories.filter( function( thisCategory ){ - return arguments.thisCategory.getSlug() == slug; - } ); + var aFound = variables.categories.filter( + function( thisCategory ) { + return arguments.thisCategory.getSlug() == slug; + } + ); if ( aFound.len() ) { return aFound[ 1 ]; } @@ -590,13 +536,15 @@ component * @override * @root Show only root level pages if enabled */ - function getPages( boolean root = true ){ + function getPages( boolean root = true ) { if ( !arguments.root ) { return variables.pages; } - return variables.pages.filter( function( thisPage ){ - return !arguments.thisPage.hasParent(); - } ); + return variables.pages.filter( + function( thisPage ) { + return !arguments.thisPage.hasParent(); + } + ); } /** @@ -605,13 +553,15 @@ component * @override * @root Show only root level contentStore if enabled */ - function getContentStore( boolean root = true ){ + function getContentStore( boolean root = true ) { if ( !arguments.root ) { return variables.contentStore; } - return variables.contentStore.filter( function( thisContent ){ - return !arguments.thisContent.hasParent(); - } ); + return variables.contentStore.filter( + function( thisContent ) { + return !arguments.thisContent.hasParent(); + } + ); } /** @@ -619,11 +569,11 @@ component * * @return The domain aliases as an array */ - array function getDomainAliases(){ + array function getDomainAliases() { return ( - !isNull( variables.domainAliases ) AND isJSON( variables.domainAliases ) ? deserializeJSON( - variables.domainAliases - ) : [] + !isNull( variables.domainAliases ) && isJSON( variables.domainAliases ) + ? deserializeJSON( variables.domainAliases ) + : [] ); } @@ -632,7 +582,7 @@ component * * @return The domain aliases as a json string */ - string function getDomainAliasesAsJSON(){ + string function getDomainAliasesAsJSON() { return isNull( variables.domainAliases ) ? "[]" : variables.domainAliases; } @@ -642,11 +592,37 @@ component * * @aliases The domain aliases as an array or a JSON array string */ - Site function setDomainAliases( any aliases = [] ){ - variables.domainAliases = isSimpleValue( arguments.aliases ) ? arguments.aliases : serializeJSON( - arguments.aliases - ); + Site function setDomainAliases( any aliases = [] ) { + variables.domainAliases = isSimpleValue( arguments.aliases ) + ? arguments.aliases + : serializeJSON( arguments.aliases ); return this; } -} + /** + * Get the server port according to lookup order + * 1. x-forwarded-port header + * 2. cgi.server_port + */ + private function getServerPort() { + var headers = getHTTPRequestData( false ).headers; + if ( structKeyExists( headers, "x-forwarded-port" ) && len( headers[ "x-forwarded-port" ] ) ) { + return headers[ "x-forwarded-port" ]; + } + return cgi.server_port; + } + + /** + * Get the server host according to lookup order + * 1. x-forwarded-host header + * 2. cgi.server_host + */ + private function getServerName() { + var headers = getHTTPRequestData( false ).headers; + if ( structKeyExists( headers, "x-forwarded-host" ) && len( headers[ "x-forwarded-host" ] ) ) { + return headers[ "x-forwarded-host" ]; + } + return cgi.server_name; + } + +} \ No newline at end of file diff --git a/modules/contentbox/models/system/SiteService.cfc b/modules/contentbox/models/system/SiteService.cfc index f48315eef..41df446f4 100644 --- a/modules/contentbox/models/system/SiteService.cfc +++ b/modules/contentbox/models/system/SiteService.cfc @@ -5,13 +5,7 @@ * --- * Manage sites in ContentBox */ -component - extends ="cborm.models.VirtualEntityService" - accessors="true" - threadsafe - singleton -{ - +component extends ="cborm.models.VirtualEntityService" accessors="true" threadsafe singleton { // DI property name="cacheStorage" inject="cacheStorage@cbStorages"; property name="cookieStorage" inject="cookieStorage@cbStorages"; @@ -28,7 +22,7 @@ component /** * Constructor */ - SiteService function init(){ + SiteService function init() { // init it super.init( entityName = "cbSite" ); return this; @@ -39,12 +33,12 @@ component * * @siteID The site to store as the current working one */ - SiteService function setCurrentWorkingsiteID( required siteID ){ + SiteService function setCurrentWorkingsiteID( required siteID ) { variables.cookieStorage.set( - name : "contentbox_admin_current_site", - value : arguments.siteID, - expires: "never" - ); + name = "contentbox_admin_current_site", + value = arguments.siteID, + expires = "never" + ); return this; } @@ -52,33 +46,36 @@ component * Get the current working site in the admin UI. We look in the cache first, * if none is set, we use the `default` site. */ - function getCurrentWorkingsiteID(){ - return variables.cookieStorage.get( name: "contentbox_admin_current_site", defaultValue: getDefaultsiteID() ); + function getCurrentWorkingsiteID() { + return variables.cookieStorage.get( + name = "contentbox_admin_current_site", + defaultValue = getDefaultsiteID() + ); } /** * Get the current working site in the admin UI. We look in the cache first, * if none is set, we use the `default` site. */ - Site function getCurrentWorkingSite(){ + Site function getCurrentWorkingSite() { // Use request storage for acceleration return variables.requestStorage.getOrSet( - name : "contentbox-current-working-site", - produce: function(){ - var oSite = newCriteria().isEq( "siteID", getCurrentWorkingsiteID() ).get(); - // Check if null, just in case - return ( !isNull( oSite ) ? oSite : getDefaultSite() ); - } - ); + name = "contentbox-current-working-site", + produce = function() { + var oSite = newCriteria().isEq( "siteID", getCurrentWorkingsiteID() ).get(); + + return ( !isNull( oSite ) ? oSite : getDefaultSite() ); + } + ); } /** * Get the default site Identifier */ - string function getDefaultsiteID(){ + string function getDefaultsiteID() { return newCriteria() .isEq( "slug", "default" ) - .withProjections( property: "siteID" ) + .withProjections( property = "siteID" ) .get(); } @@ -89,7 +86,7 @@ component * @site A persisted or new site object * @transactional Transaction the call or leave as is, useful for imports, bulk saves, etc. */ - Site function save( required site, boolean transactional = true ){ + Site function save( required site, boolean transactional = true ) { // Added this due to issue in CFML engines and mixed quuery+orm nested transactions. Remove once issue is fixed if ( arguments.transactional ) { transaction { @@ -105,40 +102,6 @@ component return arguments.site; } - /** - * Save operation called by the transactional `save()` method - * Due to transactional issues on Lucee - */ - private function _save( required site ){ - // Create all site settings if this is a new site - if ( !arguments.site.isLoaded() ) { - variables.settingService.saveAll( - variables.settingService - .getSiteSettingDefaults() - .reduce( function( result, setting, value ){ - arguments.result.append( - variables.settingService.new( { - name : arguments.setting, - value : trim( arguments.value ), - isCore : true, - site : site - } ) - ); - return arguments.result; - }, [] ) - ); - } - - // Persist the site - super.save( arguments.site ); - - // Activate the site's theme - variables.themeService.startupTheme( name: arguments.site.getActiveTheme(), site: arguments.site ); - - // Create media root folder for the site - ensureSiteMediaFolder( arguments.site ); - } - /** * This method makes sure the site has a media root folder by convention in the media library * following the patter: /{root}/sites/{slug} @@ -147,8 +110,8 @@ component * * @return True if it created it, false if it already existed. */ - boolean function ensureSiteMediaFolder( required site ){ - var siteRoot = variables.mediaService.getCoreMediaRoot( absolute: true ) & "/sites/" & arguments.site.getSlug(); + boolean function ensureSiteMediaFolder( required site ) { + var siteRoot = variables.mediaService.getCoreMediaRoot( absolute = true ) & "/sites/" & arguments.site.getSlug(); if ( !directoryExists( siteRoot ) ) { directoryCreate( siteRoot ); return true; @@ -161,16 +124,17 @@ component * * @site The site to remove */ - SiteService function delete( required site ){ + SiteService function delete( required site ) { transaction { // If on Adobe, run hard deletes due to Hibernate issue with cascade on integration tests. if ( !server.keyExists( "lucee" ) ) { - variables.settingService.deleteWhere( site: arguments.site ); - variables.contentService.deleteWhere( site: arguments.site ); - variables.categoryService.deleteWhere( site: arguments.site ); - variables.menuService.deleteWhere( site: arguments.site ); + variables.settingService.deleteWhere( site = arguments.site ); + variables.contentService.deleteWhere( site = arguments.site ); + variables.categoryService.deleteWhere( site = arguments.site ); + variables.menuService.deleteWhere( site = arguments.site ); } - arguments.site + arguments + .site .removeAllSettings() .removeAllEntries() .removeAllPages() @@ -188,7 +152,7 @@ component /** * Get the default site object */ - Site function getDefaultSite(){ + Site function getDefaultSite() { return newCriteria().isEq( "slug", "default" ).get(); } @@ -199,12 +163,17 @@ component * * @return array of { siteID, name, slug, domainRegex, domainAliases, isActive } */ - array function getAllFlat( boolean isActive ){ + array function getAllFlat( boolean isActive ) { return newCriteria() - .when( !isNull( arguments.isActive ), function( c ){ - arguments.c.isEq( "this.isActive", javacast( "boolean", isActive ) ); - } ) - .withProjections( property: "siteID,name,slug,domainRegex,domainAliases,isActive" ) + .when( + !isNull( arguments.isActive ), + function( c ) { + arguments.c.isEq( "this.isActive", javacast( "boolean", isActive ) ); + } + ) + .withProjections( + property = "siteID,name,slug,domainRegex,domainAliases,isActive" + ) .asStruct() .list( sortOrder = "name" ); } @@ -214,10 +183,10 @@ component * * @return array of { activeTheme:string, siteID:numeric } */ - array function getAllSiteThemes(){ + array function getAllSiteThemes() { return newCriteria() .isFalse( "isDeleted" ) - .withProjections( distinct: "activeTheme,siteID" ) + .withProjections( distinct = "activeTheme,siteID" ) .asStruct() .list( sortOrder = "siteID" ); } @@ -229,14 +198,17 @@ component * * @throws EntityNotFound */ - function getOrFail( required siteID ){ + function getOrFail( required siteID ) { var site = newCriteria().isEq( "siteID", arguments.siteID ).get(); if ( !isNull( site ) ) { return site; } - throw( type: "EntityNotFound", message: "No site with ID #arguments.siteID.toString()# found" ); + throw( + type = "EntityNotFound", + message = "No site with ID #arguments.siteID.toString()# found" + ); } /** @@ -246,14 +218,17 @@ component * * @throws EntityNotFound */ - function getBySlugOrFail( required slug ){ + function getBySlugOrFail( required slug ) { var site = newCriteria().isEq( "slug", arguments.slug ).get(); if ( !isNull( site ) ) { return site; } - throw( type: "EntityNotFound", message: "No site with slug #arguments.slug.toString()# found" ); + throw( + type = "EntityNotFound", + message = "No site with slug #arguments.slug.toString()# found" + ); } /** @@ -267,14 +242,13 @@ component * * @return The site object */ - Site function discoverSite(){ + Site function discoverSite() { var event = variables.requestService.getContext(); // Are we in the admin? if ( - structKeyExists( variables.loadedModules, "contentbox-admin" ) - && - findNoCase( variables.loadedModules[ "contentbox-admin" ].entryPoint, event.getCurrentRoutedUrl() ) + structKeyExists( variables.loadedModules, "contentbox-admin" ) && + findNoCase( variables.loadedModules[ "contentbox-admin" ].entryPoint, event.getCurrentRoutedUrl() ) ) { return getCurrentWorkingSite(); } @@ -306,23 +280,27 @@ component // Try to discover using the requested full URL including host + path + query string for added flexibility // Verify the site is valid, else continue search - var matchedSite = getAllFlat( isActive: true ).filter( function( thisSite ){ - // direct regex match - if ( reFindNoCase( arguments.thisSite[ "domainRegex" ], event.getFullUrl() ) ) { - return true; - } - - // Look at the site Domain Aliases for a match - if ( isJSON( arguments.thisSite[ "domainAliases" ] ) ) { - return arraySome( deserializeJSON( arguments.thisSite[ "domainAliases" ] ), function( alias ){ - return ( - structKeyExists( alias, "domainRegex" ) && reFindNoCase( alias.domainRegex, event.getFullUrl() ) - ); - } ); - } + var matchedSite = getAllFlat( isActive = true ).filter( + function( thisSite ) { + if ( reFindNoCase( arguments.thisSite[ "domainRegex" ], event.getFullUrl() ) ) { + return true; + } + + if ( isJSON( arguments.thisSite[ "domainAliases" ] ) ) { + return arraySome( + deserializeJSON( arguments.thisSite[ "domainAliases" ] ), + function( alias ) { + return ( + structKeyExists( alias, "domainRegex" ) && + reFindNoCase( alias.domainRegex, event.getFullUrl() ) + ); + } + ); + } - return false; - } ); + return false; + } + ); // Return the first matched site if ( arrayLen( matchedSite ) ) { @@ -336,10 +314,12 @@ component /** * Get all sites for export */ - function getAllForExport(){ - return getAll().map( function( thisItem ){ - return thisItem.getMemento( profile: "export" ); - } ); + function getAllForExport() { + return getAll().map( + function( thisItem ) { + return thisItem.getMemento( profile = "export" ); + } + ); } /** @@ -352,14 +332,17 @@ component * * @throws InvalidImportFormat */ - string function importFromFile( required importFile, boolean override = false ){ - var data = fileRead( arguments.importFile ); + string function importFromFile( required importFile, boolean override = false ) { + var data = fileRead( arguments.importFile ); var importLog = createObject( "java", "java.lang.StringBuilder" ).init( - "Starting import with override = #arguments.override#...
    " - ); + "Starting import with override = #arguments.override#...
    " + ); if ( !isJSON( data ) ) { - throw( message: "Cannot import file as the contents is not JSON", type: "InvalidImportFormat" ); + throw( + message = "Cannot import file as the contents is not JSON", + type = "InvalidImportFormat" + ); } variables.logger.info( "Site import from file requested." ); @@ -385,38 +368,40 @@ component required any importData, boolean override = false, required any importLog - ){ + ) { // if struct, inflate into an array if ( isStruct( arguments.importData ) ) { - arguments.importData = [ arguments.importData ]; + arguments.importData = [ arguments.importData]; } - variables.logger.info( "+ Site import will try to import (#arrayLen( arguments.importData )#) sites." ); + variables.logger.info( + "+ Site import will try to import (#arrayLen( arguments.importData )#) sites." + ); transaction { // iterate and import for ( var thisSite in arguments.importData ) { - var oSite = this.findBySlug( slug: thisSite.slug ); - oSite = ( isNull( oSite ) ? new () : oSite ); + var oSite = this.findBySlug( slug = thisSite.slug ); + oSite = ( isNull( oSite ) ? new() : oSite ); // if new or persisted with override then save. if ( !oSite.isLoaded() ) { variables.logger.info( "+ New site will be imported: #thisSite.slug#" ); arguments.importLog.append( "New site will be imported: #thisSite.slug#
    " ); importSite( - site : oSite, - memento : thisSite, - importLog: arguments.importLog, - override : arguments.override + site = oSite, + memento = thisSite, + importLog = arguments.importLog, + override = arguments.override ); - } else if ( oSite.isLoaded() and arguments.override ) { + } else if ( oSite.isLoaded() && arguments.override ) { variables.logger.info( "+ Overidding site: #thisSite.slug#" ); arguments.importLog.append( "Overriding site: #thisSite.slug#
    " ); importSite( - site : oSite, - memento : thisSite, - importLog: arguments.importLog, - override : arguments.override + site = oSite, + memento = thisSite, + importLog = arguments.importLog, + override = arguments.override ); } else { variables.logger.info( "!! Skipping persisted site: #thisSite.slug#" ); @@ -449,18 +434,18 @@ component required memento, required importLog, required override - ){ + ) { // Setup logging function - var logThis = function( message ){ + var logThis = function( message ) { variables.logger.info( arguments.message ); importLog.append( arguments.message & "
    " ); }; transaction { // Aliases - var oSite = arguments.site; - var siteData = arguments.memento; - var populator = getBeanPopulator(); + var oSite = arguments.site; + var siteData = arguments.memento; + var populator = getBeanPopulator(); // populate content from data and ignore relationships, we need to build those manually. var excludedFields = [ "categories", @@ -472,11 +457,11 @@ component "settings" ]; populator.populateFromStruct( - target = oSite, - memento = siteData, - exclude = arrayToList( excludedFields ), - composeRelationships = false - ); + target = oSite, + memento = siteData, + exclude = arrayToList( excludedFields ), + composeRelationships = false + ); // Import Settings if ( oSite.isLoaded() ) { @@ -484,98 +469,111 @@ component // If persisted, do cleanups // If on Adobe, run hard deletes due to Hibernate issue with cascade on integration tests. if ( !server.keyExists( "lucee" ) ) { - variables.settingService.deleteWhere( site: oSite ); + variables.settingService.deleteWhere( site = oSite ); } oSite.removeAllSettings(); } oSite.setSettings( - siteData.settings.map( function( thisSetting ){ - // Avoid the boolean casting to string of hell!! - arguments.thisSetting.value = toString( arguments.thisSetting.value ); - logThis( "+ Importing setting: (#thisSetting.name#) to site #oSite.getSlug()#" ); - return populator - .populateFromStruct( - target = variables.settingService.new(), - memento = thisSetting, - exclude = "site", - composeRelationships = false + siteData.settings.map( + function( thisSetting ) { + arguments.thisSetting.value = toString( arguments.thisSetting.value ); + logThis( + "+ Importing setting: (#thisSetting.name#) to site #oSite.getSlug()#" + ); + return populator.populateFromStruct( + target = variables.settingService.new(), + memento = thisSetting, + exclude = "site", + composeRelationships = false + ).setSite( oSite ); + } ) - .setSite( oSite ); - } ) - ); + ); logThis( "+ Site settings for #oSite.getSlug()# imported." ); // IMPORT CATEGORIES if ( arrayLen( siteData.categories ) ) { oSite.setCategories( - siteData.categories.map( function( thisCategory ){ - logThis( "+ Importing category: (#thisCategory.slug#) to site #oSite.getSlug()#" ); - if ( oSite.isLoaded() ) { - return variables.categoryService.getOrCreateBySlug( - category: thisCategory.slug, - site : oSite - ); - } - return variables.categoryService.new( { - category : arguments.thisCategory.category, - slug : arguments.thisCategory.slug, - site : oSite - } ); - } ) - ); + siteData.categories.map( + function( thisCategory ) { + logThis( + "+ Importing category: (#thisCategory.slug#) to site #oSite.getSlug()#" + ); + if ( oSite.isLoaded() ) { + return variables.categoryService.getOrCreateBySlug( category = thisCategory.slug, + site = oSite ); + } + return variables.categoryService.new( + { + category : arguments.thisCategory.category, + slug : arguments.thisCategory.slug, + site : oSite + } + ); + } + ) + ); logThis( "+ Site categories for #oSite.getSlug()# imported." ); } // Note it for persistence so we can do the rest of the relationships this.save( oSite ); - logThis( "+ Site (#oSite.getSlug()#) saved to session, starting to import content now..." ); + logThis( + "+ Site (#oSite.getSlug()#) saved to session, starting to import content now..." + ); // IMPORT MENUS if ( arrayLen( siteData.menus ) ) { - logThis( "+ Importing menus (#arrayLen( siteData.menus )#) to site #arguments.site.getSlug()#" ); - getWireBox() - .getInstance( "menuService@contentbox" ) - .importFromData( - importData: siteData.menus, - override : arguments.override, - importLog : arguments.importLog, - site : oSite + logThis( + "+ Importing menus (#arrayLen( siteData.menus )#) to site #arguments.site.getSlug()#" + ); + getWireBox().getInstance( "menuService@contentbox" ).importFromData( + importData = siteData.menus, + override = arguments.override, + importLog = arguments.importLog, + site = oSite ); - logThis( "+ Imported (#arrayLen( siteData.menus )#) menus to site #arguments.site.getSlug()#" ); + logThis( + "+ Imported (#arrayLen( siteData.menus )#) menus to site #arguments.site.getSlug()#" + ); } else { logThis( "!! No menus found on import data, skipping..." ); } // IMPORT ENTRIES if ( arrayLen( siteData.entries ) ) { - logThis( "+ Importing entries (#arrayLen( siteData.entries )#) to site #arguments.site.getSlug()#" ); - getWireBox() - .getInstance( "entryService@contentbox" ) - .importFromData( - importData: siteData.entries, - override : arguments.override, - importLog : arguments.importLog, - site : oSite + logThis( + "+ Importing entries (#arrayLen( siteData.entries )#) to site #arguments.site.getSlug()#" + ); + getWireBox().getInstance( "entryService@contentbox" ).importFromData( + importData = siteData.entries, + override = arguments.override, + importLog = arguments.importLog, + site = oSite ); - logThis( "+ Imported (#arrayLen( siteData.entries )#) entries to site #arguments.site.getSlug()#" ); + logThis( + "+ Imported (#arrayLen( siteData.entries )#) entries to site #arguments.site.getSlug()#" + ); } else { logThis( "!! No entries found on import data, skipping..." ); } // IMPORT PAGES if ( arrayLen( siteData.pages ) ) { - logThis( "+ Importing pages (#arrayLen( siteData.pages )#) to site #arguments.site.getSlug()#" ); - getWireBox() - .getInstance( "pageService@contentbox" ) - .importFromData( - importData: siteData.pages, - override : arguments.override, - importLog : arguments.importLog, - site : oSite + logThis( + "+ Importing pages (#arrayLen( siteData.pages )#) to site #arguments.site.getSlug()#" + ); + getWireBox().getInstance( "pageService@contentbox" ).importFromData( + importData = siteData.pages, + override = arguments.override, + importLog = arguments.importLog, + site = oSite ); - logThis( "+ Imported (#arrayLen( siteData.pages )#) pages to site #arguments.site.getSlug()#" ); + logThis( + "+ Imported (#arrayLen( siteData.pages )#) pages to site #arguments.site.getSlug()#" + ); } else { logThis( "!! No pages found on import data, skipping..." ); } @@ -585,13 +583,11 @@ component logThis( "+ Importing contentStore (#arrayLen( siteData.contentStore )#) to site #arguments.site.getSlug()#" ); - getWireBox() - .getInstance( "contentStoreService@contentbox" ) - .importFromData( - importData: siteData.contentStore, - override : arguments.override, - importLog : arguments.importLog, - site : oSite + getWireBox().getInstance( "contentStoreService@contentbox" ).importFromData( + importData = siteData.contentStore, + override = arguments.override, + importLog = arguments.importLog, + site = oSite ); logThis( @@ -601,7 +597,48 @@ component logThis( "!! No contentStore found on import data, skipping..." ); } } + // end site transaction } -} + /** + * Save operation called by the transactional `save()` method + * Due to transactional issues on Lucee + */ + private function _save( required site ) { + // Create all site settings if this is a new site + if ( !arguments.site.isLoaded() ) { + variables.settingService.saveAll( + variables + .settingService + .getSiteSettingDefaults() + .reduce( + function( result, setting, value ) { + arguments.result.append( + variables.settingService.new( + { + name : arguments.setting, + value : trim( arguments.value ), + isCore : true, + site : site + } + ) + ); + return arguments.result; + }, + [] + ) + ); + } + + // Persist the site + super.save( arguments.site ); + + // Activate the site's theme + variables.themeService.startupTheme( name = arguments.site.getActiveTheme(), site = arguments.site ); + + // Create media root folder for the site + ensureSiteMediaFolder( arguments.site ); + } + +} \ No newline at end of file diff --git a/modules/contentbox/models/ui/AdminMenuService.cfc b/modules/contentbox/models/ui/AdminMenuService.cfc index c14e8d201..d037363b8 100755 --- a/modules/contentbox/models/ui/AdminMenuService.cfc +++ b/modules/contentbox/models/ui/AdminMenuService.cfc @@ -6,7 +6,6 @@ * Manages the admin menu services for the header and top menu */ component accessors="true" threadSafe singleton { - /** * This holds the top menu structure */ @@ -38,27 +37,22 @@ component accessors="true" threadSafe singleton { /** * Injected Avatar */ - property - name ="avatar" - type ="any" - inject="Avatar@contentbox"; - + property name="avatar" type="any" inject="Avatar@contentbox"; // DI property name="log" inject="logbox:logger:{this}"; - // Top Menu Slugs - this.DASHBOARD = "dashboard"; - this.CONTENT = "content"; - this.COMMENTS = "comments"; - this.LOOK_FEEL = "lookAndFeel"; - this.MODULES = "modules"; - this.USERS = "users"; - this.TOOLS = "tools"; - this.SYSTEM = "system"; + this.DASHBOARD = "dashboard"; + this.CONTENT = "content"; + this.COMMENTS = "comments"; + this.LOOK_FEEL = "lookAndFeel"; + this.MODULES = "modules"; + this.USERS = "users"; + this.TOOLS = "tools"; + this.SYSTEM = "system"; this.ADMIN_ENTRYPOINT = ""; // Header Menu Slugs - this.HEADER_ABOUT = "about"; + this.HEADER_ABOUT = "about"; this.HEADER_PROFILE = "profile"; /** @@ -69,33 +63,33 @@ component accessors="true" threadSafe singleton { * @coldbox ColdBox Handler * @coldbox.inject coldbox */ - AdminMenuService function init( required requestService, required coldbox ){ + AdminMenuService function init( required requestService, required coldbox ) { // init menu array - variables.topMenu = []; + variables.topMenu = []; // init top menu structure holders - variables.topMenuMap = {}; + variables.topMenuMap = {}; // init header menu array - variables.headerMenu = []; + variables.headerMenu = []; // init header menu array - variables.supportMenu = []; + variables.supportMenu = []; // init header menu array - variables.utilsMenu = []; + variables.utilsMenu = []; // init header menu array - variables.profileMenu = []; + variables.profileMenu = []; // init profile menu structure - variables.headerMenuMap = {}; + variables.headerMenuMap = {}; // top menu pointer - variables.thisTopMenu = ""; + variables.thisTopMenu = ""; // header menu pointer variables.thisHeaderMenu = ""; // store request service variables.requestService = arguments.requestService; // store coldbox - variables.coldbox = arguments.coldbox; + variables.coldbox = arguments.coldbox; // Store admin entry point - this.ADMIN_ENTRYPOINT = arguments.coldbox.getSetting( "modules" )[ "contentbox-admin" ].entryPoint; + this.ADMIN_ENTRYPOINT = arguments.coldbox.getSetting( "modules" )[ "contentbox-admin" ].entryPoint; // store module info - variables.moduleConfig = arguments.coldbox.getSetting( "modules" )[ "contentbox" ]; + variables.moduleConfig = arguments.coldbox.getSetting( "modules" )[ "contentbox" ]; // create default menus createHeaderMenu(); createDefaultMenu(); @@ -108,13 +102,17 @@ component accessors="true" threadSafe singleton { * @event The event context * @menu The menu struct */ - function buildLIAttributes( required any event, required any menu ){ + function buildLIAttributes( required any event, required any menu ) { var attributes = { "class" : "#menu.class#", "data-name" : "#menu.name#" }; if ( structKeyExists( menu, "id" ) && len( menu.id ) ) { attributes[ "id" ] = menu.id; } if ( event.getPrivateValue( name = "tab#menu.name#", defaultValue = false ) ) { - listAppend( attributes.class, "active", " " ); + listAppend( + attributes.class, + "active", + " " + ); } return createAttributeList( attributes ); } @@ -130,7 +128,7 @@ component accessors="true" threadSafe singleton { required any event, required any menu, structDefaults = {} - ){ + ) { var attributes = { "class" : structKeyExists( structDefaults, "class" ) ? structDefaults.class : "" }; if ( len( arguments.menu.itemClass ) ) { @@ -182,7 +180,7 @@ component accessors="true" threadSafe singleton { * * @attributes The struct of attributes to build the list from */ - function createAttributeList( required struct attributes ){ + function createAttributeList( required struct attributes ) { var attributeList = ""; for ( var key in arguments.attributes ) { attributeList &= "#key#=""#attributes[ key ]#"""; @@ -193,13 +191,13 @@ component accessors="true" threadSafe singleton { /** * Create the default ContentBox header menu contributions */ - AdminMenuService function createHeaderMenu(){ + AdminMenuService function createHeaderMenu() { var event = requestService.getContext(); // Exit Handlers - var xehMyProfile = "#this.ADMIN_ENTRYPOINT#.authors.myprofile"; - var xehDoLogout = "#this.ADMIN_ENTRYPOINT#.security.doLogout"; - var xehAbout = "#this.ADMIN_ENTRYPOINT#.dashboard.about"; + var xehMyProfile = "#this.ADMIN_ENTRYPOINT#.authors.myprofile"; + var xehDoLogout = "#this.ADMIN_ENTRYPOINT#.security.doLogout"; + var xehAbout = "#this.ADMIN_ENTRYPOINT#.dashboard.about"; var xehAdminAction = "#this.ADMIN_ENTRYPOINT#.dashboard.reload"; // Register Profile Menu @@ -215,8 +213,7 @@ component accessors="true" threadSafe singleton { href = variables.buildLink, href_to = xehMyProfile, data = { keybinding : "ctrl+shift+a" } - ) - .addHeaderSubMenu( + ).addHeaderSubMenu( name = "logout", title = "ctrl+shift+L", label = " Logout", @@ -235,11 +232,12 @@ component accessors="true" threadSafe singleton { permissions = "RELOAD_MODULES,RELOAD_CACHES", data = { placement : "right" }, title = "Admin Actions" - ).addHeaderSubMenu( + ) + .addHeaderSubMenu( name = "rsscache", label = "Clear RSS Caches", permissions = "RELOAD_CACHES", - href = function( required menu, required event ){ + href = function( required menu, required event ) { return "javascript:adminAction( 'rss-purge', '#arguments.event.buildLink( xehAdminAction )#' )"; } ) @@ -247,7 +245,7 @@ component accessors="true" threadSafe singleton { name = "contentpurge", label = "Clear Content Caches", permissions = "RELOAD_CACHES", - href = function( required menu, required event ){ + href = function( required menu, required event ) { return "javascript:adminAction( 'content-purge', '#arguments.event.buildLink( xehAdminAction )#' )"; } ) @@ -255,7 +253,7 @@ component accessors="true" threadSafe singleton { name = "cachepurge", label = "Clear Template Cache", permissions = "RELOAD_CACHES", - href = function( required menu, required event ){ + href = function( required menu, required event ) { return "javascript:adminAction( 'cache-purge', '#arguments.event.buildLink( xehAdminAction )#' )"; } ) @@ -263,7 +261,7 @@ component accessors="true" threadSafe singleton { name = "app", label = "Reload Application", permissions = "RELOAD_MODULES", - href = function( required menu, required event ){ + href = function( required menu, required event ) { return "javascript:adminAction( 'app', '#arguments.event.buildLink( xehAdminAction )#' )"; } ) @@ -271,11 +269,11 @@ component accessors="true" threadSafe singleton { name = "orm", label = "Reload ORM", permissions = "RELOAD_MODULES", - href = function( required menu, required event ){ + href = function( required menu, required event ) { return "javascript:adminAction( 'orm', '#arguments.event.buildLink( xehAdminAction )#' )"; } - ) - ; + ); + return this; } @@ -286,7 +284,7 @@ component accessors="true" threadSafe singleton { * @menu The menu info * @event The request context */ - function buildLink( required menu, required event ){ + function buildLink( required menu, required event ) { // Normal a links if ( structKeyExists( arguments.menu, "href_to" ) ) { return arguments.event.buildLink( arguments.menu.href_to ); @@ -299,17 +297,17 @@ component accessors="true" threadSafe singleton { /** * Dynamic menu label */ - function buildProfileLabel(){ + function buildProfileLabel() { var event = requestService.getContext(); - var prc = event.getCollection( private = true ); + var prc = event.getCollection( private = true ); savecontent variable="profileLabel" { writeOutput( variables.avatar.renderAvatar( - email = prc.oCurrentAuthor.getEmail(), - size = "35", - class = "img-circle" - ) + email = prc.oCurrentAuthor.getEmail(), + size = "35", + class = "img-circle" + ) ); writeOutput( "" ); } @@ -320,45 +318,45 @@ component accessors="true" threadSafe singleton { /** * Create the default ContentBox menu */ - AdminMenuService function createDefaultMenu(){ + AdminMenuService function createDefaultMenu() { var event = requestService.getContext(); - var prc = {}; + var prc = {}; // Global Admin Exit Handlers prc.xehDashboard = "#this.ADMIN_ENTRYPOINT#.dashboard"; // Entries Tab - prc.xehEntries = "#this.ADMIN_ENTRYPOINT#.entries"; + prc.xehEntries = "#this.ADMIN_ENTRYPOINT#.entries"; prc.xehEntriesEditor = "#this.ADMIN_ENTRYPOINT#.entries.editor"; - prc.xehCategories = "#this.ADMIN_ENTRYPOINT#.categories"; - prc.xehTemplates = "#this.ADMIN_ENTRYPOINT#.contentTemplates"; + prc.xehCategories = "#this.ADMIN_ENTRYPOINT#.categories"; + prc.xehTemplates = "#this.ADMIN_ENTRYPOINT#.contentTemplates"; // Content Tab - prc.xehPages = "#this.ADMIN_ENTRYPOINT#.pages"; - prc.xehPagesEditor = "#this.ADMIN_ENTRYPOINT#.pages.editor"; + prc.xehPages = "#this.ADMIN_ENTRYPOINT#.pages"; + prc.xehPagesEditor = "#this.ADMIN_ENTRYPOINT#.pages.editor"; prc.xehContentStore = "#this.ADMIN_ENTRYPOINT#.contentstore"; prc.xehMediaManager = "#this.ADMIN_ENTRYPOINT#.mediamanager"; - prc.xehMenuManager = "#this.ADMIN_ENTRYPOINT#.menus"; + prc.xehMenuManager = "#this.ADMIN_ENTRYPOINT#.menus"; // Comments Tab - prc.xehComments = "#this.ADMIN_ENTRYPOINT#.comments"; + prc.xehComments = "#this.ADMIN_ENTRYPOINT#.comments"; prc.xehCommentsettings = "#this.ADMIN_ENTRYPOINT#.comments.settings"; - prc.xehSubscribers = "#this.ADMIN_ENTRYPOINT#.subscribers"; + prc.xehSubscribers = "#this.ADMIN_ENTRYPOINT#.subscribers"; // Look and Feel Tab - prc.xehThemes = "#this.ADMIN_ENTRYPOINT#.themes"; + prc.xehThemes = "#this.ADMIN_ENTRYPOINT#.themes"; prc.xehactiveTheme = "#this.ADMIN_ENTRYPOINT#.themes.active"; - prc.xehWidgets = "#this.ADMIN_ENTRYPOINT#.widgets"; - prc.xehGlobalHTML = "#this.ADMIN_ENTRYPOINT#.globalHTML"; + prc.xehWidgets = "#this.ADMIN_ENTRYPOINT#.widgets"; + prc.xehGlobalHTML = "#this.ADMIN_ENTRYPOINT#.globalHTML"; // Modules prc.xehModules = "#this.ADMIN_ENTRYPOINT#.modules"; // Authors Tab - prc.xehAuthors = "#this.ADMIN_ENTRYPOINT#.authors"; - prc.xehAuthorEditor = "#this.ADMIN_ENTRYPOINT#.authors.editor"; - prc.xehPermissions = "#this.ADMIN_ENTRYPOINT#.permissions"; - prc.xehRoles = "#this.ADMIN_ENTRYPOINT#.roles"; + prc.xehAuthors = "#this.ADMIN_ENTRYPOINT#.authors"; + prc.xehAuthorEditor = "#this.ADMIN_ENTRYPOINT#.authors.editor"; + prc.xehPermissions = "#this.ADMIN_ENTRYPOINT#.permissions"; + prc.xehRoles = "#this.ADMIN_ENTRYPOINT#.roles"; prc.xehPermissionGroups = "#this.ADMIN_ENTRYPOINT#.permissionGroups"; // Tools @@ -366,12 +364,12 @@ component accessors="true" threadSafe singleton { prc.xehToolsExport = "#this.ADMIN_ENTRYPOINT#.tools.exporter"; // System - prc.xehSettings = "#this.ADMIN_ENTRYPOINT#.settings"; - prc.xehSitesManager = "#this.ADMIN_ENTRYPOINT#.sites"; + prc.xehSettings = "#this.ADMIN_ENTRYPOINT#.settings"; + prc.xehSitesManager = "#this.ADMIN_ENTRYPOINT#.sites"; prc.xehSecurityRules = "#this.ADMIN_ENTRYPOINT#.securityrules"; - prc.xehRawSettings = "#this.ADMIN_ENTRYPOINT#.settings.raw"; - prc.xehAuthLogs = "#this.ADMIN_ENTRYPOINT#.settings.authLogs"; - prc.xehAbout = "#this.ADMIN_ENTRYPOINT#.about"; + prc.xehRawSettings = "#this.ADMIN_ENTRYPOINT#.settings.raw"; + prc.xehAuthLogs = "#this.ADMIN_ENTRYPOINT#.settings.authLogs"; + prc.xehAbout = "#this.ADMIN_ENTRYPOINT#.about"; // Dashboard addTopMenu( @@ -382,7 +380,10 @@ component accessors="true" threadSafe singleton { ); // Content - addTopMenu( name = this.CONTENT, label = " Content" ) + addTopMenu( + name = this.CONTENT, + label = " Content" + ) .addSubMenu( name = "Pages", label = "Sitemap", @@ -435,7 +436,10 @@ component accessors="true" threadSafe singleton { ); // Comments - addTopMenu( name = this.COMMENTS, label = " Comments" ) + addTopMenu( + name = this.COMMENTS, + label = " Comments" + ) .addSubMenu( name = "Inbox", label = "Inbox", @@ -459,7 +463,10 @@ component accessors="true" threadSafe singleton { ); // Look and Feel - addTopMenu( name = this.LOOK_FEEL, label = " Look & Feel" ) + addTopMenu( + name = this.LOOK_FEEL, + label = " Look & Feel" + ) .addSubMenu( name = "activetheme", label = "Active Theme", @@ -495,11 +502,11 @@ component accessors="true" threadSafe singleton { label = " Modules", permissions = "MODULES_ADMIN" ).addSubMenu( - name = "Manage", - label = "Manage", - href = variables.buildLink, - href_to = prc.xehModules - ); + name = "Manage", + label = "Manage", + href = variables.buildLink, + href_to = prc.xehModules + ); // User addTopMenu( name = this.USERS, label = " Users" ) @@ -533,15 +540,16 @@ component accessors="true" threadSafe singleton { ); // Tools - addTopMenu( name = this.TOOLS, label = " Tools" ) - .addSubMenu( + addTopMenu( + name = this.TOOLS, + label = " Tools" + ).addSubMenu( name = "Import", label = "Import", href = variables.buildLink, href_to = prc.xehToolsImport, permissions = "TOOLS_IMPORT" - ) - .addSubMenu( + ).addSubMenu( name = "Export", label = "Export", href = variables.buildLink, @@ -554,7 +562,8 @@ component accessors="true" threadSafe singleton { name = this.SYSTEM, label = " System", permissions = "SYSTEM_TAB" - ).addSubMenu( + ) + .addSubMenu( name = "AuthLogs", label = "Auth Logs", href = variables.buildLink, @@ -613,23 +622,23 @@ component accessors="true" threadSafe singleton { required string to, queryString = "", boolean ssl = false - ){ + ) { var event = requestService.getContext(); // Remove by 6.x if ( !isNull( arguments.linkTo ) ) { arguments.to = arguments.linkTo; } return event.buildLink( - to = "#this.ADMIN_ENTRYPOINT#.module.#arguments.module#.#arguments.to#", - queryString = arguments.queryString, - ssl = arguments.ssl - ); + to = "#this.ADMIN_ENTRYPOINT#.module.#arguments.module#.#arguments.to#", + queryString = arguments.queryString, + ssl = arguments.ssl + ); } /** * @name The name of the top menu */ - AdminMenuService function withTopMenu( required name ){ + AdminMenuService function withTopMenu( required name ) { thisTopMenu = arguments.name; return this; } @@ -639,7 +648,7 @@ component accessors="true" threadSafe singleton { * * @name The name of the header menu */ - AdminMenuService function withHeaderMenu( required name ){ + AdminMenuService function withHeaderMenu( required name ) { thisHeaderMenu = arguments.name; return this; } @@ -673,9 +682,9 @@ component accessors="true" threadSafe singleton { itemType = "a", itemClass = "", itemId = "" - ){ + ) { // stash pointer - variables.thisTopMenu = arguments.name; + variables.thisTopMenu = arguments.name; // store new top menu in reference map variables.topMenuMap[ arguments.name ] = { submenu : [] }; structAppend( @@ -718,9 +727,9 @@ component accessors="true" threadSafe singleton { itemType = "a", itemClass = "", itemId = "" - ){ + ) { // stash pointer - variables.thisHeaderMenu = arguments.name; + variables.thisHeaderMenu = arguments.name; // store new top menu in reference map variables.headerMenuMap[ arguments.name ] = { submenu : [] }; @@ -766,20 +775,22 @@ component accessors="true" threadSafe singleton { itemType = "a", itemClass = "", itemId = "" - ){ + ) { // Check if thisTopMenu set? - if ( !len( thisTopMenu ) AND !structKeyExists( arguments, "topMenu" ) ) { + if ( !len( thisTopMenu ) && !structKeyExists( arguments, "topMenu" ) ) { throw( "No top menu passed or concatenated with" ); } // check this pointer - if ( len( thisTopMenu ) AND !structKeyExists( arguments, "topMenu" ) ) { + if ( len( thisTopMenu ) && !structKeyExists( arguments, "topMenu" ) ) { arguments.topmenu = thisTopMenu; } // store in top menu if ( variables.topMenuMap.keyExists( arguments.topMenu ) ) { arrayAppend( variables.topMenuMap[ arguments.topMenu ].submenu, arguments ); } else { - log.warn( "requested top menu (#arguments.topMenu#) does not exist when calling addSubMenu()" ); + log.warn( + "requested top menu (#arguments.topMenu#) does not exist when calling addSubMenu()" + ); } // return return this; @@ -816,14 +827,14 @@ component accessors="true" threadSafe singleton { itemType = "a", itemClass = "", itemId = "" - ){ + ) { // Check if thisTopMenu set? - if ( !len( thisHeaderMenu ) AND !structKeyExists( arguments, "headerMenu" ) ) { + if ( !len( thisHeaderMenu ) && !structKeyExists( arguments, "headerMenu" ) ) { throw( "No header menu passed or concatenated with" ); } // check this pointer - if ( len( thisHeaderMenu ) AND !structKeyExists( arguments, "headerMenu" ) ) { + if ( len( thisHeaderMenu ) && !structKeyExists( arguments, "headerMenu" ) ) { arguments.headerMenu = thisHeaderMenu; } @@ -831,7 +842,9 @@ component accessors="true" threadSafe singleton { if ( headerMenuMap.keyExists( arguments.headerMenu ) ) { arrayAppend( headerMenuMap[ arguments.headerMenu ].submenu, arguments ); } else { - log.warn( "requested header menu (#arguments.headerMenu#) does not exist when calling addHeaderSubMenu()" ); + log.warn( + "requested header menu (#arguments.headerMenu#) does not exist when calling addHeaderSubMenu()" + ); } // return @@ -844,17 +857,17 @@ component accessors="true" threadSafe singleton { * @topMenu The optional top menu name to add this sub level menu to or if concatenated then it uses that one. * @name The unique name for this sub level menu */ - AdminMenuService function removeSubMenu( required topMenu, required name ){ + AdminMenuService function removeSubMenu( required topMenu, required name ) { // Verify top menu exists, else just return if ( !variables.topMenuMap.keyExists( arguments.topMenu ) ) { log.warn( - "Cannot remove submenu (#arguments.name#) as the top menu requested (#arguments.topMenu#) does not exist" - ); + "Cannot remove submenu (#arguments.name#) as the top menu requested (#arguments.topMenu#) does not exist" + ); return this; } - for ( var x = 1; x lte arrayLen( variables.topMenuMap[ arguments.topMenu ].subMenu ); x++ ) { - if ( variables.topMenuMap[ arguments.topMenu ].subMenu[ x ].name eq arguments.name ) { + for ( var x = 1; x LTE arrayLen( variables.topMenuMap[ arguments.topMenu ].subMenu ); x++ ) { + if ( variables.topMenuMap[ arguments.topMenu ].subMenu[ x ].name EQ arguments.name ) { arrayDeleteAt( variables.topMenuMap[ arguments.topMenu ].subMenu, x ); break; } @@ -870,17 +883,17 @@ component accessors="true" threadSafe singleton { * @headerMenu The optional header menu name to remove from * @name The sub menu to remove */ - AdminMenuService function removeHeaderSubMenu( required headerMenu, required name ){ + AdminMenuService function removeHeaderSubMenu( required headerMenu, required name ) { // Verify top menu exists, else just return if ( !variables.headerMenuMap.keyExists( arguments.headerMenu ) ) { log.warn( - "Cannot remove submenu (#arguments.name#) as the header menu requested (#arguments.headerMenu#) does not exist" - ); + "Cannot remove submenu (#arguments.name#) as the header menu requested (#arguments.headerMenu#) does not exist" + ); return this; } - for ( var x = 1; x lte arrayLen( variables.headerMenuMap[ arguments.headerMenu ].subMenu ); x++ ) { - if ( variables.headerMenuMap[ arguments.headerMenu ].subMenu[ x ].name eq arguments.name ) { + for ( var x = 1; x LTE arrayLen( variables.headerMenuMap[ arguments.headerMenu ].subMenu ); x++ ) { + if ( variables.headerMenuMap[ arguments.headerMenu ].subMenu[ x ].name EQ arguments.name ) { arrayDeleteAt( variables.headerMenuMap[ arguments.headerMenu ].subMenu, x ); break; } @@ -895,11 +908,11 @@ component accessors="true" threadSafe singleton { * * @topMenu The optional top menu name to add this sub level menu to or if concatenated then it uses that one. */ - AdminMenuService function removeTopMenu( required topMenu ){ + AdminMenuService function removeTopMenu( required topMenu ) { var found = false; - for ( var x = 1; x lte arrayLen( variables.topMenu ); x++ ) { - if ( variables.topMenu[ x ].name eq arguments.topMenu ) { + for ( var x = 1; x LTE arrayLen( variables.topMenu ); x++ ) { + if ( variables.topMenu[ x ].name EQ arguments.topMenu ) { found = true; arrayDeleteAt( variables.topMenu, x ); structDelete( variables.topMenuMap, arguments.topMenu ); @@ -908,7 +921,9 @@ component accessors="true" threadSafe singleton { } if ( !found ) { - log.warn( "Top menu: #arguments.topMenu# not found when calling removeTopMenu" ); + log.warn( + "Top menu: #arguments.topMenu# not found when calling removeTopMenu" + ); } // return @@ -920,11 +935,11 @@ component accessors="true" threadSafe singleton { * * @headerMenu The header menu unique name to remove */ - AdminMenuService function removeHeaderMenu( required headerMenu ){ + AdminMenuService function removeHeaderMenu( required headerMenu ) { var found = false; - for ( var x = 1; x lte arrayLen( variables.headerMenu ); x++ ) { - if ( variables.headerMenu[ x ].name eq arguments.headerMenu ) { + for ( var x = 1; x LTE arrayLen( variables.headerMenu ); x++ ) { + if ( variables.headerMenu[ x ].name EQ arguments.headerMenu ) { found = true; arrayDeleteAt( variables.headerMenu, x ); structDelete( variables.headerMenuMap, arguments.headerMenu ); @@ -933,7 +948,9 @@ component accessors="true" threadSafe singleton { } if ( !found ) { - log.warn( "Header menu: #arguments.headerMenu# not found when calling removeHeaderMenu" ); + log.warn( + "Header menu: #arguments.headerMenu# not found when calling removeHeaderMenu" + ); } // return @@ -941,16 +958,16 @@ component accessors="true" threadSafe singleton { } /** - * Generate main top admin menu navigation bar + * Generate main top admin menu navigation bar */ - any function generateMenu(){ - var event = requestService.getContext(); - var prc = event.getCollection( private = true ); - var genMenu = ""; + any function generateMenu() { + var event = requestService.getContext(); + var prc = event.getCollection( private = true ); + var genMenu = ""; var thisMenu = variables.topMenu; savecontent variable="genMenu" { - include "templates/navAdminMenu.cfm"; + include template="templates/navAdminMenu.cfm"; } // return it @@ -960,14 +977,14 @@ component accessors="true" threadSafe singleton { /** * Generate the header menu */ - any function generateHeaderMenu(){ - var event = requestService.getContext(); - var prc = event.getCollection( private = true ); - var genMenu = ""; + any function generateHeaderMenu() { + var event = requestService.getContext(); + var prc = event.getCollection( private = true ); + var genMenu = ""; var thisMenu = variables.headerMenu; savecontent variable="genMenu" { - include "templates/nav.cfm"; + include template="templates/nav.cfm"; } // return it @@ -977,14 +994,14 @@ component accessors="true" threadSafe singleton { /** * Generate the utility menu */ - any function generateUtilsMenu(){ - var event = requestService.getContext(); - var prc = event.getCollection( private = true ); - var genMenu = ""; + any function generateUtilsMenu() { + var event = requestService.getContext(); + var prc = event.getCollection( private = true ); + var genMenu = ""; var thisMenu = variables.headerMenuMap[ "utils" ]; savecontent variable="genMenu" { - include "templates/subNav.cfm"; + include template="templates/subNav.cfm"; } // return it @@ -994,14 +1011,14 @@ component accessors="true" threadSafe singleton { /** * Generate dynamic profile menu */ - any function generateProfileMenu(){ - var event = requestService.getContext(); - var prc = event.getCollection( private = true ); - var genMenu = ""; + any function generateProfileMenu() { + var event = requestService.getContext(); + var prc = event.getCollection( private = true ); + var genMenu = ""; var thisMenu = variables.headerMenuMap[ "profile" ]; savecontent variable="genMenu" { - include "templates/subNav.cfm"; + include template="templates/subNav.cfm"; } // return it @@ -1013,7 +1030,7 @@ component accessors="true" threadSafe singleton { * * @data The data struct */ - string function parseADataAttributes( required struct data ){ + string function parseADataAttributes( required struct data ) { var dataString = ""; for ( var dataKey in arguments.data ) { @@ -1025,4 +1042,4 @@ component accessors="true" threadSafe singleton { return dataString; } -} +} \ No newline at end of file diff --git a/modules/contentbox/models/ui/Avatar.cfc b/modules/contentbox/models/ui/Avatar.cfc index 37aa0c882..02e90769f 100755 --- a/modules/contentbox/models/ui/Avatar.cfc +++ b/modules/contentbox/models/ui/Avatar.cfc @@ -1,8 +1,7 @@ -/* +/* * An avatar model object for ContentBox */ component singleton { - // DI property name="requestService" inject="coldbox:requestService"; property name="requestStorage" inject="requestStorage@cbStorages"; @@ -10,7 +9,7 @@ component singleton { /** * Constructor */ - function init(){ + function init() { return this; } @@ -24,7 +23,7 @@ component singleton { required email, numeric size = 80, rating = "PG" - ){ + ) { return "//www.gravatar.com/avatar.php?gravatar_id=#lCase( hash( arguments.email ) )#&s=#arguments.size#&r=#rating#"; } @@ -39,29 +38,38 @@ component singleton { required email, numeric size = 80, string class = "gravatar" - ){ + ) { // check settings - var prc = variables.requestService.getContext().getPrivateCollection(); - if ( NOT prc.cbSettings.cb_gravatar_display ) { + var prc = variables + .requestService + .getContext() + .getPrivateCollection(); + if ( !prc.cbSettings.cb_gravatar_display ) { return ""; } // Build out avatar - return variables.requestStorage.getOrSet( "avatar-#hash( arguments.toString() )#", function(){ - // render it out - savecontent variable="local.avatar" { - writeOutput( - " + return variables.requestStorage.getOrSet( + "avatar-#hash( arguments.toString() )#", + function() { + savecontent variable="local.avatar" { + writeOutput( + " " - ); - } - return local.avatar; - } ); + ); + } + return local.avatar; + } + ); } -} +} \ No newline at end of file diff --git a/modules/contentbox/models/ui/BaseWidget.cfc b/modules/contentbox/models/ui/BaseWidget.cfc index 93b367742..21b77ef9f 100644 --- a/modules/contentbox/models/ui/BaseWidget.cfc +++ b/modules/contentbox/models/ui/BaseWidget.cfc @@ -1,12 +1,11 @@ -/** +/** * ContentBox - A Modular Content Platform * Copyright since 2012 by Ortus Solutions, Corp * www.ortussolutions.com/products/contentbox * --- * This is the base class for contentbox widgets which gives them access to ContentBox and ColdBox. */ -component accessors="true" extends="coldbox.system.FrameworkSupertype" { - +component accessors="true" extends ="coldbox.system.FrameworkSupertype" { // Shared DI all widgets receive property name="siteService" inject="siteService@contentbox"; property name="categoryService" inject="categoryService@contentbox"; @@ -23,59 +22,27 @@ component accessors="true" extends="coldbox.system.FrameworkSupertype" { property name="html" inject="HTMLHelper@coldbox"; property name="controller" inject="coldbox"; property name="log" inject="logbox:logger:{this}"; - // Local Properties - property - name ="name" - type ="string" - default=""; - - property - name ="version" - type ="string" - default=""; - - property - name ="description" - type ="string" - default=""; - - property - name ="author" - type ="string" - default=""; - - property - name ="authorURL" - type ="string" - default=""; - - property - name ="forgeBoxSlug" - type ="string" - default=""; - - property - name ="category" - type ="string" - default=""; - - property - name ="icon" - type ="string" - default=""; + property name="name" type="string" default=""; + property name="version" type="string" default=""; + property name="description" type="string" default=""; + property name="author" type="string" default=""; + property name="authorURL" type="string" default=""; + property name="forgeBoxSlug" type="string" default=""; + property name="category" type="string" default=""; + property name="icon" type="string" default=""; /** * Base Constructor */ - function init(){ - variables.name = ""; - variables.version = ""; + function init() { + variables.name = ""; + variables.version = ""; variables.description = ""; - variables.author = ""; - variables.authorURL = ""; - variables.category = ""; - variables.icon = ""; + variables.author = ""; + variables.authorURL = ""; + variables.category = ""; + variables.icon = ""; return this; } @@ -85,8 +52,11 @@ component accessors="true" extends="coldbox.system.FrameworkSupertype" { * * @throws BaseClassException */ - any function renderIt(){ - throw( message = "This is a base method that you must implement", type = "BaseClassException" ); + any function renderIt() { + throw( + message = "This is a base method that you must implement", + type = "BaseClassException" + ); } /** @@ -94,10 +64,10 @@ component accessors="true" extends="coldbox.system.FrameworkSupertype" { * * @return array */ - array function getPublicMethods(){ + array function getPublicMethods() { var publicMethods = []; - var meta = getMetadata( this ); - var method = ""; + var meta = getMetadata( this ); + var method = ""; for ( var i = 1; i <= arrayLen( meta.functions ); i++ ) { method = meta.functions[ i ]; @@ -108,9 +78,8 @@ component accessors="true" extends="coldbox.system.FrameworkSupertype" { // Add conditions if ( !listContains( "init,onMissingMethod", method.name ) ) { if ( - !structKeyExists( method, "access" ) || ( - structKeyExists( method, "access" ) && !listContains( "private,package", method.access ) - ) + !structKeyExists( method, "access" ) || + ( structKeyExists( method, "access" ) && !listContains( "private,package", method.access ) ) ) { arrayAppend( publicMethods, method ); } @@ -122,7 +91,7 @@ component accessors="true" extends="coldbox.system.FrameworkSupertype" { /** * Detect if we are in admin mode or in the UI, and retrieve the site accordingly */ - function getSite(){ + function getSite() { if ( findNoCase( variables.cb.adminRoot(), getRequestContext().getCurrentRoutedUrl() ) ) { return variables.siteService.getCurrentWorkingSite(); } @@ -130,4 +99,4 @@ component accessors="true" extends="coldbox.system.FrameworkSupertype" { return variables.cb.site(); } -} +} \ No newline at end of file diff --git a/modules/contentbox/models/ui/Paging.cfc b/modules/contentbox/models/ui/Paging.cfc index 9df7ab286..ceb5f8b0d 100755 --- a/modules/contentbox/models/ui/Paging.cfc +++ b/modules/contentbox/models/ui/Paging.cfc @@ -1,4 +1,4 @@ -/** +/** * ContentBox - A Modular Content Platform * Copyright since 2012 by Ortus Solutions, Corp * www.ortussolutions.com/products/contentbox @@ -41,11 +41,9 @@ * @author Luis Majano */ component accessors="true" singleton threadsafe { - // DI property name="controller" inject="coldbox"; property name="settingService" inject="settingService@contentbox"; - // Properties property name="pagingMaxRows"; property name="PagingBandGap"; @@ -53,11 +51,11 @@ component accessors="true" singleton threadsafe { /** * Constructor */ - function init(){ + function init() { return this; } - function onDIComplete(){ + function onDIComplete() { // Setup Paging Properties setPagingMaxRows( variables.settingService.getSetting( "cb_paging_maxrows" ) ); setPagingBandGap( variables.settingService.getSetting( "cb_paging_bandgap" ) ); @@ -71,15 +69,15 @@ component accessors="true" singleton threadsafe { * * @return struct of { startrow:numeric, maxrow:numeric } */ - struct function getBoundaries( numeric pagingMaxRows, numeric page ){ + struct function getBoundaries( numeric pagingMaxRows, numeric page ) { var boundaries = { "startrow" : 1, "maxrow" : 0 }; - var event = getController().getRequestService().getContext(); - var maxRows = !isNull( arguments.pagingMaxRows ) ? arguments.pagingMaxRows : getPagingMaxRows(); + var event = getController().getRequestService().getContext(); + var maxRows = !isNull( arguments.pagingMaxRows ) ? arguments.pagingMaxRows : getPagingMaxRows(); arguments.page = isNull( arguments.page ) ? event.getValue( "page", 1 ) : arguments.page; boundaries.startrow = ( arguments.page * maxrows - maxRows ) + 1; - boundaries.maxrow = boundaries.startrow + maxRows - 1; + boundaries.maxrow = boundaries.startrow + maxRows - 1; return boundaries; } @@ -99,32 +97,39 @@ component accessors="true" singleton threadsafe { numeric pagingMaxRows, boolean asList = false, numeric page - ){ - var event = variables.controller.getRequestService().getContext(); - var currentPage = isNull( arguments.page ) ? event.getValue( "page", 1 ) : arguments.page; + ) { + var event = variables + .controller + .getRequestService() + .getContext(); + var currentPage = isNull( arguments.page ) ? event.getValue( "page", 1 ) : arguments.page; var pagingTabsUI = ""; - var maxRows = !isNull( arguments.pagingMaxRows ) ? arguments.pagingMaxRows : getPagingMaxRows(); - var bandGap = getPagingBandGap(); - var theLink = arguments.link; - var pageFrom = 0; - var pageTo = 0; - var pageIndex = 0; + var maxRows = !isNull( arguments.pagingMaxRows ) ? arguments.pagingMaxRows : getPagingMaxRows(); + var bandGap = getPagingBandGap(); + var theLink = arguments.link; + var pageFrom = 0; + var pageTo = 0; + var pageIndex = 0; // Only page if records found if ( arguments.foundRows > 0 ) { // Calculate Total Pages var totalPages = ceiling( arguments.foundRows / maxRows ); - if ( currentPage gt totalPages ) { - variables.controller.relocate( url: replace( theLink, "@page@", totalPages ) ); + if ( currentPage GT totalPages ) { + variables.controller.relocate( url = replace( + theLink, + "@page@", + totalPages + ) ); } savecontent variable="pagingTabsUI" { - include "Paging.cfm"; + include template="Paging.cfm"; } } return pagingTabsUI; } -} +} \ No newline at end of file diff --git a/modules/contentbox/models/ui/Paging.cfm b/modules/contentbox/models/ui/Paging.cfm index 92b958cf9..0cd28d4cd 100644 --- a/modules/contentbox/models/ui/Paging.cfm +++ b/modules/contentbox/models/ui/Paging.cfm @@ -1,60 +1,113 @@ -
    -
    - - - +
    +
    + + + -
    - -
      +
    +
    - - - - + + + + + + + + + +
    + \ No newline at end of file diff --git a/modules/contentbox/models/ui/ThemeService.cfc b/modules/contentbox/models/ui/ThemeService.cfc index f4ed0332e..a960baca9 100644 --- a/modules/contentbox/models/ui/ThemeService.cfc +++ b/modules/contentbox/models/ui/ThemeService.cfc @@ -1,4 +1,4 @@ -/** +/** * ContentBox - A Modular Content Platform * Copyright since 2012 by Ortus Solutions, Corp * www.ortussolutions.com/products/contentbox @@ -6,7 +6,6 @@ * Manages ContentBox themes */ component accessors="true" threadSafe singleton { - // DI property name="settingService" inject="settingService@contentbox"; property name="siteService" inject="siteService@contentbox"; @@ -19,22 +18,18 @@ component accessors="true" threadSafe singleton { property name="html" inject="HTMLHelper@coldbox"; property name="cbHelper" inject="provider:CBHelper@contentbox"; property name="moduleService" inject="coldbox:moduleService"; - /** * The location of core themes on disk: Defaults to /modules/contentbox/themes */ property name="coreThemesPath"; - /** * The location of custom themes on disk: Defaults to /modules_app/contentbox-custom/_themes */ property name="customThemesPath"; - /** * The theme registry of records */ property name="themeRegistry" type="struct"; - /** * The cache of widgets for the active theme */ @@ -43,12 +38,12 @@ component accessors="true" threadSafe singleton { /** * Constructor */ - ThemeService function init(){ + ThemeService function init() { // init properties - variables.themeRegistry = {}; - variables.coreThemesPath = ""; + variables.themeRegistry = {}; + variables.coreThemesPath = ""; variables.customThemesPath = ""; - variables.widgetCache = {}; + variables.widgetCache = {}; return this; } @@ -56,9 +51,9 @@ component accessors="true" threadSafe singleton { /** * onDIComplete startup the theming services according to loaded module data */ - void function onDIComplete(){ + void function onDIComplete() { // setup location paths - variables.coreThemesPath = variables.moduleSettings[ "contentbox" ].path & "/themes"; + variables.coreThemesPath = variables.moduleSettings[ "contentbox" ].path & "/themes"; variables.customThemesPath = variables.moduleSettings[ "contentbox-custom" ].path & "/_themes"; // Register all themes @@ -68,18 +63,26 @@ component accessors="true" threadSafe singleton { /** * Does the current active theme have a maintenance view */ - boolean function themeMaintenanceViewExists(){ - return fileExists( expandPath( variables.CBHelper.themeRoot() & "/views/maintenance.cfm" ) ); + boolean function themeMaintenanceViewExists() { + return fileExists( + expandPath( variables.CBHelper.themeRoot() & "/views/maintenance.cfm" ) + ); } /** * Get the current active theme's maintenance layout */ - string function getThemeMaintenanceLayout(){ + string function getThemeMaintenanceLayout() { var layout = "pages"; // verify existence of convention - if ( fileExists( expandPath( variables.CBHelper.themeRoot() & "/layouts/maintenance.cfm" ) ) ) { + if ( + fileExists( + expandPath( + variables.CBHelper.themeRoot() & "/layouts/maintenance.cfm" + ) + ) + ) { layout = "maintenance"; } @@ -92,13 +95,19 @@ component accessors="true" threadSafe singleton { * @format export format * @layout layout name */ - string function getThemePrintLayout( required format, required layout ){ + string function getThemePrintLayout( required format, required layout ) { // some cleanup, just in case - arguments.layout = replaceNoCase( arguments.layout, ".cfm", "" ); + arguments.layout = replaceNoCase( + arguments.layout, + ".cfm", + "" + ); // verify existence of convention if ( fileExists( - expandPath( variables.CBHelper.themeRoot() & "/layouts/#arguments.layout#_#arguments.format#.cfm" ) + expandPath( + variables.CBHelper.themeRoot() & "/layouts/#arguments.layout#_#arguments.format#.cfm" + ) ) ) { return "#arguments.layout#_#arguments.format#"; @@ -110,11 +119,15 @@ component accessors="true" threadSafe singleton { /** * Get the current theme's search layout */ - string function getThemeSearchLayout(){ + string function getThemeSearchLayout() { var layout = "pages"; // verify existence of convention - if ( fileExists( expandPath( variables.CBHelper.themeRoot() & "/layouts/search.cfm" ) ) ) { + if ( + fileExists( + expandPath( variables.CBHelper.themeRoot() & "/layouts/search.cfm" ) + ) + ) { layout = "search"; } @@ -126,14 +139,21 @@ component accessors="true" threadSafe singleton { * * @widgetName The name of the widget */ - string function getThemeWidgetInvocationPath( required string widgetName ){ - var path = ""; - var parsedName = replaceNoCase( arguments.widgetName, "~", "", "one" ); + string function getThemeWidgetInvocationPath( required string widgetName ) { + var path = ""; + var parsedName = replaceNoCase( + arguments.widgetName, + "~", + "", + "one" + ); // if requested widget exists in the cache, return the path if ( structKeyExists( variables.widgetCache, parsedName ) ) { path = variables.widgetCache[ parsedName ].invocationPath; } else { - log.error( "Could not find '#parsedName#' widget in the currently active theme." ); + log.error( + "Could not find '#parsedName#' widget in the currently active theme." + ); } return path; } @@ -142,13 +162,16 @@ component accessors="true" threadSafe singleton { * This method is called from the UI module to make sure all site themes are online before * serving requets */ - function startupSiteThemes(){ + function startupSiteThemes() { // Startup the themes - variables.siteService + variables + .siteService .getAllSiteThemes() - .each( function( record ){ - startupTheme( name: arguments.record[ "activeTheme" ], site: arguments.record[ "siteID" ] ); - } ); + .each( + function( record ) { + startupTheme( name = arguments.record[ "activeTheme" ], site = arguments.record[ "siteID" ] ); + } + ); // Flush the settings now variables.settingService.flushSettingsCache(); @@ -165,10 +188,10 @@ component accessors="true" threadSafe singleton { required name, boolean processWidgets = true, any site = "" - ){ + ) { // Get theme record information var themeRecord = getThemeRecord( arguments.name ); - var oTheme = themeRecord.descriptor; + var oTheme = themeRecord.descriptor; // Determine object or id if ( isSimpleValue( arguments.site ) ) { arguments.site = variables.siteService.getOrFail( arguments.site ); @@ -176,23 +199,28 @@ component accessors="true" threadSafe singleton { // Register description as an interceptor with custom points variables.interceptorService.registerInterceptor( - interceptorObject: oTheme, - interceptorName : "cbtheme-#arguments.name#-#arguments.site.getsiteID()#", - customPoints : themeRecord.customInterceptionPoints - ); + interceptorObject = oTheme, + interceptorName = "cbtheme-#arguments.name#-#arguments.site.getsiteID()#", + customPoints = themeRecord.customInterceptionPoints + ); // Register theme settings? if ( structKeyExists( oTheme, "settings" ) ) { registerThemeSettings( - name : arguments.name, - settings: oTheme.settings, - site : arguments.site + name = arguments.name, + settings = oTheme.settings, + site = arguments.site ); } // build widget cache for active theme for ( var thisWidget in themeRecord.widgets.listToArray() ) { - var widgetName = replaceNoCase( thisWidget, ".cfc", "", "one" ); + var widgetName = replaceNoCase( + thisWidget, + ".cfc", + "", + "one" + ); variables.widgetCache[ widgetName ] = { name : widgetName, invocationPath : "#themeRecord.invocationPath#.widgets.#widgetName#", @@ -204,9 +232,9 @@ component accessors="true" threadSafe singleton { // activate theme modules for ( var thisModule in themeRecord.modules.listToArray() ) { variables.moduleService.registerAndActivateModule( - moduleName : thisModule, - invocationPath: "#themeRecord.invocationPath#.modules" - ); + moduleName = thisModule, + invocationPath = "#themeRecord.invocationPath#.modules" + ); } // Register all widgets @@ -216,13 +244,13 @@ component accessors="true" threadSafe singleton { // Announce theme activation variables.interceptorService.announce( - "cbadmin_onThemeActivation", - { - themeName : arguments.name, - themeRecord : themeRecord, - site : arguments.site - } - ); + "cbadmin_onThemeActivation", + { + themeName : arguments.name, + themeRecord : themeRecord, + site : arguments.site + } + ); // Call Theme Callbacks: onActivation if ( structKeyExists( oTheme, "onActivation" ) ) { @@ -239,27 +267,32 @@ component accessors="true" threadSafe singleton { * * @return ThemeService */ - function saveThemeSettings( required name, required struct settings ){ + function saveThemeSettings( required name, required struct settings ) { var oSite = variables.siteService.getCurrentWorkingSite(); transaction { // Filter out settings that are not theme based - arguments.settings - .filter( function( key, value ){ - return ( findNoCase( "cb_theme_#name#_", key ) ? true : false ); - } ) - .each( function( key, value ){ - var oSetting = variables.settingService.findSiteSetting( oSite, key ); - - if ( isNull( oSetting ) ) { - oSetting = variables.settingService.new( { name : key } ); + arguments + .settings + .filter( + function( key, value ) { + return ( findNoCase( "cb_theme_#name#_", key ) ? true : false ); } + ) + .each( + function( key, value ) { + var oSetting = variables.settingService.findSiteSetting( oSite, key ); + + if ( isNull( oSetting ) ) { + oSetting = variables.settingService.new( { name : key } ); + } - oSetting.setValue( value ); - oSetting.setSite( oSite ); + oSetting.setValue( value ); + oSetting.setSite( oSite ); - variables.settingService.save( oSetting ); - } ); + variables.settingService.save( oSetting ); + } + ); } return this; @@ -268,8 +301,11 @@ component accessors="true" threadSafe singleton { /** * Get the current active theme record for the current working site */ - struct function getActiveTheme(){ - return getThemeRecord( variables.siteService.getCurrentWorkingSite().getActiveTheme() ); + struct function getActiveTheme() { + return getThemeRecord( variables + .siteService + .getCurrentWorkingSite() + .getActiveTheme() ); } /** @@ -277,7 +313,7 @@ component accessors="true" threadSafe singleton { * * @themeName The name of the theme */ - struct function getThemeRecord( required themeName ){ + struct function getThemeRecord( required themeName ) { return variables.themeRegistry[ arguments.themeName ]; } @@ -286,7 +322,7 @@ component accessors="true" threadSafe singleton { * * @themeName The name of the theme to check */ - boolean function isActiveTheme( required themeName ){ + boolean function isActiveTheme( required themeName ) { return ( compareNoCase( getActiveTheme().name, arguments.themeName ) EQ 0 ); } @@ -295,20 +331,20 @@ component accessors="true" threadSafe singleton { * * @themeName The theme name to activate */ - function activateTheme( required themeName ){ + function activateTheme( required themeName ) { transaction { var currentSite = variables.siteService.getCurrentWorkingSite(); var themeRecord = getThemeRecord( currentSite.getActiveTheme() ); // Call deactivation event for the previous active theme variables.interceptorService.announce( - "cbadmin_onThemeDeactivation", - { - themeName : currentSite.getActiveTheme(), - themeRecord : themeRecord, - site : currentSite - } - ); + "cbadmin_onThemeDeactivation", + { + themeName : currentSite.getActiveTheme(), + themeRecord : themeRecord, + site : currentSite + } + ); // Call Old Theme Callback: onDeactivation if ( structKeyExists( themeRecord.descriptor, "onDeactivation" ) ) { @@ -322,8 +358,8 @@ component accessors="true" threadSafe singleton { // Unregister theme Descriptor Interceptor variables.interceptorService.unregister( - interceptorName = "cbTheme-#currentSite.getActiveTheme()#-#currentSite.getsiteID()#" - ); + interceptorName = "cbTheme-#currentSite.getActiveTheme()#-#currentSite.getsiteID()#" + ); // Setup the new chosen theme for the site currentSite.setActiveTheme( arguments.themeName ); @@ -342,7 +378,7 @@ component accessors="true" threadSafe singleton { /** * Get all registered themes via the registry. */ - struct function getThemes(){ + struct function getThemes() { if ( isSimpleValue( variables.themeRegistry ) ) { buildThemeRegistry(); } @@ -352,42 +388,48 @@ component accessors="true" threadSafe singleton { /** * Get all registered custom themes via the registry. */ - struct function getCustomThemes(){ + struct function getCustomThemes() { if ( isSimpleValue( variables.themeRegistry ) ) { buildThemeRegistry(); } - return variables.themeRegistry.filter( function( item, contents ){ - return ( contents.type == "custom" ? true : false ); - } ); + return variables.themeRegistry.filter( + function( item, contents ) { + return ( contents.type == "custom" ? true : false ); + } + ); } /** * Build the theme registry via discovery */ - ThemeService function buildThemeRegistry(){ + ThemeService function buildThemeRegistry() { // Get Core Themes and Process Them - getThemesOnDisk( variables.coreThemesPath ).each( function( item ){ - processThemeRecord( - name = arguments.item, - path = variables.coreThemesPath, - invocationPath = variables.moduleSettings[ "contentbox" ].invocationPath & ".themes", - includePath = variables.moduleSettings[ "contentbox" ].mapping & "/themes", - type = "core", - module = "contentbox" + getThemesOnDisk( variables.coreThemesPath ).each( + function( item ) { + processThemeRecord( + name = arguments.item, + path = variables.coreThemesPath, + invocationPath = variables.moduleSettings[ "contentbox" ].invocationPath & ".themes", + includePath = variables.moduleSettings[ "contentbox" ].mapping & "/themes", + type = "core", + module = "contentbox" + ); + } ); - } ); // Get Custom Themes and Process Them - getThemesOnDisk( variables.customThemesPath ).each( function( item ){ - processThemeRecord( - name = arguments.item, - path = variables.customThemesPath, - invocationPath = variables.moduleSettings[ "contentbox-custom" ].invocationPath & "._themes", - includePath = variables.moduleSettings[ "contentbox-custom" ].mapping & "/_themes", - type = "custom", - module = "contentbox-custom" + getThemesOnDisk( variables.customThemesPath ).each( + function( item ) { + processThemeRecord( + name = arguments.item, + path = variables.customThemesPath, + invocationPath = variables.moduleSettings[ "contentbox-custom" ].invocationPath & "._themes", + includePath = variables.moduleSettings[ "contentbox-custom" ].mapping & "/_themes", + type = "custom", + module = "contentbox-custom" + ); + } ); - } ); return this; } @@ -409,7 +451,7 @@ component accessors="true" threadSafe singleton { includePath, type, module - ){ + ) { var record = { "name" : arguments.name, "descriptor" : "", @@ -434,14 +476,16 @@ component accessors="true" threadSafe singleton { }; // Check for theme descriptor by 'theme.cfc' or '#themeName#.cfc' - var descriptorPath = arguments.path & "/#arguments.name#/Theme.cfc"; + var descriptorPath = arguments.path & "/#arguments.name#/Theme.cfc"; var descriptorInstance = arguments.invocationPath & ".#arguments.name#.Theme"; if ( !fileExists( descriptorPath ) ) { // try by theme name instead - descriptorPath = arguments.path & "/#arguments.name#/#arguments.name#.cfc"; + descriptorPath = arguments.path & "/#arguments.name#/#arguments.name#.cfc"; descriptorInstance = arguments.invocationPath & ".#arguments.name#.#arguments.name#"; if ( !fileExists( descriptorPath ) ) { - log.warn( "The theme: #arguments.name# has no theme descriptor, skipping registration." ); + log.warn( + "The theme: #arguments.name# has no theme descriptor, skipping registration." + ); variables.themeRegistry[ arguments.name ] = record; return; } @@ -451,13 +495,13 @@ component accessors="true" threadSafe singleton { var oThemeConfig = wirebox.getInstance( descriptorInstance ); // Populate Record with it - record.descriptor = oThemeConfig; - record.isValid = true; - record.themeName = oThemeConfig.name; + record.descriptor = oThemeConfig; + record.isValid = true; + record.themeName = oThemeConfig.name; record.description = oThemeConfig.description; - record.version = oThemeConfig.version; - record.author = oThemeConfig.author; - record.authorURL = oThemeConfig.authorURL; + record.version = oThemeConfig.version; + record.author = oThemeConfig.author; + record.authorURL = oThemeConfig.authorURL; // Screenshot if ( structKeyExists( oThemeConfig, "screenShotURL" ) ) { record.screenShotURL = arguments.includePath & "/#arguments.name#/" & oThemeConfig.screenShotURL; @@ -522,15 +566,15 @@ component accessors="true" threadSafe singleton { // Store layout oThemeConfiguration CFC and theme metadata record variables.themeCFCRegistry[ arguments.name ] = oThemeConfig; - variables.themeRegistry[ arguments.name ] = record; - }; + variables.themeRegistry[ arguments.name ] = record; + } /** * Remove a custom theme only. * * @themeName The theme to remove */ - boolean function removeTheme( required themeName ){ + boolean function removeTheme( required themeName ) { // verify name or even if it exists if ( !len( arguments.themeName ) || !variables.themeRegistry.keyExists( arguments.themeName ) ) { return false; @@ -550,7 +594,7 @@ component accessors="true" threadSafe singleton { // Remove settings if ( structKeyExists( themeRecord.descriptor, "settings" ) ) { - unregisterThemeSettings( settings: themeRecord.descriptor.settings ); + unregisterThemeSettings( settings = themeRecord.descriptor.settings ); } // Remove from registry @@ -575,11 +619,11 @@ component accessors="true" threadSafe singleton { * * @themeName The name of the theme */ - struct function getSettingsConstraints( required themeName ){ + struct function getSettingsConstraints( required themeName ) { // Get theme CFC var oTheme = variables.themeRegistry[ arguments.themeName ].descriptor; // Verify it has settings, else return empty struct - if ( !structKeyExists( oTheme, "settings" ) OR !arrayLen( oTheme.settings ) ) { + if ( !structKeyExists( oTheme, "settings" ) || !arrayLen( oTheme.settings ) ) { return {}; } @@ -587,7 +631,7 @@ component accessors="true" threadSafe singleton { var constraints = {}; for ( var thisSetting in oTheme.settings ) { // Check if required - if ( structKeyExists( thisSetting, "required" ) and thisSetting.required ) { + if ( structKeyExists( thisSetting, "required" ) && thisSetting.required ) { constraints[ thisSetting.name ] = { required : true }; } } @@ -600,201 +644,226 @@ component accessors="true" threadSafe singleton { * * @activeTheme The active theme struct */ - function buildSettingsForm( required struct activeTheme ){ + function buildSettingsForm( required struct activeTheme ) { // Get theme CFC - var oTheme = variables.themeRegistry[ arguments.activeTheme.name ].descriptor; - var oSite = variables.siteService.getCurrentWorkingSite(); + var oTheme = variables.themeRegistry[ arguments.activeTheme.name ].descriptor; + var oSite = variables.siteService.getCurrentWorkingSite(); var settingForm = ""; // Build Form by iteration over items - if ( !structKeyExists( oTheme, "settings" ) OR !arrayLen( oTheme.settings ) ) { + if ( !structKeyExists( oTheme, "settings" ) || !arrayLen( oTheme.settings ) ) { return settingForm; } // cfformat-ignore-start - savecontent variable="settingForm"{ - + savecontent variable="settingForm" { // Write out panel container - writeOutput( '
    ' ); + writeOutput( + "
    " + ); // Iterate and create settings var lastGroup = "NeverHadAGroup"; - var firstPanel= true; - for( var x=1; x lte arrayLen( oTheme.settings ); x++ ){ - var thisSettingMD = oTheme.settings[ x ]; - var requiredText = ""; + var firstPanel = true; + for ( var x = 1; x LTE arrayLen( oTheme.settings ); x++ ) { + var thisSettingMD = oTheme.settings[ x ]; + var requiredText = ""; var requiredValidator = ""; // get actual setting value which should be guaranteed to exist var settingName = "cb_theme_#arguments.activeTheme.name#_#thisSettingMD.name#"; - var oSetting = settingService.findSiteSetting( oSite, settingName ); + var oSetting = settingService.findSiteSetting( oSite, settingName ); thisSettingMD.defaultValue = oSetting.getValue(); // Default values for settings - if( !structKeyExists( thisSettingMD, "required" ) ){ thisSettingMD.required = false; } - if( !structKeyExists( thisSettingMD, "label" ) ){ thisSettingMD.label = thisSettingMD.name; } - if( !structKeyExists( thisSettingMD, "type" ) ){ thisSettingMD.type = "text"; } - if( !structKeyExists( thisSettingMD, "title" ) ){ thisSettingMD.title = ""; } - if( !structKeyExists( thisSettingMD, "group" ) ){ thisSettingMD.group = "Main"; } - if( !structKeyExists( thisSettingMD, "groupIntro" ) ){ thisSettingMD.groupIntro = ""; } - if( !structKeyExists( thisSettingMD, "fieldHelp" ) ){ thisSettingMD.fieldHelp = ""; } - if( !structKeyExists( thisSettingMD, "fieldDescription" ) ){ thisSettingMD.fieldDescription = ""; } + if ( !structKeyExists( thisSettingMD, "required" ) ) { + thisSettingMD.required = false; + } + if ( !structKeyExists( thisSettingMD, "label" ) ) { + thisSettingMD.label = thisSettingMD.name; + } + if ( !structKeyExists( thisSettingMD, "type" ) ) { + thisSettingMD.type = "text"; + } + if ( !structKeyExists( thisSettingMD, "title" ) ) { + thisSettingMD.title = ""; + } + if ( !structKeyExists( thisSettingMD, "group" ) ) { + thisSettingMD.group = "Main"; + } + if ( !structKeyExists( thisSettingMD, "groupIntro" ) ) { + thisSettingMD.groupIntro = ""; + } + if ( !structKeyExists( thisSettingMD, "fieldHelp" ) ) { + thisSettingMD.fieldHelp = ""; + } + if ( !structKeyExists( thisSettingMD, "fieldDescription" ) ) { + thisSettingMD.fieldDescription = ""; + } // required static strings - if( thisSettingMD.required ){ - requiredText = "*Required"; + if ( thisSettingMD.required ) { + requiredText = "*Required"; requiredValidator = "required"; } // Starting a group panel? - if ( thisSettingMD.group != lastGroup ){ - + if ( thisSettingMD.group != lastGroup ) { // Close out previous group panel-body - if ( lastGroup != "NeverHadAGroup" ){ + if ( lastGroup != "NeverHadAGroup" ) { writeOutput( "
    " ); } // Write out group panel header - writeOutput( '
    ' ); - if ( thisSettingMD.group != "" ){ - writeOutput( ' -
    -

    - " ); + if ( thisSettingMD.group != "" ) { + writeOutput( + " + - '); - } + " + ); + } // Start group body panel - writeOutput( ' -
    -
    - ' ); + writeOutput( + " +
    +
    + " + ); // Show group intro if there is one - if( len( thisSettingMD.groupIntro ) ){ - writeOutput( '
    ' & thisSettingMD.groupIntro & '
    ' ); + if ( len( thisSettingMD.groupIntro ) ) { + writeOutput( "
    " & thisSettingMD.groupIntro & "
    " ); } // Set this as the last group - lastGroup = thisSettingMD.group; + lastGroup = thisSettingMD.group; firstpanel = false; } // writeout control wrapper - writeOutput( '
    ' ); + writeOutput( "
    " ); // write out label - writeOutput( html.label( field=settingName, content="#thisSettingMD.label# #requiredText#" ) ); + writeOutput( + html.label( + field = settingName, + content = "#thisSettingMD.label# #requiredText#" + ) + ); // Generate question mark icon for field Help to open modal - if( len( thisSettingMD.fieldHelp ) ){ - writeOutput( ' ' ); + if ( len( thisSettingMD.fieldHelp ) ) { + writeOutput( + " " + ); } - // write out field description - if( len( thisSettingMD.fieldDescription ) ){ - writeOutput( '
    ' & thisSettingMD.fieldDescription & '
    ' ); + if ( len( thisSettingMD.fieldDescription ) ) { + writeOutput( "
    " & thisSettingMD.fieldDescription & "
    " ); } // write out control - switch( thisSettingMD.type ){ - case "boolean" : { + switch ( thisSettingMD.type ) { + case "boolean": { writeOutput( html.select( - name = settingName, - options = "true,false", - selectedValue= thisSettingMD.defaultValue, - title = thisSettingMD.title, - class = "form-control input-lg" - ) + name = settingName, + options = "true,false", + selectedValue = thisSettingMD.defaultValue, + title = thisSettingMD.title, + class = "form-control input-lg" + ) ); break; } - case "select" : { + case "select": { var options = ""; // Check options UDF - if( structKeyExists( thisSettingMD, "optionsUDF" ) ){ + if ( structKeyExists( thisSettingMD, "optionsUDF" ) ) { options = invoke( oTheme, thisSettingMD.optionsUDF ); - } else if( structKeyExists( thisSettingMD, "options" ) ){ + } else if ( structKeyExists( thisSettingMD, "options" ) ) { options = thisSettingMD.options; } writeOutput( html.select( - name = settingName, - options = options, - selectedValue= thisSettingMD.defaultValue, - title = thisSettingMD.title, - class = "form-control input-lg" - ) + name = settingName, + options = options, + selectedValue = thisSettingMD.defaultValue, + title = thisSettingMD.title, + class = "form-control input-lg" + ) ); break; } - case "textarea" : { + case "textarea": { writeOutput( html.textarea( - name = settingName, - required= requiredValidator, - title = thisSettingMD.title, - value = thisSettingMD.defaultValue, - class = "form-control", - rows = 5 - ) + name = settingName, + required = requiredValidator, + title = thisSettingMD.title, + value = thisSettingMD.defaultValue, + class = "form-control", + rows = 5 + ) ); break; } - case "color" : { + case "color": { writeOutput( html.inputField( - name = settingName, - class = "textfield", - required= requiredValidator, - title = thisSettingMD.title, - value = thisSettingMD.defaultValue, - class = "form-control", - type = "color" - ) + name = settingName, + class = "textfield", + required = requiredValidator, + title = thisSettingMD.title, + value = thisSettingMD.defaultValue, + class = "form-control", + type = "color" + ) ); break; } - default:{ + default: { writeOutput( html.textfield( - name = settingName, - class = "textfield", - required= requiredValidator, - title = thisSettingMD.title, - value = thisSettingMD.defaultValue, - class = "form-control" - ) + name = settingName, + class = "textfield", + required = requiredValidator, + title = thisSettingMD.title, + value = thisSettingMD.defaultValue, + class = "form-control" + ) ); } } - // End form group - writeOutput( '
    ' ); + // End form group + writeOutput( "
    " ); // Generate modal for field Help - if( len( thisSettingMD.fieldHelp ) ){ + if ( len( thisSettingMD.fieldHelp ) ) { writeOutput( generateModal( settingName, thisSettingMD ) ); } - } // end looping over theme settings // Finalize Group Panel: In case we only had one setting in this group. - if ( lastGroup != "NeverHadAGroup" ){ - writeOutput( '
    ' ); + if ( lastGroup != "NeverHadAGroup" ) { + writeOutput( "

    " ); } // Finalize Container @@ -812,24 +881,25 @@ component accessors="true" threadSafe singleton { * @settingName - The name of the setting the Theme Setting Help modal will be created for * @thisSettingMD - The setting struct itself */ - function generateModal( required settingName, required thisSettingMD ){ + function generateModal( required settingName, required thisSettingMD ) { // cfformat-ignore-start - return '