diff --git a/REUSE.toml b/REUSE.toml new file mode 100644 index 00000000..1866a1f2 --- /dev/null +++ b/REUSE.toml @@ -0,0 +1,48 @@ +version = 1 + +# REUSE compliance for MyDash. +# +# PHP files carry license + copyright metadata via PHPDoc tags +# (@license / @copyright / @author) in the main file docblock per +# ADR-014. This REUSE.toml tells `reuse lint` to accept that instead +# of requiring SPDX-* lines duplicated on every file. +# +# Non-PHP source files (Vue/JS/TS/CSS) still SHOULD carry an SPDX +# header on their first line if they have one; the annotation below +# is the fallback for files without a header. + +[[annotations]] +path = "**/*.php" +SPDX-FileCopyrightText = "2024 Conduction B.V. " +SPDX-License-Identifier = "EUPL-1.2" + +[[annotations]] +path = [ + "**/*.vue", + "**/*.js", + "**/*.ts", + "**/*.css", + "**/*.scss", + "**/*.sh", +] +SPDX-FileCopyrightText = "2024 Conduction B.V. " +SPDX-License-Identifier = "EUPL-1.2" + +# Config / data / generated files — CC0 (same treatment as many +# upstream Nextcloud apps). Adjust if a specific asset warrants a +# different licence. +[[annotations]] +path = [ + "**/*.json", + "**/*.yml", + "**/*.yaml", + "**/*.xml", + "**/*.md", + "**/*.toml", + "img/**", + "l10n/**", + "composer.lock", + "package-lock.json", +] +SPDX-FileCopyrightText = "2024 Conduction B.V. " +SPDX-License-Identifier = "CC0-1.0" diff --git a/appinfo/info.xml b/appinfo/info.xml index 86b04299..42679400 100644 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -68,7 +68,7 @@ Vrij en open source onder de EUPL-1.2-licentie. - + diff --git a/composer.json b/composer.json index 8629e44f..0659b8ef 100644 --- a/composer.json +++ b/composer.json @@ -9,8 +9,7 @@ } ], "require": { - "php": "^8.1", - "ramsey/uuid": "^4.9" + "php": "^8.1" }, "require-dev": { "cyclonedx/cyclonedx-php-composer": "^6.2", diff --git a/composer.lock b/composer.lock index 145793a5..b6f17a32 100644 --- a/composer.lock +++ b/composer.lock @@ -4,223 +4,8 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "456c42391fa056b8235c36b6efa95c15", - "packages": [ - { - "name": "brick/math", - "version": "0.13.1", - "source": { - "type": "git", - "url": "https://github.com/brick/math.git", - "reference": "fc7ed316430118cc7836bf45faff18d5dfc8de04" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/brick/math/zipball/fc7ed316430118cc7836bf45faff18d5dfc8de04", - "reference": "fc7ed316430118cc7836bf45faff18d5dfc8de04", - "shasum": "" - }, - "require": { - "php": "^8.1" - }, - "require-dev": { - "php-coveralls/php-coveralls": "^2.2", - "phpunit/phpunit": "^10.1", - "vimeo/psalm": "6.8.8" - }, - "type": "library", - "autoload": { - "psr-4": { - "Brick\\Math\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Arbitrary-precision arithmetic library", - "keywords": [ - "Arbitrary-precision", - "BigInteger", - "BigRational", - "arithmetic", - "bigdecimal", - "bignum", - "bignumber", - "brick", - "decimal", - "integer", - "math", - "mathematics", - "rational" - ], - "support": { - "issues": "https://github.com/brick/math/issues", - "source": "https://github.com/brick/math/tree/0.13.1" - }, - "funding": [ - { - "url": "https://github.com/BenMorel", - "type": "github" - } - ], - "time": "2025-03-29T13:50:30+00:00" - }, - { - "name": "ramsey/collection", - "version": "2.1.1", - "source": { - "type": "git", - "url": "https://github.com/ramsey/collection.git", - "reference": "344572933ad0181accbf4ba763e85a0306a8c5e2" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/ramsey/collection/zipball/344572933ad0181accbf4ba763e85a0306a8c5e2", - "reference": "344572933ad0181accbf4ba763e85a0306a8c5e2", - "shasum": "" - }, - "require": { - "php": "^8.1" - }, - "require-dev": { - "captainhook/plugin-composer": "^5.3", - "ergebnis/composer-normalize": "^2.45", - "fakerphp/faker": "^1.24", - "hamcrest/hamcrest-php": "^2.0", - "jangregor/phpstan-prophecy": "^2.1", - "mockery/mockery": "^1.6", - "php-parallel-lint/php-console-highlighter": "^1.0", - "php-parallel-lint/php-parallel-lint": "^1.4", - "phpspec/prophecy-phpunit": "^2.3", - "phpstan/extension-installer": "^1.4", - "phpstan/phpstan": "^2.1", - "phpstan/phpstan-mockery": "^2.0", - "phpstan/phpstan-phpunit": "^2.0", - "phpunit/phpunit": "^10.5", - "ramsey/coding-standard": "^2.3", - "ramsey/conventional-commits": "^1.6", - "roave/security-advisories": "dev-latest" - }, - "type": "library", - "extra": { - "captainhook": { - "force-install": true - }, - "ramsey/conventional-commits": { - "configFile": "conventional-commits.json" - } - }, - "autoload": { - "psr-4": { - "Ramsey\\Collection\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Ben Ramsey", - "email": "ben@benramsey.com", - "homepage": "https://benramsey.com" - } - ], - "description": "A PHP library for representing and manipulating collections.", - "keywords": [ - "array", - "collection", - "hash", - "map", - "queue", - "set" - ], - "support": { - "issues": "https://github.com/ramsey/collection/issues", - "source": "https://github.com/ramsey/collection/tree/2.1.1" - }, - "time": "2025-03-22T05:38:12+00:00" - }, - { - "name": "ramsey/uuid", - "version": "4.9.2", - "source": { - "type": "git", - "url": "https://github.com/ramsey/uuid.git", - "reference": "8429c78ca35a09f27565311b98101e2826affde0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/ramsey/uuid/zipball/8429c78ca35a09f27565311b98101e2826affde0", - "reference": "8429c78ca35a09f27565311b98101e2826affde0", - "shasum": "" - }, - "require": { - "brick/math": "^0.8.16 || ^0.9 || ^0.10 || ^0.11 || ^0.12 || ^0.13 || ^0.14", - "php": "^8.0", - "ramsey/collection": "^1.2 || ^2.0" - }, - "replace": { - "rhumsaa/uuid": "self.version" - }, - "require-dev": { - "captainhook/captainhook": "^5.25", - "captainhook/plugin-composer": "^5.3", - "dealerdirect/phpcodesniffer-composer-installer": "^1.0", - "ergebnis/composer-normalize": "^2.47", - "mockery/mockery": "^1.6", - "paragonie/random-lib": "^2", - "php-mock/php-mock": "^2.6", - "php-mock/php-mock-mockery": "^1.5", - "php-parallel-lint/php-parallel-lint": "^1.4.0", - "phpbench/phpbench": "^1.2.14", - "phpstan/extension-installer": "^1.4", - "phpstan/phpstan": "^2.1", - "phpstan/phpstan-mockery": "^2.0", - "phpstan/phpstan-phpunit": "^2.0", - "phpunit/phpunit": "^9.6", - "slevomat/coding-standard": "^8.18", - "squizlabs/php_codesniffer": "^3.13" - }, - "suggest": { - "ext-bcmath": "Enables faster math with arbitrary-precision integers using BCMath.", - "ext-gmp": "Enables faster math with arbitrary-precision integers using GMP.", - "ext-uuid": "Enables the use of PeclUuidTimeGenerator and PeclUuidRandomGenerator.", - "paragonie/random-lib": "Provides RandomLib for use with the RandomLibAdapter", - "ramsey/uuid-doctrine": "Allows the use of Ramsey\\Uuid\\Uuid as Doctrine field type." - }, - "type": "library", - "extra": { - "captainhook": { - "force-install": true - } - }, - "autoload": { - "files": [ - "src/functions.php" - ], - "psr-4": { - "Ramsey\\Uuid\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A PHP library for generating and working with universally unique identifiers (UUIDs).", - "keywords": [ - "guid", - "identifier", - "uuid" - ], - "support": { - "issues": "https://github.com/ramsey/uuid/issues", - "source": "https://github.com/ramsey/uuid/tree/4.9.2" - }, - "time": "2025-12-14T04:43:48+00:00" - } - ], + "content-hash": "b27015d08a4005dd7bb160396da1a18d", + "packages": [], "packages-dev": [ { "name": "amphp/amp", @@ -535,24 +320,24 @@ }, { "name": "composer/spdx-licenses", - "version": "1.5.9", + "version": "1.6.0", "source": { "type": "git", "url": "https://github.com/composer/spdx-licenses.git", - "reference": "edf364cefe8c43501e21e88110aac10b284c3c9f" + "reference": "5ecd0cb4177696f9fd48f1605dda81db3dee7889" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/spdx-licenses/zipball/edf364cefe8c43501e21e88110aac10b284c3c9f", - "reference": "edf364cefe8c43501e21e88110aac10b284c3c9f", + "url": "https://api.github.com/repos/composer/spdx-licenses/zipball/5ecd0cb4177696f9fd48f1605dda81db3dee7889", + "reference": "5ecd0cb4177696f9fd48f1605dda81db3dee7889", "shasum": "" }, "require": { - "php": "^5.3.2 || ^7.0 || ^8.0" + "php": "^7.2 || ^8.0" }, "require-dev": { "phpstan/phpstan": "^1.11", - "symfony/phpunit-bridge": "^3 || ^7" + "symfony/phpunit-bridge": "^6.4.25 || ^7.3.3 || ^8.0" }, "type": "library", "extra": { @@ -595,7 +380,7 @@ "support": { "irc": "ircs://irc.libera.chat:6697/composer", "issues": "https://github.com/composer/spdx-licenses/issues", - "source": "https://github.com/composer/spdx-licenses/tree/1.5.9" + "source": "https://github.com/composer/spdx-licenses/tree/1.6.0" }, "funding": [ { @@ -605,13 +390,9 @@ { "url": "https://github.com/composer", "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/composer/composer", - "type": "tidelift" } ], - "time": "2025-05-12T21:07:07+00:00" + "time": "2026-04-08T20:18:39+00:00" }, { "name": "composer/xdebug-handler", @@ -681,25 +462,25 @@ }, { "name": "consolidation/annotated-command", - "version": "4.10.4", + "version": "4.10.5", "source": { "type": "git", "url": "https://github.com/consolidation/annotated-command.git", - "reference": "69d29da4acac31a43caa4cea13b6b948f4e5c56d" + "reference": "78abf3b6853d7ff9044babd2b9c002ff433207d8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/consolidation/annotated-command/zipball/69d29da4acac31a43caa4cea13b6b948f4e5c56d", - "reference": "69d29da4acac31a43caa4cea13b6b948f4e5c56d", + "url": "https://api.github.com/repos/consolidation/annotated-command/zipball/78abf3b6853d7ff9044babd2b9c002ff433207d8", + "reference": "78abf3b6853d7ff9044babd2b9c002ff433207d8", "shasum": "" }, "require": { "consolidation/output-formatters": "^4.3.1", "php": ">=7.1.3", "psr/log": "^1 || ^2 || ^3", - "symfony/console": "^4.4.8 || ^5 || ^6 || ^7", - "symfony/event-dispatcher": "^4.4.8 || ^5 || ^6 || ^7", - "symfony/finder": "^4.4.8 || ^5 || ^6 || ^7" + "symfony/console": "^4.4.8 || ^5 || ^6 || ^7 || ^8", + "symfony/event-dispatcher": "^4.4.8 || ^5 || ^6 || ^7 || ^8", + "symfony/finder": "^4.4.8 || ^5 || ^6 || ^7 || ^8" }, "require-dev": { "composer-runtime-api": "^2.0", @@ -731,9 +512,9 @@ "description": "Initialize Symfony Console commands from annotated command class methods.", "support": { "issues": "https://github.com/consolidation/annotated-command/issues", - "source": "https://github.com/consolidation/annotated-command/tree/4.10.4" + "source": "https://github.com/consolidation/annotated-command/tree/4.10.5" }, - "time": "2025-11-14T22:57:49+00:00" + "time": "2026-03-29T00:50:52+00:00" }, { "name": "consolidation/config", @@ -797,22 +578,22 @@ }, { "name": "consolidation/log", - "version": "3.1.1", + "version": "3.1.2", "source": { "type": "git", "url": "https://github.com/consolidation/log.git", - "reference": "c1a87a94c01957697ec347fd67404d7f0030d1aa" + "reference": "a0c85d40ca18c22c93fdf78d7e8115cd438ccfe6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/consolidation/log/zipball/c1a87a94c01957697ec347fd67404d7f0030d1aa", - "reference": "c1a87a94c01957697ec347fd67404d7f0030d1aa", + "url": "https://api.github.com/repos/consolidation/log/zipball/a0c85d40ca18c22c93fdf78d7e8115cd438ccfe6", + "reference": "a0c85d40ca18c22c93fdf78d7e8115cd438ccfe6", "shasum": "" }, "require": { "php": ">=8.0.0", "psr/log": "^3", - "symfony/console": "^5 || ^6 || ^7" + "symfony/console": "^5 || ^6 || ^7 || ^8" }, "require-dev": { "phpunit/phpunit": "^7.5.20 || ^8 || ^9", @@ -843,36 +624,36 @@ "description": "Improved Psr-3 / Psr\\Log logger based on Symfony Console components.", "support": { "issues": "https://github.com/consolidation/log/issues", - "source": "https://github.com/consolidation/log/tree/3.1.1" + "source": "https://github.com/consolidation/log/tree/3.1.2" }, - "time": "2025-11-14T21:11:00+00:00" + "time": "2026-03-28T23:36:49+00:00" }, { "name": "consolidation/output-formatters", - "version": "4.7.0", + "version": "4.7.1", "source": { "type": "git", "url": "https://github.com/consolidation/output-formatters.git", - "reference": "dfc464c4d4a47594cac5eac01ce265e04b70cb94" + "reference": "a112df9a74854c8438b33b334ed619fa43edf31a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/consolidation/output-formatters/zipball/dfc464c4d4a47594cac5eac01ce265e04b70cb94", - "reference": "dfc464c4d4a47594cac5eac01ce265e04b70cb94", + "url": "https://api.github.com/repos/consolidation/output-formatters/zipball/a112df9a74854c8438b33b334ed619fa43edf31a", + "reference": "a112df9a74854c8438b33b334ed619fa43edf31a", "shasum": "" }, "require": { "dflydev/dot-access-data": "^1.1.0 || ^2 || ^3", "php": ">=7.1.3", - "symfony/console": "^4 || ^5 || ^6 || ^7", - "symfony/finder": "^4 || ^5 || ^6 || ^7" + "symfony/console": "^4 || ^5 || ^6 || ^7 || ^8", + "symfony/finder": "^4 || ^5 || ^6 || ^7 || ^8" }, "require-dev": { "php-coveralls/php-coveralls": "^2.4.2", "phpunit/phpunit": "^7 || ^8 || ^9", "squizlabs/php_codesniffer": "^3", - "symfony/var-dumper": "^4 || ^5 || ^6 || ^7", - "symfony/yaml": "^4 || ^5 || ^6 || ^7", + "symfony/var-dumper": "^4 || ^5 || ^6 || ^7 || ^8", + "symfony/yaml": "^4 || ^5 || ^6 || ^7 || ^8", "yoast/phpunit-polyfills": "^1" }, "suggest": { @@ -897,9 +678,9 @@ "description": "Format text by applying transformations provided by plug-in formatters.", "support": { "issues": "https://github.com/consolidation/output-formatters/issues", - "source": "https://github.com/consolidation/output-formatters/tree/4.7.0" + "source": "https://github.com/consolidation/output-formatters/tree/4.7.1" }, - "time": "2025-11-14T21:06:10+00:00" + "time": "2026-03-28T23:34:39+00:00" }, { "name": "consolidation/robo", @@ -1750,16 +1531,16 @@ }, { "name": "kubawerlos/php-cs-fixer-custom-fixers", - "version": "v3.36.0", + "version": "v3.37.1", "source": { "type": "git", "url": "https://github.com/kubawerlos/php-cs-fixer-custom-fixers.git", - "reference": "e1f97f6463f0b2a22e0dd320948a04132ff9c501" + "reference": "e0ec1f602a1d0836909e9079262dbaf58eaf3804" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/kubawerlos/php-cs-fixer-custom-fixers/zipball/e1f97f6463f0b2a22e0dd320948a04132ff9c501", - "reference": "e1f97f6463f0b2a22e0dd320948a04132ff9c501", + "url": "https://api.github.com/repos/kubawerlos/php-cs-fixer-custom-fixers/zipball/e0ec1f602a1d0836909e9079262dbaf58eaf3804", + "reference": "e0ec1f602a1d0836909e9079262dbaf58eaf3804", "shasum": "" }, "require": { @@ -1769,7 +1550,7 @@ "php": "^7.4 || ^8.0" }, "require-dev": { - "phpunit/phpunit": "^9.6.24 || ^10.5.51 || ^11.5.44" + "phpunit/phpunit": "^9.6.34 || ^10.5.63 || ^11.5.55" }, "type": "library", "autoload": { @@ -1790,7 +1571,7 @@ "description": "A set of custom fixers for PHP CS Fixer", "support": { "issues": "https://github.com/kubawerlos/php-cs-fixer-custom-fixers/issues", - "source": "https://github.com/kubawerlos/php-cs-fixer-custom-fixers/tree/v3.36.0" + "source": "https://github.com/kubawerlos/php-cs-fixer-custom-fixers/tree/v3.37.1" }, "funding": [ { @@ -1798,7 +1579,7 @@ "type": "github" } ], - "time": "2026-01-31T07:02:11+00:00" + "time": "2026-04-28T16:41:56+00:00" }, { "name": "league/container", @@ -2680,16 +2461,16 @@ }, { "name": "php-cs-fixer/shim", - "version": "v3.94.2", + "version": "v3.95.1", "source": { "type": "git", "url": "https://github.com/PHP-CS-Fixer/shim.git", - "reference": "80fd29f44a736136a2f05bae5464816a444b91d1" + "reference": "f81ccf51ca60cc9dd21358ffba0e79ebd2ebb78a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHP-CS-Fixer/shim/zipball/80fd29f44a736136a2f05bae5464816a444b91d1", - "reference": "80fd29f44a736136a2f05bae5464816a444b91d1", + "url": "https://api.github.com/repos/PHP-CS-Fixer/shim/zipball/f81ccf51ca60cc9dd21358ffba0e79ebd2ebb78a", + "reference": "f81ccf51ca60cc9dd21358ffba0e79ebd2ebb78a", "shasum": "" }, "require": { @@ -2726,9 +2507,9 @@ "description": "A tool to automatically fix PHP code style", "support": { "issues": "https://github.com/PHP-CS-Fixer/shim/issues", - "source": "https://github.com/PHP-CS-Fixer/shim/tree/v3.94.2" + "source": "https://github.com/PHP-CS-Fixer/shim/tree/v3.95.1" }, - "time": "2026-02-20T16:14:17+00:00" + "time": "2026-04-12T17:00:34+00:00" }, { "name": "phpcsstandards/phpcsextra", @@ -2960,16 +2741,16 @@ }, { "name": "phpdocumentor/reflection-docblock", - "version": "5.6.6", + "version": "5.6.7", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "5cee1d3dfc2d2aa6599834520911d246f656bcb8" + "reference": "31a105931bc8ffa3a123383829772e832fd8d903" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/5cee1d3dfc2d2aa6599834520911d246f656bcb8", - "reference": "5cee1d3dfc2d2aa6599834520911d246f656bcb8", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/31a105931bc8ffa3a123383829772e832fd8d903", + "reference": "31a105931bc8ffa3a123383829772e832fd8d903", "shasum": "" }, "require": { @@ -3018,9 +2799,9 @@ "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", "support": { "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", - "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.6.6" + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.6.7" }, - "time": "2025-12-22T21:13:58+00:00" + "time": "2026-03-18T20:47:46+00:00" }, { "name": "phpdocumentor/type-resolver", @@ -4022,18 +3803,18 @@ "source": { "type": "git", "url": "https://github.com/Roave/SecurityAdvisories.git", - "reference": "c1109f3f28a27aa19c894df25d682b5046dc1098" + "reference": "87a281378fdad8f5926efe259f6ca72e7a395e68" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/c1109f3f28a27aa19c894df25d682b5046dc1098", - "reference": "c1109f3f28a27aa19c894df25d682b5046dc1098", + "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/87a281378fdad8f5926efe259f6ca72e7a395e68", + "reference": "87a281378fdad8f5926efe259f6ca72e7a395e68", "shasum": "" }, "conflict": { "3f/pygmentize": "<1.2", "adaptcms/adaptcms": "<=1.3", - "admidio/admidio": "<=4.3.16", + "admidio/admidio": "<5.0.8", "adodb/adodb-php": "<=5.22.9", "aheinze/cockpit": "<2.2", "aimeos/ai-admin-graphql": ">=2022.04.1,<2022.10.10|>=2023.04.1,<2023.10.6|>=2024.04.1,<2024.07.2", @@ -4050,6 +3831,7 @@ "alextselegidis/easyappointments": "<=1.5.2", "alexusmai/laravel-file-manager": "<=3.3.1", "algolia/algoliasearch-magento-2": "<=3.16.1|>=3.17.0.0-beta1,<=3.17.1", + "almirhodzic/nova-toggle-5": "<1.3", "alt-design/alt-redirect": "<1.6.4", "altcha-org/altcha": "<1.3.1", "alterphp/easyadmin-extension-bundle": ">=1.2,<1.2.11|>=1.3,<1.3.1", @@ -4075,16 +3857,18 @@ "athlon1600/php-proxy": "<=5.1", "athlon1600/php-proxy-app": "<=3", "athlon1600/youtube-downloader": "<=4", + "aureuserp/aureuserp": "<1.3.0.0-beta1", "austintoddj/canvas": "<=3.4.2", - "auth0/auth0-php": ">=3.3,<8.18", - "auth0/login": "<7.20", - "auth0/symfony": "<=5.5", - "auth0/wordpress": "<=5.4", + "auth0/auth0-php": ">=3.3,<=8.18", + "auth0/login": "<=7.20", + "auth0/symfony": "<=5.7", + "auth0/wordpress": "<=5.5", "automad/automad": "<2.0.0.0-alpha5", "automattic/jetpack": "<9.8", "awesome-support/awesome-support": "<=6.0.7", - "aws/aws-sdk-php": "<3.368", - "azuracast/azuracast": "<=0.23.1", + "aws/aws-sdk-php": "<=3.371.3", + "ayacoo/redirect-tab": "<2.1.2|>=3,<3.1.7|>=4,<4.0.5", + "azuracast/azuracast": "<=0.23.3", "b13/seo_basics": "<0.8.2", "backdrop/backdrop": "<=1.32", "backpack/crud": "<3.4.9", @@ -4096,7 +3880,7 @@ "barrelstrength/sprout-forms": "<3.9", "barryvdh/laravel-translation-manager": "<0.6.8", "barzahlen/barzahlen-php": "<2.0.1", - "baserproject/basercms": "<=5.1.1", + "baserproject/basercms": "<=5.2.2", "bassjobsen/bootstrap-3-typeahead": ">4.0.2", "bbpress/bbpress": "<2.6.5", "bcit-ci/codeigniter": "<3.1.3", @@ -4139,13 +3923,13 @@ "cesnet/simplesamlphp-module-proxystatistics": "<3.1", "chriskacerguis/codeigniter-restserver": "<=2.7.1", "chrome-php/chrome": "<1.14", - "ci4-cms-erp/ci4ms": "<0.28.5", + "ci4-cms-erp/ci4ms": "<0.31.5", "civicrm/civicrm-core": ">=4.2,<4.2.9|>=4.3,<4.3.3", "ckeditor/ckeditor": "<4.25", "clickstorm/cs-seo": ">=6,<6.8|>=7,<7.5|>=8,<8.4|>=9,<9.3", "co-stack/fal_sftp": "<0.2.6", - "cockpit-hq/cockpit": "<2.11.4", - "code16/sharp": "<9.11.1", + "cockpit-hq/cockpit": "<2.14", + "code16/sharp": "<9.20", "codeception/codeception": "<3.1.3|>=4,<4.1.22", "codeigniter/framework": "<3.1.10", "codeigniter4/framework": "<4.6.2", @@ -4155,8 +3939,8 @@ "codingms/modules": "<4.3.11|>=5,<5.7.4|>=6,<6.4.2|>=7,<7.5.5", "commerceteam/commerce": ">=0.9.6,<0.9.9", "components/jquery": ">=1.0.3,<3.5", - "composer/composer": "<1.10.27|>=2,<2.2.26|>=2.3,<2.9.3", - "concrete5/concrete5": "<9.4.3", + "composer/composer": "<2.2.27|>=2.3,<2.9.6", + "concrete5/concrete5": "<9.4.8", "concrete5/core": "<8.5.8|>=9,<9.1", "contao-components/mediaelement": ">=2.14.2,<2.21.1", "contao/comments-bundle": ">=2,<4.13.40|>=5.0.0.0-RC1-dev,<5.3.4", @@ -4169,11 +3953,15 @@ "corveda/phpsandbox": "<1.3.5", "cosenary/instagram": "<=2.3", "couleurcitron/tarteaucitron-wp": "<0.3", - "cpsit/typo3-mailqueue": "<0.4.3|>=0.5,<0.5.1", - "craftcms/cms": "<4.17.0.0-beta1|>=5,<5.9.0.0-beta1", - "craftcms/commerce": ">=4.0.0.0-RC1-dev,<=4.10|>=5,<=5.5.1", + "cpsit/typo3-mailqueue": "<0.4.5|>=0.5,<0.5.2", + "craftcms/aws-s3": ">=2.0.2,<=2.2.4", + "craftcms/azure-blob": ">=2.0.0.0-beta1,<=2.1", + "craftcms/cms": "<=4.17.8|>=5,<5.9.15", + "craftcms/commerce": ">=4,<4.11|>=5,<5.6", "craftcms/composer": ">=4.0.0.0-RC1-dev,<=4.10|>=5.0.0.0-RC1-dev,<=5.5.1", "craftcms/craft": ">=3.5,<=4.16.17|>=5.0.0.0-RC1-dev,<=5.8.21", + "craftcms/google-cloud": ">=2.0.0.0-beta1,<=2.2", + "craftcms/webhooks": ">=3,<3.2", "croogo/croogo": "<=4.0.7", "cuyz/valinor": "<0.12", "czim/file-handling": "<1.5|>=2,<2.3", @@ -4191,7 +3979,7 @@ "derhansen/sf_event_mgt": "<4.3.1|>=5,<5.1.1|>=7,<7.4", "desperado/xml-bundle": "<=0.1.7", "dev-lancer/minecraft-motd-parser": "<=1.0.5", - "devcode-it/openstamanager": "<=2.9.8", + "devcode-it/openstamanager": "<=2.10.1", "devgroup/dotplant": "<2020.09.14-dev", "digimix/wp-svg-upload": "<=1", "directmailteam/direct-mail": "<6.0.3|>=7,<7.0.3|>=8,<9.5.2", @@ -4208,9 +3996,10 @@ "doctrine/mongodb-odm": "<1.0.2", "doctrine/mongodb-odm-bundle": "<3.0.1", "doctrine/orm": ">=1,<1.2.4|>=2,<2.4.8|>=2.5,<2.5.1|>=2.8.3,<2.8.4", - "dolibarr/dolibarr": "<21.0.3", + "dolibarr/dolibarr": "<=22.0.4", "dompdf/dompdf": "<2.0.4", "doublethreedigital/guest-entries": "<3.1.2", + "dreamfactory/df-core": "<1.0.4", "drupal-pattern-lab/unified-twig-extensions": "<=0.1", "drupal/access_code": "<2.0.5", "drupal/acquia_dam": "<1.1.5", @@ -4249,7 +4038,7 @@ "drupal/umami_analytics": "<1.0.1", "duncanmcclean/guest-entries": "<3.1.2", "dweeves/magmi": "<=0.7.24", - "ec-cube/ec-cube": "<2.4.4|>=2.11,<=2.17.1|>=3,<=3.0.18.0-patch4|>=4,<=4.1.2", + "ec-cube/ec-cube": "<2.4.4|>=2.11,<=2.17.1|>=3,<=3.0.18.0-patch4|>=4,<=4.3.1", "ecodev/newsletter": "<=4", "ectouch/ectouch": "<=2.7.2", "egroupware/egroupware": "<23.1.20260113|>=26.0.20251208,<26.0.20260113", @@ -4294,7 +4083,7 @@ "filament/actions": ">=3.2,<3.2.123", "filament/filament": ">=4,<4.3.1", "filament/infolists": ">=3,<3.2.115", - "filament/tables": ">=3,<3.2.115", + "filament/tables": ">=3,<3.2.115|>=4,<4.8.5|>=5,<5.3.5", "filegator/filegator": "<7.8", "filp/whoops": "<2.1.13", "fineuploader/php-traditional-server": "<=1.2.2", @@ -4302,10 +4091,11 @@ "fisharebest/webtrees": "<=2.1.18", "fixpunkt/fp-masterquiz": "<2.2.1|>=3,<3.5.2", "fixpunkt/fp-newsletter": "<1.1.1|>=1.2,<2.1.2|>=2.2,<3.2.6", - "flarum/core": "<1.8.10", + "flarum/core": "<=1.8.15|>=2.0.0.0-beta1,<=2.0.0.0-beta8", "flarum/flarum": "<0.1.0.0-beta8", "flarum/framework": "<1.8.10", "flarum/mentions": "<1.6.3", + "flarum/nicknames": "<1.8.3", "flarum/sticky": ">=0.1.0.0-beta14,<=0.1.0.0-beta15", "flarum/tags": "<=0.1.0.0-beta13", "floriangaerber/magnesium": "<0.3.1", @@ -4328,7 +4118,7 @@ "friendsoftypo3/openid": ">=4.5,<4.5.31|>=4.7,<4.7.16|>=6,<6.0.11|>=6.1,<6.1.6", "froala/wysiwyg-editor": "<=4.3", "frosh/adminer-platform": "<2.2.1", - "froxlor/froxlor": "<=2.2.5", + "froxlor/froxlor": "<2.3.6", "frozennode/administrator": "<=5.0.12", "fuel/core": "<1.8.1", "funadmin/funadmin": "<=7.1.0.0-RC4", @@ -4338,7 +4128,7 @@ "geshi/geshi": "<=1.0.9.1", "getformwork/formwork": "<=2.3.3", "getgrav/grav": "<1.11.0.0-beta1", - "getkirby/cms": "<3.9.8.3-dev|>=3.10,<3.10.1.2-dev|>=4,<4.7.1|>=5,<=5.2.1", + "getkirby/cms": "<5.4", "getkirby/kirby": "<3.9.8.3-dev|>=3.10,<3.10.1.2-dev|>=4,<4.7.1", "getkirby/panel": "<2.5.14", "getkirby/starterkit": "<=3.7.0.2", @@ -4347,12 +4137,13 @@ "globalpayments/php-sdk": "<2", "goalgorilla/open_social": "<12.3.11|>=12.4,<12.4.10|>=13.0.0.0-alpha1,<13.0.0.0-alpha11", "gogentooss/samlbase": "<1.2.7", - "google/protobuf": "<3.4", + "goodoneuz/pay-uz": "<=2.2.24", + "google/protobuf": "<4.33.6", "gos/web-socket-bundle": "<1.10.4|>=2,<2.6.1|>=3,<3.3", "gp247/core": "<1.1.24", "gree/jose": "<2.2.1", "gregwar/rst": "<1.0.3", - "grumpydictator/firefly-iii": "<6.1.17", + "grumpydictator/firefly-iii": "<6.1.17|>=6.4.23,<=6.5", "gugoan/economizzer": "<=0.9.0.0-beta1", "guzzlehttp/guzzle": "<6.5.8|>=7,<7.4.5", "guzzlehttp/oauth-subscriber": "<0.8.1", @@ -4367,6 +4158,7 @@ "hjue/justwriting": "<=1", "hov/jobfair": "<1.0.13|>=2,<2.0.2", "httpsoft/http-message": "<1.0.12", + "hybridauth/hybridauth": "<=3.12.2", "hyn/multi-tenant": ">=5.6,<5.7.2", "ibexa/admin-ui": ">=4.2,<4.2.3|>=4.6,<4.6.25|>=5,<5.0.3", "ibexa/admin-ui-assets": ">=4.6.0.0-alpha1,<4.6.21", @@ -4395,10 +4187,12 @@ "innologi/typo3-appointments": "<2.0.6", "intelliants/subrion": "<4.2.2", "inter-mediator/inter-mediator": "==5.5", + "invoiceninja/invoiceninja": "<5.13.4", "ipl/web": "<0.10.1", "islandora/crayfish": "<4.1", "islandora/islandora": ">=2,<2.4.1", "ivankristianto/phpwhois": "<=4.3", + "j0k3r/graby": "<=2.5", "jackalope/jackalope-doctrine-dbal": "<1.7.4", "jambagecom/div2007": "<0.10.2", "james-heinrich/getid3": "<1.9.21", @@ -4406,7 +4200,9 @@ "jasig/phpcas": "<1.3.3", "jbartels/wec-map": "<3.0.3", "jcbrand/converse.js": "<3.3.3", + "joedolson/my-calendar": "<3.7.7", "joelbutcher/socialstream": "<5.6|>=6,<6.2", + "johnbillion/query-monitor": "<3.20.4", "johnbillion/wp-crontrol": "<1.16.2|>=1.17,<1.19.2", "joomla/application": "<1.0.13", "joomla/archive": "<1.1.12|>=2,<2.0.1", @@ -4424,17 +4220,19 @@ "juzaweb/cms": "<=3.4.2", "jweiland/events2": "<8.3.8|>=9,<9.0.6", "jweiland/kk-downloader": "<1.2.2", + "kantorge/yaffa": "<=2", "kazist/phpwhois": "<=4.2.6", + "kelvinmo/simplejwt": "<=1.1", "kelvinmo/simplexrd": "<3.1.1", "kevinpapst/kimai2": "<1.16.7", - "khodakhah/nodcms": "<=3", - "kimai/kimai": "<2.46", + "khodakhah/nodcms": "<=3.4.1", + "kimai/kimai": "<2.54", "kitodo/presentation": "<3.2.3|>=3.3,<3.3.4", "klaviyo/magento2-extension": ">=1,<3", "knplabs/knp-snappy": "<=1.4.2", "kohana/core": "<3.3.3", "koillection/koillection": "<1.6.12", - "krayin/laravel-crm": "<=1.3", + "krayin/laravel-crm": "<=2.2", "kreait/firebase-php": ">=3.2,<3.8.1", "kumbiaphp/kumbiapp": "<=1.1.1", "la-haute-societe/tcpdf": "<6.2.22", @@ -4446,6 +4244,7 @@ "laravel/fortify": "<1.11.1", "laravel/framework": "<10.48.29|>=11,<11.44.1|>=12,<12.1.1", "laravel/laravel": ">=5.4,<5.4.22", + "laravel/passport": ">=13,<13.7.1", "laravel/pulse": "<1.3.1", "laravel/reverb": "<1.7", "laravel/socialite": ">=1,<2.0.10", @@ -4453,16 +4252,16 @@ "lavalite/cms": "<=10.1", "lavitto/typo3-form-to-database": "<2.2.5|>=3,<3.2.2|>=4,<4.2.3|>=5,<5.0.2", "lcobucci/jwt": ">=3.4,<3.4.6|>=4,<4.0.4|>=4.1,<4.1.5", - "league/commonmark": "<2.7", + "league/commonmark": "<=2.8.1", "league/flysystem": "<1.1.4|>=2,<2.1.1", "league/oauth2-server": ">=8.3.2,<8.4.2|>=8.5,<8.5.3", "leantime/leantime": "<3.3", "lexik/jwt-authentication-bundle": "<2.10.7|>=2.11,<2.11.3", "libreform/libreform": ">=2,<=2.0.8", - "librenms/librenms": "<26.2", + "librenms/librenms": "<26.3", "liftkit/database": "<2.13.2", "lightsaml/lightsaml": "<1.3.5", - "limesurvey/limesurvey": "<6.5.12", + "limesurvey/limesurvey": "<6.15.4", "livehelperchat/livehelperchat": "<=3.91", "livewire-filemanager/filemanager": "<=1.0.4", "livewire/livewire": "<2.12.7|>=3.0.0.0-beta1,<3.6.4", @@ -4485,8 +4284,9 @@ "maikuolan/phpmussel": ">=1,<1.6", "mainwp/mainwp": "<=4.4.3.3", "manogi/nova-tiptap": "<=3.2.6", - "mantisbt/mantisbt": "<2.27.2", + "mantisbt/mantisbt": "<2.28.1", "marcwillmann/turn": "<0.3.3", + "markhuot/craftql": "<=1.3.7", "marshmallow/nova-tiptap": "<5.7", "matomo/matomo": "<1.11", "matyhtf/framework": "<3.0.6", @@ -4516,6 +4316,7 @@ "mikehaertl/php-shellcommand": "<1.6.1", "mineadmin/mineadmin": "<=3.0.9", "miniorange/miniorange-saml": "<1.4.3", + "miraheze/ts-portal": "<=33", "mittwald/typo3_forum": "<1.2.1", "mobiledetect/mobiledetectlib": "<2.8.32", "modx/revolution": "<=3.1", @@ -4566,9 +4367,9 @@ "nzo/url-encryptor-bundle": ">=4,<4.3.2|>=5,<5.0.1", "october/backend": "<1.1.2", "october/cms": "<1.0.469|==1.0.469|==1.0.471|==1.1.1", - "october/october": "<3.7.5", - "october/rain": "<1.0.472|>=1.1,<1.1.2", - "october/system": "<=3.7.12|>=4,<=4.0.11", + "october/october": "<3.7.14|>=4,<4.1.10", + "october/rain": "<=3.7.13|>=4,<=4.1.9", + "october/system": "<3.7.16|>=4,<4.1.16", "oliverklee/phpunit": "<3.5.15", "omeka/omeka-s": "<4.0.3", "onelogin/php-saml": "<2.21.1|>=3,<3.8.1|>=4,<4.3.1", @@ -4576,9 +4377,9 @@ "open-web-analytics/open-web-analytics": "<1.8.1", "opencart/opencart": ">=0", "openid/php-openid": "<2.3", - "openmage/magento-lts": "<20.16.1", + "openmage/magento-lts": "<20.17", "opensolutions/vimbadmin": "<=3.0.15", - "opensource-workshop/connect-cms": "<1.8.7|>=2,<2.4.7", + "opensource-workshop/connect-cms": "<1.41.1|>=2,<2.41.1", "orchid/platform": ">=8,<14.43", "oro/calendar-bundle": ">=4.2,<=4.2.6|>=5,<=5.0.6|>=5.1,<5.1.1", "oro/commerce": ">=4.1,<5.0.11|>=5.1,<5.1.1", @@ -4619,16 +4420,16 @@ "phpmailer/phpmailer": "<6.5", "phpmussel/phpmussel": ">=1,<1.6", "phpmyadmin/phpmyadmin": "<5.2.2", - "phpmyfaq/phpmyfaq": "<=4.0.16", + "phpmyfaq/phpmyfaq": "<=4.1", "phpoffice/common": "<0.2.9", "phpoffice/math": "<=0.2", "phpoffice/phpexcel": "<=1.8.2", - "phpoffice/phpspreadsheet": "<1.30|>=2,<2.1.12|>=2.2,<2.4|>=3,<3.10|>=4,<5", + "phpoffice/phpspreadsheet": "<=1.30.3|>=2,<=2.1.15|>=2.2,<=2.4.4|>=3,<=3.10.4|>=4,<=5.6", "phppgadmin/phppgadmin": "<=7.13", - "phpseclib/phpseclib": "<2.0.47|>=3,<3.0.36", + "phpseclib/phpseclib": "<2.0.53|>=3,<3.0.51", "phpservermon/phpservermon": "<3.6", "phpsysinfo/phpsysinfo": "<3.4.3", - "phpunit/phpunit": "<8.5.52|>=9,<9.6.33|>=10,<10.5.62|>=11,<11.5.50|>=12,<12.5.8", + "phpunit/phpunit": "<8.5.52|>=9,<9.6.33|>=10,<10.5.62|>=11,<11.5.50|>=12,<12.5.8|>=12.5.21,<12.5.22|>=13.1.5,<13.1.6", "phpwhois/phpwhois": "<=4.2.5", "phpxmlrpc/extras": "<0.6.1", "phpxmlrpc/phpxmlrpc": "<4.9.2", @@ -4647,7 +4448,7 @@ "pixelfed/pixelfed": "<0.12.5", "plotly/plotly.js": "<2.25.2", "pocketmine/bedrock-protocol": "<8.0.2", - "pocketmine/pocketmine-mp": "<5.32.1", + "pocketmine/pocketmine-mp": "<5.42.1", "pocketmine/raklib": ">=0.14,<0.14.6|>=0.15,<0.15.1", "pressbooks/pressbooks": "<5.18", "prestashop/autoupgrade": ">=4,<4.10.1", @@ -4655,7 +4456,7 @@ "prestashop/blockwishlist": ">=2,<2.1.1", "prestashop/contactform": ">=1.0.1,<4.3", "prestashop/gamification": "<2.3.2", - "prestashop/prestashop": "<8.2.4|>=9.0.0.0-alpha1,<9.0.3", + "prestashop/prestashop": "<8.2.5|>=9.0.0.0-alpha1,<9.1", "prestashop/productcomments": "<5.0.2", "prestashop/ps_checkout": "<4.4.1|>=5,<5.0.5", "prestashop/ps_contactinfo": "<=3.3.2", @@ -4663,7 +4464,7 @@ "prestashop/ps_facetedsearch": "<3.4.1", "prestashop/ps_linklist": "<3.1", "privatebin/privatebin": "<1.4|>=1.5,<1.7.4|>=1.7.7,<2.0.3", - "processwire/processwire": "<=3.0.246", + "processwire/processwire": "<=3.0.255", "propel/propel": ">=2.0.0.0-alpha1,<=2.0.0.0-alpha7", "propel/propel1": ">=1,<=1.7.1", "psy/psysh": "<=0.11.22|>=0.12,<=0.12.18", @@ -4673,6 +4474,7 @@ "pubnub/pubnub": "<6.1", "punktde/pt_extbase": "<1.5.1", "pusher/pusher-php-server": "<2.2.1", + "putyourlightson/craft-sprig": ">=2,<2.15.2|>=3,<3.7.2", "pwweb/laravel-core": "<=0.3.6.0-beta", "pxlrbt/filament-excel": "<1.1.14|>=2.0.0.0-alpha,<2.3.3", "pyrocms/pyrocms": "<=3.9.1", @@ -4681,25 +4483,29 @@ "rainlab/blog-plugin": "<1.4.1", "rainlab/debugbar-plugin": "<3.1", "rainlab/user-plugin": "<=1.4.5", + "ralffreit/mfa-email": "<1.0.7|==2", "rankmath/seo-by-rank-math": "<=1.0.95", "rap2hpoutre/laravel-log-viewer": "<0.13", "react/http": ">=0.7,<1.9", "really-simple-plugins/complianz-gdpr": "<6.4.2", - "redaxo/source": "<=5.20.1", + "redaxo/source": "<5.21", "remdex/livehelperchat": "<4.29", "renolit/reint-downloadmanager": "<4.0.2|>=5,<5.0.1", "reportico-web/reportico": "<=8.1", - "rhukster/dom-sanitizer": "<1.0.7", + "rhukster/dom-sanitizer": "<1.0.10", "rmccue/requests": ">=1.6,<1.8", - "robrichards/xmlseclibs": "<=3.1.3", + "roadiz/documents": "<2.3.42|>=2.4,<2.5.44|>=2.6,<2.6.28|>=2.7,<2.7.9", + "robrichards/xmlseclibs": "<3.1.5", "roots/soil": "<4.1", - "roundcube/roundcubemail": "<1.5.10|>=1.6,<1.6.11", + "roundcube/roundcubemail": "<1.5.10|>=1.6,<1.6.11|>=1.7.0.0-beta,<1.7.0.0-RC5-dev", "rudloff/alltube": "<3.0.3", "rudloff/rtmpdump-bin": "<=2.3.1", "s-cart/core": "<=9.0.5", "s-cart/s-cart": "<6.9", + "s9y/serendipity": "<2.6", "sabberworm/php-css-parser": ">=1,<1.0.1|>=2,<2.0.1|>=3,<3.0.1|>=4,<4.0.1|>=5,<5.0.9|>=5.1,<5.1.3|>=5.2,<5.2.1|>=6,<6.0.2|>=7,<7.0.4|>=8,<8.0.1|>=8.1,<8.1.1|>=8.2,<8.2.1|>=8.3,<8.3.1", "sabre/dav": ">=1.6,<1.7.11|>=1.8,<1.8.9", + "saloonphp/saloon": "<4", "samwilson/unlinked-wikibase": "<1.42", "scheb/two-factor-bundle": "<3.26|>=4,<4.11", "sensiolabs/connect": "<4.2.3", @@ -4707,8 +4513,8 @@ "setasign/fpdi": "<2.6.4", "sfroemken/url_redirect": "<=1.2.1", "sheng/yiicms": "<1.2.1", - "shopware/core": "<6.6.10.9-dev|>=6.7,<6.7.6.1-dev", - "shopware/platform": "<6.6.10.7-dev|>=6.7,<6.7.3.1-dev", + "shopware/core": "<6.6.10.15-dev|>=6.7,<6.7.8.1-dev", + "shopware/platform": "<6.6.10.15-dev|>=6.7,<6.7.8.1-dev", "shopware/production": "<=6.3.5.2", "shopware/shopware": "<=5.7.17|>=6.4.6,<6.6.10.10-dev|>=6.7,<6.7.6.1-dev", "shopware/storefront": "<6.6.10.10-dev|>=6.7,<6.7.5.1-dev", @@ -4717,7 +4523,7 @@ "shuchkin/simplexlsx": ">=1.0.12,<1.1.13", "silverstripe-australia/advancedreports": ">=1,<=2", "silverstripe/admin": "<1.13.19|>=2,<2.1.8", - "silverstripe/assets": ">=1,<1.11.1", + "silverstripe/assets": "<2.4.5|>=3,<3.1.3", "silverstripe/cms": "<4.11.3", "silverstripe/comments": ">=1.3,<3.1.1", "silverstripe/forum": "<=0.6.1|>=0.7,<=0.7.3", @@ -4742,7 +4548,7 @@ "simplesamlphp/simplesamlphp-module-openid": "<1", "simplesamlphp/simplesamlphp-module-openidprovider": "<0.9", "simplesamlphp/xml-common": "<1.20", - "simplesamlphp/xml-security": "==1.6.11", + "simplesamlphp/xml-security": "<1.13.9|>=2,<2.3.1", "simplito/elliptic-php": "<1.0.6", "sitegeist/fluid-components": "<3.5", "sjbr/sr-feuser-register": "<2.6.2|>=5.1,<12.5", @@ -4752,7 +4558,7 @@ "slim/slim": "<2.6", "slub/slub-events": "<3.0.3", "smarty/smarty": "<4.5.3|>=5,<5.1.1", - "snipe/snipe-it": "<=8.3.4", + "snipe/snipe-it": "<8.3.7", "socalnick/scn-social-auth": "<1.15.2", "socialiteproviders/steam": "<1.1", "solspace/craft-freeform": "<4.1.29|>=5,<=5.14.6", @@ -4770,14 +4576,14 @@ "starcitizentools/short-description": ">=4,<4.0.1", "starcitizentools/tabber-neue": ">=1.9.1,<2.7.2|>=3,<3.1.1", "starcitizenwiki/embedvideo": "<=4", - "statamic/cms": "<5.73.11|>=6,<6.4", + "statamic/cms": "<5.73.20|>=6,<6.13", "stormpath/sdk": "<9.9.99", - "studio-42/elfinder": "<=2.1.64", + "studio-42/elfinder": "<2.1.67", "studiomitte/friendlycaptcha": "<0.1.4", "subhh/libconnect": "<7.0.8|>=8,<8.1", "sukohi/surpass": "<1", "sulu/form-bundle": ">=2,<2.5.3", - "sulu/sulu": "<1.6.44|>=2,<2.5.25|>=2.6,<2.6.9|>=3.0.0.0-alpha1,<3.0.0.0-alpha3", + "sulu/sulu": "<2.6.22|>=3,<3.0.5", "sumocoders/framework-user-bundle": "<1.4", "superbig/craft-audit": "<3.0.2", "svewap/a21glossary": "<=0.4.10", @@ -4789,7 +4595,7 @@ "sylius/grid-bundle": "<1.10.1", "sylius/paypal-plugin": "<1.6.2|>=1.7,<1.7.2|>=2,<2.0.2", "sylius/resource-bundle": ">=1,<1.3.14|>=1.4,<1.4.7|>=1.5,<1.5.2|>=1.6,<1.6.4", - "sylius/sylius": "<1.12.19|>=1.13.0.0-alpha1,<1.13.4", + "sylius/sylius": "<1.9.12|>=1.10,<1.10.16|>=1.11,<1.11.17|>=1.12,<=1.12.22|>=1.13,<=1.13.14|>=1.14,<=1.14.17|>=2,<=2.0.15|>=2.1,<=2.1.11|>=2.2,<=2.2.2", "symbiote/silverstripe-multivaluefield": ">=3,<3.1", "symbiote/silverstripe-queuedjobs": ">=3,<3.0.2|>=3.1,<3.1.4|>=4,<4.0.7|>=4.1,<4.1.2|>=4.2,<4.2.4|>=4.3,<4.3.3|>=4.4,<4.4.3|>=4.5,<4.5.1|>=4.6,<4.6.4", "symbiote/silverstripe-seed": "<6.0.3", @@ -4844,7 +4650,7 @@ "thelia/thelia": ">=2.1,<2.1.3", "theonedemon/phpwhois": "<=4.2.5", "thinkcmf/thinkcmf": "<6.0.8", - "thorsten/phpmyfaq": "<4.0.18|>=4.1.0.0-alpha,<=4.1.0.0-beta2", + "thorsten/phpmyfaq": "<4.1.1", "tikiwiki/tiki-manager": "<=17.1", "timber/timber": ">=0.16.6,<1.23.1|>=1.24,<1.24.1|>=2,<2.1", "tinymce/tinymce": "<7.2", @@ -4864,7 +4670,7 @@ "twig/twig": "<3.11.2|>=3.12,<3.14.1|>=3.16,<3.19", "typicms/core": "<16.1.7", "typo3/cms": "<9.5.29|>=10,<10.4.35|>=11,<11.5.23|>=12,<12.2", - "typo3/cms-backend": "<4.1.14|>=4.2,<4.2.15|>=4.3,<4.3.7|>=4.4,<4.4.4|>=7,<=7.6.50|>=8,<=8.7.39|>=9,<9.5.55|>=10,<=10.4.54|>=11,<=11.5.48|>=12,<=12.4.40|>=13,<=13.4.22|>=14,<=14.0.1", + "typo3/cms-backend": "<4.1.14|>=4.2,<4.2.15|>=4.3,<4.3.7|>=4.4,<4.4.4|>=7,<=7.6.50|>=8,<=8.7.39|>=9,<9.5.55|>=10,<=10.4.54|>=11,<=11.5.48|>=12,<=12.4.40|>=13,<=13.4.22|>=14,<=14.0.1|==14.2", "typo3/cms-belog": ">=10,<=10.4.47|>=11,<=11.5.41|>=12,<=12.4.24|>=13,<=13.4.2", "typo3/cms-beuser": ">=9,<9.5.55|>=10,<10.4.54|>=11,<11.5.48|>=12,<12.4.37|>=13,<13.4.18", "typo3/cms-core": "<=8.7.56|>=9,<9.5.55|>=10,<=10.4.54|>=11,<=11.5.48|>=12,<=12.4.40|>=13,<=13.4.22|>=14,<=14.0.1", @@ -4917,20 +4723,22 @@ "wallabag/wallabag": "<2.6.11", "wanglelecc/laracms": "<=1.0.3", "wapplersystems/a21glossary": "<=0.4.10", - "web-auth/webauthn-framework": ">=3.3,<3.3.4|>=4.5,<4.9", - "web-auth/webauthn-lib": ">=4.5,<4.9", + "web-auth/webauthn-framework": ">=3.3,<3.3.4|>=4.5,<4.9|>=5.2,<5.2.4", + "web-auth/webauthn-lib": ">=4.5,<4.9|>=5.2,<5.2.4", + "web-auth/webauthn-symfony-bundle": ">=5.2,<5.2.4", "web-feet/coastercms": "==5.5", "web-tp3/wec_map": "<3.0.3", "webbuilders-group/silverstripe-kapost-bridge": "<0.4", "webcoast/deferred-image-processing": "<1.0.2", "webklex/laravel-imap": "<5.3", "webklex/php-imap": "<5.3", + "webonyx/graphql-php": "<=15.31.4", "webpa/webpa": "<3.1.2", "webreinvent/vaahcms": "<=2.3.1", "wikibase/wikibase": "<=1.39.3", "wikimedia/parsoid": "<0.12.2", "willdurand/js-translation-bundle": "<2.1.1", - "winter/wn-backend-module": "<1.2.4", + "winter/wn-backend-module": "<1.2.12", "winter/wn-cms-module": "<=1.2.9", "winter/wn-dusk-plugin": "<2.1", "winter/wn-system-module": "<1.2.4", @@ -4943,11 +4751,13 @@ "wpanel/wpanel4-cms": "<=4.3.1", "wpcloud/wp-stateless": "<3.2", "wpglobus/wpglobus": "<=1.9.6", - "wwbn/avideo": "<=21", + "wpmetabox/meta-box": "<5.11.2", + "wwbn/avideo": "<=29", "xataface/xataface": "<3", "xpressengine/xpressengine": "<3.0.15", "yab/quarx": "<2.4.5", - "yeswiki/yeswiki": "<=4.5.4", + "yansongda/pay": "<=3.7.19", + "yeswiki/yeswiki": "<=4.6", "yetiforce/yetiforce-crm": "<6.5", "yidashi/yii2cmf": "<=2", "yii2mod/yii2-cms": "<1.9.2", @@ -4962,6 +4772,7 @@ "yiisoft/yii2-redis": "<2.0.20", "yikesinc/yikes-inc-easy-mailchimp-extender": "<6.8.6", "yoast-seo-for-typo3/yoast_seo": "<7.2.3", + "yoast/duplicate-post": "<=4.5", "yourls/yourls": "<=1.10.2", "yuan1994/tpadmin": "<=1.3.12", "yungifez/skuul": "<=2.6.5", @@ -5041,7 +4852,7 @@ "type": "tidelift" } ], - "time": "2026-03-02T22:09:25+00:00" + "time": "2026-04-28T23:21:55+00:00" }, { "name": "sebastian/cli-parser", @@ -6224,16 +6035,16 @@ }, { "name": "symfony/console", - "version": "v6.4.34", + "version": "v6.4.36", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "7b1f1c37eff5910ddda2831345467e593a5120ad" + "reference": "9f481cfb580db8bcecc9b2d4c63f3e13df022ad5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/7b1f1c37eff5910ddda2831345467e593a5120ad", - "reference": "7b1f1c37eff5910ddda2831345467e593a5120ad", + "url": "https://api.github.com/repos/symfony/console/zipball/9f481cfb580db8bcecc9b2d4c63f3e13df022ad5", + "reference": "9f481cfb580db8bcecc9b2d4c63f3e13df022ad5", "shasum": "" }, "require": { @@ -6298,7 +6109,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v6.4.34" + "source": "https://github.com/symfony/console/tree/v6.4.36" }, "funding": [ { @@ -6318,20 +6129,20 @@ "type": "tidelift" } ], - "time": "2026-02-23T15:42:15+00:00" + "time": "2026-03-27T15:30:51+00:00" }, { "name": "symfony/dependency-injection", - "version": "v6.4.34", + "version": "v6.4.36", "source": { "type": "git", "url": "https://github.com/symfony/dependency-injection.git", - "reference": "91e49958b8a6092e48e4711894a1aeb1b151c62a" + "reference": "cd7881a6dc84b780411199cd0584e1a53a3b9ba7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/91e49958b8a6092e48e4711894a1aeb1b151c62a", - "reference": "91e49958b8a6092e48e4711894a1aeb1b151c62a", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/cd7881a6dc84b780411199cd0584e1a53a3b9ba7", + "reference": "cd7881a6dc84b780411199cd0584e1a53a3b9ba7", "shasum": "" }, "require": { @@ -6383,7 +6194,7 @@ "description": "Allows you to standardize and centralize the way objects are constructed in your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/dependency-injection/tree/v6.4.34" + "source": "https://github.com/symfony/dependency-injection/tree/v6.4.36" }, "funding": [ { @@ -6403,7 +6214,7 @@ "type": "tidelift" } ], - "time": "2026-02-24T15:33:38+00:00" + "time": "2026-03-30T16:39:36+00:00" }, { "name": "symfony/deprecation-contracts", @@ -6474,16 +6285,16 @@ }, { "name": "symfony/event-dispatcher", - "version": "v6.4.32", + "version": "v6.4.36", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "99d7e101826e6610606b9433248f80c1997cd20b" + "reference": "fc828863e26ceec86e2513b5e46aa0b149d76b69" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/99d7e101826e6610606b9433248f80c1997cd20b", - "reference": "99d7e101826e6610606b9433248f80c1997cd20b", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/fc828863e26ceec86e2513b5e46aa0b149d76b69", + "reference": "fc828863e26ceec86e2513b5e46aa0b149d76b69", "shasum": "" }, "require": { @@ -6534,7 +6345,7 @@ "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/event-dispatcher/tree/v6.4.32" + "source": "https://github.com/symfony/event-dispatcher/tree/v6.4.36" }, "funding": [ { @@ -6554,7 +6365,7 @@ "type": "tidelift" } ], - "time": "2026-01-05T11:13:48+00:00" + "time": "2026-03-30T11:18:01+00:00" }, { "name": "symfony/event-dispatcher-contracts", @@ -6772,16 +6583,16 @@ }, { "name": "symfony/polyfill-ctype", - "version": "v1.33.0", + "version": "v1.37.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638" + "reference": "141046a8f9477948ff284fa65be2095baafb94f2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638", - "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/141046a8f9477948ff284fa65be2095baafb94f2", + "reference": "141046a8f9477948ff284fa65be2095baafb94f2", "shasum": "" }, "require": { @@ -6831,7 +6642,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.33.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.37.0" }, "funding": [ { @@ -6851,20 +6662,20 @@ "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2026-04-10T16:19:22+00:00" }, { "name": "symfony/polyfill-intl-grapheme", - "version": "v1.33.0", + "version": "v1.37.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70" + "reference": "4864388bfbd3001ce88e234fab652acd91fdc57e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/380872130d3a5dd3ace2f4010d95125fde5d5c70", - "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/4864388bfbd3001ce88e234fab652acd91fdc57e", + "reference": "4864388bfbd3001ce88e234fab652acd91fdc57e", "shasum": "" }, "require": { @@ -6913,7 +6724,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.33.0" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.37.0" }, "funding": [ { @@ -6933,11 +6744,11 @@ "type": "tidelift" } ], - "time": "2025-06-27T09:58:17+00:00" + "time": "2026-04-26T13:13:48+00:00" }, { "name": "symfony/polyfill-intl-normalizer", - "version": "v1.33.0", + "version": "v1.37.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git", @@ -6998,7 +6809,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.33.0" + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.37.0" }, "funding": [ { @@ -7022,16 +6833,16 @@ }, { "name": "symfony/polyfill-mbstring", - "version": "v1.33.0", + "version": "v1.37.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493" + "reference": "6a21eb99c6973357967f6ce3708cd55a6bec6315" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6d857f4d76bd4b343eac26d6b539585d2bc56493", - "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6a21eb99c6973357967f6ce3708cd55a6bec6315", + "reference": "6a21eb99c6973357967f6ce3708cd55a6bec6315", "shasum": "" }, "require": { @@ -7083,7 +6894,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.33.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.37.0" }, "funding": [ { @@ -7103,11 +6914,11 @@ "type": "tidelift" } ], - "time": "2024-12-23T08:48:59+00:00" + "time": "2026-04-10T17:25:58+00:00" }, { "name": "symfony/polyfill-php81", - "version": "v1.33.0", + "version": "v1.37.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php81.git", @@ -7163,7 +6974,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php81/tree/v1.33.0" + "source": "https://github.com/symfony/polyfill-php81/tree/v1.37.0" }, "funding": [ { @@ -7428,16 +7239,16 @@ }, { "name": "symfony/var-exporter", - "version": "v6.4.26", + "version": "v6.4.36", "source": { "type": "git", "url": "https://github.com/symfony/var-exporter.git", - "reference": "466fcac5fa2e871f83d31173f80e9c2684743bfc" + "reference": "f9c4a9695a9e2bbc65c920e147d8d7ae28f8d79a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-exporter/zipball/466fcac5fa2e871f83d31173f80e9c2684743bfc", - "reference": "466fcac5fa2e871f83d31173f80e9c2684743bfc", + "url": "https://api.github.com/repos/symfony/var-exporter/zipball/f9c4a9695a9e2bbc65c920e147d8d7ae28f8d79a", + "reference": "f9c4a9695a9e2bbc65c920e147d8d7ae28f8d79a", "shasum": "" }, "require": { @@ -7485,7 +7296,7 @@ "serialize" ], "support": { - "source": "https://github.com/symfony/var-exporter/tree/v6.4.26" + "source": "https://github.com/symfony/var-exporter/tree/v6.4.36" }, "funding": [ { @@ -7505,7 +7316,7 @@ "type": "tidelift" } ], - "time": "2025-09-11T09:57:09+00:00" + "time": "2026-03-10T15:06:19+00:00" }, { "name": "symfony/yaml", @@ -7635,16 +7446,16 @@ }, { "name": "twig/twig", - "version": "v3.23.0", + "version": "v3.24.0", "source": { "type": "git", "url": "https://github.com/twigphp/Twig.git", - "reference": "a64dc5d2cc7d6cafb9347f6cd802d0d06d0351c9" + "reference": "a6769aefb305efef849dc25c9fd1653358c148f0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/a64dc5d2cc7d6cafb9347f6cd802d0d06d0351c9", - "reference": "a64dc5d2cc7d6cafb9347f6cd802d0d06d0351c9", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/a6769aefb305efef849dc25c9fd1653358c148f0", + "reference": "a6769aefb305efef849dc25c9fd1653358c148f0", "shasum": "" }, "require": { @@ -7654,7 +7465,8 @@ "symfony/polyfill-mbstring": "^1.3" }, "require-dev": { - "phpstan/phpstan": "^2.0", + "php-cs-fixer/shim": "^3.0@stable", + "phpstan/phpstan": "^2.0@stable", "psr/container": "^1.0|^2.0", "symfony/phpunit-bridge": "^5.4.9|^6.4|^7.0" }, @@ -7698,7 +7510,7 @@ ], "support": { "issues": "https://github.com/twigphp/Twig/issues", - "source": "https://github.com/twigphp/Twig/tree/v3.23.0" + "source": "https://github.com/twigphp/Twig/tree/v3.24.0" }, "funding": [ { @@ -7710,7 +7522,7 @@ "type": "tidelift" } ], - "time": "2026-01-23T21:00:41+00:00" + "time": "2026-03-17T21:31:11+00:00" }, { "name": "vimeo/psalm", diff --git a/docs/adr-audit.md b/docs/adr-audit.md new file mode 100644 index 00000000..18cb4a72 --- /dev/null +++ b/docs/adr-audit.md @@ -0,0 +1,99 @@ +--- +sidebar_position: 5 +--- + +# ADR compliance audit — MyDash + +Audit of the 23 org-wide ADRs (`hydra/openspec/architecture/adr-*.md`) +against what MyDash actually does. Audit date: **2026-04-24**, after +consolidating 6 in-flight feature PRs into `development` and landing +the template + ADR cleanup PR. + +**Legend:** ✅ compliant · ⚠️ partial · ❌ gap · N/A out of scope + +## Matrix + +| ADR | Rule (short) | Status | Note | +|---|---|---|---| +| **001** Data layer | App config → `IAppConfig`, not OpenRegister | N/A | MyDash owns its own Doctrine entities (Dashboard, Tile, WidgetPlacement, ConditionalRule, AdminSetting). Self-contained domain model, no OpenRegister dependency | +| 001 | Register JSON at `lib/Settings/{app}_register.json` | N/A | no domain schemas exposed to OR | +| **002** API | URL pattern `/api/{resource}`, standard verbs | ✅ | all 17 routes in `appinfo/routes.php` follow REST conventions | +| 002 | No stack traces in error responses | ✅ | `ResponseHelper::error` now returns generic message (fixed in this PR) | +| 002 | Pagination | N/A | dashboards / tiles lists are small per-user; no pagination needed | +| **003** Backend | Controller → Service → Mapper layering | ✅ | 11 controllers delegate to 20 services which delegate to 20 mappers/entities | +| 003 | Thin controllers | ✅ | most methods under 20 lines; `DashboardApiController::update` is the largest at ~45 lines due to metadata-vs-layout branch | +| 003 | DI via constructor + `private readonly` | ✅ | verified across `lib/Controller/` and `lib/Service/` | +| 003 | No `\OC::$server` or static locators | ✅ | post-PR: `grep -rnE '\\OC::\\$server\\|Server::get(\\|new \\OC_' lib/` returns zero | +| 003 | `@spec` on every class + public method | ❌ | **deferred** — 0 of ~215 public methods tagged. Tracked as [openspec change + issue](#follow-ups) | +| 003 | Specific routes before wildcard | ✅ | no wildcard routes | +| **004** Frontend | Vue 2 + Pinia + Options API | ✅ | Vue 2.7 + Pinia stores + Webpack (template-aligned) | +| 004 | Never import from `@nextcloud/vue` directly — use `@conduction/nextcloud-vue` | ✅ | post-PR: all 9 direct imports migrated | +| 004 | All user-visible strings via `t(appName, '…')` | ✅ | verified across `src/components/` and `src/views/` | +| 004 | CSS uses NC variables only | ✅ | `--color-*` tokens used throughout | +| 004 | Never `window.confirm()` / `alert()` | ✅ | uses `NcDialog` via `@conduction/nextcloud-vue` | +| **005** Security | Admin check on backend, not frontend | ✅ | controller methods verify admin group membership | +| 005 | `#[NoAdminRequired]` paired with per-object auth check | ✅ | verified: `DashboardApiController::update` routes to `PermissionService::canEditDashboard`; `DashboardService::deleteDashboard` + `activateDashboard` check `$dashboard->getUserId() !== $userId`; `TileService` + `WidgetService` follow the same pattern at service layer | +| 005 | `#[PasswordConfirmationRequired]` on admin mutations | ⚠️ | admin-console mutations could benefit; deferred for a focused security review pass | +| 005 | No stack traces / exception messages in API responses | ✅ | `ResponseHelper::error` now returns generic `'Operation failed'` (fixed in this PR) | +| **006** Metrics | `/api/metrics` + `/api/health` | ✅ | `MetricsController` exposes Prometheus text format at `/api/metrics`; `HealthController` at `/api/health` | +| **007** i18n | English primary + Dutch required | ✅ | `l10n/en.json` (5485 B) + `l10n/nl.json` (5798 B); JS mirrors present | +| 007 | Frontend `t(appName, 'key')` + `n(...)` for plurals | ✅ | spot-check shows `translatePlural` used for count-based strings | +| 007 | Backend `$this->l10n->t('key')` | ✅ | admin settings strings use `IL10N` | +| **008** Testing | PHPUnit coverage per service / controller | ⚠️ | 8 unit tests present (DashboardFactory, VisibilityChecker, Tile, ConditionalRule, AdminSetting, …). Service+controller coverage grows with each opsx change | +| 008 | Newman/Postman collection | ❌ | **deferred** — no `tests/integration/*.postman_collection.json`. Tracked as [openspec change + issue](#follow-ups) | +| **009** Docs | User-facing features documented | ✅ | Docusaurus site at `docs/` with 10+ feature docs under `docs/features/` + intro + dev guide. Architecture doc added in this PR | +| **010** NL Design | CSS custom properties, no hardcoded colors | ✅ | verified | +| 010 | WCAG AA | ⚠️ | basic keyboard nav + focus handling present; no structured axe-core or NVDA pass run. Consistent with "admin-only app" guidance, but deeper audit worth a focused PR | +| **011** Schema standards | schema.org vocabulary | N/A | no domain schemas exposed externally | +| **012** Dedup | Reuse analysis in OpenSpec changes | ✅ | each archived change includes a "reuse analysis" section (spot-check on `2026-03-21-dashboards/design.md`) | +| **013** Container pool | Hydra infra concern | N/A | not an app concern | +| **014** Licensing | EUPL-1.2 on every source file | ✅ | post-PR: 72 PHP files have clean `@license EUPL-1.2` PHPDoc; contradictory `SPDX-License-Identifier: AGPL-3.0-or-later` block removed; `REUSE.toml` added for machine-readable REUSE compliance | +| 014 | `info.xml` licence element | ✅ | `agpl` retained as the Nextcloud app-store schema element (schema expects `agpl`) — separate from source licence; documented in commit `ab9fc70` | +| **015** Common patterns | Static generic error messages | ✅ | `ResponseHelper::error` returns generic text (fixed in this PR) | +| 015 | No raw `fetch()` — use `@nextcloud/axios` | ✅ | verified: `grep -rn 'fetch(' src/` returns zero | +| 015 | EUPL headers | ✅ | see ADR-014 | +| **016** Routes | `appinfo/routes.php` is the only registration path | ✅ | `grep -rE '#\\[ApiRoute\\|#\\[FrontpageRoute' lib/` returns zero; all 17 routes declared explicitly | +| **017** Component composition | Avoid wrapping self-contained components | ✅ | no file in `src/` exceeds 537 lines (`WidgetPicker.vue`); components use the `@conduction/nextcloud-vue` dashboard primitives | +| **018** Widget header actions | `header-actions` slot on cards | ⚠️ | MyDash renders **legacy** Nextcloud dashboard widgets (`NcDashboardWidget`) rather than OR-backed `CnDetailCard` / `CnObjectDataWidget`. That's by design (MyDash is a container app, not an OR data consumer), but ADR-018 contemplates header-actions on data cards. Treated as N/A-by-design for now; revisit if MyDash grows OR-backed tile types | +| **019** Integration registry | Sidebar tabs / linked items | N/A | MyDash is a widget **consumer**, not a registry provider | +| **020** Gate scope | Hydra gate scope is PR diff | N/A | reviewer guidance, not app code | +| **021** Bounded fix scope | Reviewer bounded-fix by change shape | N/A | reviewer guidance, not app code | +| **022** Apps consume OR abstractions | RBAC / audit / archival via OR | N/A | no OR consumption — see ADR-001 note | +| **023** Action authorization | Admin-configured action/group mappings | N/A | MyDash uses role-based permissions (`view` / `add_only` / `full`), not fine-grained action mapping | + +## Summary + +- **Compliant:** 25 rules (incl. compound rules) +- **Partial:** 4 rules (ADR-005 `PasswordConfirmationRequired`, ADR-008 + PHPUnit breadth, ADR-010 deep WCAG AA, ADR-018 `header-actions` + slot by design) +- **Gaps:** 2 rules (ADR-003 `@spec` tag coverage, ADR-008 Newman) +- **N/A:** 14 rules (no OR consumption, no registry provider, no fine- + grained actions, hydra-infra rules) + +## Follow-ups + +The two remaining `❌` items are tracked as formal OpenSpec changes + +GitHub issues so Hydra's pipeline can pick them up after this PR +merges: + +1. **`@spec` annotation pass across 64 PHP files / 215 public methods** + — needs a full `/opsx-annotate` run against the 10 archived changes + in `openspec/changes/archive/`. Scope is too large to pack into this + PR. +2. **Newman / Postman integration collection** — covering the 17 OCS + endpoints, with env-placeholder credentials and CI wiring. + +Partial items that may warrant their own follow-up PRs later (not +filed as Hydra-triggered issues today): + +3. **`PasswordConfirmationRequired` on admin-console mutations** + (ADR-005). The admin console writes template definitions + global + settings; a stolen session shouldn't silently alter those. Focused + security review. +4. **Deep WCAG AA audit** (ADR-010) — axe-core + manual screen-reader + traversal on the dashboard + tile editor surfaces. +5. **Thread `LoggerInterface` through the 6 controllers that currently + don't inject one**, so `ResponseHelper::error($e, logger: $this->logger)` + logs the real exception server-side. The leak is already closed + client-side; this restores visibility. diff --git a/docs/adr/README.md b/docs/adr/README.md new file mode 100644 index 00000000..af52b8ec --- /dev/null +++ b/docs/adr/README.md @@ -0,0 +1,63 @@ +--- +sidebar_position: 4 +--- + +# Architecture Decision Records + +MyDash inherits its ADRs from the ConductionNL platform-wide set. There +are no MyDash-specific ADRs today — every rule that applies to MyDash +comes from the 23 org-wide records maintained in the **hydra** repo: + +- Canonical location: + [`ConductionNL/hydra/openspec/architecture/`](https://github.com/ConductionNL/hydra/tree/main/openspec/architecture) +- Local checkout: + `hydra/openspec/architecture/adr-001-data-layer.md` through + `adr-023-action-authorization.md` + +## Why no app-level ADRs? + +Earlier versions of each Conduction app repo carried stale copies of +the ADRs in `.claude/openspec/architecture/`. Those drifted away from +the hydra source of truth and triggered false-positive review findings +(observed on decidesk #71, 2026-04-19 — the app-level copy of ADR-004 +said `fetch()`, hydra said `axios`). The stale copies were deleted +across every app repo; hydra is now the single source. + +When Hydra's builder + reviewer containers operate on a MyDash PR, they +bundle the current hydra ADRs into the build image and feed them to +the agents as immutable context. So the rules every PR is measured +against live in hydra, not here. + +## Quick index (per app-versions precedent) + +| ADR | Topic | +|-----|-------| +| 001 | Data layer (OpenRegister, entities, mappers) | +| 002 | API design (REST, Common Ground) | +| 003 | Backend (PHP, DI, 3-layer) | +| 004 | Frontend (Vue, components, settings) | +| 005 | Security (auth, CORS, input validation) | +| 006 | Metrics & observability | +| 007 | i18n (English primary, Dutch required) | +| 008 | Testing (PHPUnit, Newman, Playwright) | +| 009 | Documentation | +| 010 | NL Design System | +| 011 | Schema standards (schema.org, DCAT) | +| 012 | Deduplication | +| 013 | Container pool | +| 014 | Licensing (EUPL-1.2) | +| 015 | Common patterns (rate-limit retry, axios) | +| 016 | Routes (`appinfo/routes.php` is the only path) | +| 017 | Component composition | +| 018 | Widget header actions | +| 019 | Integration registry | +| 020 | Gate scope to PR diff | +| 021 | Bounded fix scope by change shape | +| 022 | Apps consume OpenRegister abstractions | +| 023 | Action-level authorisation | + +## Compliance + +MyDash's current compliance posture against the 23 ADRs is tracked in +[adr-audit.md](../adr-audit.md). That file names per-ADR status +(PASS / PARTIAL / FAIL / N/A), evidence, and follow-up items. diff --git a/docs/architecture.md b/docs/architecture.md new file mode 100644 index 00000000..70771c4b --- /dev/null +++ b/docs/architecture.md @@ -0,0 +1,165 @@ +--- +sidebar_position: 3 +--- + +# Architecture + +MyDash is a dashboard container + layout manager for Nextcloud. It lets +each user (and each administrator, for the organisation as a whole) +compose personal dashboards from tiles and legacy Nextcloud widgets, +arranged on a responsive grid with per-tile conditional visibility. + +Unlike thin UI utilities (e.g., app-versions), MyDash owns its own +domain model — dashboards, placements, tiles, conditional rules — and +persists everything in its own tables via Doctrine mappers. + +## Component map + +``` +┌─────────────────────────────────────────────────────────────┐ +│ src/ (Vue 3 + TS) │ +│ │ +│ views/Views.vue — shell / routing │ +│ views/Dashboard.vue — grid canvas │ +│ components/DashboardSwitcher — nav between dashboards │ +│ components/WidgetRenderer — legacy-widget bridge │ +│ components/WidgetPicker — "add widget" modal │ +│ components/WidgetWrapper — per-tile chrome │ +│ components/TileCard / TileEditor / WidgetStyleEditor │ +│ components/admin/AdminSettings — admin console │ +└──────────────────────────┬──────────────────────────────────┘ + │ OCS JSON via @nextcloud/axios +┌──────────────────────────▼──────────────────────────────────┐ +│ lib/Controller/ (11 classes) │ +│ │ +│ DashboardApiController — dashboard CRUD + activate │ +│ TileApiController — tile CRUD + placement │ +│ WidgetApiController — widget attach / detach │ +│ RuleApiController — conditional-visibility rules │ +│ AdminController — admin-only endpoints │ +│ MetricsController — Prometheus `/api/metrics` │ +│ HealthController — `/api/health` │ +│ PageController — admin SPA entry │ +│ │ +│ ResponseHelper — shared JSON envelope (ADR-005) │ +│ DashboardRequestValidator, RequestDataExtractor │ +└──────────────────────────┬──────────────────────────────────┘ + │ constructor DI (ADR-003) +┌──────────────────────────▼──────────────────────────────────┐ +│ lib/Service/ (20 classes) │ +│ │ +│ DashboardService / DashboardFactory / DashboardResolver │ +│ TileService / TileUpdater │ +│ WidgetService / WidgetItemLoader / WidgetFormatter │ +│ PlacementService / PlacementUpdater │ +│ PermissionService — per-object auth (ADR-005) │ +│ ConditionalService / RuleEvaluatorService / │ +│ VisibilityChecker / UserAttributeResolver │ +│ AdminSettingsService / AdminTemplateService / │ +│ TemplateService │ +│ MetricsCollector / MetricsQueryService │ +└──────────────────────────┬──────────────────────────────────┘ + │ OCP\AppFramework\Db\Mapper +┌──────────────────────────▼──────────────────────────────────┐ +│ lib/Db/ (20 classes) │ +│ │ +│ Entities: Dashboard, Tile, WidgetPlacement, │ +│ ConditionalRule, AdminSetting │ +│ Mappers: DashboardMapper, TileMapper, │ +│ WidgetPlacementMapper, ConditionalRuleMapper, │ +│ AdminSettingMapper │ +│ Traits: OwnedEntityInterface, TimestampedEntity, │ +│ GridPositionInterface, DashboardEntityIface │ +│ Helpers: ColumnTypeRegistry, JsonConfigHelper, │ +│ QueryHelper, TimestampHelper, │ +│ EntitySerializer │ +└─────────────────────────────────────────────────────────────┘ +``` + +## Request flow — update a dashboard + +1. UI sends `PUT /api/dashboard/{id}` via `@nextcloud/axios` with + `{ name?, description?, placements? }`. +2. `DashboardApiController::update()` (admin check + body validation) + delegates to `PermissionService::canEditDashboard(userId, id)` or + `canEditDashboardMetadata` depending on whether placements changed. +3. On success, `DashboardService::updateDashboard()` runs the mutation + inside a transaction. Ownership is re-verified at the service layer + (`$dashboard->getUserId() !== $userId` → `'Access denied'`). +4. `PlacementUpdater` reconciles the placement array — inserts new + placements, deletes removed ones, updates positions. +5. Controller wraps the result in `ResponseHelper::success`. + +## Authentication & authorization posture + +- **Routes**: all in `appinfo/routes.php` per ADR-016. No + `#[ApiRoute]` / `#[FrontpageRoute]` attributes on controllers. +- **Auth attributes**: every controller method carries an explicit + `#[NoAdminRequired]` / `#[PublicPage]` / `#[NoCSRFRequired]` / + `#[AuthorizedAdminSetting]` per ADR-005. +- **Per-object auth**: every mutation on `Dashboard`, `Tile`, + `WidgetPlacement`, `ConditionalRule` runs an ownership check through + `PermissionService` or the service's own `getUserId()` guard before + writing. See `DashboardService::deleteDashboard()`, + `TileService::updateTile()`, etc. +- **Error responses**: `ResponseHelper::error` returns a generic + message; the real exception is logged server-side when callers pass + their `LoggerInterface` (work in progress — tracked in + [adr-audit.md](./adr-audit.md)). + +## Dependency injection + +Every controller and service accepts its collaborators via constructor +(`private readonly` properties per ADR-003). No `\OC::$server->get()`, +no `OCP\Server::get()`, no `new \OC_App()`. Verified by `grep -rn +'\\\\OC::\$server\\|Server::get(\\|new \\\\OC_' lib/` returning zero +matches on `development`. + +## Frontend stack + +- **Vue 2.7** (not 3 — matches template baseline; shared component + expectations with other Conduction apps). +- **Webpack** via `@nextcloud/webpack-vue-config`. No Vite. +- **Pinia** stores for dashboard + widget state. +- **TypeScript** optional — most components are plain JS; typed where + it aids refactoring. +- **HTTP**: `@nextcloud/axios` exclusively. No bare `fetch()`. +- **Components**: every Nextcloud Vue import goes through + `@conduction/nextcloud-vue` (ADR-004). No direct `@nextcloud/vue` + imports. + +## Capabilities (openspec) + +Each capability has its own directory under `openspec/specs/` with a +Gherkin-style requirement list: + +| Capability | Owns | +|---|---| +| `dashboards` | dashboard CRUD, activation, ownership | +| `tiles` | tile catalogue, config schema, placement | +| `widgets` | widget CRUD, style, positioning | +| `grid-layout` | responsive grid (cols, row heights) | +| `permissions` | view / add-only / full per role | +| `conditional-visibility` | rule-based tile hide/show | +| `admin-settings` | org-wide defaults, admin console | +| `admin-templates` | curated starter dashboards | +| `prometheus-metrics` | `/api/metrics` instrumentation | +| `legacy-widget-bridge` | Nextcloud dashboard-widget compat | + +10 archived changes under `openspec/changes/archive/` document how each +capability arrived at its current shape. + +## What MyDash explicitly does NOT do + +- **No OpenRegister consumption** (ADR-001 / ADR-022 N/A). Dashboards + and tiles live in MyDash's own tables. The app is intentionally + self-contained. +- **No integration registry** (ADR-019 N/A). MyDash consumes the + Nextcloud dashboard-widget API; it does not expose an extension + point for third-party dashboards to register themselves. +- **No action-level authorisation** (ADR-023 N/A). Permission model is + role-based (`view` / `add_only` / `full` / `admin`), not mapped to + individual actions configured by the admin. +- **No government-theme targeting** beyond what comes via + `@conduction/nextcloud-vue` (ADR-010 partial). If Conduction ships + NL Design tokens in the wrapper, MyDash inherits them automatically. diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php index 09554e3c..dce7fdf9 100644 --- a/lib/AppInfo/Application.php +++ b/lib/AppInfo/Application.php @@ -12,9 +12,6 @@ * @license EUPL-1.2 https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 * @version GIT:auto * @link https://conduction.nl - * - * SPDX-FileCopyrightText: 2024 MyDash Contributors - * SPDX-License-Identifier: AGPL-3.0-or-later */ declare(strict_types=1); diff --git a/lib/Controller/AdminController.php b/lib/Controller/AdminController.php index c19e7bc1..c8e9fd6c 100644 --- a/lib/Controller/AdminController.php +++ b/lib/Controller/AdminController.php @@ -12,9 +12,6 @@ * @license EUPL-1.2 https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 * @version GIT:auto * @link https://conduction.nl - * - * SPDX-FileCopyrightText: 2024 MyDash Contributors - * SPDX-License-Identifier: AGPL-3.0-or-later */ declare(strict_types=1); diff --git a/lib/Controller/DashboardApiController.php b/lib/Controller/DashboardApiController.php index 8723bfd7..3d728f28 100644 --- a/lib/Controller/DashboardApiController.php +++ b/lib/Controller/DashboardApiController.php @@ -14,9 +14,6 @@ * @license EUPL-1.2 https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 * @version GIT:auto * @link https://conduction.nl - * - * SPDX-FileCopyrightText: 2024 MyDash Contributors - * SPDX-License-Identifier: AGPL-3.0-or-later */ declare(strict_types=1); diff --git a/lib/Controller/DashboardRequestValidator.php b/lib/Controller/DashboardRequestValidator.php index d0374364..e45a85c5 100644 --- a/lib/Controller/DashboardRequestValidator.php +++ b/lib/Controller/DashboardRequestValidator.php @@ -12,9 +12,6 @@ * @license EUPL-1.2 https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 * @version GIT:auto * @link https://conduction.nl - * - * SPDX-FileCopyrightText: 2024 MyDash Contributors - * SPDX-License-Identifier: AGPL-3.0-or-later */ declare(strict_types=1); diff --git a/lib/Controller/HealthController.php b/lib/Controller/HealthController.php index 9222fb14..5a134a8b 100644 --- a/lib/Controller/HealthController.php +++ b/lib/Controller/HealthController.php @@ -12,9 +12,6 @@ * @license EUPL-1.2 https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 * @version GIT:auto * @link https://conduction.nl - * - * SPDX-FileCopyrightText: 2024 MyDash Contributors - * SPDX-License-Identifier: AGPL-3.0-or-later */ declare(strict_types=1); diff --git a/lib/Controller/MetricsController.php b/lib/Controller/MetricsController.php index 3cbf6662..0127c6aa 100644 --- a/lib/Controller/MetricsController.php +++ b/lib/Controller/MetricsController.php @@ -12,9 +12,6 @@ * @license EUPL-1.2 https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 * @version GIT:auto * @link https://conduction.nl - * - * SPDX-FileCopyrightText: 2024 MyDash Contributors - * SPDX-License-Identifier: AGPL-3.0-or-later */ declare(strict_types=1); diff --git a/lib/Controller/PageController.php b/lib/Controller/PageController.php index 944aa6b2..209e6169 100644 --- a/lib/Controller/PageController.php +++ b/lib/Controller/PageController.php @@ -16,9 +16,6 @@ * @license EUPL-1.2 https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 * @version GIT:auto * @link https://conduction.nl - * - * SPDX-FileCopyrightText: 2024 MyDash Contributors - * SPDX-License-Identifier: AGPL-3.0-or-later */ declare(strict_types=1); diff --git a/lib/Controller/RequestDataExtractor.php b/lib/Controller/RequestDataExtractor.php index 434dd507..4bb0510c 100644 --- a/lib/Controller/RequestDataExtractor.php +++ b/lib/Controller/RequestDataExtractor.php @@ -12,9 +12,6 @@ * @license EUPL-1.2 https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 * @version GIT:auto * @link https://conduction.nl - * - * SPDX-FileCopyrightText: 2024 MyDash Contributors - * SPDX-License-Identifier: AGPL-3.0-or-later */ declare(strict_types=1); diff --git a/lib/Controller/ResponseHelper.php b/lib/Controller/ResponseHelper.php index 2d308fd0..d9e6f616 100644 --- a/lib/Controller/ResponseHelper.php +++ b/lib/Controller/ResponseHelper.php @@ -12,9 +12,6 @@ * @license EUPL-1.2 https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 * @version GIT:auto * @link https://conduction.nl - * - * SPDX-FileCopyrightText: 2024 MyDash Contributors - * SPDX-License-Identifier: AGPL-3.0-or-later */ declare(strict_types=1); @@ -23,6 +20,7 @@ use OCP\AppFramework\Http; use OCP\AppFramework\Http\JSONResponse; +use Psr\Log\LoggerInterface; /** * Helper for building common JSON responses in controllers. @@ -61,17 +59,35 @@ public static function forbidden( /** * Create an error response from an exception. * - * @param \Exception $exception The exception. - * @param int $statusCode The HTTP status code. + * Per ADR-005: never return the raw exception message to the client. + * Callers SHOULD pass their injected LoggerInterface so the real + * exception is recorded in the server log; the client only ever sees + * the generic `$message` (defaulting to "Operation failed"). + * + * @param \Exception $exception The exception. + * @param int $statusCode The HTTP status code. + * @param LoggerInterface|null $logger When provided, the exception is + * logged at ERROR level before + * the response is returned. + * @param string $message Generic client-facing message. * * @return JSONResponse The error response. */ public static function error( \Exception $exception, - int $statusCode=Http::STATUS_BAD_REQUEST + int $statusCode=Http::STATUS_BAD_REQUEST, + ?LoggerInterface $logger=null, + string $message='Operation failed' ): JSONResponse { + if ($logger !== null) { + $logger->error( + message: $exception->getMessage(), + context: ['exception' => $exception] + ); + } + return new JSONResponse( - data: ['error' => $exception->getMessage()], + data: ['error' => $message], statusCode: $statusCode ); }//end error() diff --git a/lib/Controller/RuleApiController.php b/lib/Controller/RuleApiController.php index 487b7c85..9fec1044 100644 --- a/lib/Controller/RuleApiController.php +++ b/lib/Controller/RuleApiController.php @@ -12,9 +12,6 @@ * @license EUPL-1.2 https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 * @version GIT:auto * @link https://conduction.nl - * - * SPDX-FileCopyrightText: 2024 MyDash Contributors - * SPDX-License-Identifier: AGPL-3.0-or-later */ declare(strict_types=1); diff --git a/lib/Controller/TileApiController.php b/lib/Controller/TileApiController.php index f5d03747..6277dd23 100644 --- a/lib/Controller/TileApiController.php +++ b/lib/Controller/TileApiController.php @@ -12,9 +12,6 @@ * @license EUPL-1.2 https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 * @version GIT:auto * @link https://conduction.nl - * - * SPDX-FileCopyrightText: 2024 MyDash Contributors - * SPDX-License-Identifier: AGPL-3.0-or-later */ declare(strict_types=1); diff --git a/lib/Controller/WidgetApiController.php b/lib/Controller/WidgetApiController.php index 0db24cb9..afeb8688 100644 --- a/lib/Controller/WidgetApiController.php +++ b/lib/Controller/WidgetApiController.php @@ -12,9 +12,6 @@ * @license EUPL-1.2 https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 * @version GIT:auto * @link https://conduction.nl - * - * SPDX-FileCopyrightText: 2024 MyDash Contributors - * SPDX-License-Identifier: AGPL-3.0-or-later */ declare(strict_types=1); diff --git a/lib/Db/AdminSetting.php b/lib/Db/AdminSetting.php index b45b55b4..3cb7a778 100644 --- a/lib/Db/AdminSetting.php +++ b/lib/Db/AdminSetting.php @@ -12,16 +12,12 @@ * @license https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 EUPL-1.2 * @version GIT:auto * @link https://conduction.nl - * - * SPDX-FileCopyrightText: 2024 MyDash Contributors - * SPDX-License-Identifier: AGPL-3.0-or-later */ declare(strict_types=1); namespace OCA\MyDash\Db; -use DateTime; use JsonSerializable; use OCP\AppFramework\Db\Entity; @@ -32,8 +28,8 @@ * @method void setSettingKey(string $settingKey) * @method string|null getSettingValue() * @method void setSettingValue(?string $settingValue) - * @method DateTime getUpdatedAt() - * @method void setUpdatedAt(DateTime $updatedAt) + * @method string|null getUpdatedAt() + * @method void setUpdatedAt(?string $updatedAt) */ class AdminSetting extends Entity implements JsonSerializable { @@ -89,11 +85,11 @@ class AdminSetting extends Entity implements JsonSerializable protected ?string $settingValue = null; /** - * The update timestamp. + * The update timestamp (ISO-8601 / 'c' format). * - * @var DateTime|null + * @var string|null */ - protected ?DateTime $updatedAt = null; + protected ?string $updatedAt = null; /** * Constructor @@ -145,16 +141,11 @@ public function setValueEncoded(mixed $value): void */ public function jsonSerialize(): array { - $updatedAtValue = null; - if ($this->updatedAt !== null) { - $updatedAtValue = $this->updatedAt->format(format: 'c'); - } - return [ 'id' => $this->getId(), 'key' => $this->settingKey, 'value' => $this->getValueDecoded(), - 'updatedAt' => $updatedAtValue, + 'updatedAt' => $this->updatedAt, ]; }//end jsonSerialize() }//end class diff --git a/lib/Db/AdminSettingMapper.php b/lib/Db/AdminSettingMapper.php index 97a761fe..247ee418 100644 --- a/lib/Db/AdminSettingMapper.php +++ b/lib/Db/AdminSettingMapper.php @@ -12,9 +12,6 @@ * @license EUPL-1.2 https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 * @version GIT:auto * @link https://conduction.nl - * - * SPDX-FileCopyrightText: 2024 MyDash Contributors - * SPDX-License-Identifier: AGPL-3.0-or-later */ declare(strict_types=1); diff --git a/lib/Db/ColumnTypeRegistry.php b/lib/Db/ColumnTypeRegistry.php index e324e913..1cd49072 100644 --- a/lib/Db/ColumnTypeRegistry.php +++ b/lib/Db/ColumnTypeRegistry.php @@ -12,9 +12,6 @@ * @license EUPL-1.2 https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 * @version GIT:auto * @link https://conduction.nl - * - * SPDX-FileCopyrightText: 2024 MyDash Contributors - * SPDX-License-Identifier: AGPL-3.0-or-later */ declare(strict_types=1); diff --git a/lib/Db/ConditionalRule.php b/lib/Db/ConditionalRule.php index e9e8158d..8d78fb7b 100644 --- a/lib/Db/ConditionalRule.php +++ b/lib/Db/ConditionalRule.php @@ -14,14 +14,10 @@ * @license https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 EUPL-1.2 * @version GIT:auto * @link https://conduction.nl - * - * SPDX-FileCopyrightText: 2024 MyDash Contributors - * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OCA\MyDash\Db; -use DateTime; use JsonSerializable; use OCP\AppFramework\Db\Entity; @@ -36,8 +32,8 @@ * @method void setRuleConfig(?string $ruleConfig) * @method bool getIsInclude() * @method void setIsInclude(bool $isInclude) - * @method DateTime getCreatedAt() - * @method void setCreatedAt(DateTime $createdAt) + * @method string|null getCreatedAt() + * @method void setCreatedAt(?string $createdAt) */ class ConditionalRule extends Entity implements JsonSerializable { @@ -99,11 +95,11 @@ class ConditionalRule extends Entity implements JsonSerializable protected bool $isInclude = true; /** - * The creation timestamp. + * The creation timestamp (ISO-8601 / 'c' format). * - * @var DateTime|null + * @var string|null */ - protected ?DateTime $createdAt = null; + protected ?string $createdAt = null; /** * Constructor @@ -163,18 +159,13 @@ public function setRuleConfigArray(array $config): void */ public function jsonSerialize(): array { - $createdAtValue = null; - if ($this->createdAt !== null) { - $createdAtValue = $this->createdAt->format(format: 'c'); - } - return [ 'id' => $this->getId(), 'widgetPlacementId' => $this->widgetPlacementId, 'ruleType' => $this->ruleType, 'ruleConfig' => $this->getRuleConfigArray(), 'isInclude' => $this->isInclude, - 'createdAt' => $createdAtValue, + 'createdAt' => $this->createdAt, ]; }//end jsonSerialize() }//end class diff --git a/lib/Db/ConditionalRuleMapper.php b/lib/Db/ConditionalRuleMapper.php index 19f7b591..df99c192 100644 --- a/lib/Db/ConditionalRuleMapper.php +++ b/lib/Db/ConditionalRuleMapper.php @@ -12,9 +12,6 @@ * @license EUPL-1.2 https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 * @version GIT:auto * @link https://conduction.nl - * - * SPDX-FileCopyrightText: 2024 MyDash Contributors - * SPDX-License-Identifier: AGPL-3.0-or-later */ declare(strict_types=1); diff --git a/lib/Db/Dashboard.php b/lib/Db/Dashboard.php index af867c53..474a4732 100644 --- a/lib/Db/Dashboard.php +++ b/lib/Db/Dashboard.php @@ -14,9 +14,6 @@ * @license https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 EUPL-1.2 * @version GIT:auto * @link https://conduction.nl - * - * SPDX-FileCopyrightText: 2024 MyDash Contributors - * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OCA\MyDash\Db; diff --git a/lib/Db/DashboardEntityInterface.php b/lib/Db/DashboardEntityInterface.php index 515f269e..1346cfdf 100644 --- a/lib/Db/DashboardEntityInterface.php +++ b/lib/Db/DashboardEntityInterface.php @@ -12,9 +12,6 @@ * @license EUPL-1.2 https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 * @version GIT:auto * @link https://conduction.nl - * - * SPDX-FileCopyrightText: 2024 MyDash Contributors - * SPDX-License-Identifier: AGPL-3.0-or-later */ declare(strict_types=1); diff --git a/lib/Db/DashboardMapper.php b/lib/Db/DashboardMapper.php index a98ba371..52b0f171 100644 --- a/lib/Db/DashboardMapper.php +++ b/lib/Db/DashboardMapper.php @@ -12,9 +12,6 @@ * @license EUPL-1.2 https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 * @version GIT:auto * @link https://conduction.nl - * - * SPDX-FileCopyrightText: 2024 MyDash Contributors - * SPDX-License-Identifier: AGPL-3.0-or-later */ declare(strict_types=1); diff --git a/lib/Db/EntityMapperInterface.php b/lib/Db/EntityMapperInterface.php index 43842fd5..05ab9704 100644 --- a/lib/Db/EntityMapperInterface.php +++ b/lib/Db/EntityMapperInterface.php @@ -12,9 +12,6 @@ * @license EUPL-1.2 https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 * @version GIT:auto * @link https://conduction.nl - * - * SPDX-FileCopyrightText: 2024 MyDash Contributors - * SPDX-License-Identifier: AGPL-3.0-or-later */ declare(strict_types=1); diff --git a/lib/Db/EntitySerializer.php b/lib/Db/EntitySerializer.php index 136d46b9..ebbc4c3d 100644 --- a/lib/Db/EntitySerializer.php +++ b/lib/Db/EntitySerializer.php @@ -12,9 +12,6 @@ * @license EUPL-1.2 https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 * @version GIT:auto * @link https://conduction.nl - * - * SPDX-FileCopyrightText: 2024 MyDash Contributors - * SPDX-License-Identifier: AGPL-3.0-or-later */ declare(strict_types=1); diff --git a/lib/Db/GridPositionInterface.php b/lib/Db/GridPositionInterface.php index 2e219dea..009babb9 100644 --- a/lib/Db/GridPositionInterface.php +++ b/lib/Db/GridPositionInterface.php @@ -12,9 +12,6 @@ * @license EUPL-1.2 https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 * @version GIT:auto * @link https://conduction.nl - * - * SPDX-FileCopyrightText: 2024 MyDash Contributors - * SPDX-License-Identifier: AGPL-3.0-or-later */ declare(strict_types=1); diff --git a/lib/Db/JsonConfigHelper.php b/lib/Db/JsonConfigHelper.php index d99e9d81..025cddcf 100644 --- a/lib/Db/JsonConfigHelper.php +++ b/lib/Db/JsonConfigHelper.php @@ -12,9 +12,6 @@ * @license EUPL-1.2 https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 * @version GIT:auto * @link https://conduction.nl - * - * SPDX-FileCopyrightText: 2024 MyDash Contributors - * SPDX-License-Identifier: AGPL-3.0-or-later */ declare(strict_types=1); diff --git a/lib/Db/OwnedEntityInterface.php b/lib/Db/OwnedEntityInterface.php index 88205175..10778c46 100644 --- a/lib/Db/OwnedEntityInterface.php +++ b/lib/Db/OwnedEntityInterface.php @@ -12,9 +12,6 @@ * @license EUPL-1.2 https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 * @version GIT:auto * @link https://conduction.nl - * - * SPDX-FileCopyrightText: 2024 MyDash Contributors - * SPDX-License-Identifier: AGPL-3.0-or-later */ declare(strict_types=1); diff --git a/lib/Db/QueryHelper.php b/lib/Db/QueryHelper.php index 77f0c31e..62d6e91c 100644 --- a/lib/Db/QueryHelper.php +++ b/lib/Db/QueryHelper.php @@ -12,9 +12,6 @@ * @license EUPL-1.2 https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 * @version GIT:auto * @link https://conduction.nl - * - * SPDX-FileCopyrightText: 2024 MyDash Contributors - * SPDX-License-Identifier: AGPL-3.0-or-later */ declare(strict_types=1); diff --git a/lib/Db/Tile.php b/lib/Db/Tile.php index 595e3c32..839278be 100644 --- a/lib/Db/Tile.php +++ b/lib/Db/Tile.php @@ -14,9 +14,6 @@ * @license https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 EUPL-1.2 * @version GIT:auto * @link https://conduction.nl - * - * SPDX-FileCopyrightText: 2024 MyDash Contributors - * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OCA\MyDash\Db; diff --git a/lib/Db/TileMapper.php b/lib/Db/TileMapper.php index ffddfb67..36896a4b 100644 --- a/lib/Db/TileMapper.php +++ b/lib/Db/TileMapper.php @@ -12,9 +12,6 @@ * @license EUPL-1.2 https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 * @version GIT:auto * @link https://conduction.nl - * - * SPDX-FileCopyrightText: 2024 MyDash Contributors - * SPDX-License-Identifier: AGPL-3.0-or-later */ declare(strict_types=1); diff --git a/lib/Db/TimestampHelper.php b/lib/Db/TimestampHelper.php index a51fb4b5..bf90f6ba 100644 --- a/lib/Db/TimestampHelper.php +++ b/lib/Db/TimestampHelper.php @@ -12,9 +12,6 @@ * @license EUPL-1.2 https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 * @version GIT:auto * @link https://conduction.nl - * - * SPDX-FileCopyrightText: 2024 MyDash Contributors - * SPDX-License-Identifier: AGPL-3.0-or-later */ declare(strict_types=1); diff --git a/lib/Db/TimestampedEntityInterface.php b/lib/Db/TimestampedEntityInterface.php index f0c23218..abc80afd 100644 --- a/lib/Db/TimestampedEntityInterface.php +++ b/lib/Db/TimestampedEntityInterface.php @@ -12,9 +12,6 @@ * @license EUPL-1.2 https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 * @version GIT:auto * @link https://conduction.nl - * - * SPDX-FileCopyrightText: 2024 MyDash Contributors - * SPDX-License-Identifier: AGPL-3.0-or-later */ declare(strict_types=1); diff --git a/lib/Db/WidgetPlacement.php b/lib/Db/WidgetPlacement.php index c9442b0c..53c6fd83 100644 --- a/lib/Db/WidgetPlacement.php +++ b/lib/Db/WidgetPlacement.php @@ -14,9 +14,6 @@ * @license https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 EUPL-1.2 * @version GIT:auto * @link https://conduction.nl - * - * SPDX-FileCopyrightText: 2024 MyDash Contributors - * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OCA\MyDash\Db; diff --git a/lib/Db/WidgetPlacementMapper.php b/lib/Db/WidgetPlacementMapper.php index d48aee6b..e1b7fafa 100644 --- a/lib/Db/WidgetPlacementMapper.php +++ b/lib/Db/WidgetPlacementMapper.php @@ -12,9 +12,6 @@ * @license EUPL-1.2 https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 * @version GIT:auto * @link https://conduction.nl - * - * SPDX-FileCopyrightText: 2024 MyDash Contributors - * SPDX-License-Identifier: AGPL-3.0-or-later */ declare(strict_types=1); diff --git a/lib/Migration/DashboardTableBuilder.php b/lib/Migration/DashboardTableBuilder.php index 4409b85e..8fb4c05c 100644 --- a/lib/Migration/DashboardTableBuilder.php +++ b/lib/Migration/DashboardTableBuilder.php @@ -12,9 +12,6 @@ * @license EUPL-1.2 https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 * @version GIT:auto * @link https://conduction.nl - * - * SPDX-FileCopyrightText: 2024 MyDash Contributors - * SPDX-License-Identifier: AGPL-3.0-or-later */ declare(strict_types=1); diff --git a/lib/Migration/MigrationTableBuilder.php b/lib/Migration/MigrationTableBuilder.php index 4b1d6e5d..aee8b0a3 100644 --- a/lib/Migration/MigrationTableBuilder.php +++ b/lib/Migration/MigrationTableBuilder.php @@ -12,9 +12,6 @@ * @license EUPL-1.2 https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 * @version GIT:auto * @link https://conduction.nl - * - * SPDX-FileCopyrightText: 2024 MyDash Contributors - * SPDX-License-Identifier: AGPL-3.0-or-later */ declare(strict_types=1); diff --git a/lib/Migration/PlacementTableBuilder.php b/lib/Migration/PlacementTableBuilder.php index 8a727c53..ca3b21cc 100644 --- a/lib/Migration/PlacementTableBuilder.php +++ b/lib/Migration/PlacementTableBuilder.php @@ -12,9 +12,6 @@ * @license EUPL-1.2 https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 * @version GIT:auto * @link https://conduction.nl - * - * SPDX-FileCopyrightText: 2024 MyDash Contributors - * SPDX-License-Identifier: AGPL-3.0-or-later */ declare(strict_types=1); diff --git a/lib/Migration/RulesTableBuilder.php b/lib/Migration/RulesTableBuilder.php index 066dcc3a..720b379a 100644 --- a/lib/Migration/RulesTableBuilder.php +++ b/lib/Migration/RulesTableBuilder.php @@ -12,9 +12,6 @@ * @license EUPL-1.2 https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 * @version GIT:auto * @link https://conduction.nl - * - * SPDX-FileCopyrightText: 2024 MyDash Contributors - * SPDX-License-Identifier: AGPL-3.0-or-later */ declare(strict_types=1); diff --git a/lib/Migration/SettingsTableBuilder.php b/lib/Migration/SettingsTableBuilder.php index 72f76977..5352cbda 100644 --- a/lib/Migration/SettingsTableBuilder.php +++ b/lib/Migration/SettingsTableBuilder.php @@ -12,9 +12,6 @@ * @license EUPL-1.2 https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 * @version GIT:auto * @link https://conduction.nl - * - * SPDX-FileCopyrightText: 2024 MyDash Contributors - * SPDX-License-Identifier: AGPL-3.0-or-later */ declare(strict_types=1); diff --git a/lib/Migration/Version001000Date20240101000000.php b/lib/Migration/Version001000Date20240101000000.php index c66957b2..d821dbaf 100644 --- a/lib/Migration/Version001000Date20240101000000.php +++ b/lib/Migration/Version001000Date20240101000000.php @@ -12,9 +12,6 @@ * @license EUPL-1.2 https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 * @version GIT:auto * @link https://conduction.nl - * - * SPDX-FileCopyrightText: 2024 MyDash Contributors - * SPDX-License-Identifier: AGPL-3.0-or-later */ declare(strict_types=1); diff --git a/lib/Migration/Version001001Date20260203000000.php b/lib/Migration/Version001001Date20260203000000.php index 9dd2bdd3..603a57f9 100644 --- a/lib/Migration/Version001001Date20260203000000.php +++ b/lib/Migration/Version001001Date20260203000000.php @@ -12,9 +12,6 @@ * @license EUPL-1.2 https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 * @version GIT:auto * @link https://conduction.nl - * - * SPDX-FileCopyrightText: 2024 MyDash Contributors - * SPDX-License-Identifier: AGPL-3.0-or-later */ declare(strict_types=1); diff --git a/lib/Migration/Version001002Date20260204000000.php b/lib/Migration/Version001002Date20260204000000.php index 0f7566b5..a570ee27 100644 --- a/lib/Migration/Version001002Date20260204000000.php +++ b/lib/Migration/Version001002Date20260204000000.php @@ -12,9 +12,6 @@ * @license EUPL-1.2 https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 * @version GIT:auto * @link https://conduction.nl - * - * SPDX-FileCopyrightText: 2024 MyDash Contributors - * SPDX-License-Identifier: AGPL-3.0-or-later */ declare(strict_types=1); diff --git a/lib/Migration/Version001003Date20260204120000.php b/lib/Migration/Version001003Date20260204120000.php index e73e4d01..3938551f 100644 --- a/lib/Migration/Version001003Date20260204120000.php +++ b/lib/Migration/Version001003Date20260204120000.php @@ -12,9 +12,6 @@ * @license EUPL-1.2 https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 * @version GIT:auto * @link https://conduction.nl - * - * SPDX-FileCopyrightText: 2024 MyDash Contributors - * SPDX-License-Identifier: AGPL-3.0-or-later */ declare(strict_types=1); diff --git a/lib/Migration/Version001004Date20260204150000.php b/lib/Migration/Version001004Date20260204150000.php index 51761fae..e7ca5e23 100644 --- a/lib/Migration/Version001004Date20260204150000.php +++ b/lib/Migration/Version001004Date20260204150000.php @@ -12,9 +12,6 @@ * @license EUPL-1.2 https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 * @version GIT:auto * @link https://conduction.nl - * - * SPDX-FileCopyrightText: 2026 MyDash Contributors - * SPDX-License-Identifier: AGPL-3.0-or-later */ declare(strict_types=1); diff --git a/lib/Service/AdminSettingsService.php b/lib/Service/AdminSettingsService.php index f2a6b6e3..1c9eabf2 100644 --- a/lib/Service/AdminSettingsService.php +++ b/lib/Service/AdminSettingsService.php @@ -12,9 +12,6 @@ * @license EUPL-1.2 https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 * @version GIT:auto * @link https://conduction.nl - * - * SPDX-FileCopyrightText: 2024 MyDash Contributors - * SPDX-License-Identifier: AGPL-3.0-or-later */ declare(strict_types=1); diff --git a/lib/Service/AdminTemplateService.php b/lib/Service/AdminTemplateService.php index 234c9b0a..f4b38a9c 100644 --- a/lib/Service/AdminTemplateService.php +++ b/lib/Service/AdminTemplateService.php @@ -12,9 +12,6 @@ * @license EUPL-1.2 https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 * @version GIT:auto * @link https://conduction.nl - * - * SPDX-FileCopyrightText: 2024 MyDash Contributors - * SPDX-License-Identifier: AGPL-3.0-or-later */ declare(strict_types=1); diff --git a/lib/Service/ConditionalService.php b/lib/Service/ConditionalService.php index c38d5928..75e8e52b 100644 --- a/lib/Service/ConditionalService.php +++ b/lib/Service/ConditionalService.php @@ -12,9 +12,6 @@ * @license EUPL-1.2 https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 * @version GIT:auto * @link https://conduction.nl - * - * SPDX-FileCopyrightText: 2024 MyDash Contributors - * SPDX-License-Identifier: AGPL-3.0-or-later */ declare(strict_types=1); @@ -130,7 +127,7 @@ public function addRule( $rule->setRuleType($ruleType); $rule->setRuleConfigArray($ruleConfig); $rule->setIsInclude($isInclude); - $rule->setCreatedAt(new DateTime()); + $rule->setCreatedAt((new DateTime())->format(format: 'c')); return $this->ruleMapper->insert(entity: $rule); }//end addRule() diff --git a/lib/Service/DashboardFactory.php b/lib/Service/DashboardFactory.php index afd32943..de067856 100644 --- a/lib/Service/DashboardFactory.php +++ b/lib/Service/DashboardFactory.php @@ -13,9 +13,6 @@ * @license EUPL-1.2 https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 * @version GIT:auto * @link https://conduction.nl - * - * SPDX-FileCopyrightText: 2024 MyDash Contributors - * SPDX-License-Identifier: AGPL-3.0-or-later */ declare(strict_types=1); diff --git a/lib/Service/DashboardResolver.php b/lib/Service/DashboardResolver.php index 24992f63..b609b2e3 100644 --- a/lib/Service/DashboardResolver.php +++ b/lib/Service/DashboardResolver.php @@ -12,9 +12,6 @@ * @license EUPL-1.2 https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 * @version GIT:auto * @link https://conduction.nl - * - * SPDX-FileCopyrightText: 2024 MyDash Contributors - * SPDX-License-Identifier: AGPL-3.0-or-later */ declare(strict_types=1); diff --git a/lib/Service/DashboardService.php b/lib/Service/DashboardService.php index 161ca967..f353249b 100644 --- a/lib/Service/DashboardService.php +++ b/lib/Service/DashboardService.php @@ -13,9 +13,6 @@ * @license EUPL-1.2 https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 * @version GIT:auto * @link https://conduction.nl - * - * SPDX-FileCopyrightText: 2024 MyDash Contributors - * SPDX-License-Identifier: AGPL-3.0-or-later */ declare(strict_types=1); diff --git a/lib/Service/MetricsCollector.php b/lib/Service/MetricsCollector.php index 25ed1633..75df97cd 100644 --- a/lib/Service/MetricsCollector.php +++ b/lib/Service/MetricsCollector.php @@ -12,9 +12,6 @@ * @license EUPL-1.2 https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 * @version GIT:auto * @link https://conduction.nl - * - * SPDX-FileCopyrightText: 2024 MyDash Contributors - * SPDX-License-Identifier: AGPL-3.0-or-later */ declare(strict_types=1); diff --git a/lib/Service/MetricsQueryService.php b/lib/Service/MetricsQueryService.php index fc877252..7f1995f4 100644 --- a/lib/Service/MetricsQueryService.php +++ b/lib/Service/MetricsQueryService.php @@ -12,9 +12,6 @@ * @license EUPL-1.2 https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 * @version GIT:auto * @link https://conduction.nl - * - * SPDX-FileCopyrightText: 2024 MyDash Contributors - * SPDX-License-Identifier: AGPL-3.0-or-later */ declare(strict_types=1); diff --git a/lib/Service/PermissionService.php b/lib/Service/PermissionService.php index 2d6b92d2..d75a7ebc 100644 --- a/lib/Service/PermissionService.php +++ b/lib/Service/PermissionService.php @@ -12,9 +12,6 @@ * @license EUPL-1.2 https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 * @version GIT:auto * @link https://conduction.nl - * - * SPDX-FileCopyrightText: 2024 MyDash Contributors - * SPDX-License-Identifier: AGPL-3.0-or-later */ declare(strict_types=1); diff --git a/lib/Service/PlacementService.php b/lib/Service/PlacementService.php index 4d8bc76a..c3c31b93 100644 --- a/lib/Service/PlacementService.php +++ b/lib/Service/PlacementService.php @@ -12,9 +12,6 @@ * @license EUPL-1.2 https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 * @version GIT:auto * @link https://conduction.nl - * - * SPDX-FileCopyrightText: 2024 MyDash Contributors - * SPDX-License-Identifier: AGPL-3.0-or-later */ declare(strict_types=1); diff --git a/lib/Service/PlacementUpdater.php b/lib/Service/PlacementUpdater.php index 6551d473..df444b62 100644 --- a/lib/Service/PlacementUpdater.php +++ b/lib/Service/PlacementUpdater.php @@ -12,9 +12,6 @@ * @license EUPL-1.2 https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 * @version GIT:auto * @link https://conduction.nl - * - * SPDX-FileCopyrightText: 2024 MyDash Contributors - * SPDX-License-Identifier: AGPL-3.0-or-later */ declare(strict_types=1); diff --git a/lib/Service/RuleEvaluatorService.php b/lib/Service/RuleEvaluatorService.php index db2098db..823896fc 100644 --- a/lib/Service/RuleEvaluatorService.php +++ b/lib/Service/RuleEvaluatorService.php @@ -12,9 +12,6 @@ * @license EUPL-1.2 https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 * @version GIT:auto * @link https://conduction.nl - * - * SPDX-FileCopyrightText: 2024 MyDash Contributors - * SPDX-License-Identifier: AGPL-3.0-or-later */ declare(strict_types=1); diff --git a/lib/Service/TemplateService.php b/lib/Service/TemplateService.php index dbb432e5..5e5bb576 100644 --- a/lib/Service/TemplateService.php +++ b/lib/Service/TemplateService.php @@ -12,9 +12,6 @@ * @license EUPL-1.2 https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 * @version GIT:auto * @link https://conduction.nl - * - * SPDX-FileCopyrightText: 2024 MyDash Contributors - * SPDX-License-Identifier: AGPL-3.0-or-later */ declare(strict_types=1); @@ -29,7 +26,6 @@ use OCP\AppFramework\Db\DoesNotExistException; use OCP\IGroupManager; use OCP\IUserManager; -use Ramsey\Uuid\Uuid; /** * Service for managing admin dashboard templates. @@ -139,8 +135,9 @@ private function buildDashboardFromTemplate( string $userId, Dashboard $template ): Dashboard { + $now = (new DateTime())->format(format: 'Y-m-d H:i:s'); $dashboard = new Dashboard(); - $dashboard->setUuid(Uuid::uuid4()->toString()); + $dashboard->setUuid($this->generateUuid()); $dashboard->setName($template->getName()); $dashboard->setDescription( $template->getDescription() @@ -157,12 +154,28 @@ private function buildDashboardFromTemplate( $template->getPermissionLevel() ); $dashboard->setIsActive(true); - $dashboard->setCreatedAt(new DateTime()); - $dashboard->setUpdatedAt(new DateTime()); + $dashboard->setCreatedAt($now); + $dashboard->setUpdatedAt($now); return $dashboard; }//end buildDashboardFromTemplate() + /** + * Generate a v4 UUID using random_bytes (no external dependency). + * + * @return string A v4 UUID. + */ + private function generateUuid(): string + { + $data = random_bytes(length: 16); + $data[6] = chr((ord($data[6]) & 0x0F) | 0x40); + $data[8] = chr((ord($data[8]) & 0x3F) | 0x80); + return vsprintf( + format: '%s%s-%s-%s-%s-%s%s%s', + values: str_split(string: bin2hex(string: $data), length: 4) + ); + }//end generateUuid() + /** * Copy widget placements from a template to a new dashboard. * @@ -221,8 +234,9 @@ private function clonePlacement( ); $placement->setShowTitle($source->getShowTitle()); $placement->setSortOrder($source->getSortOrder()); - $placement->setCreatedAt(new DateTime()); - $placement->setUpdatedAt(new DateTime()); + $now = (new DateTime())->format(format: 'Y-m-d H:i:s'); + $placement->setCreatedAt($now); + $placement->setUpdatedAt($now); return $placement; }//end clonePlacement() diff --git a/lib/Service/TileService.php b/lib/Service/TileService.php index 381899f9..251b5b57 100644 --- a/lib/Service/TileService.php +++ b/lib/Service/TileService.php @@ -12,9 +12,6 @@ * @license EUPL-1.2 https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 * @version GIT:auto * @link https://conduction.nl - * - * SPDX-FileCopyrightText: 2024 MyDash Contributors - * SPDX-License-Identifier: AGPL-3.0-or-later */ declare(strict_types=1); diff --git a/lib/Service/TileUpdater.php b/lib/Service/TileUpdater.php index b8dd5de7..1ea4da51 100644 --- a/lib/Service/TileUpdater.php +++ b/lib/Service/TileUpdater.php @@ -12,9 +12,6 @@ * @license EUPL-1.2 https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 * @version GIT:auto * @link https://conduction.nl - * - * SPDX-FileCopyrightText: 2024 MyDash Contributors - * SPDX-License-Identifier: AGPL-3.0-or-later */ declare(strict_types=1); diff --git a/lib/Service/UserAttributeResolver.php b/lib/Service/UserAttributeResolver.php index e0324c14..81a862f5 100644 --- a/lib/Service/UserAttributeResolver.php +++ b/lib/Service/UserAttributeResolver.php @@ -12,15 +12,13 @@ * @license EUPL-1.2 https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 * @version GIT:auto * @link https://conduction.nl - * - * SPDX-FileCopyrightText: 2024 MyDash Contributors - * SPDX-License-Identifier: AGPL-3.0-or-later */ declare(strict_types=1); namespace OCA\MyDash\Service; +use OCP\IConfig; use OCP\IUserManager; /** @@ -32,9 +30,11 @@ class UserAttributeResolver * Constructor * * @param IUserManager $userManager The user manager interface. + * @param IConfig $config Config service for per-user prefs (e.g. language). */ public function __construct( private readonly IUserManager $userManager, + private readonly IConfig $config, ) { }//end __construct() @@ -56,7 +56,12 @@ public function getUserAttributeValue( } return match ($attribute) { - 'locale' => $user->getLanguage() ?? 'en', + 'locale' => $this->config->getUserValue( + userId: $userId, + appName: 'core', + key: 'lang', + default: 'en' + ), 'email' => $user->getEMailAddress(), 'displayName' => $user->getDisplayName(), 'quota' => (string) $user->getQuota(), diff --git a/lib/Service/VisibilityChecker.php b/lib/Service/VisibilityChecker.php index b839135f..8e0ff83c 100644 --- a/lib/Service/VisibilityChecker.php +++ b/lib/Service/VisibilityChecker.php @@ -12,9 +12,6 @@ * @license EUPL-1.2 https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 * @version GIT:auto * @link https://conduction.nl - * - * SPDX-FileCopyrightText: 2024 MyDash Contributors - * SPDX-License-Identifier: AGPL-3.0-or-later */ declare(strict_types=1); diff --git a/lib/Service/WidgetFormatter.php b/lib/Service/WidgetFormatter.php index 50ab762e..1556360e 100644 --- a/lib/Service/WidgetFormatter.php +++ b/lib/Service/WidgetFormatter.php @@ -12,9 +12,6 @@ * @license EUPL-1.2 https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 * @version GIT:auto * @link https://conduction.nl - * - * SPDX-FileCopyrightText: 2024 MyDash Contributors - * SPDX-License-Identifier: AGPL-3.0-or-later */ declare(strict_types=1); diff --git a/lib/Service/WidgetItemLoader.php b/lib/Service/WidgetItemLoader.php index cbb436d6..941feff5 100644 --- a/lib/Service/WidgetItemLoader.php +++ b/lib/Service/WidgetItemLoader.php @@ -12,9 +12,6 @@ * @license EUPL-1.2 https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 * @version GIT:auto * @link https://conduction.nl - * - * SPDX-FileCopyrightText: 2024 MyDash Contributors - * SPDX-License-Identifier: AGPL-3.0-or-later */ declare(strict_types=1); diff --git a/lib/Service/WidgetService.php b/lib/Service/WidgetService.php index e600e665..fa1819c5 100644 --- a/lib/Service/WidgetService.php +++ b/lib/Service/WidgetService.php @@ -12,9 +12,6 @@ * @license EUPL-1.2 https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 * @version GIT:auto * @link https://conduction.nl - * - * SPDX-FileCopyrightText: 2024 MyDash Contributors - * SPDX-License-Identifier: AGPL-3.0-or-later */ declare(strict_types=1); diff --git a/lib/Settings/MyDashAdmin.php b/lib/Settings/MyDashAdmin.php index 2ea0f8d5..519fd385 100644 --- a/lib/Settings/MyDashAdmin.php +++ b/lib/Settings/MyDashAdmin.php @@ -16,9 +16,6 @@ * @license EUPL-1.2 https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 * @version GIT:auto * @link https://conduction.nl - * - * SPDX-FileCopyrightText: 2024 MyDash Contributors - * SPDX-License-Identifier: AGPL-3.0-or-later */ declare(strict_types=1); diff --git a/lib/Settings/MyDashAdminSection.php b/lib/Settings/MyDashAdminSection.php index b87906ab..a9f0bc17 100644 --- a/lib/Settings/MyDashAdminSection.php +++ b/lib/Settings/MyDashAdminSection.php @@ -12,9 +12,6 @@ * @license EUPL-1.2 https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 * @version GIT:auto * @link https://conduction.nl - * - * SPDX-FileCopyrightText: 2024 MyDash Contributors - * SPDX-License-Identifier: AGPL-3.0-or-later */ declare(strict_types=1); diff --git a/openspec/changes/newman-integration-suite-2026-04-24/design.md b/openspec/changes/newman-integration-suite-2026-04-24/design.md new file mode 100644 index 00000000..d009eb1d --- /dev/null +++ b/openspec/changes/newman-integration-suite-2026-04-24/design.md @@ -0,0 +1,106 @@ +# Design — Newman integration collection + +## Shape + +One Postman 2.1 collection at +`tests/integration/mydash.postman_collection.json`. Top-level folders: + +- `Health + Metrics` +- `Dashboards` +- `Tiles` +- `Widgets` +- `Rules` +- `Admin` + +Each folder contains requests hitting one controller. Per-request +assertions live in `event[listen=test].script.exec[]`. + +## Auth + +HTTP basic over the OCS API. Collection-level `auth` uses +`{{admin_user}}` / `{{admin_password}}` variables. Requests that need +a non-admin caller override auth inline with `{{member_user}}` / +`{{member_password}}`. + +Required OCS headers on every request: + +``` +OCS-APIRequest: true +Accept: application/json +``` + +## Test pollution strategy + +The collection creates + tears down its own fixture dashboard. Order: + +1. `Dashboards / Fixture setup` — `POST /api/dashboard` with a + recognisable name (`newman-fixture-`). Captures the new + dashboard id in a collection variable (`{{fixture_dashboard_id}}`). +2. All write tests target `{{fixture_dashboard_id}}` — never a pre- + existing dashboard. +3. `Dashboards / Fixture teardown` — `DELETE /api/dashboard/{{fixture_dashboard_id}}` + at the end of the folder. + +If a test fails mid-run, the teardown still runs (Newman's `— +--bail` is NOT set). The fixture's timestamped name makes orphaned +fixtures easy to find with `newman-fixture-*` grep. + +## Environment + +`tests/integration/README.md` documents local run: + +```bash +npm install -g newman +newman run tests/integration/mydash.postman_collection.json \ + --env-var base_url=http://nextcloud.local \ + --env-var admin_user=admin \ + --env-var admin_password=admin \ + --env-var member_user=regular \ + --env-var member_password=regular +``` + +CI passes `base_url=http://localhost:8080`. + +## Assertion shape + +Each request gets ≥ 2 assertions. Minimal pattern: + +```javascript +pm.test('200 OK', () => pm.response.to.have.status(200)); +pm.test('OCS envelope', () => { + const json = pm.response.json(); + pm.expect(json).to.have.nested.property('ocs.meta.status'); + pm.expect(json.ocs.meta.status).to.equal('ok'); +}); +``` + +Forbidden / admin-gated endpoints get the mirror: + +```javascript +pm.test('403 Forbidden for non-admin', () => pm.response.to.have.status(403)); +pm.test('Error envelope', () => { + const json = pm.response.json(); + pm.expect(json).to.have.property('error'); +}); +``` + +## CI wiring + +`.github/workflows/code-quality.yml` already sets `enable-newman: true`. +The reusable quality workflow picks up any +`tests/integration/*.postman_collection.json` automatically. Soft +failures on network-dependent calls (app-store proxy) use +`pm.expect([200, 502, 503]).to.include(pm.response.code)` so CI +without egress passes the build. + +## Risk + +- **Tile / Widget / Rule shape drift**: the collection assertions + match the payload shape as of 2026-04-24. If a request/response + model changes (e.g., additional fields), the shape tests need to + be updated alongside the code change. Each change proposal that + modifies a controller should update this collection. +- **Admin role in CI**: the reusable workflow provisions an admin + user by default. The member-level tests need a second user + provisioned — design.md for the workflow may need an extension + if this capability isn't already there. diff --git a/openspec/changes/newman-integration-suite-2026-04-24/proposal.md b/openspec/changes/newman-integration-suite-2026-04-24/proposal.md new file mode 100644 index 00000000..10a62af8 --- /dev/null +++ b/openspec/changes/newman-integration-suite-2026-04-24/proposal.md @@ -0,0 +1,58 @@ +# Newman / Postman integration collection + +Closes the ADR-008 `Newman/Postman collection` gap flagged in +[`docs/adr-audit.md`](../../../docs/adr-audit.md). + +## Why + +ADR-008 requires a Newman collection covering every OCS endpoint. +MyDash's 17 routes have no integration coverage today — +`tests/integration/` does not exist. The only end-to-end visibility +today is manual QA + CI PHPUnit. + +`.github/workflows/code-quality.yml` already declares +`enable-newman: true` in its reusable-workflow call. The runner picks +up any `*.postman_collection.json` in `tests/integration/` — there's +just no file to run. + +## Scope + +Add `tests/integration/mydash.postman_collection.json` covering +**all 17 routes** across the 7 controllers: + +- **Health + Metrics** (public / admin) — 2 endpoints +- **Dashboard API** — 6 endpoints (list, getActive, create, update, + delete, activate) +- **Tile API** — 3+ endpoints (full CRUD shape TBD by audit of + `TileApiController`) +- **Widget API** — 4 endpoints (addWidget, addTile, updateWidget, + removeWidget) +- **Rule API** — 4 endpoints (list, create, update, delete) +- **Admin** — admin-only endpoints + +Assertions follow the app-versions precedent: +- Happy-path status code + shape of the OCS envelope +- Admin-only endpoints: 403 for non-admin callers +- Destructive endpoints (`DELETE` / `POST /activate`) are exercised + against a fresh fixture dashboard created in the same collection + run, then cleaned up — no test pollution +- App-store / external-HTTP endpoints accept soft status codes + (200/502/503) so CI without egress still passes the build + +## Not in scope + +- UI end-to-end tests (Playwright). That's ADR-008's E2E deliverable, + a separate effort. +- Load / perf testing. +- Mutation testing. + +## Acceptance + +1. `tests/integration/mydash.postman_collection.json` exists with ≥ 17 + request definitions. +2. Each request has ≥ 2 assertions (status code + payload shape). +3. `tests/integration/README.md` documents the local-run command and + env-placeholder credentials (`base_url`, `admin_user`, + `admin_password`, `member_user`, `member_password`). +4. CI's `Code Quality → Integration Tests (Newman)` job runs green on + the PR. diff --git a/openspec/changes/newman-integration-suite-2026-04-24/specs/README.md b/openspec/changes/newman-integration-suite-2026-04-24/specs/README.md new file mode 100644 index 00000000..7675f1c9 --- /dev/null +++ b/openspec/changes/newman-integration-suite-2026-04-24/specs/README.md @@ -0,0 +1,13 @@ +# Spec delta — Newman integration collection + +This change is **test-infrastructure only**. It does not modify any +capability's Requirements. The requirements being tested were all +established by the 10 archived changes under `openspec/changes/archive/`; +this change adds executable verification for them. + +No `/spec.md` delta — the existing Requirements already +describe the expected behaviour; the Newman collection simply +exercises each route and asserts on the behaviour the specs have +already committed to. + +Hence this directory holds only this README. diff --git a/openspec/changes/newman-integration-suite-2026-04-24/tasks.md b/openspec/changes/newman-integration-suite-2026-04-24/tasks.md new file mode 100644 index 00000000..56b762ee --- /dev/null +++ b/openspec/changes/newman-integration-suite-2026-04-24/tasks.md @@ -0,0 +1,65 @@ +# Tasks — Newman integration collection + +## Task 1: Inventory every route + +- [ ] From `appinfo/routes.php`, enumerate every route. Expected + count: 17. +- [ ] Group by controller. For each route, note: + - HTTP verb + path + - Auth posture (admin-required vs user-required vs public) + - Request body shape (from the controller's method signature + + docblock) + - Expected response shape + +## Task 2: Write the collection + +- [ ] Create `tests/integration/mydash.postman_collection.json` + (Postman 2.1 schema) with collection-level basic auth using + `{{admin_user}}` / `{{admin_password}}` variables. +- [ ] Six folders: `Health + Metrics`, `Dashboards`, `Tiles`, + `Widgets`, `Rules`, `Admin`. +- [ ] Start with `Dashboards / Fixture setup` — POST /api/dashboard + capturing `{{fixture_dashboard_id}}`. +- [ ] Add one request per route. Each request: + - Sets `OCS-APIRequest: true` + `Accept: application/json` + - Uses `{{fixture_dashboard_id}}` for any dashboard-scoped operations + - Carries ≥ 2 test-event assertions per the shapes in design.md +- [ ] End with `Dashboards / Fixture teardown` — DELETE + /api/dashboard/{{fixture_dashboard_id}}. + +## Task 3: Member-vs-admin branch tests + +- [ ] For every admin-gated endpoint, add a companion request under + `Admin / Forbidden for members` that overrides auth to + `{{member_user}}` + `{{member_password}}` and asserts 403. +- [ ] Member-level happy paths (dashboards they own) go in + `Dashboards / Member happy path` — at least list + get + update + on a member-owned fixture dashboard. + +## Task 4: README + +- [ ] Create `tests/integration/README.md` with: + - Local-run command + env-var names + - Fixture-cleanup note (teardown runs at end of Dashboards folder) + - Pointer to `.github/workflows/code-quality.yml` for the CI wiring + - Guidance: every PR touching a controller MUST update the matching + request in this collection + +## Task 5: CI verification + +- [ ] Confirm `.github/workflows/code-quality.yml` already passes + `enable-newman: true`. No wiring change needed if so. +- [ ] If the reusable quality workflow doesn't provision a second + (member-level) user, add a preamble step that creates `regular` / + `regular` before Newman runs. Alternatively, scope this change + to admin-level tests only and defer member-branch coverage to a + follow-up. +- [ ] Push + wait for the CI run. `Code Quality → Integration Tests + (Newman)` must be green. + +## Task 6: Docs + +- [ ] Update `docs/adr-audit.md` — flip ADR-008 `Newman/Postman + collection` row from ❌ to ✅. +- [ ] Remove the "Newman / Postman integration collection" item + from `docs/adr-audit.md`'s follow-ups list. diff --git a/openspec/changes/spec-annotation-pass-2026-04-24/design.md b/openspec/changes/spec-annotation-pass-2026-04-24/design.md new file mode 100644 index 00000000..dfa56990 --- /dev/null +++ b/openspec/changes/spec-annotation-pass-2026-04-24/design.md @@ -0,0 +1,96 @@ +# Design — `@spec` annotation pass + +## Approach + +This is a mechanical follow-up to an already-completed coverage scan. +The scan (`openspec/coverage-report.md`, 2026-04-24) emitted +`coverage-report.json` — a machine-readable classification that the +`/opsx-annotate mydash` skill consumes. + +Procedure: + +1. Load `coverage-report.json`. +2. For each Bucket 1 entry (61 methods): + - Open the file at `entry.file`. + - Add a file-level `@spec` tag in the main docblock if not present, + pointing at the primary capability (first Requirement reference). + - Add a method-level `@spec` tag above the declaration of the + method at `entry.method`. Format: + `@spec openspec/specs//spec.md#requirement-`. +3. For each Bucket 2b entry (5 methods in `legacy-widget-bridge` — + specs landed by PR #23): + - Same as Bucket 1, referencing the `legacy-widget-bridge` spec. +4. Resolve the 3 NEEDS-REVIEW flags (see below). +5. Skip plumbing (9 methods). + +## NEEDS-REVIEW decisions + +### `DashboardResolver::getEffectivePermissionLevel` vs +### `PermissionService::getEffectivePermissionLevel` + +Both methods compute the same thing (effective permission for a +user+dashboard pair). The coverage scan's confidence dropped because +the duplication makes ownership ambiguous. + +**Proposed call**: both methods own the same Requirement — +`permissions/spec.md#requirement-effective-permission-level`. The +`DashboardResolver` variant is a facade that delegates to +`PermissionService` (grep confirms); tag both with `@spec` pointing at +the same Requirement. Mark `DashboardResolver`'s copy as a +"delegating thin wrapper" in its docblock so future readers know +the authoritative implementation lives in `PermissionService`. + +### `MyDashAdmin::getForm` + `MyDashAdminSection::getID` + +These are `OCP\Settings\ISettings` / `OCP\Settings\IIconSection` +interface implementations — required by Nextcloud for admin UI +registration. They're pure boilerplate and don't implement domain +behaviour. + +**Proposed call**: treat as **plumbing** (skip annotation). Document +in the file-level docblock: "Nextcloud admin-UI registration +boilerplate; behaviour defined by OCP\Settings contracts, not a +MyDash spec." This matches how `app-versions` handled its +`Application::__construct` (Nextcloud framework hook, no `@spec`). + +## Format convention + +File-level tag (in the main PHPDoc docblock, after `@link`): + +```php + * @link https://conduction.nl + * + * @spec openspec/specs/dashboards/spec.md + */ +``` + +Method-level tag (in the method's PHPDoc, last before attributes): + +```php + /** + * List all dashboards for the current user. + * + * @return JSONResponse + * + * @spec openspec/specs/dashboards/spec.md#requirement-list-user-dashboards + */ + #[NoAdminRequired] + public function list(): JSONResponse +``` + +Format matches the app-versions precedent +(`openspec/specs/version-management/spec.md#requirement-list-installed-apps`) +and the 2 retrofit commits that already landed on this repo +(`@spec openspec/changes/archive/2026-04-24-retrofit-legacy-widget-bridge/tasks.md#task-1`). + +## Risk + +- **Anchor drift**: markdown-anchor format for Requirement headings + depends on heading capitalisation + bracketed tier. Verify the + anchors resolve in rendered Docusaurus output before merging. +- **Coverage-scan staleness**: the scan was 2026-04-24. If significant + code landed between then and this change merging, re-run + `/opsx-coverage-scan` first. +- **NEEDS-REVIEW decision bleed**: the two decisions above MUST land + in design.md before tasks.md executes, so the builder doesn't + improvise. diff --git a/openspec/changes/spec-annotation-pass-2026-04-24/proposal.md b/openspec/changes/spec-annotation-pass-2026-04-24/proposal.md new file mode 100644 index 00000000..b82dff94 --- /dev/null +++ b/openspec/changes/spec-annotation-pass-2026-04-24/proposal.md @@ -0,0 +1,61 @@ +# `@spec` annotation pass across `lib/` + +Closes the ADR-003 `@spec` tag gap flagged in +[`docs/adr-audit.md`](../../../docs/adr-audit.md). MyDash has **zero** +`@spec` PHPDoc tags across 64 PHP files and ~215 public methods today. + +## Why now + +The coverage scan at `openspec/coverage-report.md` (2026-04-24) has +already done the hard classification work: + +- **Bucket 1** — 61 methods matched cleanly to existing Requirements + across 9 capability specs. Confidence ≥ 0.75 for all; ready to + annotate mechanically. +- **Bucket 2a** — 4 methods in a `dashboards` cluster need the + existing `dashboards` spec extended first (separate change — not + this one). +- **Bucket 2b** — 5 methods in a `legacy-widget-bridge` cluster; + **already specced** and landed by PR #23 (retrofit commit), so the + 5 methods here get `@spec` tags in this pass too. +- **Plumbing** — 9 methods that don't need specs (constructors, + getters, framework hooks). + +Three NEEDS-REVIEW flags from the coverage report (listed below) +require human judgement before annotation — this change proposes the +call for each. + +## Scope + +- Run `/opsx-annotate mydash` against the 61 Bucket 1 methods + + 5 Bucket 2b methods (from the retrofit that just landed) — 66 methods + total. +- Resolve the 3 `NEEDS-REVIEW` flags: + - `DashboardResolver::getEffectivePermissionLevel` (0.80) — duplicates + same method in `PermissionService`. Decide: does it belong to + `permissions` spec (most likely) or `dashboards` (delegator + pattern)? + - `MyDashAdmin::getForm` (0.80) + `MyDashAdminSection::getID` (0.75) — + admin-UI-registration boilerplate. Decide: extend `admin-settings` + spec with a new Requirement, or treat as plumbing (skip). +- Add file-level `@spec` tags pointing at each file's owning + capability; method-level `@spec` tags pointing at the specific + Requirement (format: + `openspec/specs//spec.md#requirement-`). +- Update `docs/adr-audit.md` — flip ADR-003 `@spec` row from ❌ to ✅. + +## Not in scope + +- New capability specs. Bucket 2a (`dashboards` extension) is a + separate change. +- Behaviour changes. This is annotation-only — no code logic touched. +- The 9 "plumbing" methods are skipped by design. + +## Acceptance + +1. `grep -rc '@spec' lib/` reports ≥ 75 hits (66 methods + ~10 + file-level tags). +2. The 3 NEEDS-REVIEW flags are resolved with a decision recorded in + design.md. +3. `docs/adr-audit.md` reflects the new compliance status. +4. No diff in behaviour — `composer test:unit` is green. diff --git a/openspec/changes/spec-annotation-pass-2026-04-24/specs/README.md b/openspec/changes/spec-annotation-pass-2026-04-24/specs/README.md new file mode 100644 index 00000000..bf29f784 --- /dev/null +++ b/openspec/changes/spec-annotation-pass-2026-04-24/specs/README.md @@ -0,0 +1,21 @@ +# Spec delta — `@spec` annotation pass + +This change is **annotation-only**. It does not modify any capability +spec's Requirements. It only adds PHPDoc `@spec` tags to 66 methods +across 17 files, pointing at the already-landed Requirements in: + +- `openspec/specs/admin-settings/spec.md` +- `openspec/specs/admin-templates/spec.md` +- `openspec/specs/conditional-visibility/spec.md` +- `openspec/specs/dashboards/spec.md` +- `openspec/specs/grid-layout/spec.md` +- `openspec/specs/permissions/spec.md` +- `openspec/specs/prometheus-metrics/spec.md` +- `openspec/specs/tiles/spec.md` +- `openspec/specs/widgets/spec.md` +- `openspec/changes/archive/2026-04-24-retrofit-legacy-widget-bridge/specs/version-management/spec.md` + (retrofit destination — Bucket 2b methods) + +No new Requirements, no modifications. Hence this directory holds no +`/spec.md` delta file — this README documents the +annotation-only nature of the change explicitly. diff --git a/openspec/changes/spec-annotation-pass-2026-04-24/tasks.md b/openspec/changes/spec-annotation-pass-2026-04-24/tasks.md new file mode 100644 index 00000000..e42098ba --- /dev/null +++ b/openspec/changes/spec-annotation-pass-2026-04-24/tasks.md @@ -0,0 +1,53 @@ +# Tasks — `@spec` annotation pass + +## Task 1: Refresh the coverage scan if stale + +- [ ] Check `openspec/coverage-report.md` header — if the scan date + is more than 14 days before the PR is built, re-run + `/opsx-coverage-scan mydash` to regenerate. +- [ ] Confirm `coverage-report.json` sidecar exists and parses. + +## Task 2: Annotate Bucket 1 (61 methods) + +- [ ] Run `/opsx-annotate mydash --bucket 1 --write`. +- [ ] The skill adds `@spec` tags on method docblocks + file-level + tags where missing. Commit with message + `spec(annotate): bucket 1 — 61 methods via opsx-annotate`. + +## Task 3: Annotate Bucket 2b (5 methods in legacy-widget-bridge) + +- [ ] Run `/opsx-annotate mydash --cluster legacy-widget-bridge + --write`. +- [ ] These methods were specced by PR #23 (retrofit, archived at + `openspec/changes/archive/2026-04-24-retrofit-legacy-widget-bridge/`). + Tags point at the archive's `tasks.md#task-1`, consistent with + app-versions precedent. + +## Task 4: Resolve NEEDS-REVIEW flags per design.md + +- [ ] `DashboardResolver::getEffectivePermissionLevel` — annotate with + `@spec openspec/specs/permissions/spec.md#requirement-effective-permission-level` + + add a docblock note naming `PermissionService` as the authoritative + implementation. +- [ ] `PermissionService::getEffectivePermissionLevel` — same + `@spec` target. +- [ ] `MyDashAdmin::getForm` + `MyDashAdminSection::getID` — skip, + add docblock comment "Nextcloud admin-UI registration boilerplate" + instead of `@spec`. + +## Task 5: Verification + +- [ ] `grep -rc '@spec' lib/` ≥ 75. +- [ ] `composer test:unit` green. +- [ ] `composer check:strict` green (including phpcs — tags must match + PHPDoc style). +- [ ] Rendered Docusaurus links in the 66 method tags resolve (spot- + check 5 across different capabilities). + +## Task 6: Docs + +- [ ] Update `docs/adr-audit.md` — flip ADR-003 `@spec` row from ❌ to ✅. +- [ ] Remove the "`@spec` annotation pass" item from the follow-ups + section of `docs/adr-audit.md`. +- [ ] Re-run `opsx-coverage-scan mydash` at the end; confirm Bucket 1 + count drops to 0 or matches only plumbing methods. diff --git a/phpcs-custom-sniffs/CustomSniffs/Sniffs/Functions/NamedParametersSniff.php b/phpcs-custom-sniffs/CustomSniffs/Sniffs/Functions/NamedParametersSniff.php index 92740a93..7fca7cb7 100644 --- a/phpcs-custom-sniffs/CustomSniffs/Sniffs/Functions/NamedParametersSniff.php +++ b/phpcs-custom-sniffs/CustomSniffs/Sniffs/Functions/NamedParametersSniff.php @@ -70,6 +70,13 @@ public function process(File $phpcsFile, $stackPtr): void return; } + // Skip Entity magic setter/getter calls. Nextcloud's OCP\AppFramework\Db\Entity + // routes set*/get*/is* through __call which uses $args[0] and breaks with named + // parameters. Files extending Entity must use positional args for these. + if ($this->isEntityMagicAccessor(phpcsFile: $phpcsFile, stackPtr: $stackPtr) === true) { + return; + } + // Get the closing parenthesis. if (isset($tokens[$openParen]['parenthesis_closer']) === false) { return; @@ -97,6 +104,116 @@ public function process(File $phpcsFile, $stackPtr): void }//end process() + /** + * Check whether the call at $stackPtr is an Entity magic accessor (setX/getX/isX) + * inside a class that extends OCP\AppFramework\Db\Entity. + * + * @param File $phpcsFile The file being scanned. + * @param int $stackPtr Position of the method-name T_STRING. + * + * @return bool True if this is a magic Entity accessor call. + */ + private function isEntityMagicAccessor(File $phpcsFile, int $stackPtr): bool + { + $tokens = $phpcsFile->getTokens(); + $name = $tokens[$stackPtr]['content']; + + // Only setX/getX/isX names with at least one trailing capital qualify. + if (preg_match(pattern: '/^(set|get|is)[A-Z]/', subject: $name) !== 1) { + return false; + } + + // Must be a $this->name() call (already established by isInternalCall, but + // re-check defensively because we also want to ignore self::set*() etc.). + $prev = $phpcsFile->findPrevious( + types: [T_WHITESPACE, T_COMMENT, T_DOC_COMMENT], + start: ($stackPtr - 1), + end: null, + exclude: true + ); + if ($prev === false || $tokens[$prev]['code'] !== T_OBJECT_OPERATOR) { + return false; + } + + return $this->classExtendsEntity(phpcsFile: $phpcsFile); + + }//end isEntityMagicAccessor() + + + /** + * Check if the file's primary class extends OCP\AppFramework\Db\Entity. + * + * Considers both fully-qualified and use-aliased "Entity" parents. + * + * @param File $phpcsFile The file being scanned. + * + * @return bool True when the class extends Entity. + */ + private function classExtendsEntity(File $phpcsFile): bool + { + $tokens = $phpcsFile->getTokens(); + $classPtr = $phpcsFile->findNext(types: T_CLASS, start: 0); + if ($classPtr === false) { + return false; + } + + $extendsPtr = $phpcsFile->findNext( + types: T_EXTENDS, + start: $classPtr, + end: ($tokens[$classPtr]['scope_opener'] ?? $phpcsFile->numTokens) + ); + if ($extendsPtr === false) { + return false; + } + + // Read the parent class identifier (handles namespaced parents like \OCP\…\Entity). + $parentName = ''; + $j = ($extendsPtr + 1); + $end = ($tokens[$classPtr]['scope_opener'] ?? $phpcsFile->numTokens); + while ($j < $end) { + $code = $tokens[$j]['code']; + if ($code === T_OPEN_CURLY_BRACKET || $code === T_IMPLEMENTS || $code === T_COMMA) { + break; + } + + if ($code !== T_WHITESPACE && $code !== T_COMMENT && $code !== T_DOC_COMMENT) { + $parentName .= $tokens[$j]['content']; + } + + $j++; + } + + $parentName = trim(string: $parentName); + if ($parentName === '') { + return false; + } + + // Direct fully-qualified match. + if ($parentName === '\OCP\AppFramework\Db\Entity' + || $parentName === 'OCP\AppFramework\Db\Entity' + ) { + return true; + } + + // Aliased: parent is "Entity" and a use statement imports it from the OCP path. + if ($parentName === 'Entity') { + for ($i = 0; $i < $classPtr; $i++) { + if ($tokens[$i]['code'] !== T_USE) { + continue; + } + + $usePath = $this->getUseStatementPath(phpcsFile: $phpcsFile, usePtr: $i); + if ($usePath === 'OCP\AppFramework\Db\Entity') { + return true; + } + } + } + + return false; + + }//end classExtendsEntity() + + /** * Check if the T_STRING at $stackPtr is part of a function/method definition (not a call). * diff --git a/sbom.cdx.json b/sbom.cdx.json index 4a5c5bfd..af4d1e2d 100644 --- a/sbom.cdx.json +++ b/sbom.cdx.json @@ -2,10 +2,10 @@ "$schema": "http://cyclonedx.org/schema/bom-1.5.schema.json", "bomFormat": "CycloneDX", "specVersion": "1.5", - "serialNumber": "urn:uuid:5ee74da4-b04e-46fe-aed5-0e55fa312c8f", + "serialNumber": "urn:uuid:63778652-7d92-4d43-894d-552f1737e964", "version": 1, "metadata": { - "timestamp": "2026-04-30T20:22:12Z", + "timestamp": "2026-04-30T21:15:36Z", "tools": [ { "name": "composer", @@ -82,10 +82,10 @@ } ], "component": { - "bom-ref": "mydash/mydash-dev-feature/impl-fork-current-as-personal", + "bom-ref": "mydash/mydash-dev-feature/template-and-adr-cleanup", "type": "application", "name": "mydash", - "version": "dev-feature/impl-fork-current-as-personal", + "version": "dev-feature/template-and-adr-cleanup", "group": "mydash", "description": "Enhanced dashboard with grid layout and admin controls for Nextcloud", "author": "MyDash Contributors", @@ -96,15 +96,15 @@ } } ], - "purl": "pkg:composer/mydash/mydash@dev-feature/impl-fork-current-as-personal", + "purl": "pkg:composer/mydash/mydash@dev-feature/template-and-adr-cleanup", "properties": [ { "name": "cdx:composer:package:distReference", - "value": "61bf45da1f763733e53dd02389928eb6d20f9813" + "value": "462587d3c7c5821c665e2956c06183e57fafbabc" }, { "name": "cdx:composer:package:sourceReference", - "value": "61bf45da1f763733e53dd02389928eb6d20f9813" + "value": "462587d3c7c5821c665e2956c06183e57fafbabc" }, { "name": "cdx:composer:package:type", @@ -114,163 +114,6 @@ } }, "components": [ - { - "bom-ref": "brick/math-0.13.1.0", - "type": "library", - "name": "math", - "version": "0.13.1", - "group": "brick", - "description": "Arbitrary-precision arithmetic library", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/brick/math@0.13.1", - "externalReferences": [ - { - "type": "distribution", - "url": "https://api.github.com/repos/brick/math/zipball/fc7ed316430118cc7836bf45faff18d5dfc8de04", - "comment": "dist reference: fc7ed316430118cc7836bf45faff18d5dfc8de04" - }, - { - "type": "vcs", - "url": "https://github.com/brick/math.git", - "comment": "source reference: fc7ed316430118cc7836bf45faff18d5dfc8de04" - }, - { - "type": "issue-tracker", - "url": "https://github.com/brick/math/issues", - "comment": "as detected from Composer manifest 'support.issues'" - }, - { - "type": "vcs", - "url": "https://github.com/brick/math/tree/0.13.1", - "comment": "as detected from Composer manifest 'support.source'" - } - ], - "properties": [ - { - "name": "cdx:composer:package:distReference", - "value": "fc7ed316430118cc7836bf45faff18d5dfc8de04" - }, - { - "name": "cdx:composer:package:sourceReference", - "value": "fc7ed316430118cc7836bf45faff18d5dfc8de04" - }, - { - "name": "cdx:composer:package:type", - "value": "library" - } - ] - }, - { - "bom-ref": "ramsey/collection-2.1.1.0", - "type": "library", - "name": "collection", - "version": "2.1.1", - "group": "ramsey", - "description": "A PHP library for representing and manipulating collections.", - "author": "Ben Ramsey", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/ramsey/collection@2.1.1", - "externalReferences": [ - { - "type": "distribution", - "url": "https://api.github.com/repos/ramsey/collection/zipball/344572933ad0181accbf4ba763e85a0306a8c5e2", - "comment": "dist reference: 344572933ad0181accbf4ba763e85a0306a8c5e2" - }, - { - "type": "vcs", - "url": "https://github.com/ramsey/collection.git", - "comment": "source reference: 344572933ad0181accbf4ba763e85a0306a8c5e2" - }, - { - "type": "issue-tracker", - "url": "https://github.com/ramsey/collection/issues", - "comment": "as detected from Composer manifest 'support.issues'" - }, - { - "type": "vcs", - "url": "https://github.com/ramsey/collection/tree/2.1.1", - "comment": "as detected from Composer manifest 'support.source'" - } - ], - "properties": [ - { - "name": "cdx:composer:package:distReference", - "value": "344572933ad0181accbf4ba763e85a0306a8c5e2" - }, - { - "name": "cdx:composer:package:sourceReference", - "value": "344572933ad0181accbf4ba763e85a0306a8c5e2" - }, - { - "name": "cdx:composer:package:type", - "value": "library" - } - ] - }, - { - "bom-ref": "ramsey/uuid-4.9.2.0", - "type": "library", - "name": "uuid", - "version": "4.9.2", - "group": "ramsey", - "description": "A PHP library for generating and working with universally unique identifiers (UUIDs).", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/ramsey/uuid@4.9.2", - "externalReferences": [ - { - "type": "distribution", - "url": "https://api.github.com/repos/ramsey/uuid/zipball/8429c78ca35a09f27565311b98101e2826affde0", - "comment": "dist reference: 8429c78ca35a09f27565311b98101e2826affde0" - }, - { - "type": "vcs", - "url": "https://github.com/ramsey/uuid.git", - "comment": "source reference: 8429c78ca35a09f27565311b98101e2826affde0" - }, - { - "type": "issue-tracker", - "url": "https://github.com/ramsey/uuid/issues", - "comment": "as detected from Composer manifest 'support.issues'" - }, - { - "type": "vcs", - "url": "https://github.com/ramsey/uuid/tree/4.9.2", - "comment": "as detected from Composer manifest 'support.source'" - } - ], - "properties": [ - { - "name": "cdx:composer:package:distReference", - "value": "8429c78ca35a09f27565311b98101e2826affde0" - }, - { - "name": "cdx:composer:package:sourceReference", - "value": "8429c78ca35a09f27565311b98101e2826affde0" - }, - { - "name": "cdx:composer:package:type", - "value": "library" - } - ] - }, { "type": "library", "name": "helper-string-parser", @@ -17921,23 +17764,7 @@ ], "dependencies": [ { - "ref": "brick/math-0.13.1.0" - }, - { - "ref": "ramsey/collection-2.1.1.0" - }, - { - "ref": "ramsey/uuid-4.9.2.0", - "dependsOn": [ - "brick/math-0.13.1.0", - "ramsey/collection-2.1.1.0" - ] - }, - { - "ref": "mydash/mydash-dev-feature/impl-fork-current-as-personal", - "dependsOn": [ - "ramsey/uuid-4.9.2.0" - ] + "ref": "mydash/mydash-dev-feature/template-and-adr-cleanup" } ] } diff --git a/src/components/DashboardSwitcher.vue b/src/components/DashboardSwitcher.vue index c0b0638c..a10b3e9a 100644 --- a/src/components/DashboardSwitcher.vue +++ b/src/components/DashboardSwitcher.vue @@ -7,15 +7,17 @@