diff --git a/.github/workflows/pull-request-from-branch-check.yaml b/.github/workflows/pull-request-from-branch-check.yaml
new file mode 100644
index 00000000..a40f09fa
--- /dev/null
+++ b/.github/workflows/pull-request-from-branch-check.yaml
@@ -0,0 +1,18 @@
+name: Main Branch Protection
+
+on:
+ pull_request:
+ branches:
+ - master
+
+jobs:
+ check-branch:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Check branch
+ run: |
+ if [[ ${GITHUB_HEAD_REF} != development ]] && [[ ${GITHUB_HEAD_REF} != documentation ]] && ! [[ ${GITHUB_HEAD_REF} =~ ^hotfix/ ]];
+ then
+ echo "Error: Pull request must come from 'development', 'documentation' or 'hotfix/' branch"
+ exit 1
+ fi
diff --git a/.github/workflows/pull-request-lint-check.yaml b/.github/workflows/pull-request-lint-check.yaml
new file mode 100644
index 00000000..0c04e87a
--- /dev/null
+++ b/.github/workflows/pull-request-lint-check.yaml
@@ -0,0 +1,20 @@
+name: Lint Check
+
+on:
+ pull_request:
+ branches:
+ - never
+
+jobs:
+ lint-check:
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v2
+
+ - name: Install dependencies
+ run: npm i
+
+ - name: Linting
+ run: npm run lint
diff --git a/appinfo/info.xml b/appinfo/info.xml
index e9496ccd..b2573a89 100644
--- a/appinfo/info.xml
+++ b/appinfo/info.xml
@@ -22,7 +22,7 @@ Create a [bug report](https://github.com/OpenCatalogi/.github/issues/new/choose)
Create a [feature request](https://github.com/OpenCatalogi/.github/issues/new/choose)
]]>
- 0.6.43
+ 0.6.52
agpl
Conduction
Acato
diff --git a/appinfo/routes.php b/appinfo/routes.php
index 427b3af4..b9baa786 100644
--- a/appinfo/routes.php
+++ b/appinfo/routes.php
@@ -6,6 +6,7 @@
'publications' => ['url' => '/api/publications'],
'organizations' => ['url' => '/api/organizations'],
'themes' => ['url' => '/api/themes'],
+ 'pages' => ['url' => '/api/pages'],
'attachments' => ['url' => '/api/attachments'],
'catalogi' => ['url' => '/api/catalogi'],
'listings' => ['url' => '/api/listings'],
@@ -37,6 +38,8 @@
['name' => 'search#publication', 'url' => '/api/search/publications/{publicationId}', 'verb' => 'GET', 'requirements' => ['publicationId' => '[^/]+']],
['name' => 'search#attachments', 'url' => '/api/search/publications/{publicationId}/attachments', 'verb' => 'GET', 'requirements' => ['publicationId' => '[^/]+']],
['name' => 'search#themes', 'url' => '/api/search/themes', 'verb' => 'GET'],
- ['name' => 'search#theme', 'url' => '/api/search/themes/{themeId}', 'verb' => 'GET', 'requirements' => ['themeId' => '\d+']]
+ ['name' => 'search#theme', 'url' => '/api/search/themes/{themeId}', 'verb' => 'GET', 'requirements' => ['themeId' => '\d+']],
+ ['name' => 'search#pages', 'url' => '/api/public/pages', 'verb' => 'GET'],
+ ['name' => 'search#page', 'url' => '/api/public/pages/{pageSlug}', 'verb' => 'GET', 'requirements' => ['pageId' => '.+']]
]
];
diff --git a/composer.json b/composer.json
index 89c318a1..9672442e 100644
--- a/composer.json
+++ b/composer.json
@@ -44,8 +44,9 @@
"adbario/php-dot-notation": "^3.3.0",
"bamarni/composer-bin-plugin": "^1.8",
"elasticsearch/elasticsearch": "^v8.14.0",
- "guzzlehttp/guzzle": "^7.0",
+ "guzzlehttp/guzzle": "^7.0",
"mpdf/mpdf": "^8.2",
+ "opis/json-schema": "^2.3",
"symfony/twig-bundle": "^6.4",
"symfony/uid": "^6.4"
},
diff --git a/composer.lock b/composer.lock
index 7284d015..917ed994 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "93ceb09bbb9b85fb7d77d68d6d6eefe1",
+ "content-hash": "bd1fa2147c63c25a4c1759758bcf0fbb",
"packages": [
{
"name": "adbario/php-dot-notation",
@@ -119,20 +119,21 @@
},
{
"name": "elastic/transport",
- "version": "v8.8.0",
+ "version": "v8.10.0",
"source": {
"type": "git",
"url": "https://github.com/elastic/elastic-transport-php.git",
- "reference": "cdf9f63a16ec6bfb4c881ab89aa0e2a61fb7c20b"
+ "reference": "8be37d679637545e50b1cea9f8ee903888783021"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/elastic/elastic-transport-php/zipball/cdf9f63a16ec6bfb4c881ab89aa0e2a61fb7c20b",
- "reference": "cdf9f63a16ec6bfb4c881ab89aa0e2a61fb7c20b",
+ "url": "https://api.github.com/repos/elastic/elastic-transport-php/zipball/8be37d679637545e50b1cea9f8ee903888783021",
+ "reference": "8be37d679637545e50b1cea9f8ee903888783021",
"shasum": ""
},
"require": {
"composer-runtime-api": "^2.0",
+ "open-telemetry/api": "^1.0",
"php": "^7.4 || ^8.0",
"php-http/discovery": "^1.14",
"php-http/httplug": "^2.3",
@@ -143,9 +144,11 @@
},
"require-dev": {
"nyholm/psr7": "^1.5",
+ "open-telemetry/sdk": "^1.0",
"php-http/mock-client": "^1.5",
"phpstan/phpstan": "^1.4",
- "phpunit/phpunit": "^9.5"
+ "phpunit/phpunit": "^9.5",
+ "symfony/http-client": "^5.4"
},
"type": "library",
"autoload": {
@@ -168,26 +171,26 @@
],
"support": {
"issues": "https://github.com/elastic/elastic-transport-php/issues",
- "source": "https://github.com/elastic/elastic-transport-php/tree/v8.8.0"
+ "source": "https://github.com/elastic/elastic-transport-php/tree/v8.10.0"
},
- "time": "2023-11-08T10:51:51+00:00"
+ "time": "2024-08-14T08:55:07+00:00"
},
{
"name": "elasticsearch/elasticsearch",
- "version": "v8.14.0",
+ "version": "v8.15.0",
"source": {
"type": "git",
"url": "https://github.com/elastic/elasticsearch-php.git",
- "reference": "bff3c3e2402f6a20449404637f91a5ae214eff46"
+ "reference": "34c2444fa8d4c3e6c8b009bd8dea90bca007203b"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/elastic/elasticsearch-php/zipball/bff3c3e2402f6a20449404637f91a5ae214eff46",
- "reference": "bff3c3e2402f6a20449404637f91a5ae214eff46",
+ "url": "https://api.github.com/repos/elastic/elasticsearch-php/zipball/34c2444fa8d4c3e6c8b009bd8dea90bca007203b",
+ "reference": "34c2444fa8d4c3e6c8b009bd8dea90bca007203b",
"shasum": ""
},
"require": {
- "elastic/transport": "^8.8",
+ "elastic/transport": "^8.10",
"guzzlehttp/guzzle": "^7.0",
"php": "^7.4 || ^8.0",
"psr/http-client": "^1.0",
@@ -226,28 +229,28 @@
],
"support": {
"issues": "https://github.com/elastic/elasticsearch-php/issues",
- "source": "https://github.com/elastic/elasticsearch-php/tree/v8.14.0"
+ "source": "https://github.com/elastic/elasticsearch-php/tree/v8.15.0"
},
- "time": "2024-06-12T19:58:31+00:00"
+ "time": "2024-08-14T14:32:50+00:00"
},
{
"name": "guzzlehttp/guzzle",
- "version": "7.8.1",
+ "version": "7.9.2",
"source": {
"type": "git",
"url": "https://github.com/guzzle/guzzle.git",
- "reference": "41042bc7ab002487b876a0683fc8dce04ddce104"
+ "reference": "d281ed313b989f213357e3be1a179f02196ac99b"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/guzzle/guzzle/zipball/41042bc7ab002487b876a0683fc8dce04ddce104",
- "reference": "41042bc7ab002487b876a0683fc8dce04ddce104",
+ "url": "https://api.github.com/repos/guzzle/guzzle/zipball/d281ed313b989f213357e3be1a179f02196ac99b",
+ "reference": "d281ed313b989f213357e3be1a179f02196ac99b",
"shasum": ""
},
"require": {
"ext-json": "*",
- "guzzlehttp/promises": "^1.5.3 || ^2.0.1",
- "guzzlehttp/psr7": "^1.9.1 || ^2.5.1",
+ "guzzlehttp/promises": "^1.5.3 || ^2.0.3",
+ "guzzlehttp/psr7": "^2.7.0",
"php": "^7.2.5 || ^8.0",
"psr/http-client": "^1.0",
"symfony/deprecation-contracts": "^2.2 || ^3.0"
@@ -258,9 +261,9 @@
"require-dev": {
"bamarni/composer-bin-plugin": "^1.8.2",
"ext-curl": "*",
- "php-http/client-integration-tests": "dev-master#2c025848417c1135031fdf9c728ee53d0a7ceaee as 3.0.999",
+ "guzzle/client-integration-tests": "3.0.2",
"php-http/message-factory": "^1.1",
- "phpunit/phpunit": "^8.5.36 || ^9.6.15",
+ "phpunit/phpunit": "^8.5.39 || ^9.6.20",
"psr/log": "^1.1 || ^2.0 || ^3.0"
},
"suggest": {
@@ -338,7 +341,7 @@
],
"support": {
"issues": "https://github.com/guzzle/guzzle/issues",
- "source": "https://github.com/guzzle/guzzle/tree/7.8.1"
+ "source": "https://github.com/guzzle/guzzle/tree/7.9.2"
},
"funding": [
{
@@ -354,20 +357,20 @@
"type": "tidelift"
}
],
- "time": "2023-12-03T20:35:24+00:00"
+ "time": "2024-07-24T11:22:20+00:00"
},
{
"name": "guzzlehttp/promises",
- "version": "2.0.2",
+ "version": "2.0.4",
"source": {
"type": "git",
"url": "https://github.com/guzzle/promises.git",
- "reference": "bbff78d96034045e58e13dedd6ad91b5d1253223"
+ "reference": "f9c436286ab2892c7db7be8c8da4ef61ccf7b455"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/guzzle/promises/zipball/bbff78d96034045e58e13dedd6ad91b5d1253223",
- "reference": "bbff78d96034045e58e13dedd6ad91b5d1253223",
+ "url": "https://api.github.com/repos/guzzle/promises/zipball/f9c436286ab2892c7db7be8c8da4ef61ccf7b455",
+ "reference": "f9c436286ab2892c7db7be8c8da4ef61ccf7b455",
"shasum": ""
},
"require": {
@@ -375,7 +378,7 @@
},
"require-dev": {
"bamarni/composer-bin-plugin": "^1.8.2",
- "phpunit/phpunit": "^8.5.36 || ^9.6.15"
+ "phpunit/phpunit": "^8.5.39 || ^9.6.20"
},
"type": "library",
"extra": {
@@ -421,7 +424,7 @@
],
"support": {
"issues": "https://github.com/guzzle/promises/issues",
- "source": "https://github.com/guzzle/promises/tree/2.0.2"
+ "source": "https://github.com/guzzle/promises/tree/2.0.4"
},
"funding": [
{
@@ -437,20 +440,20 @@
"type": "tidelift"
}
],
- "time": "2023-12-03T20:19:20+00:00"
+ "time": "2024-10-17T10:06:22+00:00"
},
{
"name": "guzzlehttp/psr7",
- "version": "2.6.2",
+ "version": "2.7.0",
"source": {
"type": "git",
"url": "https://github.com/guzzle/psr7.git",
- "reference": "45b30f99ac27b5ca93cb4831afe16285f57b8221"
+ "reference": "a70f5c95fb43bc83f07c9c948baa0dc1829bf201"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/guzzle/psr7/zipball/45b30f99ac27b5ca93cb4831afe16285f57b8221",
- "reference": "45b30f99ac27b5ca93cb4831afe16285f57b8221",
+ "url": "https://api.github.com/repos/guzzle/psr7/zipball/a70f5c95fb43bc83f07c9c948baa0dc1829bf201",
+ "reference": "a70f5c95fb43bc83f07c9c948baa0dc1829bf201",
"shasum": ""
},
"require": {
@@ -465,8 +468,8 @@
},
"require-dev": {
"bamarni/composer-bin-plugin": "^1.8.2",
- "http-interop/http-factory-tests": "^0.9",
- "phpunit/phpunit": "^8.5.36 || ^9.6.15"
+ "http-interop/http-factory-tests": "0.9.0",
+ "phpunit/phpunit": "^8.5.39 || ^9.6.20"
},
"suggest": {
"laminas/laminas-httphandlerrunner": "Emit PSR-7 responses"
@@ -537,7 +540,7 @@
],
"support": {
"issues": "https://github.com/guzzle/psr7/issues",
- "source": "https://github.com/guzzle/psr7/tree/2.6.2"
+ "source": "https://github.com/guzzle/psr7/tree/2.7.0"
},
"funding": [
{
@@ -553,7 +556,7 @@
"type": "tidelift"
}
],
- "time": "2023-12-03T20:05:35+00:00"
+ "time": "2024-07-18T11:15:46+00:00"
},
{
"name": "mpdf/mpdf",
@@ -788,6 +791,325 @@
],
"time": "2024-06-12T14:39:25+00:00"
},
+ {
+ "name": "open-telemetry/api",
+ "version": "1.1.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/opentelemetry-php/api.git",
+ "reference": "542064815d38a6df55af7957cd6f1d7d967c99c6"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/opentelemetry-php/api/zipball/542064815d38a6df55af7957cd6f1d7d967c99c6",
+ "reference": "542064815d38a6df55af7957cd6f1d7d967c99c6",
+ "shasum": ""
+ },
+ "require": {
+ "open-telemetry/context": "^1.0",
+ "php": "^8.1",
+ "psr/log": "^1.1|^2.0|^3.0",
+ "symfony/polyfill-php82": "^1.26"
+ },
+ "conflict": {
+ "open-telemetry/sdk": "<=1.0.8"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "1.1.x-dev"
+ },
+ "spi": {
+ "OpenTelemetry\\API\\Instrumentation\\AutoInstrumentation\\HookManagerInterface": [
+ "OpenTelemetry\\API\\Instrumentation\\AutoInstrumentation\\ExtensionHookManager"
+ ]
+ }
+ },
+ "autoload": {
+ "files": [
+ "Trace/functions.php"
+ ],
+ "psr-4": {
+ "OpenTelemetry\\API\\": "."
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "Apache-2.0"
+ ],
+ "authors": [
+ {
+ "name": "opentelemetry-php contributors",
+ "homepage": "https://github.com/open-telemetry/opentelemetry-php/graphs/contributors"
+ }
+ ],
+ "description": "API for OpenTelemetry PHP.",
+ "keywords": [
+ "Metrics",
+ "api",
+ "apm",
+ "logging",
+ "opentelemetry",
+ "otel",
+ "tracing"
+ ],
+ "support": {
+ "chat": "https://app.slack.com/client/T08PSQ7BQ/C01NFPCV44V",
+ "docs": "https://opentelemetry.io/docs/php",
+ "issues": "https://github.com/open-telemetry/opentelemetry-php/issues",
+ "source": "https://github.com/open-telemetry/opentelemetry-php"
+ },
+ "time": "2024-10-15T22:42:37+00:00"
+ },
+ {
+ "name": "open-telemetry/context",
+ "version": "1.1.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/opentelemetry-php/context.git",
+ "reference": "0cba875ea1953435f78aec7f1d75afa87bdbf7f3"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/opentelemetry-php/context/zipball/0cba875ea1953435f78aec7f1d75afa87bdbf7f3",
+ "reference": "0cba875ea1953435f78aec7f1d75afa87bdbf7f3",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^8.1",
+ "symfony/polyfill-php82": "^1.26"
+ },
+ "suggest": {
+ "ext-ffi": "To allow context switching in Fibers"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "1.0.x-dev"
+ }
+ },
+ "autoload": {
+ "files": [
+ "fiber/initialize_fiber_handler.php"
+ ],
+ "psr-4": {
+ "OpenTelemetry\\Context\\": "."
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "Apache-2.0"
+ ],
+ "authors": [
+ {
+ "name": "opentelemetry-php contributors",
+ "homepage": "https://github.com/open-telemetry/opentelemetry-php/graphs/contributors"
+ }
+ ],
+ "description": "Context implementation for OpenTelemetry PHP.",
+ "keywords": [
+ "Context",
+ "opentelemetry",
+ "otel"
+ ],
+ "support": {
+ "chat": "https://app.slack.com/client/T08PSQ7BQ/C01NFPCV44V",
+ "docs": "https://opentelemetry.io/docs/php",
+ "issues": "https://github.com/open-telemetry/opentelemetry-php/issues",
+ "source": "https://github.com/open-telemetry/opentelemetry-php"
+ },
+ "time": "2024-08-21T00:29:20+00:00"
+ },
+ {
+ "name": "opis/json-schema",
+ "version": "2.3.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/opis/json-schema.git",
+ "reference": "c48df6d7089a45f01e1c82432348f2d5976f9bfb"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/opis/json-schema/zipball/c48df6d7089a45f01e1c82432348f2d5976f9bfb",
+ "reference": "c48df6d7089a45f01e1c82432348f2d5976f9bfb",
+ "shasum": ""
+ },
+ "require": {
+ "ext-json": "*",
+ "opis/string": "^2.0",
+ "opis/uri": "^1.0",
+ "php": "^7.4 || ^8.0"
+ },
+ "require-dev": {
+ "ext-bcmath": "*",
+ "ext-intl": "*",
+ "phpunit/phpunit": "^9.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Opis\\JsonSchema\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "Apache-2.0"
+ ],
+ "authors": [
+ {
+ "name": "Sorin Sarca",
+ "email": "sarca_sorin@hotmail.com"
+ },
+ {
+ "name": "Marius Sarca",
+ "email": "marius.sarca@gmail.com"
+ }
+ ],
+ "description": "Json Schema Validator for PHP",
+ "homepage": "https://opis.io/json-schema",
+ "keywords": [
+ "json",
+ "json-schema",
+ "schema",
+ "validation",
+ "validator"
+ ],
+ "support": {
+ "issues": "https://github.com/opis/json-schema/issues",
+ "source": "https://github.com/opis/json-schema/tree/2.3.0"
+ },
+ "time": "2022-01-08T20:38:03+00:00"
+ },
+ {
+ "name": "opis/string",
+ "version": "2.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/opis/string.git",
+ "reference": "9ebf1a1f873f502f6859d11210b25a4bf5d141e7"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/opis/string/zipball/9ebf1a1f873f502f6859d11210b25a4bf5d141e7",
+ "reference": "9ebf1a1f873f502f6859d11210b25a4bf5d141e7",
+ "shasum": ""
+ },
+ "require": {
+ "ext-iconv": "*",
+ "ext-json": "*",
+ "php": "^7.4 || ^8.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Opis\\String\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "Apache-2.0"
+ ],
+ "authors": [
+ {
+ "name": "Marius Sarca",
+ "email": "marius.sarca@gmail.com"
+ },
+ {
+ "name": "Sorin Sarca",
+ "email": "sarca_sorin@hotmail.com"
+ }
+ ],
+ "description": "Multibyte strings as objects",
+ "homepage": "https://opis.io/string",
+ "keywords": [
+ "multi-byte",
+ "opis",
+ "string",
+ "string manipulation",
+ "utf-8"
+ ],
+ "support": {
+ "issues": "https://github.com/opis/string/issues",
+ "source": "https://github.com/opis/string/tree/2.0.1"
+ },
+ "time": "2022-01-14T15:42:23+00:00"
+ },
+ {
+ "name": "opis/uri",
+ "version": "1.1.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/opis/uri.git",
+ "reference": "0f3ca49ab1a5e4a6681c286e0b2cc081b93a7d5a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/opis/uri/zipball/0f3ca49ab1a5e4a6681c286e0b2cc081b93a7d5a",
+ "reference": "0f3ca49ab1a5e4a6681c286e0b2cc081b93a7d5a",
+ "shasum": ""
+ },
+ "require": {
+ "opis/string": "^2.0",
+ "php": "^7.4 || ^8.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Opis\\Uri\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "Apache-2.0"
+ ],
+ "authors": [
+ {
+ "name": "Marius Sarca",
+ "email": "marius.sarca@gmail.com"
+ },
+ {
+ "name": "Sorin Sarca",
+ "email": "sarca_sorin@hotmail.com"
+ }
+ ],
+ "description": "Build, parse and validate URIs and URI-templates",
+ "homepage": "https://opis.io",
+ "keywords": [
+ "URI Template",
+ "parse url",
+ "punycode",
+ "uri",
+ "uri components",
+ "url",
+ "validate uri"
+ ],
+ "support": {
+ "issues": "https://github.com/opis/uri/issues",
+ "source": "https://github.com/opis/uri/tree/1.1.0"
+ },
+ "time": "2021-05-22T15:57:08+00:00"
+ },
{
"name": "paragonie/random_compat",
"version": "v9.99.100",
@@ -840,16 +1162,16 @@
},
{
"name": "php-http/discovery",
- "version": "1.19.4",
+ "version": "1.20.0",
"source": {
"type": "git",
"url": "https://github.com/php-http/discovery.git",
- "reference": "0700efda8d7526335132360167315fdab3aeb599"
+ "reference": "82fe4c73ef3363caed49ff8dd1539ba06044910d"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/php-http/discovery/zipball/0700efda8d7526335132360167315fdab3aeb599",
- "reference": "0700efda8d7526335132360167315fdab3aeb599",
+ "url": "https://api.github.com/repos/php-http/discovery/zipball/82fe4c73ef3363caed49ff8dd1539ba06044910d",
+ "reference": "82fe4c73ef3363caed49ff8dd1539ba06044910d",
"shasum": ""
},
"require": {
@@ -913,22 +1235,22 @@
],
"support": {
"issues": "https://github.com/php-http/discovery/issues",
- "source": "https://github.com/php-http/discovery/tree/1.19.4"
+ "source": "https://github.com/php-http/discovery/tree/1.20.0"
},
- "time": "2024-03-29T13:00:05+00:00"
+ "time": "2024-10-02T11:20:13+00:00"
},
{
"name": "php-http/httplug",
- "version": "2.4.0",
+ "version": "2.4.1",
"source": {
"type": "git",
"url": "https://github.com/php-http/httplug.git",
- "reference": "625ad742c360c8ac580fcc647a1541d29e257f67"
+ "reference": "5cad731844891a4c282f3f3e1b582c46839d22f4"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/php-http/httplug/zipball/625ad742c360c8ac580fcc647a1541d29e257f67",
- "reference": "625ad742c360c8ac580fcc647a1541d29e257f67",
+ "url": "https://api.github.com/repos/php-http/httplug/zipball/5cad731844891a4c282f3f3e1b582c46839d22f4",
+ "reference": "5cad731844891a4c282f3f3e1b582c46839d22f4",
"shasum": ""
},
"require": {
@@ -970,9 +1292,9 @@
],
"support": {
"issues": "https://github.com/php-http/httplug/issues",
- "source": "https://github.com/php-http/httplug/tree/2.4.0"
+ "source": "https://github.com/php-http/httplug/tree/2.4.1"
},
- "time": "2023-04-14T15:10:03+00:00"
+ "time": "2024-09-23T11:39:58+00:00"
},
{
"name": "php-http/promise",
@@ -1385,16 +1707,16 @@
},
{
"name": "setasign/fpdi",
- "version": "v2.6.0",
+ "version": "v2.6.1",
"source": {
"type": "git",
"url": "https://github.com/Setasign/FPDI.git",
- "reference": "a6db878129ec6c7e141316ee71872923e7f1b7ad"
+ "reference": "09a816004fcee9ed3405bd164147e3fdbb79a56f"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/Setasign/FPDI/zipball/a6db878129ec6c7e141316ee71872923e7f1b7ad",
- "reference": "a6db878129ec6c7e141316ee71872923e7f1b7ad",
+ "url": "https://api.github.com/repos/Setasign/FPDI/zipball/09a816004fcee9ed3405bd164147e3fdbb79a56f",
+ "reference": "09a816004fcee9ed3405bd164147e3fdbb79a56f",
"shasum": ""
},
"require": {
@@ -1445,7 +1767,7 @@
],
"support": {
"issues": "https://github.com/Setasign/FPDI/issues",
- "source": "https://github.com/Setasign/FPDI/tree/v2.6.0"
+ "source": "https://github.com/Setasign/FPDI/tree/v2.6.1"
},
"funding": [
{
@@ -1453,20 +1775,20 @@
"type": "tidelift"
}
],
- "time": "2023-12-11T16:03:32+00:00"
+ "time": "2024-09-02T10:17:15+00:00"
},
{
"name": "symfony/config",
- "version": "v6.4.8",
+ "version": "v6.4.14",
"source": {
"type": "git",
"url": "https://github.com/symfony/config.git",
- "reference": "12e7e52515ce37191b193cf3365903c4f3951e35"
+ "reference": "4e55e7e4ffddd343671ea972216d4509f46c22ef"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/config/zipball/12e7e52515ce37191b193cf3365903c4f3951e35",
- "reference": "12e7e52515ce37191b193cf3365903c4f3951e35",
+ "url": "https://api.github.com/repos/symfony/config/zipball/4e55e7e4ffddd343671ea972216d4509f46c22ef",
+ "reference": "4e55e7e4ffddd343671ea972216d4509f46c22ef",
"shasum": ""
},
"require": {
@@ -1512,7 +1834,7 @@
"description": "Helps you find, load, combine, autofill and validate configuration values of any kind",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/config/tree/v6.4.8"
+ "source": "https://github.com/symfony/config/tree/v6.4.14"
},
"funding": [
{
@@ -1528,20 +1850,20 @@
"type": "tidelift"
}
],
- "time": "2024-05-31T14:49:08+00:00"
+ "time": "2024-11-04T11:33:53+00:00"
},
{
"name": "symfony/dependency-injection",
- "version": "v6.4.10",
+ "version": "v6.4.13",
"source": {
"type": "git",
"url": "https://github.com/symfony/dependency-injection.git",
- "reference": "5caf9c5f6085f13b27d70a236b776c07e4a1c3eb"
+ "reference": "728ae8f4e190133ce99d6d5f0bc1e8c8bd7c7a96"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/5caf9c5f6085f13b27d70a236b776c07e4a1c3eb",
- "reference": "5caf9c5f6085f13b27d70a236b776c07e4a1c3eb",
+ "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/728ae8f4e190133ce99d6d5f0bc1e8c8bd7c7a96",
+ "reference": "728ae8f4e190133ce99d6d5f0bc1e8c8bd7c7a96",
"shasum": ""
},
"require": {
@@ -1593,7 +1915,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.10"
+ "source": "https://github.com/symfony/dependency-injection/tree/v6.4.13"
},
"funding": [
{
@@ -1609,7 +1931,7 @@
"type": "tidelift"
}
],
- "time": "2024-07-26T07:32:07+00:00"
+ "time": "2024-10-25T15:07:50+00:00"
},
{
"name": "symfony/deprecation-contracts",
@@ -1680,16 +2002,16 @@
},
{
"name": "symfony/error-handler",
- "version": "v6.4.10",
+ "version": "v6.4.14",
"source": {
"type": "git",
"url": "https://github.com/symfony/error-handler.git",
- "reference": "231f1b2ee80f72daa1972f7340297d67439224f0"
+ "reference": "9e024324511eeb00983ee76b9aedc3e6ecd993d9"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/error-handler/zipball/231f1b2ee80f72daa1972f7340297d67439224f0",
- "reference": "231f1b2ee80f72daa1972f7340297d67439224f0",
+ "url": "https://api.github.com/repos/symfony/error-handler/zipball/9e024324511eeb00983ee76b9aedc3e6ecd993d9",
+ "reference": "9e024324511eeb00983ee76b9aedc3e6ecd993d9",
"shasum": ""
},
"require": {
@@ -1735,7 +2057,7 @@
"description": "Provides tools to manage errors and ease debugging PHP code",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/error-handler/tree/v6.4.10"
+ "source": "https://github.com/symfony/error-handler/tree/v6.4.14"
},
"funding": [
{
@@ -1751,20 +2073,20 @@
"type": "tidelift"
}
],
- "time": "2024-07-26T12:30:32+00:00"
+ "time": "2024-11-05T15:34:40+00:00"
},
{
"name": "symfony/event-dispatcher",
- "version": "v6.4.8",
+ "version": "v6.4.13",
"source": {
"type": "git",
"url": "https://github.com/symfony/event-dispatcher.git",
- "reference": "8d7507f02b06e06815e56bb39aa0128e3806208b"
+ "reference": "0ffc48080ab3e9132ea74ef4e09d8dcf26bf897e"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/8d7507f02b06e06815e56bb39aa0128e3806208b",
- "reference": "8d7507f02b06e06815e56bb39aa0128e3806208b",
+ "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/0ffc48080ab3e9132ea74ef4e09d8dcf26bf897e",
+ "reference": "0ffc48080ab3e9132ea74ef4e09d8dcf26bf897e",
"shasum": ""
},
"require": {
@@ -1815,7 +2137,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.8"
+ "source": "https://github.com/symfony/event-dispatcher/tree/v6.4.13"
},
"funding": [
{
@@ -1831,7 +2153,7 @@
"type": "tidelift"
}
],
- "time": "2024-05-31T14:49:08+00:00"
+ "time": "2024-09-25T14:18:03+00:00"
},
{
"name": "symfony/event-dispatcher-contracts",
@@ -1911,16 +2233,16 @@
},
{
"name": "symfony/filesystem",
- "version": "v6.4.9",
+ "version": "v6.4.13",
"source": {
"type": "git",
"url": "https://github.com/symfony/filesystem.git",
- "reference": "b51ef8059159330b74a4d52f68e671033c0fe463"
+ "reference": "4856c9cf585d5a0313d8d35afd681a526f038dd3"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/filesystem/zipball/b51ef8059159330b74a4d52f68e671033c0fe463",
- "reference": "b51ef8059159330b74a4d52f68e671033c0fe463",
+ "url": "https://api.github.com/repos/symfony/filesystem/zipball/4856c9cf585d5a0313d8d35afd681a526f038dd3",
+ "reference": "4856c9cf585d5a0313d8d35afd681a526f038dd3",
"shasum": ""
},
"require": {
@@ -1957,7 +2279,7 @@
"description": "Provides basic utilities for the filesystem",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/filesystem/tree/v6.4.9"
+ "source": "https://github.com/symfony/filesystem/tree/v6.4.13"
},
"funding": [
{
@@ -1973,20 +2295,20 @@
"type": "tidelift"
}
],
- "time": "2024-06-28T09:49:33+00:00"
+ "time": "2024-10-25T15:07:50+00:00"
},
{
"name": "symfony/http-foundation",
- "version": "v6.4.10",
+ "version": "v6.4.14",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-foundation.git",
- "reference": "117f1f20a7ade7bcea28b861fb79160a21a1e37b"
+ "reference": "ba020a321a95519303a3f09ec2824d34d601c388"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/http-foundation/zipball/117f1f20a7ade7bcea28b861fb79160a21a1e37b",
- "reference": "117f1f20a7ade7bcea28b861fb79160a21a1e37b",
+ "url": "https://api.github.com/repos/symfony/http-foundation/zipball/ba020a321a95519303a3f09ec2824d34d601c388",
+ "reference": "ba020a321a95519303a3f09ec2824d34d601c388",
"shasum": ""
},
"require": {
@@ -2034,7 +2356,7 @@
"description": "Defines an object-oriented layer for the HTTP specification",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/http-foundation/tree/v6.4.10"
+ "source": "https://github.com/symfony/http-foundation/tree/v6.4.14"
},
"funding": [
{
@@ -2050,20 +2372,20 @@
"type": "tidelift"
}
],
- "time": "2024-07-26T12:36:27+00:00"
+ "time": "2024-11-05T16:39:55+00:00"
},
{
"name": "symfony/http-kernel",
- "version": "v6.4.10",
+ "version": "v6.4.14",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-kernel.git",
- "reference": "147e0daf618d7575b5007055340d09aece5cf068"
+ "reference": "8278a947d0369754a47b758a9e17b72cab970951"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/http-kernel/zipball/147e0daf618d7575b5007055340d09aece5cf068",
- "reference": "147e0daf618d7575b5007055340d09aece5cf068",
+ "url": "https://api.github.com/repos/symfony/http-kernel/zipball/8278a947d0369754a47b758a9e17b72cab970951",
+ "reference": "8278a947d0369754a47b758a9e17b72cab970951",
"shasum": ""
},
"require": {
@@ -2148,7 +2470,7 @@
"description": "Provides a structured process for converting a Request into a Response",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/http-kernel/tree/v6.4.10"
+ "source": "https://github.com/symfony/http-kernel/tree/v6.4.14"
},
"funding": [
{
@@ -2164,24 +2486,24 @@
"type": "tidelift"
}
],
- "time": "2024-07-26T14:52:04+00:00"
+ "time": "2024-11-06T09:45:21+00:00"
},
{
"name": "symfony/polyfill-ctype",
- "version": "v1.30.0",
+ "version": "v1.31.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-ctype.git",
- "reference": "0424dff1c58f028c451efff2045f5d92410bd540"
+ "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/0424dff1c58f028c451efff2045f5d92410bd540",
- "reference": "0424dff1c58f028c451efff2045f5d92410bd540",
+ "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638",
+ "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638",
"shasum": ""
},
"require": {
- "php": ">=7.1"
+ "php": ">=7.2"
},
"provide": {
"ext-ctype": "*"
@@ -2227,7 +2549,7 @@
"portable"
],
"support": {
- "source": "https://github.com/symfony/polyfill-ctype/tree/v1.30.0"
+ "source": "https://github.com/symfony/polyfill-ctype/tree/v1.31.0"
},
"funding": [
{
@@ -2243,24 +2565,24 @@
"type": "tidelift"
}
],
- "time": "2024-05-31T15:07:36+00:00"
+ "time": "2024-09-09T11:45:10+00:00"
},
{
"name": "symfony/polyfill-mbstring",
- "version": "v1.30.0",
+ "version": "v1.31.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-mbstring.git",
- "reference": "fd22ab50000ef01661e2a31d850ebaa297f8e03c"
+ "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/fd22ab50000ef01661e2a31d850ebaa297f8e03c",
- "reference": "fd22ab50000ef01661e2a31d850ebaa297f8e03c",
+ "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/85181ba99b2345b0ef10ce42ecac37612d9fd341",
+ "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341",
"shasum": ""
},
"require": {
- "php": ">=7.1"
+ "php": ">=7.2"
},
"provide": {
"ext-mbstring": "*"
@@ -2307,7 +2629,7 @@
"shim"
],
"support": {
- "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.30.0"
+ "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.31.0"
},
"funding": [
{
@@ -2323,24 +2645,24 @@
"type": "tidelift"
}
],
- "time": "2024-06-19T12:30:46+00:00"
+ "time": "2024-09-09T11:45:10+00:00"
},
{
- "name": "symfony/polyfill-php80",
- "version": "v1.30.0",
+ "name": "symfony/polyfill-php81",
+ "version": "v1.31.0",
"source": {
"type": "git",
- "url": "https://github.com/symfony/polyfill-php80.git",
- "reference": "77fa7995ac1b21ab60769b7323d600a991a90433"
+ "url": "https://github.com/symfony/polyfill-php81.git",
+ "reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/77fa7995ac1b21ab60769b7323d600a991a90433",
- "reference": "77fa7995ac1b21ab60769b7323d600a991a90433",
+ "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c",
+ "reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c",
"shasum": ""
},
"require": {
- "php": ">=7.1"
+ "php": ">=7.2"
},
"type": "library",
"extra": {
@@ -2354,7 +2676,7 @@
"bootstrap.php"
],
"psr-4": {
- "Symfony\\Polyfill\\Php80\\": ""
+ "Symfony\\Polyfill\\Php81\\": ""
},
"classmap": [
"Resources/stubs"
@@ -2365,10 +2687,6 @@
"MIT"
],
"authors": [
- {
- "name": "Ion Bazan",
- "email": "ion.bazan@gmail.com"
- },
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
@@ -2378,7 +2696,7 @@
"homepage": "https://symfony.com/contributors"
}
],
- "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions",
+ "description": "Symfony polyfill backporting some PHP 8.1+ features to lower PHP versions",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
@@ -2387,7 +2705,7 @@
"shim"
],
"support": {
- "source": "https://github.com/symfony/polyfill-php80/tree/v1.30.0"
+ "source": "https://github.com/symfony/polyfill-php81/tree/v1.31.0"
},
"funding": [
{
@@ -2403,24 +2721,24 @@
"type": "tidelift"
}
],
- "time": "2024-05-31T15:07:36+00:00"
+ "time": "2024-09-09T11:45:10+00:00"
},
{
- "name": "symfony/polyfill-php81",
- "version": "v1.30.0",
+ "name": "symfony/polyfill-php82",
+ "version": "v1.31.0",
"source": {
"type": "git",
- "url": "https://github.com/symfony/polyfill-php81.git",
- "reference": "3fb075789fb91f9ad9af537c4012d523085bd5af"
+ "url": "https://github.com/symfony/polyfill-php82.git",
+ "reference": "5d2ed36f7734637dacc025f179698031951b1692"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/3fb075789fb91f9ad9af537c4012d523085bd5af",
- "reference": "3fb075789fb91f9ad9af537c4012d523085bd5af",
+ "url": "https://api.github.com/repos/symfony/polyfill-php82/zipball/5d2ed36f7734637dacc025f179698031951b1692",
+ "reference": "5d2ed36f7734637dacc025f179698031951b1692",
"shasum": ""
},
"require": {
- "php": ">=7.1"
+ "php": ">=7.2"
},
"type": "library",
"extra": {
@@ -2434,7 +2752,7 @@
"bootstrap.php"
],
"psr-4": {
- "Symfony\\Polyfill\\Php81\\": ""
+ "Symfony\\Polyfill\\Php82\\": ""
},
"classmap": [
"Resources/stubs"
@@ -2454,7 +2772,7 @@
"homepage": "https://symfony.com/contributors"
}
],
- "description": "Symfony polyfill backporting some PHP 8.1+ features to lower PHP versions",
+ "description": "Symfony polyfill backporting some PHP 8.2+ features to lower PHP versions",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
@@ -2463,7 +2781,7 @@
"shim"
],
"support": {
- "source": "https://github.com/symfony/polyfill-php81/tree/v1.30.0"
+ "source": "https://github.com/symfony/polyfill-php82/tree/v1.31.0"
},
"funding": [
{
@@ -2479,24 +2797,24 @@
"type": "tidelift"
}
],
- "time": "2024-06-19T12:30:46+00:00"
+ "time": "2024-09-09T11:45:10+00:00"
},
{
"name": "symfony/polyfill-php83",
- "version": "v1.30.0",
+ "version": "v1.31.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php83.git",
- "reference": "dbdcdf1a4dcc2743591f1079d0c35ab1e2dcbbc9"
+ "reference": "2fb86d65e2d424369ad2905e83b236a8805ba491"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/dbdcdf1a4dcc2743591f1079d0c35ab1e2dcbbc9",
- "reference": "dbdcdf1a4dcc2743591f1079d0c35ab1e2dcbbc9",
+ "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/2fb86d65e2d424369ad2905e83b236a8805ba491",
+ "reference": "2fb86d65e2d424369ad2905e83b236a8805ba491",
"shasum": ""
},
"require": {
- "php": ">=7.1"
+ "php": ">=7.2"
},
"type": "library",
"extra": {
@@ -2539,7 +2857,7 @@
"shim"
],
"support": {
- "source": "https://github.com/symfony/polyfill-php83/tree/v1.30.0"
+ "source": "https://github.com/symfony/polyfill-php83/tree/v1.31.0"
},
"funding": [
{
@@ -2555,24 +2873,24 @@
"type": "tidelift"
}
],
- "time": "2024-06-19T12:35:24+00:00"
+ "time": "2024-09-09T11:45:10+00:00"
},
{
"name": "symfony/polyfill-uuid",
- "version": "v1.30.0",
+ "version": "v1.31.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-uuid.git",
- "reference": "2ba1f33797470debcda07fe9dce20a0003df18e9"
+ "reference": "21533be36c24be3f4b1669c4725c7d1d2bab4ae2"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-uuid/zipball/2ba1f33797470debcda07fe9dce20a0003df18e9",
- "reference": "2ba1f33797470debcda07fe9dce20a0003df18e9",
+ "url": "https://api.github.com/repos/symfony/polyfill-uuid/zipball/21533be36c24be3f4b1669c4725c7d1d2bab4ae2",
+ "reference": "21533be36c24be3f4b1669c4725c7d1d2bab4ae2",
"shasum": ""
},
"require": {
- "php": ">=7.1"
+ "php": ">=7.2"
},
"provide": {
"ext-uuid": "*"
@@ -2618,7 +2936,7 @@
"uuid"
],
"support": {
- "source": "https://github.com/symfony/polyfill-uuid/tree/v1.30.0"
+ "source": "https://github.com/symfony/polyfill-uuid/tree/v1.31.0"
},
"funding": [
{
@@ -2634,7 +2952,7 @@
"type": "tidelift"
}
],
- "time": "2024-05-31T15:07:36+00:00"
+ "time": "2024-09-09T11:45:10+00:00"
},
{
"name": "symfony/service-contracts",
@@ -2799,16 +3117,16 @@
},
{
"name": "symfony/twig-bridge",
- "version": "v6.4.9",
+ "version": "v6.4.13",
"source": {
"type": "git",
"url": "https://github.com/symfony/twig-bridge.git",
- "reference": "9bcb26445b9d4ef1087c389234bf33fb00e10ea6"
+ "reference": "ec3511eef0576f378b2758da9e1c157086babd59"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/twig-bridge/zipball/9bcb26445b9d4ef1087c389234bf33fb00e10ea6",
- "reference": "9bcb26445b9d4ef1087c389234bf33fb00e10ea6",
+ "url": "https://api.github.com/repos/symfony/twig-bridge/zipball/ec3511eef0576f378b2758da9e1c157086babd59",
+ "reference": "ec3511eef0576f378b2758da9e1c157086babd59",
"shasum": ""
},
"require": {
@@ -2888,7 +3206,7 @@
"description": "Provides integration for Twig with various Symfony components",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/twig-bridge/tree/v6.4.9"
+ "source": "https://github.com/symfony/twig-bridge/tree/v6.4.13"
},
"funding": [
{
@@ -2904,20 +3222,20 @@
"type": "tidelift"
}
],
- "time": "2024-06-21T16:04:15+00:00"
+ "time": "2024-10-25T15:07:50+00:00"
},
{
"name": "symfony/twig-bundle",
- "version": "v6.4.8",
+ "version": "v6.4.13",
"source": {
"type": "git",
"url": "https://github.com/symfony/twig-bundle.git",
- "reference": "ef17bc8fc2cb2376b235cd1b98f0275a78c5ba65"
+ "reference": "c3beeb5336aba1ea03c37e526968c2fde3ef25c4"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/twig-bundle/zipball/ef17bc8fc2cb2376b235cd1b98f0275a78c5ba65",
- "reference": "ef17bc8fc2cb2376b235cd1b98f0275a78c5ba65",
+ "url": "https://api.github.com/repos/symfony/twig-bundle/zipball/c3beeb5336aba1ea03c37e526968c2fde3ef25c4",
+ "reference": "c3beeb5336aba1ea03c37e526968c2fde3ef25c4",
"shasum": ""
},
"require": {
@@ -2972,7 +3290,7 @@
"description": "Provides a tight integration of Twig into the Symfony full-stack framework",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/twig-bundle/tree/v6.4.8"
+ "source": "https://github.com/symfony/twig-bundle/tree/v6.4.13"
},
"funding": [
{
@@ -2988,20 +3306,20 @@
"type": "tidelift"
}
],
- "time": "2024-05-31T14:49:08+00:00"
+ "time": "2024-09-25T14:18:03+00:00"
},
{
"name": "symfony/uid",
- "version": "v6.4.8",
+ "version": "v6.4.13",
"source": {
"type": "git",
"url": "https://github.com/symfony/uid.git",
- "reference": "35904eca37a84bb764c560cbfcac9f0ac2bcdbdf"
+ "reference": "18eb207f0436a993fffbdd811b5b8fa35fa5e007"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/uid/zipball/35904eca37a84bb764c560cbfcac9f0ac2bcdbdf",
- "reference": "35904eca37a84bb764c560cbfcac9f0ac2bcdbdf",
+ "url": "https://api.github.com/repos/symfony/uid/zipball/18eb207f0436a993fffbdd811b5b8fa35fa5e007",
+ "reference": "18eb207f0436a993fffbdd811b5b8fa35fa5e007",
"shasum": ""
},
"require": {
@@ -3046,7 +3364,7 @@
"uuid"
],
"support": {
- "source": "https://github.com/symfony/uid/tree/v6.4.8"
+ "source": "https://github.com/symfony/uid/tree/v6.4.13"
},
"funding": [
{
@@ -3062,20 +3380,20 @@
"type": "tidelift"
}
],
- "time": "2024-05-31T14:49:08+00:00"
+ "time": "2024-09-25T14:18:03+00:00"
},
{
"name": "symfony/var-dumper",
- "version": "v6.4.10",
+ "version": "v6.4.14",
"source": {
"type": "git",
"url": "https://github.com/symfony/var-dumper.git",
- "reference": "a71cc3374f5fb9759da1961d28c452373b343dd4"
+ "reference": "93c09246038178717a9c14b809ea8151ffcf7091"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/var-dumper/zipball/a71cc3374f5fb9759da1961d28c452373b343dd4",
- "reference": "a71cc3374f5fb9759da1961d28c452373b343dd4",
+ "url": "https://api.github.com/repos/symfony/var-dumper/zipball/93c09246038178717a9c14b809ea8151ffcf7091",
+ "reference": "93c09246038178717a9c14b809ea8151ffcf7091",
"shasum": ""
},
"require": {
@@ -3131,7 +3449,7 @@
"dump"
],
"support": {
- "source": "https://github.com/symfony/var-dumper/tree/v6.4.10"
+ "source": "https://github.com/symfony/var-dumper/tree/v6.4.14"
},
"funding": [
{
@@ -3147,20 +3465,20 @@
"type": "tidelift"
}
],
- "time": "2024-07-26T12:30:32+00:00"
+ "time": "2024-11-05T15:34:40+00:00"
},
{
"name": "symfony/var-exporter",
- "version": "v6.4.9",
+ "version": "v6.4.13",
"source": {
"type": "git",
"url": "https://github.com/symfony/var-exporter.git",
- "reference": "f9a060622e0d93777b7f8687ec4860191e16802e"
+ "reference": "0f605f72a363f8743001038a176eeb2a11223b51"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/var-exporter/zipball/f9a060622e0d93777b7f8687ec4860191e16802e",
- "reference": "f9a060622e0d93777b7f8687ec4860191e16802e",
+ "url": "https://api.github.com/repos/symfony/var-exporter/zipball/0f605f72a363f8743001038a176eeb2a11223b51",
+ "reference": "0f605f72a363f8743001038a176eeb2a11223b51",
"shasum": ""
},
"require": {
@@ -3208,7 +3526,7 @@
"serialize"
],
"support": {
- "source": "https://github.com/symfony/var-exporter/tree/v6.4.9"
+ "source": "https://github.com/symfony/var-exporter/tree/v6.4.13"
},
"funding": [
{
@@ -3224,28 +3542,27 @@
"type": "tidelift"
}
],
- "time": "2024-06-24T15:53:56+00:00"
+ "time": "2024-09-25T14:18:03+00:00"
},
{
"name": "twig/twig",
- "version": "v3.11.0",
+ "version": "v3.14.2",
"source": {
"type": "git",
"url": "https://github.com/twigphp/Twig.git",
- "reference": "e80fb8ebba85c7341a97a9ebf825d7fd4b77708d"
+ "reference": "0b6f9d8370bb3b7f1ce5313ed8feb0fafd6e399a"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/twigphp/Twig/zipball/e80fb8ebba85c7341a97a9ebf825d7fd4b77708d",
- "reference": "e80fb8ebba85c7341a97a9ebf825d7fd4b77708d",
+ "url": "https://api.github.com/repos/twigphp/Twig/zipball/0b6f9d8370bb3b7f1ce5313ed8feb0fafd6e399a",
+ "reference": "0b6f9d8370bb3b7f1ce5313ed8feb0fafd6e399a",
"shasum": ""
},
"require": {
- "php": ">=7.2.5",
+ "php": ">=8.0.2",
"symfony/deprecation-contracts": "^2.5|^3",
"symfony/polyfill-ctype": "^1.8",
"symfony/polyfill-mbstring": "^1.3",
- "symfony/polyfill-php80": "^1.22",
"symfony/polyfill-php81": "^1.29"
},
"require-dev": {
@@ -3292,7 +3609,7 @@
],
"support": {
"issues": "https://github.com/twigphp/Twig/issues",
- "source": "https://github.com/twigphp/Twig/tree/v3.11.0"
+ "source": "https://github.com/twigphp/Twig/tree/v3.14.2"
},
"funding": [
{
@@ -3304,7 +3621,7 @@
"type": "tidelift"
}
],
- "time": "2024-08-08T16:15:16+00:00"
+ "time": "2024-11-07T12:36:22+00:00"
}
],
"packages-dev": [
@@ -3314,12 +3631,12 @@
"source": {
"type": "git",
"url": "https://github.com/nextcloud-deps/ocp.git",
- "reference": "65b6744fca5d4b3c366754295e5cb0680a580c51"
+ "reference": "5054ce1e0018c7f0946df391a54861f8172d7be2"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/nextcloud-deps/ocp/zipball/65b6744fca5d4b3c366754295e5cb0680a580c51",
- "reference": "65b6744fca5d4b3c366754295e5cb0680a580c51",
+ "url": "https://api.github.com/repos/nextcloud-deps/ocp/zipball/5054ce1e0018c7f0946df391a54861f8172d7be2",
+ "reference": "5054ce1e0018c7f0946df391a54861f8172d7be2",
"shasum": ""
},
"require": {
@@ -3350,7 +3667,7 @@
"issues": "https://github.com/nextcloud-deps/ocp/issues",
"source": "https://github.com/nextcloud-deps/ocp/tree/stable29"
},
- "time": "2024-07-11T00:37:34+00:00"
+ "time": "2024-09-17T00:34:06+00:00"
},
{
"name": "psr/clock",
@@ -3406,23 +3723,23 @@
"source": {
"type": "git",
"url": "https://github.com/Roave/SecurityAdvisories.git",
- "reference": "d44cb505c9515db5ff905ec510d69a0f80b366a2"
+ "reference": "e63317470a1b96346be224a68f9e64567e1001c3"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/d44cb505c9515db5ff905ec510d69a0f80b366a2",
- "reference": "d44cb505c9515db5ff905ec510d69a0f80b366a2",
+ "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/e63317470a1b96346be224a68f9e64567e1001c3",
+ "reference": "e63317470a1b96346be224a68f9e64567e1001c3",
"shasum": ""
},
"conflict": {
"3f/pygmentize": "<1.2",
- "admidio/admidio": "<4.2.13",
+ "admidio/admidio": "<4.3.12",
"adodb/adodb-php": "<=5.20.20|>=5.21,<=5.21.3",
"aheinze/cockpit": "<2.2",
- "aimeos/ai-admin-graphql": ">=2022.04.1,<2022.10.10|>=2023.04.1,<2023.10.6|>=2024.04.1,<2024.04.6",
+ "aimeos/ai-admin-graphql": ">=2022.04.1,<2022.10.10|>=2023.04.1,<2023.10.6|>=2024.04.1,<2024.07.2",
"aimeos/ai-admin-jsonadm": "<2020.10.13|>=2021.04.1,<2021.10.6|>=2022.04.1,<2022.10.3|>=2023.04.1,<2023.10.4|==2024.04.1",
"aimeos/ai-client-html": ">=2020.04.1,<2020.10.27|>=2021.04.1,<2021.10.22|>=2022.04.1,<2022.10.13|>=2023.04.1,<2023.10.15|>=2024.04.1,<2024.04.7",
- "aimeos/ai-controller-frontend": "<2020.10.15|>=2021.04.1,<2021.10.8|>=2022.04.1,<2022.10.8|>=2023.04.1,<2023.10.9",
+ "aimeos/ai-controller-frontend": "<2020.10.15|>=2021.04.1,<2021.10.8|>=2022.04.1,<2022.10.8|>=2023.04.1,<2023.10.9|==2024.04.1",
"aimeos/aimeos-core": ">=2022.04.1,<2022.10.17|>=2023.04.1,<2023.10.17|>=2024.04.1,<2024.04.7",
"aimeos/aimeos-typo3": "<19.10.12|>=20,<20.10.5",
"airesvsg/acf-to-rest-api": "<=3.1",
@@ -3431,6 +3748,7 @@
"alextselegidis/easyappointments": "<1.5",
"alterphp/easyadmin-extension-bundle": ">=1.2,<1.2.11|>=1.3,<1.3.1",
"amazing/media2click": ">=1,<1.3.3",
+ "ameos/ameos_tarteaucitron": "<1.2.23",
"amphp/artax": "<1.0.6|>=2,<2.0.6",
"amphp/http": "<=1.7.2|>=2,<=2.1",
"amphp/http-client": ">=4,<4.4",
@@ -3449,12 +3767,12 @@
"athlon1600/php-proxy-app": "<=3",
"austintoddj/canvas": "<=3.4.2",
"auth0/wordpress": "<=4.6",
- "automad/automad": "<=1.10.9",
+ "automad/automad": "<2.0.0.0-alpha5",
"automattic/jetpack": "<9.8",
"awesome-support/awesome-support": "<=6.0.7",
"aws/aws-sdk-php": "<3.288.1",
"azuracast/azuracast": "<0.18.3",
- "backdrop/backdrop": "<1.24.2",
+ "backdrop/backdrop": "<1.27.3|>=1.28,<1.28.2",
"backpack/crud": "<3.4.9",
"bacula-web/bacula-web": "<8.0.0.0-RC2-dev",
"badaso/core": "<2.7",
@@ -3463,13 +3781,13 @@
"barrelstrength/sprout-forms": "<3.9",
"barryvdh/laravel-translation-manager": "<0.6.2",
"barzahlen/barzahlen-php": "<2.0.1",
- "baserproject/basercms": "<5.0.9",
+ "baserproject/basercms": "<=5.1.1",
"bassjobsen/bootstrap-3-typeahead": ">4.0.2",
"bbpress/bbpress": "<2.6.5",
"bcosca/fatfree": "<3.7.2",
"bedita/bedita": "<4",
"bigfork/silverstripe-form-capture": ">=3,<3.1.1",
- "billz/raspap-webgui": "<2.9.5",
+ "billz/raspap-webgui": "<=3.1.4",
"bk2k/bootstrap-package": ">=7.1,<7.1.2|>=8,<8.0.8|>=9,<9.0.4|>=9.1,<9.1.3|>=10,<10.0.10|>=11,<11.0.3",
"blueimp/jquery-file-upload": "==6.4.4",
"bmarshall511/wordpress_zero_spam": "<5.2.13",
@@ -3508,31 +3826,34 @@
"codeigniter4/shield": "<1.0.0.0-beta8",
"codiad/codiad": "<=2.8.4",
"composer/composer": "<1.10.27|>=2,<2.2.24|>=2.3,<2.7.7",
- "concrete5/concrete5": "<9.2.8",
+ "concrete5/concrete5": "<9.3.4",
"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",
- "contao/contao": ">=3,<3.5.37|>=4,<4.4.56|>=4.5,<4.9.40|>=4.10,<4.11.7|>=4.13,<4.13.21|>=5.1,<5.1.4",
+ "contao/contao": "<=5.4.1",
"contao/core": "<3.5.39",
- "contao/core-bundle": "<4.13.40|>=5,<5.3.4",
+ "contao/core-bundle": "<4.13.49|>=5,<5.3.15|>=5.4,<5.4.3",
"contao/listing-bundle": ">=3,<=3.5.30|>=4,<4.4.8",
"contao/managed-edition": "<=1.5",
"corveda/phpsandbox": "<1.3.5",
"cosenary/instagram": "<=2.3",
- "craftcms/cms": "<4.6.2",
+ "craftcms/cms": "<4.6.2|>=5,<=5.2.2",
"croogo/croogo": "<4",
"cuyz/valinor": "<0.12",
+ "czim/file-handling": "<1.5|>=2,<2.3",
"czproject/git-php": "<4.0.3",
+ "damienharper/auditor-bundle": "<5.2.6",
"dapphp/securimage": "<3.6.6",
"darylldoyle/safe-svg": "<1.9.10",
"datadog/dd-trace": ">=0.30,<0.30.2",
"datatables/datatables": "<1.10.10",
"david-garcia/phpwhois": "<=4.3.1",
"dbrisinajumi/d2files": "<1",
- "dcat/laravel-admin": "<=2.1.3.0-beta",
+ "dcat/laravel-admin": "<=2.1.3",
"derhansen/fe_change_pwd": "<2.0.5|>=3,<3.0.3",
"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",
"devgroup/dotplant": "<2020.09.14-dev",
"directmailteam/direct-mail": "<6.0.3|>=7,<7.0.3|>=8,<9.5.2",
"doctrine/annotations": "<1.2.7",
@@ -3547,8 +3868,9 @@
"dolibarr/dolibarr": "<19.0.2",
"dompdf/dompdf": "<2.0.4",
"doublethreedigital/guest-entries": "<3.1.2",
- "drupal/core": ">=6,<6.38|>=7,<7.96|>=8,<10.1.8|>=10.2,<10.2.2",
- "drupal/drupal": ">=5,<5.11|>=6,<6.38|>=7,<7.80|>=8,<8.9.16|>=9,<9.1.12|>=9.2,<9.2.4",
+ "drupal/core": ">=6,<6.38|>=7,<7.96|>=8,<10.2.9|>=10.3,<10.3.6|>=11,<11.0.5",
+ "drupal/core-recommended": ">=8,<10.2.9|>=10.3,<10.3.6|>=11,<11.0.5",
+ "drupal/drupal": ">=5,<5.11|>=6,<6.38|>=7,<7.80|>=8,<10.2.9|>=10.3,<10.3.6|>=11,<11.0.5",
"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",
@@ -3572,29 +3894,33 @@
"ezsystems/ezdemo-ls-extension": ">=5.4,<5.4.2.1-dev",
"ezsystems/ezfind-ls": ">=5.3,<5.3.6.1-dev|>=5.4,<5.4.11.1-dev|>=2017.12,<2017.12.0.1-dev",
"ezsystems/ezplatform": "<=1.13.6|>=2,<=2.5.24",
- "ezsystems/ezplatform-admin-ui": ">=1.3,<1.3.5|>=1.4,<1.4.6|>=1.5,<1.5.29|>=2.3,<2.3.26",
+ "ezsystems/ezplatform-admin-ui": ">=1.3,<1.3.5|>=1.4,<1.4.6|>=1.5,<1.5.29|>=2.3,<2.3.26|>=3.3,<3.3.39",
"ezsystems/ezplatform-admin-ui-assets": ">=4,<4.2.1|>=5,<5.0.1|>=5.1,<5.1.1",
"ezsystems/ezplatform-graphql": ">=1.0.0.0-RC1-dev,<1.0.13|>=2.0.0.0-beta1,<2.3.12",
"ezsystems/ezplatform-kernel": "<1.2.5.1-dev|>=1.3,<1.3.35",
"ezsystems/ezplatform-rest": ">=1.2,<=1.2.2|>=1.3,<1.3.8",
- "ezsystems/ezplatform-richtext": ">=2.3,<2.3.7.1-dev",
+ "ezsystems/ezplatform-richtext": ">=2.3,<2.3.7.1-dev|>=3.3,<3.3.40",
"ezsystems/ezplatform-solr-search-engine": ">=1.7,<1.7.12|>=2,<2.0.2|>=3.3,<3.3.15",
"ezsystems/ezplatform-user": ">=1,<1.0.1",
"ezsystems/ezpublish-kernel": "<6.13.8.2-dev|>=7,<7.5.31",
"ezsystems/ezpublish-legacy": "<=2017.12.7.3|>=2018.6,<=2019.03.5.1",
"ezsystems/platform-ui-assets-bundle": ">=4.2,<4.2.3",
"ezsystems/repository-forms": ">=2.3,<2.3.2.1-dev|>=2.5,<2.5.15",
- "ezyang/htmlpurifier": "<4.1.1",
+ "ezyang/htmlpurifier": "<=4.2",
"facade/ignition": "<1.16.15|>=2,<2.4.2|>=2.5,<2.5.2",
"facturascripts/facturascripts": "<=2022.08",
"fastly/magento2": "<1.2.26",
"feehi/cms": "<=2.1.1",
"feehi/feehicms": "<=2.1.1",
"fenom/fenom": "<=2.12.1",
+ "filament/actions": ">=3.2,<3.2.123",
+ "filament/infolists": ">=3,<3.2.115",
+ "filament/tables": ">=3,<3.2.115",
"filegator/filegator": "<7.8",
"filp/whoops": "<2.1.13",
"fineuploader/php-traditional-server": "<=1.2.2",
"firebase/php-jwt": "<6",
+ "fisharebest/webtrees": "<=2.1.18",
"fixpunkt/fp-masterquiz": "<2.2.1|>=3,<3.5.2",
"fixpunkt/fp-newsletter": "<1.1.1|>=2,<2.1.2|>=2.2,<3.2.6",
"flarum/core": "<1.8.5",
@@ -3617,19 +3943,19 @@
"friendsofsymfony/rest-bundle": ">=1.2,<1.2.2",
"friendsofsymfony/user-bundle": ">=1,<1.3.5",
"friendsofsymfony1/swiftmailer": ">=4,<5.4.13|>=6,<6.2.5",
- "friendsofsymfony1/symfony1": ">=1.1,<1.15.19",
+ "friendsofsymfony1/symfony1": ">=1.1,<1.5.19",
"friendsoftypo3/mediace": ">=7.6.2,<7.6.5",
"friendsoftypo3/openid": ">=4.5,<4.5.31|>=4.7,<4.7.16|>=6,<6.0.11|>=6.1,<6.1.6",
"froala/wysiwyg-editor": "<3.2.7|>=4.0.1,<=4.1.3",
- "froxlor/froxlor": "<2.1.9",
+ "froxlor/froxlor": "<=2.2.0.0-RC3",
"frozennode/administrator": "<=5.0.12",
"fuel/core": "<1.8.1",
- "funadmin/funadmin": "<=3.2|>=3.3.2,<=3.3.3",
+ "funadmin/funadmin": "<=5.0.2",
"gaoming13/wechat-php-sdk": "<=1.10.2",
"genix/cms": "<=1.1.11",
"getformwork/formwork": "<1.13.1|==2.0.0.0-beta1",
"getgrav/grav": "<1.7.46",
- "getkirby/cms": "<4.1.1",
+ "getkirby/cms": "<=3.6.6.5|>=3.7,<=3.7.5.4|>=3.8,<=3.8.4.3|>=3.9,<=3.9.8.1|>=3.10,<=3.10.1|>=4,<=4.3",
"getkirby/kirby": "<=2.5.12",
"getkirby/panel": "<2.5.14",
"getkirby/starterkit": "<=3.7.0.2",
@@ -3655,8 +3981,9 @@
"hov/jobfair": "<1.0.13|>=2,<2.0.2",
"httpsoft/http-message": "<1.0.12",
"hyn/multi-tenant": ">=5.6,<5.7.2",
- "ibexa/admin-ui": ">=4.2,<4.2.3",
+ "ibexa/admin-ui": ">=4.2,<4.2.3|>=4.6.0.0-beta1,<4.6.9",
"ibexa/core": ">=4,<4.0.7|>=4.1,<4.1.4|>=4.2,<4.2.3|>=4.5,<4.5.6|>=4.6,<4.6.2",
+ "ibexa/fieldtype-richtext": ">=4.6,<4.6.10",
"ibexa/graphql": ">=2.5,<2.5.31|>=3.3,<3.3.28|>=4.2,<4.2.3",
"ibexa/post-install": "<=1.0.4",
"ibexa/solr": ">=4.5,<4.5.4",
@@ -3675,9 +4002,11 @@
"in2code/femanager": "<5.5.3|>=6,<6.3.4|>=7,<7.2.3",
"in2code/ipandlanguageredirect": "<5.1.2",
"in2code/lux": "<17.6.1|>=18,<24.0.2",
+ "in2code/powermail": "<7.5.1|>=8,<8.5.1|>=9,<10.9.1|>=11,<12.4.1",
"innologi/typo3-appointments": "<2.0.6",
"intelliants/subrion": "<4.2.2",
"inter-mediator/inter-mediator": "==5.5",
+ "ipl/web": "<0.10.1",
"islandora/islandora": ">=2,<2.4.1",
"ivankristianto/phpwhois": "<=4.3",
"jackalope/jackalope-doctrine-dbal": "<1.7.4",
@@ -3703,21 +4032,24 @@
"kelvinmo/simplexrd": "<3.1.1",
"kevinpapst/kimai2": "<1.16.7",
"khodakhah/nodcms": "<=3",
- "kimai/kimai": "<2.16",
+ "kimai/kimai": "<=2.20.1",
"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",
- "krayin/laravel-crm": "<1.2.2",
+ "krayin/laravel-crm": "<=1.3",
"kreait/firebase-php": ">=3.2,<3.8.1",
"kumbiaphp/kumbiapp": "<=1.1.1",
"la-haute-societe/tcpdf": "<6.2.22",
"laminas/laminas-diactoros": "<2.18.1|==2.19|==2.20|==2.21|==2.22|==2.23|>=2.24,<2.24.2|>=2.25,<2.25.2",
"laminas/laminas-form": "<2.17.1|>=3,<3.0.2|>=3.1,<3.1.1",
"laminas/laminas-http": "<2.14.2",
+ "lara-zeus/artemis": ">=1,<=1.0.6",
+ "lara-zeus/dynamic-dashboard": ">=3,<=3.0.1",
"laravel/fortify": "<1.11.1",
"laravel/framework": "<6.20.44|>=7,<7.30.6|>=8,<8.75",
"laravel/laravel": ">=5.4,<5.4.22",
+ "laravel/reverb": "<1.4",
"laravel/socialite": ">=1,<2.0.10",
"latte/latte": "<2.10.8",
"lavalite/cms": "<=9|==10.1",
@@ -3730,26 +4062,30 @@
"librenms/librenms": "<2017.08.18",
"liftkit/database": "<2.13.2",
"lightsaml/lightsaml": "<1.3.5",
- "limesurvey/limesurvey": "<3.27.19",
+ "limesurvey/limesurvey": "<6.5.12",
"livehelperchat/livehelperchat": "<=3.91",
- "livewire/livewire": ">2.2.4,<2.2.6|>=3.3.5,<3.4.9",
+ "livewire/livewire": "<2.12.7|>=3.0.0.0-beta1,<3.5.2",
"lms/routes": "<2.1.1",
"localizationteam/l10nmgr": "<7.4|>=8,<8.7|>=9,<9.2",
"luyadev/yii-helpers": "<1.2.1",
- "magento/community-edition": "<2.4.5|==2.4.5|>=2.4.5.0-patch1,<2.4.5.0-patch8|==2.4.6|>=2.4.6.0-patch1,<2.4.6.0-patch6|==2.4.7",
+ "maestroerror/php-heic-to-jpg": "<1.0.5",
+ "magento/community-edition": "<2.4.5|==2.4.5|>=2.4.5.0-patch1,<2.4.5.0-patch10|==2.4.6|>=2.4.6.0-patch1,<2.4.6.0-patch8|>=2.4.7.0-beta1,<2.4.7.0-patch3",
"magento/core": "<=1.9.4.5",
"magento/magento1ce": "<1.9.4.3-dev",
"magento/magento1ee": ">=1,<1.14.4.3-dev",
- "magento/product-community-edition": ">=2,<2.2.10|>=2.3,<2.3.2.0-patch2",
+ "magento/product-community-edition": "<2.4.4.0-patch9|>=2.4.5,<2.4.5.0-patch8|>=2.4.6,<2.4.6.0-patch6|>=2.4.7,<2.4.7.0-patch1",
"magneto/core": "<1.9.4.4-dev",
"maikuolan/phpmussel": ">=1,<1.6",
"mainwp/mainwp": "<=4.4.3.3",
- "mantisbt/mantisbt": "<2.26.2",
+ "mantisbt/mantisbt": "<=2.26.3",
"marcwillmann/turn": "<0.3.3",
"matyhtf/framework": "<3.0.6",
- "mautic/core": "<4.4.12|>=5.0.0.0-alpha,<5.0.4",
+ "mautic/core": "<4.4.13|>=5,<5.1.1",
+ "mautic/core-lib": ">=1.0.0.0-beta,<4.4.13|>=5.0.0.0-alpha,<5.1.1",
+ "maximebf/debugbar": "<1.19",
"mdanter/ecc": "<2",
- "mediawiki/core": "<1.36.2",
+ "mediawiki/cargo": "<3.6.1",
+ "mediawiki/core": "<1.39.5|==1.40",
"mediawiki/matomo": "<2.4.3",
"mediawiki/semantic-media-wiki": "<4.0.2",
"melisplatform/melis-asset-manager": "<5.0.1",
@@ -3760,7 +4096,7 @@
"microsoft/microsoft-graph": ">=1.16,<1.109.1|>=2,<2.0.1",
"microsoft/microsoft-graph-beta": "<2.0.1",
"microsoft/microsoft-graph-core": "<2.0.2",
- "microweber/microweber": "<=2.0.4",
+ "microweber/microweber": "<=2.0.16",
"mikehaertl/php-shellcommand": "<1.6.1",
"miniorange/miniorange-saml": "<1.4.3",
"mittwald/typo3_forum": "<1.2.1",
@@ -3769,7 +4105,7 @@
"mojo42/jirafeau": "<4.4",
"mongodb/mongodb": ">=1,<1.9.2",
"monolog/monolog": ">=1.8,<1.12",
- "moodle/moodle": "<4.3.5|>=4.4.0.0-beta,<4.4.1",
+ "moodle/moodle": "<4.3.6|>=4.4.0.0-beta,<4.4.2",
"mos/cimage": "<0.7.19",
"movim/moxl": ">=0.8,<=0.10",
"movingbytes/social-network": "<=1.2.1",
@@ -3782,6 +4118,7 @@
"munkireport/softwareupdate": "<1.6",
"mustache/mustache": ">=2,<2.14.1",
"namshi/jose": "<2.2",
+ "nategood/httpful": "<1",
"neoan3-apps/template": "<1.1.1",
"neorazorx/facturascripts": "<2022.04",
"neos/flow": ">=1,<1.0.4|>=1.1,<1.1.1|>=2,<2.0.1|>=2.3,<2.3.16|>=3,<3.0.12|>=3.1,<3.1.10|>=3.2,<3.2.13|>=3.3,<3.3.13|>=4,<4.0.6",
@@ -3804,16 +4141,16 @@
"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.4.4",
+ "october/october": "<=3.6.4",
"october/rain": "<1.0.472|>=1.1,<1.1.2",
"october/system": "<1.0.476|>=1.1,<1.1.12|>=2,<2.2.34|>=3,<3.5.15",
"omeka/omeka-s": "<4.0.3",
"onelogin/php-saml": "<2.10.4",
"oneup/uploader-bundle": ">=1,<1.9.3|>=2,<2.1.5",
"open-web-analytics/open-web-analytics": "<1.7.4",
- "opencart/opencart": "<=3.0.3.9|>=4",
+ "opencart/opencart": ">=0",
"openid/php-openid": "<2.3",
- "openmage/magento-lts": "<20.5",
+ "openmage/magento-lts": "<20.10.1",
"opensolutions/vimbadmin": "<=3.0.15",
"opensource-workshop/connect-cms": "<1.7.2|>=2,<2.3.2",
"orchid/platform": ">=9,<9.4.4|>=14.0.0.0-alpha4,<14.5",
@@ -3823,6 +4160,7 @@
"oro/crm-call-bundle": ">=4.2,<=4.2.5|>=5,<5.0.4|>=5.1,<5.1.1",
"oro/customer-portal": ">=4.1,<=4.1.13|>=4.2,<=4.2.10|>=5,<=5.0.11|>=5.1,<=5.1.3",
"oro/platform": ">=1.7,<1.7.4|>=3.1,<3.1.29|>=4.1,<4.1.17|>=4.2,<=4.2.10|>=5,<=5.0.12|>=5.1,<=5.1.3",
+ "oveleon/contao-cookiebar": "<1.16.3|>=2,<2.1.3",
"oxid-esales/oxideshop-ce": "<4.5",
"oxid-esales/paymorrow-module": ">=1,<1.0.2|>=2,<2.0.1",
"packbackbooks/lti-1-3-php-library": "<5",
@@ -3846,7 +4184,7 @@
"phenx/php-svg-lib": "<0.5.2",
"php-censor/php-censor": "<2.0.13|>=2.1,<2.1.5",
"php-mod/curl": "<2.3.2",
- "phpbb/phpbb": "<3.2.10|>=3.3,<3.3.1",
+ "phpbb/phpbb": "<3.3.11",
"phpems/phpems": ">=6,<=6.1.3",
"phpfastcache/phpfastcache": "<6.1.5|>=7,<7.1.2|>=8,<8.0.7",
"phpmailer/phpmailer": "<6.5",
@@ -3854,8 +4192,8 @@
"phpmyadmin/phpmyadmin": "<5.2.1",
"phpmyfaq/phpmyfaq": "<3.2.5|==3.2.5",
"phpoffice/common": "<0.2.9",
- "phpoffice/phpexcel": "<1.8",
- "phpoffice/phpspreadsheet": "<1.16",
+ "phpoffice/phpexcel": "<1.8.1",
+ "phpoffice/phpspreadsheet": "<1.29.2|>=2,<2.1.1|>=2.2,<2.3",
"phpseclib/phpseclib": "<2.0.47|>=3,<3.0.36",
"phpservermon/phpservermon": "<3.6",
"phpsysinfo/phpsysinfo": "<3.4.3",
@@ -3864,9 +4202,10 @@
"phpxmlrpc/extras": "<0.6.1",
"phpxmlrpc/phpxmlrpc": "<4.9.2",
"pi/pi": "<=2.5",
- "pimcore/admin-ui-classic-bundle": "<=1.4.2",
+ "pimcore/admin-ui-classic-bundle": "<1.5.4",
"pimcore/customer-management-framework-bundle": "<4.0.6",
"pimcore/data-hub": "<1.2.4",
+ "pimcore/data-importer": "<1.8.9|>=1.9,<1.9.3",
"pimcore/demo": "<10.3",
"pimcore/ecommerce-framework-bundle": "<1.0.10",
"pimcore/perspective-editor": "<1.5.1",
@@ -3888,15 +4227,16 @@
"prestashop/ps_facetedsearch": "<3.4.1",
"prestashop/ps_linklist": "<3.1",
"privatebin/privatebin": "<1.4|>=1.5,<1.7.4",
- "processwire/processwire": "<=3.0.210",
+ "processwire/processwire": "<=3.0.229",
"propel/propel": ">=2.0.0.0-alpha1,<=2.0.0.0-alpha7",
"propel/propel1": ">=1,<=1.7.1",
- "pterodactyl/panel": "<1.11.6",
+ "pterodactyl/panel": "<1.11.8",
"ptheofan/yii2-statemachine": ">=2.0.0.0-RC1-dev,<=2",
"ptrofimov/beanstalk_console": "<1.7.14",
"pubnub/pubnub": "<6.1",
"pusher/pusher-php-server": "<2.2.1",
"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",
"qcubed/qcubed": "<=3.1.1",
"quickapps/cms": "<=2.0.0.0-beta2",
@@ -3907,7 +4247,7 @@
"rap2hpoutre/laravel-log-viewer": "<0.13",
"react/http": ">=0.7,<1.9",
"really-simple-plugins/complianz-gdpr": "<6.4.2",
- "redaxo/source": "<=5.15.1",
+ "redaxo/source": "<=5.17.1",
"remdex/livehelperchat": "<4.29",
"reportico-web/reportico": "<=8.1",
"rhukster/dom-sanitizer": "<1.0.7",
@@ -3924,8 +4264,8 @@
"serluck/phpwhois": "<=4.2.6",
"sfroemken/url_redirect": "<=1.2.1",
"sheng/yiicms": "<=1.2",
- "shopware/core": "<6.5.8.8-dev|>=6.6.0.0-RC1-dev,<6.6.1",
- "shopware/platform": "<6.5.8.8-dev|>=6.6.0.0-RC1-dev,<6.6.1",
+ "shopware/core": "<=6.5.8.12|>=6.6,<=6.6.5",
+ "shopware/platform": "<=6.5.8.12|>=6.6,<=6.6.5",
"shopware/production": "<=6.3.5.2",
"shopware/shopware": "<=5.7.17",
"shopware/storefront": "<=6.4.8.1|>=6.5.8,<6.5.8.7-dev",
@@ -3937,11 +4277,12 @@
"silverstripe/cms": "<4.11.3",
"silverstripe/comments": ">=1.3,<3.1.1",
"silverstripe/forum": "<=0.6.1|>=0.7,<=0.7.3",
- "silverstripe/framework": "<4.13.39|>=5,<5.1.11",
+ "silverstripe/framework": "<5.2.16",
"silverstripe/graphql": ">=2,<2.0.5|>=3,<3.8.2|>=4,<4.3.7|>=5,<5.1.3",
"silverstripe/hybridsessions": ">=1,<2.4.1|>=2.5,<2.5.1",
"silverstripe/recipe-cms": ">=4.5,<4.5.3",
"silverstripe/registry": ">=2.1,<2.1.2|>=2.2,<2.2.1",
+ "silverstripe/reports": "<5.2.3",
"silverstripe/restfulserver": ">=1,<1.0.9|>=2,<2.0.4|>=2.1,<2.1.2",
"silverstripe/silverstripe-omnipay": "<2.5.2|>=3,<3.0.2|>=3.1,<3.1.4|>=3.2,<3.2.1",
"silverstripe/subsites": ">=2,<2.6.1",
@@ -3962,7 +4303,7 @@
"slim/slim": "<2.6",
"slub/slub-events": "<3.0.3",
"smarty/smarty": "<4.5.3|>=5,<5.1.1",
- "snipe/snipe-it": "<6.4.2",
+ "snipe/snipe-it": "<7.0.10",
"socalnick/scn-social-auth": "<1.15.2",
"socialiteproviders/steam": "<1.1",
"spatie/browsershot": "<3.57.4",
@@ -3972,14 +4313,15 @@
"spoonity/tcpdf": "<6.2.22",
"squizlabs/php_codesniffer": ">=1,<2.8.1|>=3,<3.0.1",
"ssddanbrown/bookstack": "<24.05.1",
+ "starcitizentools/citizen-skin": ">=2.6.3,<2.31",
"statamic/cms": "<4.46|>=5.3,<5.6.2",
"stormpath/sdk": "<9.9.99",
- "studio-42/elfinder": "<2.1.62",
+ "studio-42/elfinder": "<=2.1.64",
"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.4.17|>=2.5,<2.5.13",
+ "sulu/sulu": "<1.6.44|>=2,<2.5.21|>=2.6,<2.6.5",
"sumocoders/framework-user-bundle": "<1.4",
"superbig/craft-audit": "<3.0.2",
"swag/paypal": "<5.4.4",
@@ -3990,7 +4332,7 @@
"sylius/grid-bundle": "<1.10.1",
"sylius/paypal-plugin": ">=1,<1.2.4|>=1.3,<1.3.1",
"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.9.10|>=1.10,<1.10.11|>=1.11,<1.11.2|>=1.12.0.0-alpha1,<1.12.16|>=1.13.0.0-alpha1,<1.13.1",
+ "sylius/sylius": "<1.12.19|>=1.13.0.0-alpha1,<1.13.4",
"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",
@@ -4001,7 +4343,8 @@
"symfony/error-handler": ">=4.4,<4.4.4|>=5,<5.0.4",
"symfony/form": ">=2.3,<2.3.35|>=2.4,<2.6.12|>=2.7,<2.7.50|>=2.8,<2.8.49|>=3,<3.4.20|>=4,<4.0.15|>=4.1,<4.1.9|>=4.2,<4.2.1",
"symfony/framework-bundle": ">=2,<2.3.18|>=2.4,<2.4.8|>=2.5,<2.5.2|>=2.7,<2.7.51|>=2.8,<2.8.50|>=3,<3.4.26|>=4,<4.1.12|>=4.2,<4.2.7|>=5.3.14,<5.3.15|>=5.4.3,<5.4.4|>=6.0.3,<6.0.4",
- "symfony/http-foundation": ">=2,<2.8.52|>=3,<3.4.35|>=4,<4.2.12|>=4.3,<4.3.8|>=4.4,<4.4.7|>=5,<5.0.7",
+ "symfony/http-client": ">=4.3,<5.4.46|>=6,<6.4.14|>=7,<7.1.7",
+ "symfony/http-foundation": "<5.4.46|>=6,<6.4.14|>=7,<7.1.7",
"symfony/http-kernel": ">=2,<4.4.50|>=5,<5.4.20|>=6,<6.0.20|>=6.1,<6.1.12|>=6.2,<6.2.6",
"symfony/intl": ">=2.7,<2.7.38|>=2.8,<2.8.31|>=3,<3.2.14|>=3.3,<3.3.13",
"symfony/maker-bundle": ">=1.27,<1.29.2|>=1.30,<1.31.1",
@@ -4009,20 +4352,22 @@
"symfony/phpunit-bridge": ">=2.8,<2.8.50|>=3,<3.4.26|>=4,<4.1.12|>=4.2,<4.2.7",
"symfony/polyfill": ">=1,<1.10",
"symfony/polyfill-php55": ">=1,<1.10",
+ "symfony/process": "<5.4.46|>=6,<6.4.14|>=7,<7.1.7",
"symfony/proxy-manager-bridge": ">=2.7,<2.7.51|>=2.8,<2.8.50|>=3,<3.4.26|>=4,<4.1.12|>=4.2,<4.2.7",
"symfony/routing": ">=2,<2.0.19",
+ "symfony/runtime": ">=5.3,<5.4.46|>=6,<6.4.14|>=7,<7.1.7",
"symfony/security": ">=2,<2.7.51|>=2.8,<3.4.49|>=4,<4.4.24|>=5,<5.2.8",
- "symfony/security-bundle": ">=2,<4.4.50|>=5,<5.4.20|>=6,<6.0.20|>=6.1,<6.1.12|>=6.2,<6.2.6",
+ "symfony/security-bundle": ">=2,<4.4.50|>=5,<5.4.20|>=6,<6.0.20|>=6.1,<6.1.12|>=6.2,<6.4.10|>=7,<7.0.10|>=7.1,<7.1.3",
"symfony/security-core": ">=2.4,<2.6.13|>=2.7,<2.7.9|>=2.7.30,<2.7.32|>=2.8,<3.4.49|>=4,<4.4.24|>=5,<5.2.9",
"symfony/security-csrf": ">=2.4,<2.7.48|>=2.8,<2.8.41|>=3,<3.3.17|>=3.4,<3.4.11|>=4,<4.0.11",
"symfony/security-guard": ">=2.8,<3.4.48|>=4,<4.4.23|>=5,<5.2.8",
"symfony/security-http": ">=2.3,<2.3.41|>=2.4,<2.7.51|>=2.8,<2.8.50|>=3,<3.4.26|>=4,<4.2.12|>=4.3,<4.3.8|>=4.4,<4.4.7|>=5,<5.0.7|>=5.1,<5.2.8|>=5.3,<5.3.2|>=5.4,<5.4.31|>=6,<6.3.8",
"symfony/serializer": ">=2,<2.0.11|>=4.1,<4.4.35|>=5,<5.3.12",
- "symfony/symfony": ">=2,<4.4.51|>=5,<5.4.31|>=6,<6.3.8",
+ "symfony/symfony": "<5.4.46|>=6,<6.4.14|>=7,<7.1.7",
"symfony/translation": ">=2,<2.0.17",
"symfony/twig-bridge": ">=2,<4.4.51|>=5,<5.4.31|>=6,<6.3.8",
"symfony/ux-autocomplete": "<2.11.2",
- "symfony/validator": ">=2,<2.0.24|>=2.1,<2.1.12|>=2.2,<2.2.5|>=2.3,<2.3.3",
+ "symfony/validator": "<5.4.43|>=6,<6.4.11|>=7,<7.1.4",
"symfony/var-exporter": ">=4.2,<4.2.12|>=4.3,<4.3.8",
"symfony/web-profiler-bundle": ">=2,<2.3.19|>=2.4,<2.4.9|>=2.5,<2.5.4",
"symfony/webhook": ">=6.3,<6.3.8",
@@ -4046,17 +4391,18 @@
"tinymighty/wiki-seo": "<1.2.2",
"titon/framework": "<9.9.99",
"tobiasbg/tablepress": "<=2.0.0.0-RC1",
- "topthink/framework": "<6.0.17|>=6.1,<6.1.5|>=8,<8.0.4",
+ "topthink/framework": "<6.0.17|>=6.1,<=8.0.4",
"topthink/think": "<=6.1.1",
- "topthink/thinkphp": "<=3.2.3",
+ "topthink/thinkphp": "<=3.2.3|>=6.1.3,<=8.0.4",
"torrentpier/torrentpier": "<=2.4.3",
"tpwd/ke_search": "<4.0.3|>=4.1,<4.6.6|>=5,<5.0.2",
- "tribalsystems/zenario": "<9.5.60602",
+ "tribalsystems/zenario": "<=9.7.61188",
"truckersmp/phpwhois": "<=4.3.1",
"ttskch/pagination-service-provider": "<1",
- "twig/twig": "<1.44.7|>=2,<2.15.3|>=3,<3.4.3",
+ "twbs/bootstrap": "<=3.4.1|>=4,<=4.6.2",
+ "twig/twig": "<3.11.2|>=3.12,<3.14.1",
"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.24|>=10,<=10.4.13|>=11,<=11.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.24|>=10,<10.4.46|>=11,<11.5.40|>=12,<12.4.21|>=13,<13.3.1",
"typo3/cms-core": "<=8.7.56|>=9,<=9.5.47|>=10,<=10.4.44|>=11,<=11.5.36|>=12,<=12.4.14|>=13,<=13.1",
"typo3/cms-extbase": "<6.2.24|>=7,<7.6.8|==8.1.1",
"typo3/cms-fluid": "<4.3.4|>=4.4,<4.4.1",
@@ -4073,6 +4419,7 @@
"ua-parser/uap-php": "<3.8",
"uasoft-indonesia/badaso": "<=2.9.7",
"unisharp/laravel-filemanager": "<2.6.4",
+ "unopim/unopim": "<0.1.4",
"userfrosting/userfrosting": ">=0.3.1,<4.6.3",
"usmanhalalit/pixie": "<1.0.3|>=2,<2.0.2",
"uvdesk/community-skeleton": "<=1.1.1",
@@ -4106,6 +4453,7 @@
"winter/wn-dusk-plugin": "<2.1",
"winter/wn-system-module": "<1.2.4",
"wintercms/winter": "<=1.2.3",
+ "wireui/wireui": "<1.19.3|>=2,<2.1.3",
"woocommerce/woocommerce": "<6.6|>=8.8,<8.8.5|>=8.9,<8.9.3",
"wp-cli/wp-cli": ">=0.12,<2.5",
"wp-graphql/wp-graphql": "<=1.14.5",
@@ -4117,7 +4465,7 @@
"xataface/xataface": "<3",
"xpressengine/xpressengine": "<3.0.15",
"yab/quarx": "<2.4.5",
- "yeswiki/yeswiki": "<4.1",
+ "yeswiki/yeswiki": "<=4.4.4",
"yetiforce/yetiforce-crm": "<=6.4",
"yidashi/yii2cmf": "<=2",
"yii2mod/yii2-cms": "<1.9.2",
@@ -4208,7 +4556,7 @@
"type": "tidelift"
}
],
- "time": "2024-07-15T18:05:53+00:00"
+ "time": "2024-11-07T19:04:57+00:00"
}
],
"aliases": [],
diff --git a/css/main.css b/css/main.css
index 1e75f17d..a3db6830 100644
--- a/css/main.css
+++ b/css/main.css
@@ -168,3 +168,47 @@
margin-left: 16px;
color: inherit;
}
+
+/* CodeMirror */
+.codeMirrorContainer {
+ margin-block-start: 6px;
+ text-align: left;
+}
+
+.prettifyButton {
+ margin-block-start: 10px;
+}
+
+.codeMirrorContainer * .cm-content {
+ border-radius: 0 !important;
+ border: none !important;
+}
+.codeMirrorContainer * .cm-editor {
+ outline: none !important;
+}
+.codeMirrorContainer.light > .vue-codemirror {
+ border: 1px dotted silver;
+}
+.codeMirrorContainer.dark > .vue-codemirror {
+ border: 1px dotted grey;
+}
+
+/* value text color */
+.codeMirrorContainer.light * .cm-content *::selection {
+ color: inherit !important;
+ background-color: #add6ff80 !important;
+}
+.codeMirrorContainer.dark * .cm-content *::selection {
+ color: inherit !important;
+ background-color: #add6ff26 !important;
+}
+
+/* value text color */
+.codeMirrorContainer.dark :deep(.ͼ2 .cm-activeLine) {
+ background-color: #add6ff26;
+}
+
+/* text cursor */
+.codeMirrorContainer :deep(.cm-content) * {
+ cursor: text !important;
+}
diff --git a/docs/developers/aan-de-slag-met-development.md b/docs/developers/aan-de-slag-met-development.md
index c7b9e541..0e316832 100644
--- a/docs/developers/aan-de-slag-met-development.md
+++ b/docs/developers/aan-de-slag-met-development.md
@@ -117,6 +117,18 @@ De commando's om deze linter in de CLI te gebruiken zijn [hier te vinden](https:
De ontwikkeling van de API wordt bijgehouden met de documentatietool [Stoplight.io](https://stoplight.io/), die automatisch een [OpenAPI Specificatie (OAS)](https://www.noraonline.nl/wiki/FS:Openapi-specification) genereert uit de documentatie. De Stoplight voor OpenCatalogi is [hier](https://conduction.stoplight.io/docs/open-catalogi/6yuj08rgf7w44-open-catalogi-api) te vinden.
+### Authenticatie
+
+Een applicatie kan op twee manieren authenticeren bij de OpenCatalogi API (voor het bewerken van gegevens).
+
+#### Basic Auth
+
+Hiervoor worden de username en wachtwoord van de gebruiker in Nextcloud meegegeven in een Basic Auth structuur (waarin de volgende header volgt: `Authorization: Basic {{base64(username:password)}}`).
+
+#### OAuth
+
+OAuth is een veiliger en uitgebreider authenticatiemethode die wij aanraden voor productieomgevingen. Lees hiervoor de [documentatie](https://docs.nextcloud.com/server/latest/admin_manual/configuration_server/oauth2.html) voor het inregelen van OAuth van Nextcloud
+
## Frontend Development
### Storage en Typing
diff --git a/lib/Controller/AttachmentsController.php b/lib/Controller/AttachmentsController.php
index d692f5ff..ffb8160a 100644
--- a/lib/Controller/AttachmentsController.php
+++ b/lib/Controller/AttachmentsController.php
@@ -16,6 +16,7 @@
use OCP\IAppConfig;
use OCP\IRequest;
use OCP\IUserSession;
+use OCP\IURLGenerator;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Symfony\Component\Uid\Uuid;
@@ -37,6 +38,7 @@ class AttachmentsController extends Controller
* @param FileService $fileService The file service
* @param IUserSession $userSession The user session
* @param ObjectService $objectService The object service
+ * @param IURLGenerator $urlGenerator The URL generator
*/
public function __construct
(
@@ -46,7 +48,8 @@ public function __construct
private readonly AttachmentMapper $attachmentMapper,
private readonly FileService $fileService,
private readonly IUserSession $userSession,
- private readonly ObjectService $objectService
+ private readonly ObjectService $objectService,
+ private readonly IURLGenerator $urlGenerator
)
{
parent::__construct($appName, $request);
@@ -129,6 +132,17 @@ public function create(ObjectService $objectService): JSONResponse
// Save the new attachment object.
$object = $this->objectService->saveObject('attachment', $data);
+ // If object is a class change it to array
+ if (is_object($object) === true) {
+ $object = $object->jsonSerialize();
+ }
+
+ // If we do not have an uri, we need to generate one
+ if (isset($object['uri']) === false) {
+ $object['uri'] = $this->urlGenerator->getAbsoluteURL($this->urlGenerator->linkToRoute('openCatalogi.attachments.show', ['id' => $object['id']]));
+ $object = $this->objectService->saveObject('attachment', $object);
+ }
+
// Return the created object as a JSON response.
return new JSONResponse($object);
}
@@ -153,6 +167,9 @@ public function update(string|int $id, ObjectService $objectService): JSONRespon
// Ensure the ID in the data matches the ID in the URL
$data['id'] = $id;
+ // If we do not have an uri, we need to generate one
+ $data['uri'] = $this->urlGenerator->getAbsoluteURL($this->urlGenerator->linkToRoute('openCatalogi.attachments.show', ['id' => $data['id']]));
+
// Save the updated attachment object
$object = $this->objectService->saveObject('attachment', $data);
diff --git a/lib/Controller/CatalogiController.php b/lib/Controller/CatalogiController.php
index 002e7fea..23d43adf 100644
--- a/lib/Controller/CatalogiController.php
+++ b/lib/Controller/CatalogiController.php
@@ -13,6 +13,7 @@
use OCP\AppFramework\Http\JSONResponse;
use OCP\IAppConfig;
use OCP\IRequest;
+use OCP\IURLGenerator;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
@@ -33,6 +34,7 @@ class CatalogiController extends Controller
* @param ObjectService $objectService The object service
* @param DirectoryService $directoryService The directory service
* @param BroadcastService $broadcastService The broadcast service
+ * @param IURLGenerator $urlGenerator The URL generator
*/
public function __construct(
$appName,
@@ -41,7 +43,8 @@ public function __construct(
private readonly CatalogMapper $catalogMapper,
private readonly ObjectService $objectService,
private readonly DirectoryService $directoryService,
- private readonly BroadcastService $broadcastService
+ private readonly BroadcastService $broadcastService,
+ private readonly IURLGenerator $urlGenerator
)
{
parent::__construct($appName, $request);
@@ -114,6 +117,17 @@ public function create(ObjectService $objectService): JSONResponse
// Save the new catalog object
$object = $this->objectService->saveObject('catalog', $data);
+ // If object is a class change it to array
+ if (is_object($object) === true) {
+ $object = $object->jsonSerialize();
+ }
+
+ // If we do not have an uri, we need to generate one
+ if (isset($object['uri']) === false) {
+ $object['uri'] = $this->urlGenerator->getAbsoluteURL($this->urlGenerator->linkToRoute('openCatalogi.catalogs.show', ['id' => $object['id']]));
+ $object = $this->objectService->saveObject('catalog', $object);
+ }
+
// Update all external directories
$this->broadcastService->broadcast();
@@ -142,6 +156,9 @@ public function update(string|int $id, ObjectService $objectService): JSONRespon
// Ensure the ID in the data matches the ID in the URL
$data['id'] = $id;
+ // If we do not have an uri, we need to generate one
+ $data['uri'] = $this->urlGenerator->getAbsoluteURL($this->urlGenerator->linkToRoute('openCatalogi.catalogs.show', ['id' => $data['id']]));
+
// Save the updated catalog object
$object = $this->objectService->saveObject('catalog', $data);
diff --git a/lib/Controller/DirectoryController.php b/lib/Controller/DirectoryController.php
index 0c456f15..4b6218db 100644
--- a/lib/Controller/DirectoryController.php
+++ b/lib/Controller/DirectoryController.php
@@ -6,6 +6,7 @@
use OCA\OpenCatalogi\Db\ListingMapper;
use OCA\OpenCatalogi\Service\DirectoryService;
use OCA\OpenCatalogi\Service\ObjectService;
+use OCA\OpenCatalogi\Exception\DirectoryUrlException;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Db\DoesNotExistException;
use OCP\AppFramework\Db\MultipleObjectsReturnedException;
@@ -75,25 +76,17 @@ public function update(): JSONResponse
{
// Get the URL from the request parameters
$url = $this->request->getParam('directory');
-
- // http://directory.opencatalogi.nl
- // Check if the URL parameter is provided
- if (empty($url) === true) {
- return new JSONResponse(['error' => 'directory parameter is required'], 400);
- }
-
- // Check if URL contains 'local' and throw exception if it does
- if (str_contains(strtolower($url), 'local')) {
- return new JSONResponse(['error' => 'Local URLs are not allowed'], 400);
- }
-
- // Validate the URL
- if (!filter_var($url, FILTER_VALIDATE_URL)) {
- return new JSONResponse(['error' => 'Invalid URL provided'], 400);
- }
// Sync the external directory with the provided URL
- $data = $this->directoryService->syncExternalDirectory($url);
+ try {
+ $data = $this->directoryService->syncExternalDirectory($url);
+ } catch (DirectoryUrlException $exception) {
+ if($exception->getMessage() === 'URL is required') {
+ $exception->setMessage('Property "directory" is required');
+ }
+
+ return new JSONResponse(data: ['message' => $exception->getMessage()], statusCode: 400);
+ }
// Return JSON response with the sync result
return new JSONResponse($data);
diff --git a/lib/Controller/ListingsController.php b/lib/Controller/ListingsController.php
index ba24d12f..450a6166 100644
--- a/lib/Controller/ListingsController.php
+++ b/lib/Controller/ListingsController.php
@@ -6,6 +6,7 @@
use OCA\OpenCatalogi\Db\ListingMapper;
use OCA\OpenCatalogi\Service\ObjectService;
use OCA\OpenCatalogi\Service\DirectoryService;
+use OCA\OpenCatalogi\Exception\DirectoryUrlException;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Db\DoesNotExistException;
use OCP\AppFramework\Db\MultipleObjectsReturnedException;
@@ -186,13 +187,16 @@ public function add(): JSONResponse
// Get the URL parameter from the request
$url = $this->request->getParam('url');
- // Check if the URL parameter is provided
- if (empty($url) === true) {
- return new JSONResponse(['error' => 'URL parameter is required'], 400);
- }
-
// Add the new listing using the provided URL
- $result = $this->directoryService->syncExternalDirectory($url);
+ try {
+ $result = $this->directoryService->syncExternalDirectory($url);
+ } catch (DirectoryUrlException $exception) {
+ if($exception->getMessage() === 'URL is required') {
+ $exception->setMessage('Property "url" is required');
+ }
+
+ return new JSONResponse(data: ['message' => $exception->getMessage()], statusCode: 400);
+ }
// Return the result as a JSON response
return new JSONResponse(['success' => $result]);
diff --git a/lib/Controller/OrganizationsController.php b/lib/Controller/OrganizationsController.php
index e4865f42..9ee1f6c1 100644
--- a/lib/Controller/OrganizationsController.php
+++ b/lib/Controller/OrganizationsController.php
@@ -10,7 +10,7 @@
use OCP\AppFramework\Http\JSONResponse;
use OCP\IAppConfig;
use OCP\IRequest;
-
+use OCP\IURLGenerator;
/**
* Class OrganizationsController
*
@@ -26,13 +26,15 @@ class OrganizationsController extends Controller
* @param IAppConfig $config The app configuration
* @param OrganizationMapper $organizationMapper The organization mapper
* @param ObjectService $objectService The object service
+ * @param IURLGenerator $urlGenerator The URL generator
*/
public function __construct(
$appName,
IRequest $request,
private readonly IAppConfig $config,
private readonly OrganizationMapper $organizationMapper,
- private readonly ObjectService $objectService
+ private readonly ObjectService $objectService,
+ private readonly IURLGenerator $urlGenerator
)
{
parent::__construct($appName, $request);
@@ -109,6 +111,17 @@ public function create(): JSONResponse
// Save the new organization object
$object = $this->objectService->saveObject('organization', $data);
+ // If object is a class change it to array
+ if (is_object($object) === true) {
+ $object = $object->jsonSerialize();
+ }
+
+ // If we do not have an uri, we need to generate one
+ if (isset($object['uri']) === false) {
+ $object['uri'] = $this->urlGenerator->getAbsoluteURL($this->urlGenerator->linkToRoute('openCatalogi.organizations.show', ['id' => $object['id']]));
+ $object = $this->objectService->saveObject('organization', $object);
+ }
+
// Return the created object as a JSON response
return new JSONResponse($object);
}
@@ -130,6 +143,9 @@ public function update(string|int $id): JSONResponse
// Ensure the ID in the data matches the ID in the URL
$data['id'] = $id;
+ // If we do not have an uri, we need to generate one
+ $data['uri'] = $this->urlGenerator->getAbsoluteURL($this->urlGenerator->linkToRoute('openCatalogi.organizations.show', ['id' => $data['id']]));
+
// Save the updated organization object
$object = $this->objectService->saveObject('organization', $data);
diff --git a/lib/Controller/PagesController.php b/lib/Controller/PagesController.php
new file mode 100644
index 00000000..d10ef7d5
--- /dev/null
+++ b/lib/Controller/PagesController.php
@@ -0,0 +1,145 @@
+request->getParams();
+
+ // Fetch page objects based on filters and order
+ $data = $this->objectService->getResultArrayForRequest('page', $requestParams);
+
+ // Return JSON response
+ return new JSONResponse($data);
+ }
+
+ /**
+ * Retrieve a specific page by its ID.
+ *
+ * @param string|int $id The ID of the page to retrieve
+ * @return JSONResponse JSON response containing the requested page
+ *
+ * @NoAdminRequired
+ * @NoCSRFRequired
+ */
+ public function show(string|int $id): JSONResponse
+ {
+ // Fetch the page object by its ID
+ $object = $this->objectService->getObject('page', $id);
+
+ // Return the page as a JSON response
+ return new JSONResponse($object);
+ }
+
+ /**
+ * Create a new page.
+ *
+ * @return JSONResponse The response containing the created page object.
+ *
+ * @NoAdminRequired
+ * @NoCSRFRequired
+ */
+ public function create(): JSONResponse
+ {
+ // Get all parameters from the request
+ $data = $this->request->getParams();
+
+ // Remove the 'id' field if it exists, as we're creating a new object
+ unset($data['id']);
+
+ // Save the new page object
+ $object = $this->objectService->saveObject('page', $data);
+
+ // Return the created object as a JSON response
+ return new JSONResponse($object);
+ }
+
+ /**
+ * Update an existing page.
+ *
+ * @param string|int $id The ID of the page to update.
+ * @return JSONResponse The response containing the updated page object.
+ *
+ * @NoAdminRequired
+ * @NoCSRFRequired
+ */
+ public function update(string|int $id): JSONResponse
+ {
+ // Get all parameters from the request
+ $data = $this->request->getParams();
+
+ // Ensure the ID in the data matches the ID in the URL
+ $data['id'] = $id;
+
+ // Save the updated page object
+ $object = $this->objectService->saveObject('page', $data);
+
+ // Return the updated object as a JSON response
+ return new JSONResponse($object);
+ }
+
+ /**
+ * Delete a page.
+ *
+ * @param string|int $id The ID of the page to delete.
+ * @return JSONResponse The response indicating the result of the deletion.
+ *
+ * @NoAdminRequired
+ * @NoCSRFRequired
+ */
+ public function destroy(string|int $id): JSONResponse
+ {
+ // Delete the page object
+ $result = $this->objectService->deleteObject('page', $id);
+
+ // Return the result as a JSON response
+ return new JSONResponse(['success' => $result], $result === true ? '200' : '404');
+ }
+}
diff --git a/lib/Controller/PublicationTypesController.php b/lib/Controller/PublicationTypesController.php
index eba92db1..ee90e82f 100644
--- a/lib/Controller/PublicationTypesController.php
+++ b/lib/Controller/PublicationTypesController.php
@@ -11,7 +11,7 @@
use OCP\AppFramework\Http\JSONResponse;
use OCP\IAppConfig;
use OCP\IRequest;
-
+use OCP\IURLGenerator;
/**
* Class PublicationTypesController
*
@@ -29,6 +29,7 @@ class PublicationTypesController extends Controller
* @param ObjectService $objectService The object service
* @param DirectoryService $directoryService The directory service
* @param BroadcastService $broadcastService The broadcast service
+ * @param IURLGenerator $urlGenerator The URL generator
*/
public function __construct(
$appName,
@@ -37,7 +38,8 @@ public function __construct(
private readonly PublicationTypeMapper $publicationTypeMapper,
private readonly ObjectService $objectService,
private readonly DirectoryService $directoryService,
- private readonly BroadcastService $broadcastService
+ private readonly BroadcastService $broadcastService,
+ private readonly IURLGenerator $urlGenerator
)
{
parent::__construct($appName, $request);
@@ -118,6 +120,17 @@ public function create(): JSONResponse
// Save the new publication type object
$object = $this->objectService->saveObject('publicationType', $data);
+ // If object is a class change it to array
+ if (is_object($object) === true) {
+ $object = $object->jsonSerialize();
+ }
+
+ // If we do not have an uri, we need to generate one
+ if (isset($object['uri']) === false) {
+ $object['uri'] = $this->urlGenerator->getAbsoluteURL($this->urlGenerator->linkToRoute('openCatalogi.publicationTypes.show', ['id' => $object['id']]));
+ $object = $this->objectService->saveObject('publicationType', $object);
+ }
+
// Update all external directories
$this->broadcastService->broadcast();
@@ -142,6 +155,10 @@ public function update(string|int $id): JSONResponse
// Ensure the ID in the data matches the ID in the URL
$data['id'] = $id;
+ // If we do not have an uri, we need to generate one
+ $data['uri'] = $this->urlGenerator->getAbsoluteURL($this->urlGenerator->linkToRoute('openCatalogi.publicationTypes.show', ['id' => $data['id']]));
+
+
// Save the updated publication type object
$object = $this->objectService->saveObject('publicationType', $data);
@@ -169,7 +186,7 @@ public function destroy(string|int $id): JSONResponse
// Return the result as a JSON response
return new JSONResponse(['success' => $result], $result === true ? '200' : '404');
}
-
+
/**
* Synchronize or delete a publication type based on listing status
*
diff --git a/lib/Controller/PublicationsController.php b/lib/Controller/PublicationsController.php
index 47a1c8de..5a770793 100644
--- a/lib/Controller/PublicationsController.php
+++ b/lib/Controller/PublicationsController.php
@@ -24,6 +24,7 @@
use OCP\AppFramework\OCS\OCSNotFoundException;
use OCP\IAppConfig;
use OCP\IRequest;
+use OCP\IURLGenerator;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Symfony\Component\Uid\Uuid;
@@ -49,6 +50,8 @@ class PublicationsController extends Controller
* @param FileService $fileService The file service
* @param DownloadService $downloadService The download service
* @param ObjectService $objectService The object service
+ * @param IURLGenerator $urlGenerator The URL generator
+ *
*/
public function __construct
(
@@ -59,7 +62,8 @@ public function __construct
private readonly IAppConfig $config,
private readonly FileService $fileService,
private readonly DownloadService $downloadService,
- private ObjectService $objectService
+ private readonly ObjectService $objectService,
+ private readonly IURLGenerator $urlGenerator
)
{
parent::__construct($appName, $request);
@@ -197,6 +201,17 @@ public function create(ObjectService $objectService): JSONResponse
// Save the new publication object
$object = $this->objectService->saveObject('publication', $data);
+ // If object is a class change it to array
+ if (is_object($object) === true) {
+ $object = $object->jsonSerialize();
+ }
+
+ // If we do not have an uri, we need to generate one
+ if (isset($object['uri']) === false) {
+ $object['uri'] = $this->urlGenerator->getAbsoluteURL($this->urlGenerator->linkToRoute('openCatalogi.publications.show', ['id' => $object['id']]));
+ $object = $this->objectService->saveObject('publication', $object);
+ }
+
// Return the created object as a JSON response
return new JSONResponse($object);
}
@@ -221,6 +236,9 @@ public function update(string|int $id, ObjectService $objectService): JSONRespon
// Ensure the ID in the data matches the ID in the URL
$data['id'] = $id;
+ // If we do not have an uri, we need to generate one
+ $data['uri'] = $this->urlGenerator->getAbsoluteURL($this->urlGenerator->linkToRoute('openCatalogi.publications.show', ['id' => $data['id']]));
+
// Save the updated publication object
$object = $this->objectService->saveObject('publication', $data);
diff --git a/lib/Controller/SearchController.php b/lib/Controller/SearchController.php
index a911980e..b315d1d4 100644
--- a/lib/Controller/SearchController.php
+++ b/lib/Controller/SearchController.php
@@ -277,4 +277,73 @@ public function theme(string|int $themeId): JSONResponse
return new JSONResponse($object);
}
+ /**
+ * Return all pages.
+ *
+ * @CORS
+ * @PublicPage
+ * @NoAdminRequired
+ * @NoCSRFRequired
+ *
+ * @return JSONResponse The Response containing all pages.
+ */
+ public function pages(): JSONResponse
+ {
+ // Get all page objects with request parameters
+ $objects = $this->objectService->getResultArrayForRequest(objectType: 'page', requestParams: $this->request->getParams());
+
+ // Format dates for each result
+ $formattedResults = array_map(function($object) {
+ // Format created_at if it exists
+ if (isset($object['created_at'])) {
+ $created = new \DateTime($object['created_at']);
+ $object['created_at'] = $created->format('Y-m-d\TH:i:s.u\Z');
+ }
+ // Format updated_at if it exists
+ if (isset($object['updated_at'])) {
+ $updated = new \DateTime($object['updated_at']);
+ $object['updated_at'] = $updated->format('Y-m-d\TH:i:s.u\Z');
+ }
+ return $object;
+ }, $objects['results']);
+
+ // Prepare the response data with formatted dates
+ $data = [
+ 'data' => $formattedResults
+ ];
+
+ return new JSONResponse($data);
+ }
+
+ /**
+ * Return a specific page by slug.
+ *
+ * @CORS
+ * @PublicPage
+ * @NoAdminRequired
+ * @NoCSRFRequired
+ *
+ * @param string $pageSlug The slug of the page
+ * @return JSONResponse The Response containing the requested page
+ * @throws GuzzleException
+ */
+ public function page(string $pageSlug): JSONResponse
+ {
+ // Get the page object by slug
+ $object = $this->objectService->getObject('page', $pageSlug);
+
+ // Format the date fields to match required format
+ if (isset($object['created_at'])) {
+ $created = new \DateTime($object['created_at']);
+ $object['created_at'] = $created->format('Y-m-d\TH:i:s.u\Z');
+ }
+ if (isset($object['updated_at'])) {
+ $updated = new \DateTime($object['updated_at']);
+ $object['updated_at'] = $updated->format('Y-m-d\TH:i:s.u\Z');
+ }
+
+ return new JSONResponse($object);
+ }
+
+
}
diff --git a/lib/Controller/SettingsController.php b/lib/Controller/SettingsController.php
index 8da08328..5d975be3 100644
--- a/lib/Controller/SettingsController.php
+++ b/lib/Controller/SettingsController.php
@@ -45,7 +45,7 @@ public function index(): JSONResponse
{
// Initialize the data array
$data = [];
- $data['objectTypes'] = ['attachment', 'catalog', 'listing', 'publicationtype', 'organization', 'publication', 'theme'];
+ $data['objectTypes'] = ['attachment', 'catalog', 'listing', 'publicationtype', 'organization', 'publication', 'theme', 'page'];
$data['openRegisters'] = false;
$data['availableRegisters'] = [];
@@ -78,7 +78,10 @@ public function index(): JSONResponse
'publication_register' => '',
'theme_source' => 'internal',
'theme_schema' => '',
- 'theme_register' => ''
+ 'theme_register' => '',
+ 'page_source' => 'internal',
+ 'page_schema' => '',
+ 'page_register' => '',
];
// Get the current values for the object types from the configuration
diff --git a/lib/Controller/ThemesController.php b/lib/Controller/ThemesController.php
index 341d5292..b5233943 100644
--- a/lib/Controller/ThemesController.php
+++ b/lib/Controller/ThemesController.php
@@ -11,7 +11,7 @@
use OCP\AppFramework\Http\JSONResponse;
use OCP\IAppConfig;
use OCP\IRequest;
-
+use OCP\IURLGenerator;
/**
* Class ThemesController
*
@@ -27,6 +27,7 @@ class ThemesController extends Controller
* @param ThemeMapper $themeMapper The theme mapper for database operations
* @param IAppConfig $config The app configuration
* @param ObjectService $objectService The service for handling object operations
+ * @param IURLGenerator $urlGenerator The URL generator
*/
public function __construct
(
@@ -34,7 +35,8 @@ public function __construct
IRequest $request,
private readonly ThemeMapper $themeMapper,
private readonly IAppConfig $config,
- private readonly ObjectService $objectService
+ private readonly ObjectService $objectService,
+ private readonly IURLGenerator $urlGenerator
)
{
parent::__construct($appName, $request);
@@ -97,6 +99,17 @@ public function create(): JSONResponse
// Save the new theme object
$object = $this->objectService->saveObject('theme', $data);
+ // If object is a class change it to array
+ if (is_object($object) === true) {
+ $object = $object->jsonSerialize();
+ }
+
+ // If we do not have an uri, we need to generate one
+ if (isset($object['uri']) === false) {
+ $object['uri'] = $this->urlGenerator->getAbsoluteURL($$this->urlGenerator->linkToRoute('openCatalogi.themes.show', ['id' => $object['id']]));
+ $object = $this->objectService->saveObject('theme', $object);
+ }
+
// Return the created object as a JSON response
return new JSONResponse($object);
}
@@ -118,6 +131,9 @@ public function update(string|int $id): JSONResponse
// Ensure the ID in the data matches the ID in the URL
$data['id'] = $id;
+ // If we do not have an uri, we need to generate one
+ $data['uri'] = $this->urlGenerator->getAbsoluteURL($this->urlGenerator->linkToRoute('openCatalogi.themes.show', ['id' => $data['id']]));
+
// Save the updated theme object
$object = $this->objectService->saveObject('theme', $data);
diff --git a/lib/Db/Attachment.php b/lib/Db/Attachment.php
index dd55e64a..5aa72062 100644
--- a/lib/Db/Attachment.php
+++ b/lib/Db/Attachment.php
@@ -9,6 +9,7 @@
class Attachment extends Entity implements JsonSerializable
{
protected ?string $uuid = null;
+ protected ?string $uri = null;
protected ?string $version = '0.0.1';
protected ?string $reference = null;
protected ?string $title = null;
@@ -32,6 +33,7 @@ class Attachment extends Entity implements JsonSerializable
public function __construct() {
$this->addType(fieldName: 'uuid', type: 'string');
+ $this->addType(fieldName: 'uri', type: 'string');
$this->addType(fieldName: 'version', type: 'string');
$this->addType(fieldName: 'reference', type: 'string');
$this->addType(fieldName: 'title', type: 'string');
@@ -96,6 +98,7 @@ public function jsonSerialize(): array
$array = [
'id' => $this->id,
'uuid' => $this->uuid,
+ 'uri' => $this->uri,
'version' => $this->version,
'reference' => $this->reference,
'title' => $this->title,
diff --git a/lib/Db/AttachmentMapper.php b/lib/Db/AttachmentMapper.php
index f5eec661..9ecd4b8a 100644
--- a/lib/Db/AttachmentMapper.php
+++ b/lib/Db/AttachmentMapper.php
@@ -9,6 +9,7 @@
use OCP\AppFramework\Db\QBMapper;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\IDBConnection;
+use OCP\IURLGenerator;
use Symfony\Component\Uid\Uuid;
/**
@@ -25,8 +26,9 @@ class AttachmentMapper extends QBMapper
* Constructor for AttachmentMapper
*
* @param IDBConnection $db The database connection
+ * @param IURLGenerator $urlGenerator The URL generator
*/
- public function __construct(IDBConnection $db)
+ public function __construct(IDBConnection $db, IURLGenerator $urlGenerator)
{
parent::__construct($db, tableName: 'ocat_attachments');
}
@@ -105,6 +107,8 @@ public function findAll(int $limit = null, int $offset = null, array $filters =
public function createFromArray(array $object): Attachment
{
$attachment = new Attachment();
+
+ // Hydrate the attachment with the new data
$attachment->hydrate(object: $object);
// Set uuid if not provided
@@ -112,6 +116,9 @@ public function createFromArray(array $object): Attachment
$attachment->setUuid(Uuid::v4());
}
+ // Set the uri
+ $attachment->setUri($this->urlGenerator->getAbsoluteURL($this->urlGenerator->linkToRoute('opencatalogi.attachments.show', ['id' => $attachment->getUuid()])));
+
return $this->insert(entity: $attachment);
}
@@ -126,7 +133,7 @@ public function createFromArray(array $object): Attachment
* @throws DoesNotExistException If the entity is not found
* @throws MultipleObjectsReturnedException|\OCP\DB\Exception If multiple entities are found
*/
- public function updateFromArray(int $id, array $object, bool $updateVersion = true): Attachment
+ public function updateFromArray(int $id, array $object, bool $updateVersion = true, bool $patch = false): Attachment
{
$attachment = $this->find($id);
// Fallback to create if the attachment does not exist
@@ -135,8 +142,12 @@ public function updateFromArray(int $id, array $object, bool $updateVersion = tr
return $this->createFromArray($object);
}
+ // Hydrate the attachment with the new data
$attachment->hydrate($object);
+ // Set the uri
+ $attachment->setUri($this->urlGenerator->getAbsoluteURL($this->urlGenerator->linkToRoute('opencatalogi.attachments.show', ['id' => $attachment->getUuid()])));
+
if ($updateVersion === true) {
// Update the version
$version = explode('.', $attachment->getVersion());
diff --git a/lib/Db/Catalog.php b/lib/Db/Catalog.php
index bc24ca9d..20aed8db 100644
--- a/lib/Db/Catalog.php
+++ b/lib/Db/Catalog.php
@@ -9,6 +9,7 @@
class Catalog extends Entity implements JsonSerializable
{
protected ?string $uuid = null;
+ protected ?string $uri = null;
protected ?string $version = '0.0.1';
protected ?string $title = null;
protected ?string $summary = null;
@@ -23,6 +24,7 @@ class Catalog extends Entity implements JsonSerializable
public function __construct() {
$this->addType(fieldName: 'uuid', type: 'string');
+ $this->addType(fieldName: 'uri', type: 'string');
$this->addType(fieldName: 'version', type: 'string');
$this->addType(fieldName: 'title', type: 'string');
$this->addType(fieldName: 'summary', type: 'string');
@@ -78,6 +80,7 @@ public function jsonSerialize(): array
{
$array = [
'id' => $this->id,
+ 'uri' => $this->uri,
'uuid' => $this->uuid,
'version' => $this->version,
'title' => $this->title,
diff --git a/lib/Db/CatalogMapper.php b/lib/Db/CatalogMapper.php
index 825597de..6e924fe0 100644
--- a/lib/Db/CatalogMapper.php
+++ b/lib/Db/CatalogMapper.php
@@ -7,6 +7,7 @@
use OCP\AppFramework\Db\QBMapper;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\IDBConnection;
+use OCP\IURLGenerator;
use Symfony\Component\Uid\Uuid;
/**
@@ -23,8 +24,9 @@ class CatalogMapper extends QBMapper
* Constructor for CatalogMapper
*
* @param IDBConnection $db The database connection
+ * @param IURLGenerator $urlGenerator The URL generator
*/
- public function __construct(IDBConnection $db)
+ public function __construct(IDBConnection $db, IURLGenerator $urlGenerator)
{
parent::__construct($db, tableName: 'ocat_catalogi');
}
@@ -128,6 +130,9 @@ public function createFromArray(array $object): Catalog
$catalog->setUuid(Uuid::v4());
}
+ // Set the uri
+ $catalog->setUri($this->urlGenerator->getAbsoluteURL($this->urlGenerator->linkToRoute('opencatalogi.catalogs.show', ['id' => $catalog->getUuid()])));
+
return $this->insert(entity: $catalog);
}
@@ -140,17 +145,21 @@ public function createFromArray(array $object): Catalog
*
* @return Catalog The updated Catalog entity
*/
- public function updateFromArray(int $id, array $object, bool $updateVersion = true): Catalog
+ public function updateFromArray(int $id, array $object, bool $updateVersion = true, bool $patch = false): Catalog
{
- $catalog = $this->find($id);
+ $catalog = $this->find($id);
// Fallback to create if the catalog does not exist
if ($catalog === null) {
$object['uuid'] = $id;
return $this->createFromArray($object);
}
+ // Hydrate the catalog with the new data
$catalog->hydrate($object);
+ // Set the uri
+ $catalog->setUri($this->urlGenerator->getAbsoluteURL($this->urlGenerator->linkToRoute('opencatalogi.catalogs.show', ['id' => $catalog->getUuid()])));
+
if ($updateVersion === true) {
// Update the version
$version = explode('.', $catalog->getVersion());
diff --git a/lib/Db/ListingMapper.php b/lib/Db/ListingMapper.php
index 21a4300d..05ddab36 100644
--- a/lib/Db/ListingMapper.php
+++ b/lib/Db/ListingMapper.php
@@ -234,7 +234,7 @@ public function createFromArray(array $object): Listing
* @return Listing The updated Listing entity
* @throws Exception
*/
- public function updateFromArray(int|string $id, array $object, bool $updateVersion = true): Listing
+ public function updateFromArray(int|string $id, array $object, bool $updateVersion = true, bool $patch = false): Listing
{
$listing = $this->find($id);
// Fallback to create if the listing does not exist
diff --git a/lib/Db/Organization.php b/lib/Db/Organization.php
index 8d227c7a..9e46735e 100644
--- a/lib/Db/Organization.php
+++ b/lib/Db/Organization.php
@@ -9,6 +9,7 @@
class Organization extends Entity implements JsonSerializable
{
protected ?string $uuid = null;
+ protected ?string $uri = null;
protected ?string $version = '0.0.1';
protected ?string $title = null;
protected ?string $summary = null;
@@ -24,6 +25,7 @@ class Organization extends Entity implements JsonSerializable
public function __construct() {
$this->addType(fieldName: 'uuid', type: 'string');
+ $this->addType(fieldName: 'uri', type: 'string');
$this->addType(fieldName: 'version', type: 'string');
$this->addType(fieldName: 'title', type: 'string');
$this->addType(fieldName: 'summary', type: 'string');
@@ -79,6 +81,7 @@ public function jsonSerialize(): array
$array = [
'id' => $this->id,
'uuid' => $this->uuid,
+ 'uri' => $this->uri,
'version' => $this->version,
'title' => $this->title,
'summary' => $this->summary,
diff --git a/lib/Db/OrganizationMapper.php b/lib/Db/OrganizationMapper.php
index c83a859c..ee2b638b 100644
--- a/lib/Db/OrganizationMapper.php
+++ b/lib/Db/OrganizationMapper.php
@@ -7,6 +7,7 @@
use OCP\AppFramework\Db\QBMapper;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\IDBConnection;
+use OCP\IURLGenerator;
use Symfony\Component\Uid\Uuid;
/**
@@ -23,6 +24,7 @@ class OrganizationMapper extends QBMapper
* Constructor for OrganizationMapper
*
* @param IDBConnection $db The database connection
+ * @param IURLGenerator $urlGenerator The URL generator
*/
public function __construct(IDBConnection $db)
{
@@ -123,6 +125,10 @@ public function createFromArray(array $object): Organization
if ($organization->getUuid() === null) {
$organization->setUuid(Uuid::v4());
}
+
+ // Set the uri
+ $organization->setUri($this->urlGenerator->getAbsoluteURL($this->urlGenerator->linkToRoute('opencatalogi.organizations.show', ['id' => $organization->getUuid()])));
+
return $this->insert(entity: $organization);
}
@@ -135,7 +141,7 @@ public function createFromArray(array $object): Organization
*
* @return Organization The updated Organization entity
*/
- public function updateFromArray(int $id, array $object, bool $updateVersion = true): Organization
+ public function updateFromArray(int $id, array $object, bool $updateVersion = true, bool $patch = false): Organization
{
$organization = $this->find($id);
// Fallback to create if the organization does not exist
@@ -144,8 +150,12 @@ public function updateFromArray(int $id, array $object, bool $updateVersion = tr
return $this->createFromArray($object);
}
+ // Hydrate the organization with the new data
$organization->hydrate($object);
+ // Set the uri
+ $organization->setUri($this->urlGenerator->getAbsoluteURL($this->urlGenerator->linkToRoute('opencatalogi.organizations.show', ['id' => $organization->getUuid()])));
+
if ($updateVersion === true) {
// Update the version
$version = explode('.', $organization->getVersion());
diff --git a/lib/Db/Page.php b/lib/Db/Page.php
new file mode 100644
index 00000000..6ccc281f
--- /dev/null
+++ b/lib/Db/Page.php
@@ -0,0 +1,115 @@
+addType(fieldName: 'uuid', type: 'string');
+ $this->addType(fieldName: 'version', type: 'string');
+ $this->addType(fieldName: 'name', type: 'string');
+ $this->addType(fieldName: 'slug', type: 'string');
+ $this->addType(fieldName: 'contents', type: 'json');
+ $this->addType(fieldName: 'created', type: 'datetime');
+ $this->addType(fieldName: 'updated', type: 'datetime');
+ }
+
+ /**
+ * Get array of JSON field names
+ *
+ * @return array List of field names that are JSON type
+ */
+ public function getJsonFields(): array
+ {
+ return array_keys(
+ array_filter($this->getFieldTypes(), function ($field) {
+ return $field === 'json';
+ })
+ );
+ }
+
+ /**
+ * Hydrate the entity from an array of data
+ *
+ * @param array $object Data to hydrate from
+ * @return self
+ */
+ public function hydrate(array $object): self
+ {
+ $jsonFields = $this->getJsonFields();
+
+ // Remove any fields that start with an underscore
+ // These are typically internal fields that shouldn't be updated directly
+ foreach ($object as $key => $value) {
+ if (str_starts_with($key, '_')) {
+ unset($object[$key]);
+ }
+ }
+
+ foreach ($object as $key => $value) {
+ if (in_array($key, $jsonFields) === true && $value === []) {
+ $value = null;
+ }
+
+ $method = 'set'.ucfirst($key);
+
+ try {
+ $this->$method($value);
+ } catch (\Exception $exception) {
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * Serialize the entity to JSON
+ *
+ * @return array Serialized data
+ */
+ public function jsonSerialize(): array
+ {
+ $array = [
+ 'id' => $this->id,
+ 'uuid' => $this->uuid,
+ 'version' => $this->version,
+ 'name' => $this->name,
+ 'slug' => $this->slug,
+ 'contents' => $this->contents,
+ 'created_at' => $this->created?->format('c'),
+ 'updated_at' => $this->updated?->format('c')
+ ];
+
+ $jsonFields = $this->getJsonFields();
+
+ foreach ($array as $key => $value) {
+ if (in_array($key, $jsonFields) === true && $value === null) {
+ $array[$key] = [];
+ }
+ }
+
+ return $array;
+ }
+}
diff --git a/lib/Db/PageMapper.php b/lib/Db/PageMapper.php
new file mode 100644
index 00000000..b3793c01
--- /dev/null
+++ b/lib/Db/PageMapper.php
@@ -0,0 +1,151 @@
+db->getQueryBuilder();
+
+ $qb->select('*')
+ ->from('ocat_pages')
+ ->where($qb->expr()->orX(
+ $qb->expr()->eq('id', $qb->createNamedParameter($id, IQueryBuilder::PARAM_INT)),
+ $qb->expr()->eq('uuid', $qb->createNamedParameter($id, IQueryBuilder::PARAM_STR)),
+ $qb->expr()->eq('slug', $qb->createNamedParameter($id, IQueryBuilder::PARAM_STR))
+ ));
+
+ try {
+ return $this->findEntity($qb);
+ } catch (\OCP\AppFramework\Db\DoesNotExistException $e) {
+ return null;
+ }
+ }
+
+ /**
+ * Find multiple Pages by their IDs or UUIDs
+ *
+ * @param array $ids An array of IDs or UUIDs
+ * @return array An array of found Page entities
+ */
+ public function findMultiple(array $ids): array
+ {
+ $qb = $this->db->getQueryBuilder();
+
+ $qb->select('*')
+ ->from('ocat_pages')
+ ->where($qb->expr()->orX(
+ $qb->expr()->in('id', $qb->createNamedParameter($ids, IQueryBuilder::PARAM_INT_ARRAY)),
+ $qb->expr()->in('uuid', $qb->createNamedParameter($ids, IQueryBuilder::PARAM_STR_ARRAY))
+ ));
+
+ return $this->findEntities(query: $qb);
+ }
+
+ /**
+ * Find all Pages with optional limit and offset
+ *
+ * @param int|null $limit Maximum number of results to return
+ * @param int|null $offset Number of results to skip
+ * @return array An array of all found Page entities
+ */
+ public function findAll(int $limit = null, int $offset = null, array $filters = [], array $sort = [], ?string $search = null): array
+ {
+ $qb = $this->db->getQueryBuilder();
+
+ $qb->select('*')
+ ->from('ocat_pages')
+ ->setMaxResults($limit)
+ ->setFirstResult($offset);
+
+ return $this->findEntities(query: $qb);
+ }
+
+ /**
+ * Create a new Page from an array of data
+ *
+ * @param array $object An array of Page data
+ * @return Page The newly created Page entity
+ */
+ public function createFromArray(array $object): Page
+ {
+ $page = new Page();
+ $page->hydrate(object: $object);
+
+ // Set uuid if not provided
+ if ($page->getUuid() === null) {
+ $page->setUuid(Uuid::v4());
+ }
+
+ // Generate slug from name if not provided
+ if ($page->getSlug() === null && $page->getName() !== null) {
+ // Convert to lowercase and replace spaces with dashes
+ $slug = strtolower($page->getName());
+ $slug = preg_replace('/[^a-z0-9-]/', '-', $slug);
+ $slug = preg_replace('/-+/', '-', $slug);
+ $slug = trim($slug, '-');
+ $page->setSlug($slug);
+ }
+
+ return $this->insert(entity: $page);
+ }
+
+ /**
+ * Update an existing Page from an array of data
+ *
+ * @param int $id The ID of the Page to update
+ * @param array $object An array of updated Page data
+ * @return Page The updated Page entity
+ * @throws DoesNotExistException If the entity is not found
+ * @throws MultipleObjectsReturnedException|\OCP\DB\Exception If multiple entities are found
+ */
+ public function updateFromArray(int $id, array $object): Page
+ {
+ $page = $this->find($id);
+ // Fallback to create if the page does not exist
+ if ($page === null) {
+ $object['uuid'] = $id;
+ return $this->createFromArray($object);
+ }
+
+ $page->hydrate($object);
+
+ return $this->update($page);
+ }
+}
diff --git a/lib/Db/Publication.php b/lib/Db/Publication.php
index 4b3d3e49..f82a17ec 100644
--- a/lib/Db/Publication.php
+++ b/lib/Db/Publication.php
@@ -9,6 +9,7 @@
class Publication extends Entity implements JsonSerializable
{
protected ?string $uuid = null;
+ protected ?string $uri = null;
protected ?string $version = '0.0.1';
protected ?string $title = null;
protected ?string $reference = null;
@@ -39,6 +40,7 @@ class Publication extends Entity implements JsonSerializable
public function __construct() {
$this->addType(fieldName: 'uuid', type: 'string');
+ $this->addType(fieldName: 'uri', type: 'string');
$this->addType(fieldName: 'version', type: 'string');
$this->addType(fieldName: 'title', type: 'string');
$this->addType(fieldName: 'reference', type: 'string');
@@ -125,6 +127,7 @@ public function jsonSerialize(): array
{
$array = [
'id' => $this->id,
+ 'uri' => $this->uri,
'uuid' => $this->uuid,
'version' => $this->version,
'title' => $this->title,
diff --git a/lib/Db/PublicationMapper.php b/lib/Db/PublicationMapper.php
index c3ced4f0..47c48d2f 100644
--- a/lib/Db/PublicationMapper.php
+++ b/lib/Db/PublicationMapper.php
@@ -8,6 +8,7 @@
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\DB\Types;
use OCP\IDBConnection;
+use OCP\IURLGenerator;
use Symfony\Component\Uid\Uuid;
/**
@@ -24,8 +25,9 @@ class PublicationMapper extends QBMapper
* Constructor for PublicationMapper
*
* @param IDBConnection $db The database connection
+ * @param IURLGenerator $urlGenerator The URL generator
*/
- public function __construct(IDBConnection $db)
+ public function __construct(IDBConnection $db, IURLGenerator $urlGenerator)
{
parent::__construct($db, tableName: 'ocat_publications');
}
@@ -212,6 +214,9 @@ public function createFromArray(array $object): Publication
$publication->setUuid(Uuid::v4());
}
+ // Set the uri
+ $publication->setUri($this->urlGenerator->getAbsoluteURL($this->urlGenerator->linkToRoute('opencatalogi.publications.show', ['id' => $publication->getUuid()])));
+
return $this->insert(entity: $publication);
}
@@ -224,7 +229,7 @@ public function createFromArray(array $object): Publication
*
* @return Publication The updated Publication entity
*/
- public function updateFromArray(int $id, array $object, bool $updateVersion = true): Publication
+ public function updateFromArray(int $id, array $object, bool $updateVersion = true, bool $patch = false): Publication
{
$publication = $this->find(id: $id);
// Fallback to create if the publication does not exist
@@ -233,8 +238,12 @@ public function updateFromArray(int $id, array $object, bool $updateVersion = tr
return $this->createFromArray($object);
}
+ // Hydrate the publication with the new data
$publication->hydrate(object: $object);
+ // Set the uri
+ $publication->setUri($this->urlGenerator->getAbsoluteURL($this->urlGenerator->linkToRoute('opencatalogi.publications.show', ['id' => $publication->getUuid()])));
+
if ($updateVersion === true) {
// Update the version
$version = explode('.', $publication->getVersion());
diff --git a/lib/Db/PublicationType.php b/lib/Db/PublicationType.php
index 510a1e6c..dd729b82 100644
--- a/lib/Db/PublicationType.php
+++ b/lib/Db/PublicationType.php
@@ -5,23 +5,26 @@
use DateTime;
use JsonSerializable;
use OCP\AppFramework\Db\Entity;
+use OCP\IURLGenerator;
class PublicationType extends Entity implements JsonSerializable
{
protected ?string $uuid = null;
+ protected ?string $uri = null;
protected ?string $version = '0.0.1';
protected ?string $title = null;
protected ?string $description = null;
- protected ?array $required = null;
- protected ?array $properties = null;
+ protected ?array $required = [];
+ protected ?array $properties = [];
protected ?string $source = null;
protected ?string $summary = null;
- protected ?array $archive = null;
+ protected ?array $archive = [];
protected ?DateTime $updated = null;
protected ?DateTime $created = null;
public function __construct() {
$this->addType(fieldName: 'uuid', type: 'string');
+ $this->addType(fieldName: 'uri', type: 'string');
$this->addType(fieldName: 'version', type: 'string');
$this->addType(fieldName: 'title', type: 'string');
$this->addType(fieldName: 'description', type: 'string');
@@ -47,14 +50,6 @@ public function hydrate(array $object): self
{
$jsonFields = $this->getJsonFields();
- // Remove any fields that start with an underscore
- // These are typically internal fields that shouldn't be updated directly
- foreach ($object as $key => $value) {
- if (str_starts_with($key, '_')) {
- unset($object[$key]);
- }
- }
-
foreach ($object as $key => $value) {
if (in_array($key, $jsonFields) === true && $value === []) {
$value = null;
@@ -72,38 +67,34 @@ public function hydrate(array $object): self
return $this;
}
+ /**
+ * Serializes the schema to an array
+ *
+ * @return array
+ */
public function jsonSerialize(): array
{
- $properties = [];
- foreach ($this->properties ?? [] as $key => $property) {
- $properties[$key] = $property;
- if (isset($property['type']) === false) {
- $properties[$key] = $property;
- continue;
- }
- switch ($property['format'] ?? '') {
- case 'string':
- case 'array':
- $properties[$key]['default'] = (string) ($property['default'] ?? '');
- break;
- case 'int':
- case 'integer':
- case 'number':
- $properties[$key]['default'] = (int) ($property['default'] ?? 0);
- break;
- case 'bool':
- $properties[$key]['default'] = (bool) ($property['default'] ?? false);
- break;
+ $required = $this->required ?? [];
+ $properties = [];
+ if (isset($this->properties) === true) {
+ foreach ($this->properties as $key => $property) {
+ $title = $property['title'] ?? $key;
+ if ($property['required'] === true && in_array($title, $required) === false) {
+ $required[] = $title;
+ }
+
+ $properties[$title] = $property;
}
}
$array = [
'id' => $this->id,
+ 'uri' => $this->uri,
'uuid' => $this->uuid,
'version' => $this->version,
'title' => $this->title,
'description' => $this->description,
- 'required' => $this->required,
+ 'required' => $required,
'properties' => $properties,
'source' => $this->source,
'summary' => $this->summary,
@@ -122,4 +113,38 @@ public function jsonSerialize(): array
return $array;
}
+
+ /**
+ * Generate a JSON-Schema definition for the data field of a publication.
+ *
+ * @param IURLGenerator $urlGenerator An URL generator to generate the identifier of the schema.
+ *
+ * @return object The JSON-Schema object defining the data field of a publication.
+ */
+ public function getSchemaObject(IURLGenerator $urlGenerator): object
+ {
+ $data = $this->jsonSerialize();
+ unset($data['id'], $data['uuid'], $data['summary'], $data['archive'], $data['source'],
+ $data['updated'], $data['created']);
+
+ $data['type'] = 'object';
+
+ // required on properties will break the validator, only have it set on object level
+ // array_filter is used so that empty string or 0 validation rules are removed, so we dont validate what we didnt set
+ if (isset($data['properties']) === true && empty($data['properties']) === false) {
+ foreach ($data['properties'] as $key => $property) {
+ $title = $property['title'] ?? $key;
+ $data['properties'][$title] = array_filter($property);
+ if (array_key_exists('required', $data['properties'][$key])) {
+ unset($data['properties'][$key]['required']);
+ }
+ }
+ }
+
+ // Validator needs this specific $schema
+ $data['$schema'] = 'https://json-schema.org/draft/2020-12/schema';
+ $data['$id'] = $urlGenerator->getAbsoluteURL($urlGenerator->linkToRoute('opencatalogi.publication_types.show', ['id' => $this->getUuid()]));
+
+ return json_decode(json_encode($data));
+ }
}
diff --git a/lib/Db/PublicationTypeMapper.php b/lib/Db/PublicationTypeMapper.php
index 7a2cc336..1e1e5fc4 100644
--- a/lib/Db/PublicationTypeMapper.php
+++ b/lib/Db/PublicationTypeMapper.php
@@ -9,6 +9,7 @@
use OCP\AppFramework\Db\QBMapper;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\IDBConnection;
+use OCP\IURLGenerator;
use Symfony\Component\Uid\Uuid;
/**
@@ -23,8 +24,9 @@ class PublicationTypeMapper extends QBMapper
* Constructor for PublicationTypeMapper
*
* @param IDBConnection $db The database connection
+ * @param IURLGenerator $urlGenerator The URL generator
*/
- public function __construct(IDBConnection $db)
+ public function __construct(IDBConnection $db, IURLGenerator $urlGenerator)
{
parent::__construct($db, tableName: 'ocat_publication_types');
}
@@ -141,6 +143,10 @@ public function createFromArray(array $object): PublicationType
if ($publicationType->getUuid() === null) {
$publicationType->setUuid(Uuid::v4());
}
+
+ // Set the uri
+ $publicationType->setUri($this->urlGenerator->getAbsoluteURL($this->urlGenerator->linkToRoute('opencatalogi.publication_types.show', ['id' => $publicationType->getUuid()])));
+
return $this->insert(entity: $publicationType);
}
@@ -155,7 +161,7 @@ public function createFromArray(array $object): PublicationType
* @throws DoesNotExistException If the entity is not found
* @throws MultipleObjectsReturnedException|\OCP\DB\Exception If multiple entities are found
*/
- public function updateFromArray(int $id, array $object, bool $updateVersion = true): PublicationType
+ public function updateFromArray(int $id, array $object, bool $updateVersion = true, bool $patch = false): PublicationType
{
$publicationType = $this->find($id);
// Fallback to create if the publication type does not exist
@@ -164,8 +170,12 @@ public function updateFromArray(int $id, array $object, bool $updateVersion = tr
return $this->createFromArray($object);
}
+ // Hydrate the publication type with the new data
$publicationType->hydrate($object);
+ // Set the uri
+ $publicationType->setUri($this->urlGenerator->getAbsoluteURL($this->urlGenerator->linkToRoute('opencatalogi.publication_types.show', ['id' => $publicationType->getUuid()])));
+
if ($updateVersion === true) {
// Update the version
$version = explode('.', $publicationType->getVersion());
diff --git a/lib/Db/Theme.php b/lib/Db/Theme.php
index 881fdf95..8451c217 100644
--- a/lib/Db/Theme.php
+++ b/lib/Db/Theme.php
@@ -9,6 +9,7 @@
class Theme extends Entity implements JsonSerializable
{
protected ?string $uuid = null;
+ protected ?string $uri = null;
protected ?string $version = '0.0.1';
protected ?string $title = null;
protected ?string $summary = null;
@@ -19,6 +20,7 @@ class Theme extends Entity implements JsonSerializable
public function __construct() {
$this->addType(fieldName: 'uuid', type: 'string');
+ $this->addType(fieldName: 'uri', type: 'string');
$this->addType(fieldName: 'version', type: 'string');
$this->addType(fieldName: 'title', type: 'string');
$this->addType(fieldName: 'summary', type: 'string');
@@ -70,6 +72,7 @@ public function jsonSerialize(): array
{
$array = [
'id' => $this->id,
+ 'uri' => $this->uri,
'uuid' => $this->uuid,
'version' => $this->version,
'title' => $this->title,
diff --git a/lib/Db/ThemeMapper.php b/lib/Db/ThemeMapper.php
index 272159ea..d1aa975a 100644
--- a/lib/Db/ThemeMapper.php
+++ b/lib/Db/ThemeMapper.php
@@ -9,6 +9,7 @@
use OCP\AppFramework\Db\QBMapper;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\IDBConnection;
+use OCP\IURLGenerator;
use Symfony\Component\Uid\Uuid;
/**
@@ -25,8 +26,9 @@ class ThemeMapper extends QBMapper
* Constructor for ThemeMapper
*
* @param IDBConnection $db The database connection
+ * @param IURLGenerator $urlGenerator The URL generator
*/
- public function __construct(IDBConnection $db)
+ public function __construct(IDBConnection $db, IURLGenerator $urlGenerator)
{
parent::__construct($db, tableName: 'ocat_themes');
}
@@ -127,6 +129,10 @@ public function createFromArray(array $object): Theme
if ($theme->getUuid() === null) {
$theme->setUuid(Uuid::v4());
}
+
+ // Set the uri
+ $theme->setUri($this->urlGenerator->getAbsoluteURL($this->urlGenerator->linkToRoute('opencatalogi.themes.show', ['id' => $theme->getUuid()])));
+
return $this->insert(entity: $theme);
}
@@ -141,7 +147,7 @@ public function createFromArray(array $object): Theme
* @throws DoesNotExistException If the entity is not found
* @throws MultipleObjectsReturnedException|\OCP\DB\Exception If multiple entities are found
*/
- public function updateFromArray(int $id, array $object, bool $updateVersion = true): Theme
+ public function updateFromArray(int $id, array $object, bool $updateVersion = true, bool $patch = false): Theme
{
$theme = $this->find($id);
// Fallback to create if the theme does not exist
@@ -150,7 +156,12 @@ public function updateFromArray(int $id, array $object, bool $updateVersion = tr
return $this->createFromArray($object);
}
+ // Hydrate the theme with the new data
$theme->hydrate($object);
+
+ // Set the uri
+ $theme->setUri($this->urlGenerator->getAbsoluteURL($this->urlGenerator->linkToRoute('opencatalogi.themes.show', ['id' => $theme->getUuid()])));
+
if ($updateVersion === true) {
// Update the version
diff --git a/lib/Exception/DirectoryUrlException.php b/lib/Exception/DirectoryUrlException.php
new file mode 100644
index 00000000..07e636d0
--- /dev/null
+++ b/lib/Exception/DirectoryUrlException.php
@@ -0,0 +1,14 @@
+message = $message;
+ }
+
+}
diff --git a/lib/Migration/Version6Date20241129151236.php b/lib/Migration/Version6Date20241129151236.php
new file mode 100644
index 00000000..16192cb8
--- /dev/null
+++ b/lib/Migration/Version6Date20241129151236.php
@@ -0,0 +1,74 @@
+hasTable(tableName: 'ocat_pages') === false) {
+ $table = $schema->createTable(tableName: 'ocat_pages');
+
+ // Primary key and identifier columns
+ $table->addColumn(name: 'id', typeName: Types::BIGINT, options: ['autoincrement' => true, 'notnull' => true, 'length' => 4]);
+ $table->addColumn(name: 'uuid', typeName: Types::STRING, options: ['notnull' => true, 'length' => 255]);
+ $table->addColumn(name: 'version', typeName: Types::STRING, options: ['notnull' => true, 'length' => 255, 'default' => '0.0.1']);
+
+ // Meta columns
+ $table->addColumn(name: 'name', typeName: Types::STRING, options: ['notnull' => true, 'length' => 255]);
+ $table->addColumn(name: 'slug', typeName: Types::STRING, options: ['notnull' => true, 'length' => 255]);
+ $table->addColumn(name: 'contents', typeName: Types::JSON, options: ['notnull' => false]);
+ $table->addColumn(name: 'updated', typeName: Types::DATETIME, options: ['notnull' => true, 'default' => 'CURRENT_TIMESTAMP']);
+ $table->addColumn(name: 'created', typeName: Types::DATETIME, options: ['notnull' => true, 'default' => 'CURRENT_TIMESTAMP']);
+
+ // Keys and indexes
+ $table->setPrimaryKey(columnNames: ['id']);
+ $table->addIndex(['uuid'], 'ocat_pages_uuid_index');
+ $table->addIndex(['slug'], 'ocat_pages_slug_index');
+ }
+
+ return $schema;
+ }
+
+ /**
+ * @param IOutput $output
+ * @param Closure(): ISchemaWrapper $schemaClosure
+ * @param array $options
+ */
+ public function postSchemaChange(IOutput $output, Closure $schemaClosure, array $options): void {
+ }
+}
diff --git a/lib/Migration/Version6Date20241208222530.php b/lib/Migration/Version6Date20241208222530.php
new file mode 100644
index 00000000..33ecb6c5
--- /dev/null
+++ b/lib/Migration/Version6Date20241208222530.php
@@ -0,0 +1,139 @@
+getTable('ocat_attachments');
+ if ($table->hasColumn('uri') === false) {
+ $table->addColumn(
+ name: 'uri',
+ typeName: Types::STRING,
+ options: [
+ 'notnull' => true,
+ 'length' => 255
+ ]
+ )->setDefault('');
+ }
+
+ // Update catalogi table
+ $table = $schema->getTable('ocat_catalogi');
+ if ($table->hasColumn('uri') === false) {
+ $table->addColumn(
+ name: 'uri',
+ typeName: Types::STRING,
+ options: [
+ 'notnull' => true,
+ 'length' => 255
+ ]
+ )->setDefault('');
+ }
+
+ // Update organizations table
+ $table = $schema->getTable('ocat_organizations');
+ if ($table->hasColumn('uri') === false) {
+ $table->addColumn(
+ name: 'uri',
+ typeName: Types::STRING,
+ options: [
+ 'notnull' => true,
+ 'length' => 255
+ ]
+ )->setDefault('');
+ }
+
+ // Update publications table
+ $table = $schema->getTable('ocat_publications');
+ if ($table->hasColumn('uri') === false) {
+ $table->addColumn(
+ name: 'uri',
+ typeName: Types::STRING,
+ options: [
+ 'notnull' => true,
+ 'length' => 255
+ ]
+ )->setDefault('');
+ }
+
+ // Update publication types table
+ $table = $schema->getTable('ocat_publication_types');
+ if ($table->hasColumn('uri') === false) {
+ $table->addColumn(
+ name: 'uri',
+ typeName: Types::STRING,
+ options: [
+ 'notnull' => true,
+ 'length' => 255
+ ]
+ )->setDefault('');
+ if (!$table->hasIndex('ocat_publication_uuid_index')) {
+ $table->addIndex(['uuid'], 'ocat_publication_uuid_index');
+ }
+ }
+
+ // Update themes table
+ $table = $schema->getTable('ocat_themes');
+ if ($table->hasColumn('uri') === false) {
+ $table->addColumn(
+ name: 'uri',
+ typeName: Types::STRING,
+ options: [
+ 'notnull' => true,
+ 'length' => 255
+ ]
+ )->setDefault('');
+ if (!$table->hasIndex('ocat_themes_uuid_index')) {
+ $table->addIndex(['uuid'], 'ocat_themes_uuid_index');
+ }
+ }
+
+ return $schema;
+ }
+
+ /**
+ * @param IOutput $output
+ * @param Closure(): ISchemaWrapper $schemaClosure
+ * @param array $options
+ */
+ public function postSchemaChange(IOutput $output, Closure $schemaClosure, array $options): void {
+ }
+}
diff --git a/lib/Service/BroadcastService.php b/lib/Service/BroadcastService.php
index f39b6f29..95b3ac11 100644
--- a/lib/Service/BroadcastService.php
+++ b/lib/Service/BroadcastService.php
@@ -15,7 +15,10 @@
use OCA\OpenCatalogi\Service\ObjectService;
/**
- * Service class for handling directory-related operations
+ * Service for broadcasting this OpenCatalogi directory to other instances.
+ *
+ * Provides functionality to notify external instances about this directory
+ * through HTTP POST requests, either to a specific URL or to all known directories.
*/
class BroadcastService
{
@@ -73,10 +76,10 @@ public function broadcast(?string $url = null): void {
'directory' => $directoryUrl
]
]);
-
+
// Log successful broadcast
\OC::$server->getLogger()->info('Successfully broadcasted to ' . $hook);
-
+
} catch (\Exception $e) {
// Throw a warning since broadcasting failure shouldn't break the application flow
// but we still want to notify about the issue
diff --git a/lib/Service/DirectoryService.php b/lib/Service/DirectoryService.php
index fa3833a6..05521f3f 100644
--- a/lib/Service/DirectoryService.php
+++ b/lib/Service/DirectoryService.php
@@ -4,22 +4,32 @@
use DateTime;
use GuzzleHttp\Client;
+use GuzzleHttp\Exception\ClientException;
use GuzzleHttp\Exception\GuzzleException;
+use GuzzleHttp\Exception\RequestException;
+use GuzzleHttp\Exception\ServerException;
use OCA\OpenCatalogi\Db\Catalog;
use OCA\OpenCatalogi\Db\CatalogMapper;
use OCA\OpenCatalogi\Db\Listing;
use OCA\OpenCatalogi\Db\ListingMapper;
use OCA\OpenCatalogi\Service\BroadcastService;
+use OCA\OpenCatalogi\Exception\DirectoryUrlException;
use OCP\AppFramework\Db\DoesNotExistException;
use OCP\AppFramework\Db\MultipleObjectsReturnedException;
+use OCP\AppFramework\Http\JSONResponse;
use OCP\IAppConfig;
use OCP\IURLGenerator;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
+use Symfony\Component\Routing\Generator\UrlGenerator;
use Symfony\Component\Uid\Uuid;
/**
- * Service class for handling directory-related operations
+ * Service for managing and synchronizing directories and listings.
+ *
+ * This service facilitates operations related to directories, catalogs, and listings.
+ * It supports synchronization with external directories, validation and updates
+ * of listings, and integration with publication types.
*/
class DirectoryService
{
@@ -40,7 +50,7 @@ class DirectoryService
* @param ObjectService $objectService Object service for handling objects
* @param CatalogMapper $catalogMapper Mapper for catalog objects
* @param ListingMapper $listingMapper Mapper for listing objects
- * @param BroadcastService $broadcastService Broadcast service for broadcasting
+ * @param BroadcastService $broadcastService Broadcast service for broadcasting
*/
public function __construct(
private readonly IURLGenerator $urlGenerator,
@@ -48,7 +58,7 @@ public function __construct(
private readonly ObjectService $objectService,
private readonly CatalogMapper $catalogMapper,
private readonly ListingMapper $listingMapper,
- private readonly BroadcastService $broadcastService
+ private readonly BroadcastService $broadcastService,
)
{
$this->client = new Client([]);
@@ -87,7 +97,7 @@ private function getDirectoryFromListing(Listing|array $listing): array
// $listing['id'] = $listing['uuid'];
// Remove unneeded fields
- unset($listing['status'], $listing['lastSync'], $listing['default'], $listing['available'], $listing['catalog'], $listing['statusCode'],
+ unset($listing['status'], $listing['lastSync'], $listing['default'], $listing['available'], $listing['statusCode'],
// $listing['uuid'], //@todo this breaks stuff when trying to find and update a listing
$listing['hash']);
@@ -153,6 +163,7 @@ private function getDirectoryFromCatalog(Catalog|array $catalog): array
// Add the search and directory urls
$catalog['search'] = $this->urlGenerator->getAbsoluteURL($this->urlGenerator->linkToRoute("opencatalogi.search.index"));
$catalog['directory'] = $this->urlGenerator->getAbsoluteURL($this->urlGenerator->linkToRoute("opencatalogi.directory.index"));
+ $catalog['catalog'] = $catalog['id'];
// Process publication types
if (isset($catalog['publicationTypes']) && is_array($catalog['publicationTypes'])) {
@@ -228,16 +239,20 @@ public function doCronSync(): array {
// Extract unique directory URLs
// Get unique directories from listings
$uniqueDirectories = array_unique(array_column($listings, 'directory'));
-
+
// Add default OpenCatalogi directory if not already present
$defaultDirectory = 'https://directory.opencatalogi.nl/apps/opencatalogi/api/directory';
if (!in_array($defaultDirectory, $uniqueDirectories)) {
$uniqueDirectories[] = $defaultDirectory;
- }
+ }
// Sync each unique directory
foreach ($uniqueDirectories as $directoryUrl) {
- $result = $this->syncExternalDirectory($directoryUrl);
+ try {
+ $result = $this->syncExternalDirectory($directoryUrl);
+ } catch (DirectoryUrlException $exception) {
+ continue;
+ }
$results = array_merge_recursive($results, $result);
}
@@ -252,7 +267,7 @@ public function doCronSync(): array {
*/
public function validateExternalListing(array $listing): bool
{
- if (empty($listing['id']) || !Uuid::isValid($listing['id'])) {
+ if (empty($listing['catalog']) === true || Uuid::isValid($listing['catalog']) === false) {
return false;
}
@@ -269,7 +284,7 @@ public function validateExternalListing(array $listing): bool
* @return array The updated listing
* @throws DoesNotExistException|MultipleObjectsReturnedException|ContainerExceptionInterface|NotFoundExceptionInterface
*/
- public function updateListing(array $newListing, array $oldListing): array{
+ public function updateListing(array $newListing, array $oldListing): array{
// Let's see if these changed by checking them against the hash
$newHash = hash('sha256', json_encode($newListing));
$oldHash = hash('sha256', json_encode($oldListing));
@@ -283,7 +298,36 @@ public function updateListing(array $newListing, array $oldListing): array{
return $newListing->jsonSerialize();
}
-
+ /**
+ * Checks if the URL complies to basic rules.
+ *
+ * @param string $url The url to check.
+ * @return void
+ * @throws DirectoryUrlException Thrown if the url is invalid.
+ */
+ private function checkConditions(string $url): void
+ {
+ if (empty($url) === true) {
+ throw new DirectoryUrlException('URL is required');
+ }
+
+ // Check if URL contains the base url of this instance.
+ if (str_contains(haystack: strtolower($url), needle: $this->urlGenerator->getBaseUrl()) === true) {
+ throw new DirectoryUrlException('Cannot load current directory');
+ }
+
+ // Check if URL contains 'local' and throw exception if it does
+ if (str_contains(strtolower($url), 'local') === true) {
+ throw new DirectoryUrlException('Local urls are not allowed');
+ }
+
+ // Validate the URL
+ if (filter_var($url, FILTER_VALIDATE_URL) === false) {
+ throw new DirectoryUrlException('Invalid URL provided');
+ }
+ }
+
+
/**
* Synchronize with an external directory
*
@@ -292,25 +336,32 @@ public function updateListing(array $newListing, array $oldListing): array{
* @return array An array containing synchronization results
* @throws DoesNotExistException|MultipleObjectsReturnedException|ContainerExceptionInterface|NotFoundExceptionInterface
* @throws GuzzleException|\OCP\DB\Exception
+ * @throws DirectoryUrlException
*/
public function syncExternalDirectory(string $url): array
{
// Log successful broadcast
\OC::$server->getLogger()->info('Synchronizing directory with ' . $url);
+ $this->checkConditions($url);
+
try {
+ $checkUrls[] = $url;
// Get the directory data
$result = $this->client->get($url);
// Fallback to the /api/directory endpoint if the result is not JSON
if (str_contains($result->getHeader('Content-Type')[0], 'application/json') === false) {
+
+ $checkUrls[] = $url.'/index.php/apps/opencatalogi/api/directory';
$url = rtrim($url, '/').'/apps/opencatalogi/api/directory';
$result = $this->client->get($url);
+ $checkUrls[] = $url;
}
- } catch (\GuzzleHttp\Exception\ClientException $e) {
+ } catch (ClientException|RequestException|ServerException $e) {
// If we get a 404, the directory no longer exists
if ($e->getResponse()->getStatusCode() === 404) {
- // Delete all listings for this directory since it no longer exists
+ // Delete all listings for this directory since it no longer exists
$this->deleteListingsByDirectory('listing', $url);
throw new \Exception('Directory no longer exists at ' . $url);
}
@@ -322,10 +373,7 @@ public function syncExternalDirectory(string $url): array
// Get all current listings for this directory
$currentListings = $this->objectService->getObjects(
- objectType: 'listing',
- filters: [
- 'directory'=>$url,
- ]
+ objectType: 'listing'
);
// Remove any listings without a catalog ID from the database
@@ -342,17 +390,20 @@ public function syncExternalDirectory(string $url): array
// array_column() with null as second parameter returns complete array entries
// This will return complete listing objects indexed by their catalog ID
$oldListings = array_column(
- $currentListings,
+ $currentListings,
null, // null returns complete array entries rather than a specific column
'catalog' // Index by catalog ID
);
+ $oldListingDirectories = array_unique(array: array_column(array: $currentListings, column_key: 'directory'));
+
// Initialize arrays to store results
$addedListings = [];
$updatedListings = [];
$invalidListings = [];
$foundDirectories = [];
$removedListings = [];
+ $discoveredDirectories = [];
// Process each new listing
foreach ($newListings as $listing) {
@@ -362,13 +413,22 @@ public function syncExternalDirectory(string $url): array
continue;
}
+ if (in_array(needle: $listing['directory'], haystack: $checkUrls) === false
+ && in_array(needle: $listing['directory'], haystack: $oldListingDirectories) === false
+ ) {
+ $discoveredDirectories[] = $listing['directory'];
+
+ continue;
+ } else if (in_array(needle: $listing['directory'], haystack: $checkUrls) === false) {
+ continue;
+ }
+
// Check if we already have this listing by looking up its catalog ID in the oldListings array
- $oldListing = $oldListings[$listing['id']] ?? null;
+ $oldListing = $oldListings[$listing['catalog']] ?? null;
// If no existing listing found, prepare the new listing data
if ($oldListing === null) {
$listing['hash'] = hash('sha256', json_encode($listing));
- $listing['catalog'] = $listing['id'];
unset($listing['id']);
} else {
// Update existing listing
@@ -394,10 +454,14 @@ public function syncExternalDirectory(string $url): array
}
// Lets inform our new friends that we exist
- foreach($foundDirectories as $foundDirectory){
+ foreach ($foundDirectories as $foundDirectory){
$this->broadcastService->broadcast($foundDirectory);
}
+ foreach ($discoveredDirectories as $discoveredDirectory) {
+ $this->syncExternalDirectory($discoveredDirectory);
+ }
+
// Return the results
return [
'invalidListings' => $invalidListings,
@@ -420,7 +484,7 @@ private function deleteListingsByDirectory(string $directoryUrl): void {
]
);
// Delete all listings
- foreach ($currentListings as $listing) {
+ foreach ($currentListings as $listing) {
$this->objectService->deleteObject('listing', $listing['id']);
}
}
diff --git a/lib/Service/DownloadService.php b/lib/Service/DownloadService.php
index 7d582b9a..6376e3b7 100644
--- a/lib/Service/DownloadService.php
+++ b/lib/Service/DownloadService.php
@@ -17,7 +17,11 @@
use Exception;
/**
- * Service class for handling download-related operations
+ * Service for managing download-related operations.
+ *
+ * Provides functionality to create and manage publication files and archives, including
+ * generating PDFs and ZIP files containing metadata and attachments, and storing files
+ * in NextCloud.
*/
class DownloadService
{
diff --git a/lib/Service/ElasticSearchClientAdapter.php b/lib/Service/ElasticSearchClientAdapter.php
index 83efcb6c..1ef01041 100644
--- a/lib/Service/ElasticSearchClientAdapter.php
+++ b/lib/Service/ElasticSearchClientAdapter.php
@@ -5,7 +5,10 @@
use Elastic\Elasticsearch\Client;
/**
- * Adapter class for Elasticsearch client operations
+ * Adapter for Elasticsearch client operations.
+ *
+ * Provides a wrapper around the Elasticsearch client to facilitate search, indexing,
+ * retrieval, updating, and deletion of documents.
*/
class ElasticSearchClientAdapter
{
@@ -83,4 +86,4 @@ public function delete(array $params)
// Delete the document and return the response
return $this->client->delete($params);
}
-}
\ No newline at end of file
+}
diff --git a/lib/Service/ElasticSearchService.php b/lib/Service/ElasticSearchService.php
index 73afa1fe..d721e670 100644
--- a/lib/Service/ElasticSearchService.php
+++ b/lib/Service/ElasticSearchService.php
@@ -8,6 +8,13 @@
use Elastic\Elasticsearch\ClientBuilder;
use Symfony\Component\Uid\Uuid;
+/**
+ * Service for managing interactions with Elasticsearch.
+ *
+ * Provides functionality for indexing, updating, deleting, and searching objects in Elasticsearch,
+ * as well as processing and formatting query results and aggregations.
+ */
+
class ElasticSearchService
{
diff --git a/lib/Service/FileService.php b/lib/Service/FileService.php
index 4d2fe177..70ec13e2 100644
--- a/lib/Service/FileService.php
+++ b/lib/Service/FileService.php
@@ -29,8 +29,12 @@
use ZipArchive;
/**
- * Service class for handling file operations in OpenCatalogi
+ * Service for handling file operations in OpenCatalogi.
+ *
+ * Provides functionalities for managing files and folders in NextCloud, creating and managing
+ * share links, handling uploaded files, generating PDF and ZIP files, and managing temporary files.
*/
+
class FileService
{
/**
diff --git a/lib/Service/ObjectService.php b/lib/Service/ObjectService.php
index f0688c0b..d237e181 100644
--- a/lib/Service/ObjectService.php
+++ b/lib/Service/ObjectService.php
@@ -12,6 +12,7 @@
use OCP\App\IAppManager;
use OCP\AppFramework\Db\DoesNotExistException;
use OCP\AppFramework\Db\MultipleObjectsReturnedException;
+use OCP\IURLGenerator;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Symfony\Component\Uid\Uuid;
@@ -19,6 +20,7 @@
use OCP\IAppConfig;
// Import mappers
use OCA\OpenCatalogi\Db\AttachmentMapper;
+use OCA\OpenCatalogi\Db\PageMapper;
use OCA\OpenCatalogi\Db\CatalogMapper;
use OCA\OpenCatalogi\Db\ListingMapper;
use OCA\OpenCatalogi\Db\PublicationTypeMapper;
@@ -27,13 +29,19 @@
use OCA\OpenCatalogi\Db\ThemeMapper;
/**
- * Service class for handling object-related operations
+ * Service for handling object-related operations.
+ *
+ * Provides functionality for retrieving, saving, updating, and deleting objects,
+ * as well as extending entities with related data and managing object mappings.
*/
+
class ObjectService
{
/** @var string $appName The name of the app */
private string $appName;
+ private ValidationService $validationService;
+
/**
* Constructor for ObjectService.
*
@@ -44,6 +52,7 @@ class ObjectService
* @param OrganizationMapper $organizationMapper Mapper for organizations
* @param PublicationMapper $publicationMapper Mapper for publications
* @param ThemeMapper $themeMapper Mapper for themes
+ * @param PageMapper $pageMapper Mapper for pages
* @param ContainerInterface $container Container for dependency injection
* @param IAppManager $appManager App manager interface
* @param IAppConfig $config App configuration interface
@@ -56,11 +65,15 @@ public function __construct(
private OrganizationMapper $organizationMapper,
private PublicationMapper $publicationMapper,
private ThemeMapper $themeMapper,
+ private PageMapper $pageMapper,
private ContainerInterface $container,
private readonly IAppManager $appManager,
private readonly IAppConfig $config,
+ IURLGenerator $urlGenerator,
) {
$this->appName = 'opencatalogi';
+
+ $this->validationService = new ValidationService(objectService: $this, urlGenerator: $urlGenerator);
}
/**
@@ -106,6 +119,7 @@ private function getMapper(string $objectType): mixed
'organization' => $this->organizationMapper,
'publication' => $this->publicationMapper,
'theme' => $this->themeMapper,
+ 'page' => $this->pageMapper,
default => throw new InvalidArgumentException("Unknown object type: $objectType"),
};
}
@@ -298,11 +312,16 @@ public function getAllObjects(string $objectType, ?int $limit = null, ?int $offs
*/
public function saveObject(string $objectType, array $object, bool $updateVersion = true): mixed
{
+ if ($objectType === 'publication') {
+ $object = $this->validationService->validatePublication($object);
+ }
+
// Get the appropriate mapper for the object type
$mapper = $this->getMapper($objectType);
+
// If the object has an id, update it; otherwise, create a new object
if (isset($object['id']) === true) {
- return $mapper->updateFromArray($object['id'], $object, $updateVersion);
+ return $mapper->updateFromArray($object['id'], $object, $updateVersion, patch: true);
}
else {
return $mapper->createFromArray($object);
diff --git a/lib/Service/SearchService.php b/lib/Service/SearchService.php
index 9ecfdafd..0a0b6704 100644
--- a/lib/Service/SearchService.php
+++ b/lib/Service/SearchService.php
@@ -8,10 +8,13 @@
use Symfony\Component\Uid\Uuid;
/**
- * Class SearchService
- *
- * This service handles search operations and related functionalities.
+ * Service for managing search operations and related functionalities.
+ *
+ * Provides methods for performing search queries, merging search results and aggregations,
+ * and creating database-specific filters and sort parameters. Handles both local and
+ * distributed search queries across multiple directories.
*/
+
class SearchService
{
/** @var Client */
@@ -25,7 +28,7 @@ class SearchService
/**
* SearchService constructor.
- *
+ *
* @param ElasticSearchService $elasticService
* @param DirectoryService $directoryService
* @param IURLGenerator $urlGenerator
@@ -40,7 +43,7 @@ public function __construct(
/**
* Merge facets from existing and new aggregations.
- *
+ *
* @param array $existingAggregation
* @param array $newAggregation
* @return array Merged facets
@@ -75,7 +78,7 @@ public function mergeFacets(array $existingAggregation, array $newAggregation):
/**
* Merge existing and new aggregations.
- *
+ *
* @param array|null $existingAggregations
* @param array|null $newAggregations
* @return array Merged aggregations
@@ -98,7 +101,7 @@ private function mergeAggregations(?array $existingAggregations, ?array $newAggr
/**
* Comparison function for sorting result arrays.
- *
+ *
* @param array $a
* @param array $b
* @return int
@@ -110,7 +113,7 @@ public function sortResultArray(array $a, array $b): int
/**
* Perform a search operation.
- *
+ *
* @param array $parameters Search parameters
* @param array $elasticConfig Elasticsearch configuration
* @param array $dbConfig Database configuration
diff --git a/lib/Service/ValidationService.php b/lib/Service/ValidationService.php
index fd58b2b3..8c36e6b7 100644
--- a/lib/Service/ValidationService.php
+++ b/lib/Service/ValidationService.php
@@ -3,118 +3,93 @@
namespace OCA\OpenCatalogi\Service;
use OCA\OpenCatalogi\Db\CatalogMapper;
+use OCA\OpenCatalogi\Db\Publication;
+use OCA\OpenCatalogi\Db\PublicationType;
+use OCP\AppFramework\Db\DoesNotExistException;
+use OCP\AppFramework\Db\MultipleObjectsReturnedException;
use OCP\AppFramework\OCS\OCSBadRequestException;
use OCP\AppFramework\OCS\OCSNotFoundException;
use OCP\IAppConfig;
+use OCP\IURLGenerator;
+use Opis\JsonSchema\Errors\ErrorFormatter;
+use Opis\JsonSchema\Validator;
+use Psr\Container\ContainerExceptionInterface;
+use Psr\Container\NotFoundExceptionInterface;
+use Symfony\Component\Uid\Uuid;
/**
- * Class ValidationService
+ * Service for validating catalogs and publications.
*
- * This service handles validation of catalogs and publications.
+ * Provides methods to validate publications against the schema defined in their associated
+ * publication types, ensuring data consistency and integrity. Handles default values and
+ * reports validation errors.
*/
+
class ValidationService
{
- /**
- * @var string The name of the application.
- */
- private string $appName;
- /**
- * @var array The current MongoDB Config.
- */
- private array $mongodbConfig;
-
- /**
- * ValidationService constructor.
- *
- * @param IAppConfig $config The application config
- * @param CatalogMapper $catalogMapper The catalog mapper.
- * @param ObjectService $objectService The object service.
- */
public function __construct(
- private readonly IAppConfig $config,
- private readonly CatalogMapper $catalogMapper,
private readonly ObjectService $objectService,
- ) {
- $this->appName = 'opencatalogi';
-
- // Initialize MongoDB configuration
- $this->mongodbConfig = [
- 'base_uri' => $this->config->getValueString(app: $this->appName, key: 'mongodbLocation'),
- 'headers' => ['api-key' => $this->config->getValueString(app: $this->appName, key: 'mongodbKey')],
- 'mongodbCluster' => $this->config->getValueString(app: $this->appName, key:'mongodbCluster')
- ];
- }
-
- /**
- * Get the MongoDB configuration.
- *
- * @return array The mongodb config.
- */
- public function getMongodbConfig(): array
+ private readonly IURLGenerator $urlGenerator,
+ )
{
- return $this->mongodbConfig;
}
/**
- * Fetches a catalog from either the local database or mongodb
+ * Validate a publication to the definition defined in the PublicationType.
+ *
+ * @param Publication $publication The publication to validate.
*
- * @param string $id The id of the catalog to be fetched.
- * @return array The JSON Serialised catalog.
+ * @return array The validated publication.
*
- * @throws OCSNotFoundException If the catalog is not found.
- * @throws \GuzzleHttp\Exception\GuzzleException
+ * @throws DoesNotExistException
+ * @throws MultipleObjectsReturnedException
+ * @throws ContainerExceptionInterface
+ * @throws NotFoundExceptionInterface
*/
- public function getCatalog (string $id): array
+ public function validatePublication(array $publication): array
{
- // Check if MongoDB storage is enabled
- if ($this->config->hasKey(app: $this->appName, key: 'mongoStorage') !== false
- && $this->config->getValueString(app: $this->appName, key: 'mongoStorage') === '1'
- ) {
- $filter = ['id' => $id, '_schema' => 'catalog'];
-
- try {
- return $this->objectService->findObject(filters: $filter, config: $this->getMongodbConfig());
- } catch (OCSNotFoundException $exception) {
- throw new OCSNotFoundException(message: 'Catalog not found for id: ' . $id);
- }
+ if (isset($publication['publicationType']) === false) {
+ return $publication;
}
- // If MongoDB storage is not enabled, fetch from local database
- return $this->catalogMapper->find(id: $id)->jsonSerialize();
- }
+ $publicationTypeId = $publication['publicationType'];
+ $publicationType = $this->objectService->getObject(objectType: 'publicationType', id: $publicationTypeId);
- /**
- * Validates a publication against the rules set for the publication.
- *
- * @param array $publication The publication to be validated.
- * @return array The publication after it has been validated.
- *
- * @throws OCSBadRequestException Thrown if the object does not validate
- * @throws OCSNotFoundException Thrown if the catalog is not found
- */
- public function validatePublication(array $publication): array
- {
- // Check for required fields
- $requiredFields = ['catalogi', 'publicationType'];
- foreach ($requiredFields as $field) {
- if (isset($publication[$field]) === false) {
- throw new OCSBadRequestException(message: $field . ' is required but not given.');
+ $publicationType = (new PublicationType())->hydrate($publicationType);
+
+ if (Uuid::isValid($publicationTypeId) === true) {
+ $publicationType->setUuid($publicationTypeId);
+ }
+
+ $validator = new Validator();
+ $validator->setMaxErrors(100);
+
+ if (empty($publicationType->getProperties()) === true) {
+ return $publication;
+ }
+
+
+ // Check for default values, and only set the property if the property is empty
+ foreach ($publicationType->getProperties() as $property) {
+ if (isset($property['default']) === true && empty($property['default']) === false && (isset($publication['data'][$property['title']]) === false || empty($publication['data'][$property['title']]) === true)) {
+ $publication['data'][$property['title']] = $property['default'];
}
}
- $catalog = $publication['catalogi'];
- $publicationType = $publication['publicationType'];
+ $result = $validator->validate(data: (object) json_decode(json_encode($publication['data'])), schema: $publicationType->getSchemaObject($this->urlGenerator));
- try {
- $catalog = $this->getCatalog($catalog);
- } catch (OCSNotFoundException $exception) {
- throw new OCSNotFoundException(message: $exception->getMessage());
- }
+ $publication['validation'] = [
+ 'errors' => [],
+ 'valid' => true
+ ];
- // Check if the given publicationType is present in the catalog
- if (in_array(needle: $publicationType, haystack: $catalog['publicationType']) === false) {
- throw new OCSBadRequestException(message: 'Given publicationType object not present in catalog');
+ if ($result->hasError()) {
+ $errors = (new ErrorFormatter())->format($result->error());
+ foreach ($errors as $error) {
+ $publication['validation']['errors'][] = $error[0];
+ }
+ $publication['validation']['valid'] = false;
}
return $publication;
diff --git a/lib/Templates/publication.html.twig b/lib/Templates/publication.html.twig
new file mode 100644
index 00000000..cd43ad73
--- /dev/null
+++ b/lib/Templates/publication.html.twig
@@ -0,0 +1,163 @@
+
Publicatie {{ publication.title }}
+
+
+
+{% if publication.catalogi|default %}
+ Catalogi
+
+
+ Titel: |
+ {{ publication.catalogi.title }} |
+
+ {% if publication.catalogi.summary|default %}
+
+ Samenvatting: |
+ {{ publication.catalogi.summary }} |
+
+ {% endif %}
+ {% if publication.catalogi.description|default %}
+
+ Beschrijving: |
+ {{ publication.catalogi.description }} |
+
+ {% endif %}
+ {# {% if publication.catalogi.organisation|default %}#}
+ {# #}
+ {# Organisatie: | #}
+ {# {{ publication.catalogi.organisation }} | #}
+ {#
#}
+ {# {% endif %}#}
+
+{% endif %}
+
+{% if publication.metaData|default %}
+ Publicatie Type
+
+
+ Titel: |
+ {{ publication.metaData.title }} |
+
+ {% if publication.metaData.version|default %}
+
+ Versie: |
+ {{ publication.metaData.version }} |
+
+ {% endif %}
+ {% if publication.metaData.description|default %}
+
+ Beschrijving: |
+ {{ publication.metaData.description }} |
+
+ {% endif %}
+ {# {% if publication.metaData.required|default %}#}
+ {# #}
+ {# Vereisten: | #}
+ {# {{ publication.metaData.required }} | #}
+ {#
#}
+ {# {% endif %}#}
+
+{% endif %}
+
+
+
+
+ {% if publication.reference|default %}
+
+ Referentie: |
+ {{ publication.reference }} |
+
+ {% endif %}
+ {% if publication.summary|default %}
+
+ Samenvatting: |
+ {{ publication.summary }} |
+
+ {% endif %}
+ {% if publication.description|default %}
+
+ Beschrijving: |
+ {{ publication.description }} |
+
+ {% endif %}
+ {% if publication.category|default %}
+
+ Categorie: |
+ {{ publication.category }} |
+
+ {% endif %}
+ {% if publication.portal|default %}
+
+ Portal: |
+ {{ publication.portal }} |
+
+ {% endif %}
+ {% if publication.image|default %}
+
+ Foto: |
+ {{ publication.image }} |
+
+ {% endif %}
+ {# {% if publication.themes|default %}#}
+ {# #}
+ {# Thema's: | #}
+ {# {{ publication.themes }} | #}
+ {#
#}
+ {# {% endif %}#}
+ {% if publication.featured is defined %}
+
+ Uitgelicht: |
+ {% if publication.featured == true %}Ja{% else %}Nee{% endif %} |
+
+ {% endif %}
+ {% if publication.license|default %}
+
+ Licentie: |
+ {{ publication.license }} |
+
+ {% endif %}
+ {% if publication.status|default %}
+
+ Status: |
+ {{ publication.status }} |
+
+ {% endif %}
+ {% if publication.published|default %}
+
+ Gepubliceerd: |
+ {{ publication.published | date("d-m-Y H:i") }} |
+
+ {% endif %}
+ {% if publication.modified|default %}
+
+ Gewijzigd: |
+ {{ publication.modified | date("d-m-Y H:i") }} |
+
+ {% endif %}
+
+
+{% if publication.data|default %}
+
+ Eigenschappen
+
+
+ Naam |
+ Data |
+
+ {% for key, value in publication.data %}
+
+ {{ key }} |
+ {{ value }} |
+
+ {% endfor %}
+
+{% endif %}
diff --git a/package-lock.json b/package-lock.json
index 13151b64..3842f7fa 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -9,6 +9,7 @@
"version": "1.0.0",
"license": "AGPL-3.0-or-later",
"dependencies": {
+ "@codemirror/lang-json": "^6.0.1",
"@fortawesome/fontawesome-svg-core": "^6.6.0",
"@fortawesome/free-solid-svg-icons": "^6.6.0",
"@nextcloud/axios": "^2.5.0",
@@ -17,6 +18,7 @@
"@nextcloud/l10n": "^3.1.0",
"@nextcloud/router": "^3.0.1",
"@nextcloud/vue": "^8.17.0",
+ "@uiw/codemirror-theme-vscode": "^4.23.6",
"@vueuse/core": "^11.0.1",
"apexcharts": "^3.52.0",
"axios": "^1.7.4",
@@ -32,6 +34,7 @@
"validator": "^13.12.0",
"vue": "^2.7.14",
"vue-apexcharts": "^1.6.2",
+ "vue-codemirror6": "^1.3.8",
"vue-loader": "^15.11.1 <16.0.0",
"vue-loading-overlay": "^3.4.3",
"vue-material-design-icons": "^5.3.0",
@@ -1942,6 +1945,99 @@
"url": "https://opencollective.com/node-fetch"
}
},
+ "node_modules/@codemirror/autocomplete": {
+ "version": "6.18.3",
+ "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.18.3.tgz",
+ "integrity": "sha512-1dNIOmiM0z4BIBwxmxEfA1yoxh1MF/6KPBbh20a5vphGV0ictKlgQsbJs6D6SkR6iJpGbpwRsa6PFMNlg9T9pQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@codemirror/language": "^6.0.0",
+ "@codemirror/state": "^6.0.0",
+ "@codemirror/view": "^6.17.0",
+ "@lezer/common": "^1.0.0"
+ },
+ "peerDependencies": {
+ "@codemirror/language": "^6.0.0",
+ "@codemirror/state": "^6.0.0",
+ "@codemirror/view": "^6.0.0",
+ "@lezer/common": "^1.0.0"
+ }
+ },
+ "node_modules/@codemirror/commands": {
+ "version": "6.7.1",
+ "resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.7.1.tgz",
+ "integrity": "sha512-llTrboQYw5H4THfhN4U3qCnSZ1SOJ60ohhz+SzU0ADGtwlc533DtklQP0vSFaQuCPDn3BPpOd1GbbnUtwNjsrw==",
+ "license": "MIT",
+ "dependencies": {
+ "@codemirror/language": "^6.0.0",
+ "@codemirror/state": "^6.4.0",
+ "@codemirror/view": "^6.27.0",
+ "@lezer/common": "^1.1.0"
+ }
+ },
+ "node_modules/@codemirror/lang-json": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/@codemirror/lang-json/-/lang-json-6.0.1.tgz",
+ "integrity": "sha512-+T1flHdgpqDDlJZ2Lkil/rLiRy684WMLc74xUnjJH48GQdfJo/pudlTRreZmKwzP8/tGdKf83wlbAdOCzlJOGQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@codemirror/language": "^6.0.0",
+ "@lezer/json": "^1.0.0"
+ }
+ },
+ "node_modules/@codemirror/language": {
+ "version": "6.10.6",
+ "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.10.6.tgz",
+ "integrity": "sha512-KrsbdCnxEztLVbB5PycWXFxas4EOyk/fPAfruSOnDDppevQgid2XZ+KbJ9u+fDikP/e7MW7HPBTvTb8JlZK9vA==",
+ "license": "MIT",
+ "dependencies": {
+ "@codemirror/state": "^6.0.0",
+ "@codemirror/view": "^6.23.0",
+ "@lezer/common": "^1.1.0",
+ "@lezer/highlight": "^1.0.0",
+ "@lezer/lr": "^1.0.0",
+ "style-mod": "^4.0.0"
+ }
+ },
+ "node_modules/@codemirror/lint": {
+ "version": "6.8.4",
+ "resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.8.4.tgz",
+ "integrity": "sha512-u4q7PnZlJUojeRe8FJa/njJcMctISGgPQ4PnWsd9268R4ZTtU+tfFYmwkBvgcrK2+QQ8tYFVALVb5fVJykKc5A==",
+ "license": "MIT",
+ "dependencies": {
+ "@codemirror/state": "^6.0.0",
+ "@codemirror/view": "^6.35.0",
+ "crelt": "^1.0.5"
+ }
+ },
+ "node_modules/@codemirror/search": {
+ "version": "6.5.8",
+ "resolved": "https://registry.npmjs.org/@codemirror/search/-/search-6.5.8.tgz",
+ "integrity": "sha512-PoWtZvo7c1XFeZWmmyaOp2G0XVbOnm+fJzvghqGAktBW3cufwJUWvSCcNG0ppXiBEM05mZu6RhMtXPv2hpllig==",
+ "license": "MIT",
+ "dependencies": {
+ "@codemirror/state": "^6.0.0",
+ "@codemirror/view": "^6.0.0",
+ "crelt": "^1.0.5"
+ }
+ },
+ "node_modules/@codemirror/state": {
+ "version": "6.4.1",
+ "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.4.1.tgz",
+ "integrity": "sha512-QkEyUiLhsJoZkbumGZlswmAhA7CBU02Wrz7zvH4SrcifbsqwlXShVXg65f3v/ts57W3dqyamEriMhij1Z3Zz4A==",
+ "license": "MIT"
+ },
+ "node_modules/@codemirror/view": {
+ "version": "6.35.0",
+ "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.35.0.tgz",
+ "integrity": "sha512-I0tYy63q5XkaWsJ8QRv5h6ves7kvtrBWjBcnf/bzohFJQc5c14a1AQRdE8QpPF9eMp5Mq2FMm59TCj1gDfE7kw==",
+ "license": "MIT",
+ "dependencies": {
+ "@codemirror/state": "^6.4.0",
+ "style-mod": "^4.1.0",
+ "w3c-keyname": "^2.2.4"
+ }
+ },
"node_modules/@csstools/css-parser-algorithms": {
"version": "2.7.1",
"resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-2.7.1.tgz",
@@ -3295,6 +3391,41 @@
"license": "MIT",
"peer": true
},
+ "node_modules/@lezer/common": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.2.3.tgz",
+ "integrity": "sha512-w7ojc8ejBqr2REPsWxJjrMFsA/ysDCFICn8zEOR9mrqzOu2amhITYuLD8ag6XZf0CFXDrhKqw7+tW8cX66NaDA==",
+ "license": "MIT"
+ },
+ "node_modules/@lezer/highlight": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.2.1.tgz",
+ "integrity": "sha512-Z5duk4RN/3zuVO7Jq0pGLJ3qynpxUVsh7IbUbGj88+uV2ApSAn6kWg2au3iJb+0Zi7kKtqffIESgNcRXWZWmSA==",
+ "license": "MIT",
+ "dependencies": {
+ "@lezer/common": "^1.0.0"
+ }
+ },
+ "node_modules/@lezer/json": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@lezer/json/-/json-1.0.2.tgz",
+ "integrity": "sha512-xHT2P4S5eeCYECyKNPhr4cbEL9tc8w83SPwRC373o9uEdrvGKTZoJVAGxpOsZckMlEh9W23Pc72ew918RWQOBQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@lezer/common": "^1.2.0",
+ "@lezer/highlight": "^1.0.0",
+ "@lezer/lr": "^1.0.0"
+ }
+ },
+ "node_modules/@lezer/lr": {
+ "version": "1.4.2",
+ "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.4.2.tgz",
+ "integrity": "sha512-pu0K1jCIdnQ12aWNaAVU5bzi7Bd1w54J3ECgANPmYLtQKP0HBj2cE/5coBD66MT10xbtIuUr7tg0Shbsvk0mDA==",
+ "license": "MIT",
+ "dependencies": {
+ "@lezer/common": "^1.0.0"
+ }
+ },
"node_modules/@linusborg/vue-simple-portal": {
"version": "0.1.5",
"resolved": "https://registry.npmjs.org/@linusborg/vue-simple-portal/-/vue-simple-portal-0.1.5.tgz",
@@ -5186,6 +5317,37 @@
"url": "https://opencollective.com/eslint"
}
},
+ "node_modules/@uiw/codemirror-theme-vscode": {
+ "version": "4.23.6",
+ "resolved": "https://registry.npmjs.org/@uiw/codemirror-theme-vscode/-/codemirror-theme-vscode-4.23.6.tgz",
+ "integrity": "sha512-xUo1ic+Kk5hnv5gy+cXU12GZVSnDjic8s8weKq8loPHF1dSR1e6gkKVIKZRnvoOZ302taKRk7phWpBUaWIuKQg==",
+ "license": "MIT",
+ "dependencies": {
+ "@uiw/codemirror-themes": "4.23.6"
+ },
+ "funding": {
+ "url": "https://jaywcjlove.github.io/#/sponsor"
+ }
+ },
+ "node_modules/@uiw/codemirror-themes": {
+ "version": "4.23.6",
+ "resolved": "https://registry.npmjs.org/@uiw/codemirror-themes/-/codemirror-themes-4.23.6.tgz",
+ "integrity": "sha512-0dpuLQW+V6zrKvfvor/eo71V3tpr2L2Hsu8QZAdtSzksjWABxTOzH3ShaBRxCEsrz6sU9sa9o7ShwBMMDz59bQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@codemirror/language": "^6.0.0",
+ "@codemirror/state": "^6.0.0",
+ "@codemirror/view": "^6.0.0"
+ },
+ "funding": {
+ "url": "https://jaywcjlove.github.io/#/sponsor"
+ },
+ "peerDependencies": {
+ "@codemirror/language": ">=6.0.0",
+ "@codemirror/state": ">=6.0.0",
+ "@codemirror/view": ">=6.0.0"
+ }
+ },
"node_modules/@ungap/structured-clone": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz",
@@ -7634,6 +7796,21 @@
"node": ">= 0.12.0"
}
},
+ "node_modules/codemirror": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-6.0.1.tgz",
+ "integrity": "sha512-J8j+nZ+CdWmIeFIGXEFbFPtpiYacFMDR8GlHK3IyHQJMCaVRfGx9NT+Hxivv1ckLWPvNdZqndbr/7lVhrf/Svg==",
+ "license": "MIT",
+ "dependencies": {
+ "@codemirror/autocomplete": "^6.0.0",
+ "@codemirror/commands": "^6.0.0",
+ "@codemirror/language": "^6.0.0",
+ "@codemirror/lint": "^6.0.0",
+ "@codemirror/search": "^6.0.0",
+ "@codemirror/state": "^6.0.0",
+ "@codemirror/view": "^6.0.0"
+ }
+ },
"node_modules/collapse-white-space": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/collapse-white-space/-/collapse-white-space-2.1.0.tgz",
@@ -8169,6 +8346,12 @@
"node": ">=8"
}
},
+ "node_modules/crelt": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz",
+ "integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==",
+ "license": "MIT"
+ },
"node_modules/cross-spawn": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
@@ -20729,6 +20912,12 @@
"webpack": "^5.0.0"
}
},
+ "node_modules/style-mod": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.2.tgz",
+ "integrity": "sha512-wnD1HyVqpJUI2+eKZ+eo1UwghftP6yuFheBqqe+bWCotBjC2K1YnteJILRMs3SM4V/0dLEW1SC27MWP5y+mwmw==",
+ "license": "MIT"
+ },
"node_modules/style-search": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/style-search/-/style-search-0.1.0.tgz",
@@ -22884,6 +23073,54 @@
"apexcharts": "^3.26.0"
}
},
+ "node_modules/vue-codemirror6": {
+ "version": "1.3.8",
+ "resolved": "https://registry.npmjs.org/vue-codemirror6/-/vue-codemirror6-1.3.8.tgz",
+ "integrity": "sha512-pCOzKzBBSFKi/SjUg+XGranV1vt+8S22z56BES/OeZtmyuj2M0CE0aczYS8qbTWNnKcuJcI5FRDHzVXy2v2Htg==",
+ "license": "MIT",
+ "dependencies": {
+ "@codemirror/commands": "^6.7.1",
+ "@codemirror/language": "^6.10.3",
+ "@codemirror/lint": "^6.8.3",
+ "@codemirror/state": "^6.4.1",
+ "@codemirror/view": "^6.35.0",
+ "codemirror": "^6.0.1",
+ "style-mod": "^4.1.2",
+ "vue-demi": "latest"
+ },
+ "engines": {
+ "pnpm": ">=9.14.2"
+ },
+ "peerDependencies": {
+ "vue": "^2.7.14 || ^3.4"
+ }
+ },
+ "node_modules/vue-codemirror6/node_modules/vue-demi": {
+ "version": "0.14.10",
+ "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz",
+ "integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==",
+ "hasInstallScript": true,
+ "license": "MIT",
+ "bin": {
+ "vue-demi-fix": "bin/vue-demi-fix.js",
+ "vue-demi-switch": "bin/vue-demi-switch.js"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/antfu"
+ },
+ "peerDependencies": {
+ "@vue/composition-api": "^1.0.0-rc.1",
+ "vue": "^3.0.0-0 || ^2.6.0"
+ },
+ "peerDependenciesMeta": {
+ "@vue/composition-api": {
+ "optional": true
+ }
+ }
+ },
"node_modules/vue-color": {
"version": "2.8.1",
"resolved": "https://registry.npmjs.org/vue-color/-/vue-color-2.8.1.tgz",
@@ -23137,6 +23374,12 @@
"vue": "^2.5.0"
}
},
+ "node_modules/w3c-keyname": {
+ "version": "2.2.8",
+ "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz",
+ "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==",
+ "license": "MIT"
+ },
"node_modules/w3c-xmlserializer": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz",
diff --git a/package.json b/package.json
index fdccad01..f7809035 100644
--- a/package.json
+++ b/package.json
@@ -20,6 +20,7 @@
"extends @nextcloud/browserslist-config"
],
"dependencies": {
+ "@codemirror/lang-json": "^6.0.1",
"@fortawesome/fontawesome-svg-core": "^6.6.0",
"@fortawesome/free-solid-svg-icons": "^6.6.0",
"@nextcloud/axios": "^2.5.0",
@@ -28,6 +29,7 @@
"@nextcloud/l10n": "^3.1.0",
"@nextcloud/router": "^3.0.1",
"@nextcloud/vue": "^8.17.0",
+ "@uiw/codemirror-theme-vscode": "^4.23.6",
"@vueuse/core": "^11.0.1",
"apexcharts": "^3.52.0",
"axios": "^1.7.4",
@@ -43,6 +45,7 @@
"validator": "^13.12.0",
"vue": "^2.7.14",
"vue-apexcharts": "^1.6.2",
+ "vue-codemirror6": "^1.3.8",
"vue-loader": "^15.11.1 <16.0.0",
"vue-loading-overlay": "^3.4.3",
"vue-material-design-icons": "^5.3.0",
diff --git a/phpcs.xml b/phpcs.xml
new file mode 100644
index 00000000..eb935f62
--- /dev/null
+++ b/phpcs.xml
@@ -0,0 +1,154 @@
+
+
+ The coding standard for PHP_CodeSniffer itself, for more config -> for https://github.com/squizlabs/PHP_CodeSniffer/wiki/Configuration-Options.
+
+ README.md
+ src
+
+
+
+
+
+
+
+ error
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+
+
+
+ 0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ error
+
+
+
+
+ error
+
+
+
+
+ tests/bootstrap\.php
+
+
+
+
+ tests/Core/Tokenizer/StableCommentWhitespaceWinTest\.php
+
+
+
diff --git a/src/entities/index.js b/src/entities/index.js
index 2f4bb056..b5726d33 100644
--- a/src/entities/index.js
+++ b/src/entities/index.js
@@ -7,3 +7,4 @@ export * from './organization/index.js'
export * from './publication/index.js'
export * from './theme/index.js'
export * from './publicationType/index.js'
+export * from './page/index.js'
\ No newline at end of file
diff --git a/src/entities/listing/listing.mock.ts b/src/entities/listing/listing.mock.ts
index a90adb17..1e9917db 100644
--- a/src/entities/listing/listing.mock.ts
+++ b/src/entities/listing/listing.mock.ts
@@ -26,6 +26,7 @@ export const mockListingsData = (): TListing[] => [
tooi: 'string',
rsin: 'string',
pki: 'string',
+ image: 'string',
},
},
{
@@ -52,6 +53,7 @@ export const mockListingsData = (): TListing[] => [
tooi: 'string',
rsin: 'string',
pki: 'string',
+ image: 'string',
},
},
{
diff --git a/src/entities/organization/organization.mock.ts b/src/entities/organization/organization.mock.ts
index d2a0fd20..ae84f2f6 100644
--- a/src/entities/organization/organization.mock.ts
+++ b/src/entities/organization/organization.mock.ts
@@ -11,6 +11,7 @@ export const mockOrganizationData = (): TOrganization[] => [
tooi: '7843432',
rsin: '827342654',
pki: '543573424',
+ image: '',
},
{
id: '2',
@@ -21,6 +22,7 @@ export const mockOrganizationData = (): TOrganization[] => [
tooi: '',
rsin: '',
pki: '',
+ image: '',
},
{ // invalid data
id: '3',
@@ -31,6 +33,7 @@ export const mockOrganizationData = (): TOrganization[] => [
tooi: '5435',
rsin: '54',
pki: '6565',
+ image: '',
},
]
diff --git a/src/entities/organization/organization.ts b/src/entities/organization/organization.ts
index f8fd86cc..90d1f3b9 100644
--- a/src/entities/organization/organization.ts
+++ b/src/entities/organization/organization.ts
@@ -11,6 +11,7 @@ export class Organization implements TOrganization {
public tooi: string
public rsin: string
public pki: string
+ public image: string
constructor(data: TOrganization) {
this.hydrate(data)
@@ -26,6 +27,7 @@ export class Organization implements TOrganization {
this.tooi = data?.tooi || ''
this.rsin = data?.rsin || ''
this.pki = data?.pki || ''
+ this.image = data?.image || ''
}
/* istanbul ignore next */
@@ -42,6 +44,7 @@ export class Organization implements TOrganization {
tooi: z.string().regex(/^\d{1,}$/, 'is niet een geldige TOOI nummer').or(z.literal('')),
rsin: z.string().regex(/^\d{9}$/, 'is niet een geldige RSIN nummer').or(z.literal('')),
pki: z.string().regex(/^\d{1,}$/, 'is niet een geldige PKI nummer').or(z.literal('')),
+ image: z.string(),
})
const result = schema.safeParse({
diff --git a/src/entities/organization/organization.types.ts b/src/entities/organization/organization.types.ts
index 4d9b5b09..2f4480a4 100644
--- a/src/entities/organization/organization.types.ts
+++ b/src/entities/organization/organization.types.ts
@@ -7,4 +7,5 @@ export type TOrganization = {
tooi: string
rsin: string
pki: string
+ image: string
}
diff --git a/src/entities/page/index.js b/src/entities/page/index.js
new file mode 100644
index 00000000..b496e408
--- /dev/null
+++ b/src/entities/page/index.js
@@ -0,0 +1,4 @@
+export * from './page.ts'
+export * from './page.types.ts'
+export * from './page.mock.ts'
+
diff --git a/src/entities/page/page.mock.ts b/src/entities/page/page.mock.ts
new file mode 100644
index 00000000..347adfc9
--- /dev/null
+++ b/src/entities/page/page.mock.ts
@@ -0,0 +1,43 @@
+import { Page } from './page'
+import { TPage } from './page.types'
+
+/**
+ * Mock data function that returns an array of page data objects
+ * Used for testing and development purposes
+ */
+export const mockPageData = (): TPage[] => [
+ { // full data
+ id: '1',
+ uuid: '123e4567-e89b-12d3-a456-426614174000',
+ name: 'Test Page',
+ slug: 'test-page',
+ contents: '{}',
+ createdAt: new Date().toISOString(),
+ updatedAt: new Date().toISOString()
+ },
+ // @ts-expect-error -- expected missing contents
+ { // partial data
+ id: '2',
+ uuid: '123e4567-e89b-12d3-a456-426614174001',
+ name: 'Another Page',
+ slug: 'another-page',
+ createdAt: new Date().toISOString(),
+ updatedAt: new Date().toISOString()
+ },
+ { // invalid data
+ id: '3',
+ uuid: '123e4567-e89b-12d3-a456-426614174002',
+ name: '',
+ slug: '',
+ contents: '{}',
+ createdAt: new Date().toISOString(),
+ updatedAt: new Date().toISOString()
+ },
+]
+
+/**
+ * Creates an array of Page instances from provided data or default mock data
+ * @param data Optional array of page data to convert to Page instances
+ * @returns Array of Page instances
+ */
+export const mockPage = (data: TPage[] = mockPageData()): TPage[] => data.map(item => new Page(item))
diff --git a/src/entities/page/page.spec.ts b/src/entities/page/page.spec.ts
new file mode 100644
index 00000000..fa342192
--- /dev/null
+++ b/src/entities/page/page.spec.ts
@@ -0,0 +1,47 @@
+/* eslint-disable no-console */
+import { Page } from './page'
+import { mockPage } from './page.mock'
+
+describe('Page Store', () => {
+ it('create Page entity with full data', () => {
+ const page = new Page(mockPage()[0])
+
+ expect(page).toBeInstanceOf(Page)
+ expect(page).toEqual(mockPage()[0])
+ expect(page.uuid).toBe(mockPage()[0].uuid)
+ expect(page.name).toBe(mockPage()[0].name)
+ expect(page.slug).toBe(mockPage()[0].slug)
+ expect(page.contents).toEqual(mockPage()[0].contents)
+ expect(page.createdAt).toBe(mockPage()[0].createdAt)
+ expect(page.updatedAt).toBe(mockPage()[0].updatedAt)
+
+ expect(page.validate().success).toBe(true)
+ })
+
+ it('create Page entity with partial data', () => {
+ const page = new Page(mockPage()[1])
+
+ expect(page).toBeInstanceOf(Page)
+ expect(page.id).toBe(mockPage()[1].id)
+ expect(page.uuid).toBe(mockPage()[1].uuid)
+ expect(page.name).toBe(mockPage()[1].name)
+ expect(page.slug).toBe(mockPage()[1].slug)
+ expect(page.contents).toBeNull()
+ expect(page.createdAt).toBe(mockPage()[1].createdAt)
+ expect(page.updatedAt).toBe(mockPage()[1].updatedAt)
+
+ expect(page.validate().success).toBe(true)
+ })
+
+ it('create Page entity with falsy data', () => {
+ const page = new Page(mockPage()[2])
+
+ expect(page).toBeInstanceOf(Page)
+ expect(page).toEqual(mockPage()[2])
+ expect(page.uuid).toBe(mockPage()[2].uuid)
+ expect(page.name).toBe('')
+ expect(page.contents).toBeNull()
+
+ expect(page.validate().success).toBe(false)
+ })
+})
diff --git a/src/entities/page/page.ts b/src/entities/page/page.ts
new file mode 100644
index 00000000..a58e1d78
--- /dev/null
+++ b/src/entities/page/page.ts
@@ -0,0 +1,61 @@
+import { SafeParseReturnType, z } from 'zod'
+import { TPage } from './page.types'
+
+/**
+ * Page class representing a page entity with validation
+ * Implements the TPage interface for type safety
+ */
+export class Page implements TPage {
+
+ public id: string
+ public uuid: string
+ public name: string
+ public slug: string
+ public contents: string
+ public createdAt: string
+ public updatedAt: string
+
+ /**
+ * Creates a new Page instance
+ * @param data Initial page data conforming to TPage interface
+ */
+ constructor(data: TPage) {
+ this.hydrate(data)
+ }
+
+ /* istanbul ignore next */ // Jest does not recognize the code coverage of these 2 methods
+ /**
+ * Hydrates the page object with provided data
+ * @param data Page data to populate the instance
+ */
+ private hydrate(data: TPage) {
+ this.id = data?.id?.toString() || ''
+ this.uuid = data?.uuid || ''
+ this.name = data?.name || ''
+ this.slug = data?.slug || ''
+ this.contents = data?.contents || '{}'
+ this.createdAt = data?.createdAt || ''
+ this.updatedAt = data?.updatedAt || ''
+ }
+
+ /* istanbul ignore next */
+ /**
+ * Validates the page data against a schema
+ * @return SafeParseReturnType containing validation result
+ */
+ public validate(): SafeParseReturnType {
+ // Schema validation for page data
+ const schema = z.object({
+ name: z.string().min(1, 'naam is verplicht'),
+ slug: z.string().min(1, 'slug is verplicht'),
+ contents: z.string().min(1, 'inhoud is verplicht'),
+ })
+
+ const result = schema.safeParse({
+ ...this,
+ })
+
+ return result
+ }
+
+}
diff --git a/src/entities/page/page.types.ts b/src/entities/page/page.types.ts
new file mode 100644
index 00000000..b005d72c
--- /dev/null
+++ b/src/entities/page/page.types.ts
@@ -0,0 +1,13 @@
+/**
+ * Type definition for a Page object
+ * Represents the structure of a page with content and metadata
+ */
+export type TPage = {
+ id: string // Unique identifier for the page
+ uuid: string // Unique identifier for the page
+ name: string // Title/heading of the page
+ contents: string // JSON object, Main content/body of the page - can contain any type of content
+ slug: string // URL-friendly version of the title
+ createdAt: string // Creation timestamp
+ updatedAt: string // Last update timestamp
+}
diff --git a/src/entities/publication/publication.mock.ts b/src/entities/publication/publication.mock.ts
index 5e717bc0..890cbdda 100644
--- a/src/entities/publication/publication.mock.ts
+++ b/src/entities/publication/publication.mock.ts
@@ -15,6 +15,7 @@ export const mockPublicationsData = (): TPublication[] => [
publicationType: '4',
published: '2024-09-04T12:36:39Z',
modified: '2024-09-04T12:36:39Z',
+ organization: '1',
featured: true,
data: {
key: 'anyvalue',
@@ -60,6 +61,7 @@ export const mockPublicationsData = (): TPublication[] => [
published: '2024-09-04T12:36:39Z',
modified: '2024-09-04T12:36:39Z',
featured: true,
+ organization: '1',
data: {
type: '',
},
diff --git a/src/entities/publication/publication.ts b/src/entities/publication/publication.ts
index dee7e62f..2e5bd360 100644
--- a/src/entities/publication/publication.ts
+++ b/src/entities/publication/publication.ts
@@ -21,6 +21,7 @@ export class Publication implements TPublication {
public attachments: number[]
public attachmentCount: number
public themes: string[]
+ public organization: string
public data: Record
public anonymization: {
@@ -60,6 +61,7 @@ export class Publication implements TPublication {
this.description = data.description || ''
this.reference = data.reference || ''
this.image = data.image || ''
+ this.organization = data.organization || ''
this.category = data.category || ''
this.portal = data.portal || ''
this.featured = (typeof data.featured === 'boolean' && data.featured)
@@ -113,6 +115,7 @@ export class Publication implements TPublication {
portal: z.string().url('is niet een url').or(z.literal('')),
featured: z.boolean(),
schema: z.string(),
+ organization: z.string(),
status: z.enum(['Concept', 'Published', 'Withdrawn', 'Archived', 'Revised', 'Rejected']),
attachments: z.union([z.string(), z.number()]).array(),
attachmentCount: z.number(),
diff --git a/src/entities/publication/publication.types.ts b/src/entities/publication/publication.types.ts
index 52a80a94..a99f6194 100644
--- a/src/entities/publication/publication.types.ts
+++ b/src/entities/publication/publication.types.ts
@@ -17,6 +17,7 @@ export type TPublication = {
status: 'Concept' | 'Published' | 'Withdrawn' | 'Archived' | 'Revised' | 'Rejected'
attachments: number[]
attachmentCount: number
+ organization: string
themes: string[]
data: Record
anonymization: {
diff --git a/src/modals/Modals.vue b/src/modals/Modals.vue
index 498e7bf8..94482e76 100644
--- a/src/modals/Modals.vue
+++ b/src/modals/Modals.vue
@@ -25,11 +25,12 @@ import { publicationStore } from './../store/store.js'
+
+
diff --git a/src/modals/organization/AddOrganizationModal.vue b/src/modals/organization/AddOrganizationModal.vue
index b42652da..8776de83 100644
--- a/src/modals/organization/AddOrganizationModal.vue
+++ b/src/modals/organization/AddOrganizationModal.vue
@@ -64,6 +64,12 @@ import { navigationStore, organizationStore } from '../../store/store.js'
:value.sync="organization.pki"
:error="!!inputValidation.fieldErrors?.['pki']"
:helper-text="inputValidation.fieldErrors?.['pki']?.[0]" />
+
+
+import { navigationStore, pageStore } from '../../store/store.js'
+import { getTheme } from '../../services/getTheme.js'
+
+
+
+
+
+
Pagina toevoegen
+
+
+ Pagina succesvol toegevoegd
+
+
+ Er is iets fout gegaan bij het toevoegen van Pagina
+
+
+ {{ error }}
+
+
+
+
+
+
+
+
+ Toevoegen
+
+
+
+
+
+
+
+
diff --git a/src/modals/page/EditPageModal.vue b/src/modals/page/EditPageModal.vue
new file mode 100644
index 00000000..c1d45ce3
--- /dev/null
+++ b/src/modals/page/EditPageModal.vue
@@ -0,0 +1,207 @@
+
+
+
+
+
Pagina bewerken
+
+
+ Pagina succesvol bewerkt
+
+
+ Er is iets fout gegaan bij het bewerken van Pagina
+
+
+ {{ error }}
+
+
+
+
+
+
+
+
+ Opslaan
+
+
+
+
+
+
+
+
diff --git a/src/modals/publication/AddPublicationModal.vue b/src/modals/publication/AddPublicationModal.vue
index 5a9e2910..c5252d27 100644
--- a/src/modals/publication/AddPublicationModal.vue
+++ b/src/modals/publication/AddPublicationModal.vue
@@ -1,5 +1,5 @@
@@ -164,6 +164,11 @@ import { publicationTypeStore, navigationStore, publicationStore, catalogiStore
:value.sync="publication.license"
:error="!!inputValidation.fieldErrors?.['license']"
:helper-text="inputValidation.fieldErrors?.['license']?.[0]" />
+
@@ -222,6 +227,10 @@ export default {
image: '',
data: {},
},
+ organizations: {
+ value: [],
+ options: [],
+ },
catalogiList: [], // this is the entire dataset of catalogi
catalogi: {},
publicationTypeList: [], // this is the entire dataset of publication types
@@ -270,6 +279,7 @@ export default {
catalog: this.catalogi.value?.id,
publicationType: this.publicationType?.value?.id,
published: this.publication.published !== '' ? new Date(this.publication.published).toISOString() : new Date().toISOString(),
+ organization: this.organizations.value?.id,
})
const result = testClass.validate()
@@ -300,6 +310,7 @@ export default {
this.fetchCatalogi()
this.fetchPublicationType()
+ this.fetchOrganizations()
this.hasUpdated = true
}
},
@@ -346,6 +357,26 @@ export default {
this.publicationTypeLoading = false
})
},
+ fetchOrganizations() {
+ this.organizationsLoading = true
+
+ organizationStore.getAllOrganization()
+ .then(({ response, data }) => {
+
+ this.organizations = {
+ options: data.map((organization) => ({
+ id: organization.id,
+ label: organization.title,
+ })),
+ }
+
+ this.organizationsLoading = false
+ })
+ .catch((err) => {
+ console.error(err)
+ this.organizationsLoading = false
+ })
+ },
isJsonString(str) {
try {
JSON.parse(str)
@@ -363,6 +394,7 @@ export default {
catalog: this.catalogi?.value?.id,
publicationType: this.publicationType?.value?.id,
published: this.publication.published !== '' ? new Date(this.publication.published).toISOString() : new Date().toISOString(),
+ organization: this.organizations.value?.id,
})
publicationStore.addPublication(publicationItem)
diff --git a/src/modals/publication/EditPublicationModal.vue b/src/modals/publication/EditPublicationModal.vue
index 7b9d5919..7e3cb45b 100644
--- a/src/modals/publication/EditPublicationModal.vue
+++ b/src/modals/publication/EditPublicationModal.vue
@@ -1,5 +1,5 @@
+
{
+ const selectedOrganization = data.filter((org) => org?.id.toString() === publicationStore.publicationItem?.organization.toString())[0] || null
+
+ this.organizations = {
+ options: data.map((organization) => ({
+ id: organization.id,
+ label: organization.title,
+ })),
+ value: selectedOrganization
+ ? {
+ id: selectedOrganization?.id,
+ label: selectedOrganization?.title,
+ }
+ : null,
+ }
+
+ this.organizationsLoading = false
+ })
+ .catch((err) => {
+ console.error(err)
+ this.organizationsLoading = false
+ })
+ },
updatePublication() {
this.loading = true
@@ -232,6 +274,7 @@ export default {
},
publicationType: this.publicationItem.publicationType.id ?? this.publicationItem.publicationType,
published: this.publicationItem.published !== '' ? new Date(this.publicationItem.published).toISOString() : new Date().toISOString(),
+ organization: this.organizations.value?.id,
})
publicationStore.editPublication(publicationItem)
diff --git a/src/modals/publicationData/AddPublicationDataModal.vue b/src/modals/publicationData/AddPublicationDataModal.vue
index 8074c1b9..89a7dbf2 100644
--- a/src/modals/publicationData/AddPublicationDataModal.vue
+++ b/src/modals/publicationData/AddPublicationDataModal.vue
@@ -1,5 +1,7 @@
-
+
import { navigationStore, publicationStore } from '../../store/store.js'
+import { getTheme } from '../../services/getTheme.js'
+
-
+
+
+
+
+
+
@@ -92,6 +97,7 @@ import Finance from 'vue-material-design-icons/Finance.vue'
import BookOpenVariantOutline from 'vue-material-design-icons/BookOpenVariantOutline.vue'
import OfficeBuildingOutline from 'vue-material-design-icons/OfficeBuildingOutline.vue'
import ShapeOutline from 'vue-material-design-icons/ShapeOutline.vue'
+import Web from 'vue-material-design-icons/Web.vue'
export default {
name: 'MainMenu',
@@ -114,6 +120,7 @@ export default {
BookOpenVariantOutline,
OfficeBuildingOutline,
ShapeOutline,
+ Web,
},
data() {
return {
diff --git a/src/sidebars/directory/DirectorySideBar.vue b/src/sidebars/directory/DirectorySideBar.vue
index 55cdb449..849c394d 100644
--- a/src/sidebars/directory/DirectorySideBar.vue
+++ b/src/sidebars/directory/DirectorySideBar.vue
@@ -46,7 +46,7 @@ import { navigationStore, directoryStore, publicationTypeStore } from '../../sto
Samenvatting:
- {{ directoryStore.listingItem?.summery }}
+ {{ directoryStore.listingItem?.summary }}
Status:
diff --git a/src/store/modules/page.spec.ts b/src/store/modules/page.spec.ts
new file mode 100644
index 00000000..be1dbb7a
--- /dev/null
+++ b/src/store/modules/page.spec.ts
@@ -0,0 +1,53 @@
+/* eslint-disable no-console */
+import { setActivePinia, createPinia } from 'pinia'
+
+import { usePageStore } from './page.js'
+import { mockPage, Page } from '../../entities/index.js'
+
+describe('Page Store', () => {
+ beforeEach(() => {
+ setActivePinia(createPinia())
+ })
+
+ it('sets page item correctly', () => {
+ const store = usePageStore()
+
+ store.setPageItem(mockPage()[0])
+
+ expect(store.pageItem).toBeInstanceOf(Page)
+ expect(store.pageItem).toEqual(mockPage()[0])
+ expect(store.pageItem.validate().success).toBe(true)
+
+ store.setPageItem(mockPage()[1])
+
+ expect(store.pageItem).toBeInstanceOf(Page)
+ expect(store.pageItem).toEqual(mockPage()[1])
+ expect(store.pageItem.validate().success).toBe(true)
+
+ store.setPageItem(mockPage()[2])
+
+ expect(store.pageItem).toBeInstanceOf(Page)
+ expect(store.pageItem).toEqual(mockPage()[2])
+ expect(store.pageItem.validate().success).toBe(false)
+ })
+
+ it('sets page list correctly', () => {
+ const store = usePageStore()
+
+ store.setPageList(mockPage())
+
+ expect(store.pageList).toHaveLength(mockPage().length)
+
+ expect(store.pageList[0]).toBeInstanceOf(Page)
+ expect(store.pageList[0]).toEqual(mockPage()[0])
+ expect(store.pageList[0].validate().success).toBe(true)
+
+ expect(store.pageList[1]).toBeInstanceOf(Page)
+ expect(store.pageList[1]).toEqual(mockPage()[1])
+ expect(store.pageList[1].validate().success).toBe(true)
+
+ expect(store.pageList[2]).toBeInstanceOf(Page)
+ expect(store.pageList[2]).toEqual(mockPage()[2])
+ expect(store.pageList[2].validate().success).toBe(false)
+ })
+})
diff --git a/src/store/modules/page.ts b/src/store/modules/page.ts
new file mode 100644
index 00000000..0d132a7c
--- /dev/null
+++ b/src/store/modules/page.ts
@@ -0,0 +1,160 @@
+/* eslint-disable no-console */
+import { Page, TPage } from '../../entities/index.js'
+import { defineStore } from 'pinia'
+
+const apiEndpoint = '/index.php/apps/opencatalogi/api/pages'
+
+interface Options {
+ /**
+ * Do not save the received item to the store, this can be enabled if API calls get run in a loop
+ */
+ doNotSetStore?: boolean
+}
+
+interface PageStoreState {
+ pageItem: Page;
+ pageList: Page[];
+}
+
+export const usePageStore = defineStore('page', {
+ state: () => ({
+ pageItem: null,
+ pageList: [],
+ } as PageStoreState),
+ actions: {
+ setPageItem(pageItem: Page | TPage) {
+ this.pageItem = pageItem && new Page(pageItem as TPage)
+ console.log('Active page item set to ' + pageItem && pageItem?.id)
+ },
+ setPageList(pageList: Page[] | TPage[]) {
+ this.pageList = pageList.map(
+ (pageItem) => new Page(pageItem as TPage),
+ )
+ console.log('Page list set to ' + pageList.length + ' items')
+ },
+ /* istanbul ignore next */ // ignore this for Jest until moved into a service
+ async refreshPageList(search: string = null) {
+ // @todo this might belong in a service?
+ let endpoint = apiEndpoint
+ if (search !== null && search !== '') {
+ endpoint = endpoint + '?_search=' + search
+ }
+ const response = await fetch(
+ endpoint, {
+ method: 'GET',
+ },
+ )
+ const rawData = (await response.json()).results
+ const data = rawData.map((pageItem: TPage) => new Page(pageItem))
+
+ this.setPageList(data)
+
+ return { response, data }
+ },
+ /* istanbul ignore next */
+ async getAllPages(options: Options = {}) {
+ const response = await fetch(
+ `${apiEndpoint}`,
+ { method: 'get' },
+ )
+
+ const rawData = await response.json()
+
+ const data = rawData.results.map((pageItem: TPage) => new Page(pageItem))
+
+ options.doNotSetStore !== true && this.setPageList(data)
+
+ return { response, data }
+ },
+ /* istanbul ignore next */
+ async getOnePage(id: number, options: Options = {}) {
+ if (!id) {
+ throw Error('Passed id is falsy')
+ }
+
+ const response = await fetch(
+ `${apiEndpoint}/${id}`,
+ { method: 'get' },
+ )
+
+ const data = new Page(await response.json())
+
+ options.doNotSetStore !== true && this.setPageItem(data)
+
+ return { response, data }
+ },
+ /* istanbul ignore next */
+ async addPage(item: Page) {
+ if (!(item instanceof Page)) {
+ throw Error('Please pass a Page item from the Page class')
+ }
+
+ const validateResult = item.validate()
+ if (!validateResult.success) {
+ throw Error(validateResult.error.issues[0].message)
+ }
+
+ const response = await fetch(apiEndpoint,
+ {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify(validateResult.data),
+ },
+ )
+
+ const data = new Page(await response.json())
+
+ this.refreshPageList()
+ this.setPageItem(data)
+
+ return { response, data }
+ },
+ /* istanbul ignore next */
+ async editPage(pageItem: Page) {
+ if (!(pageItem instanceof Page)) {
+ throw Error('Please pass a Page item from the Page class')
+ }
+
+ const validateResult = pageItem.validate()
+ if (!validateResult.success) {
+ throw Error(validateResult.error.issues[0].message)
+ }
+
+ const response = await fetch(
+ `${apiEndpoint}/${pageItem.id}`,
+ {
+ method: 'PUT',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify(validateResult.data),
+ },
+ )
+
+ const data = new Page(await response.json())
+
+ this.refreshPageList()
+ this.setPageItem(data)
+
+ return { response, data }
+ },
+ /* istanbul ignore next */
+ async deletePage(id: number) {
+ if (!id) {
+ throw Error('Passed id is falsy')
+ }
+
+ const response = await fetch(
+ `${apiEndpoint}/${id}`,
+ { method: 'DELETE' },
+ )
+
+ this.refreshPageList()
+ this.setPageItem(null)
+
+ return { response }
+ },
+ },
+})
diff --git a/src/store/store.js b/src/store/store.js
index 1eddc424..0f1b9e7d 100644
--- a/src/store/store.js
+++ b/src/store/store.js
@@ -14,6 +14,7 @@ import { useOrganizationStore } from './modules/organization'
import { usePublicationStore } from './modules/publication'
import { useSearchStore } from './modules/search'
import { useThemeStore } from './modules/theme'
+import { usePageStore } from './modules/page'
const navigationStore = useNavigationStore(pinia)
const searchStore = useSearchStore(pinia)
@@ -24,6 +25,7 @@ const publicationStore = usePublicationStore(pinia)
const organizationStore = useOrganizationStore(pinia)
const themeStore = useThemeStore(pinia)
const configurationStore = useConfigurationStore(pinia)
+const pageStore = usePageStore(pinia)
export {
// generic
@@ -37,4 +39,5 @@ export {
organizationStore,
themeStore,
configurationStore,
+ pageStore,
}
diff --git a/src/views/AdminSettings.vue b/src/views/AdminSettings.vue
index 9e739032..67d5c8dd 100644
--- a/src/views/AdminSettings.vue
+++ b/src/views/AdminSettings.vue
@@ -19,7 +19,7 @@
-
+
Het lijkt erop dat je een open register hebt geselecteerd maar dat deze nog niet geïnstalleerd is. Dit kan problemen geven. Wil je de instelling resetten?
@@ -244,6 +244,41 @@
+
Pagina
+
+
+
+
+
+
+
+
+
+
+
+
+ Opslaan
+
+
+
Publicatie Type
+
@@ -23,6 +24,7 @@ import { NcAppContent } from '@nextcloud/vue'
import Catalogi from './catalogi/CatalogiIndex.vue'
import Organizations from './organizations/OrganizationIndex.vue'
import Themes from './themes/ThemeIndex.vue'
+import Pages from './pages/PageIndex.vue'
import Dashboard from './dashboard/DashboardIndex.vue'
import Directory from './directory/DirectoryIndex.vue'
import PublicationType from './publicationType/PublicationTypeIndex.vue'
@@ -35,6 +37,7 @@ export default {
Catalogi,
Organizations,
Themes,
+ Pages,
Dashboard,
Directory,
PublicationType,
diff --git a/src/views/organizations/OrganizationDetail.vue b/src/views/organizations/OrganizationDetail.vue
index ff32db36..8f2555fa 100644
--- a/src/views/organizations/OrganizationDetail.vue
+++ b/src/views/organizations/OrganizationDetail.vue
@@ -61,6 +61,10 @@ import { navigationStore, organizationStore } from '../../store/store.js'
Beschrijving:
{{ organization.description }}
+
+ Afbeelding:
+ {{ organization.image }}
+
diff --git a/src/views/pages/PageDetail.vue b/src/views/pages/PageDetail.vue
new file mode 100644
index 00000000..2debbbd3
--- /dev/null
+++ b/src/views/pages/PageDetail.vue
@@ -0,0 +1,231 @@
+
+
+
+
+
+
+ {{ page.name }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Help
+
+
+
+
+
+ Bewerken
+
+
+
+
+
+ Kopiëren
+
+
+
+
+
+ Verwijderen
+
+
+
+
+
+
+ Name:
+ {{ page.name }}
+
+
+ Slug:
+ {{ page.slug }}
+
+
+ Laatst bijgewerkt:
+ {{ page.updatedAt }}
+
+
+
+
+
+
+
+
+
+
diff --git a/src/views/pages/PageIndex.vue b/src/views/pages/PageIndex.vue
new file mode 100644
index 00000000..5e4cbbb3
--- /dev/null
+++ b/src/views/pages/PageIndex.vue
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Pagina toevoegen
+
+
+
+
+
+
+
+
+
diff --git a/src/views/pages/PageList.vue b/src/views/pages/PageList.vue
new file mode 100644
index 00000000..e253895c
--- /dev/null
+++ b/src/views/pages/PageList.vue
@@ -0,0 +1,202 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ page?.slug }}
+
+
+
+
+
+
+ Bewerken
+
+
+
+
+
+ Kopiëren
+
+
+
+
+
+ Verwijderen
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/views/publications/PublicationDetail.vue b/src/views/publications/PublicationDetail.vue
index bb8b5529..71530b20 100644
--- a/src/views/publications/PublicationDetail.vue
+++ b/src/views/publications/PublicationDetail.vue
@@ -1,5 +1,5 @@
@@ -178,6 +178,26 @@ import { ref } from 'vue'
+
+
Organisatie:
+
Loading...
+
+ {{ organization?.title }}
+
+
+
+
+
+ {{ organization?.title }}
+
+
+
+
+ Geen organisatie gekoppeld
+
+
@@ -329,7 +349,7 @@ import { ref } from 'vue'
-
+
-
+
Geen thema's gevonden
@@ -519,11 +539,13 @@ export default {
publication: [],
catalogi: [],
publicationType: [],
+ organization: [],
themes: [],
prive: false,
loading: false,
catalogiLoading: false,
publicationTypeLoading: false,
+ organizationLoading: false,
hasUpdated: false,
userGroups: [
{
@@ -594,6 +616,7 @@ export default {
this.fetchPublicationType(data.publicationType)
this.fetchThemes()
publicationStore.getPublicationAttachments(id)
+ data?.organization && this.fetchOrganization(data.organization, true)
// this.loading = false
})
.catch((err) => {
@@ -616,6 +639,20 @@ export default {
if (loading) { this.catalogiLoading = false }
})
},
+ fetchOrganization(organizationId, loading) {
+ if (loading) { this.organizationLoading = true }
+
+ organizationStore.getOneOrganization(organizationId, { doNotSetStore: true })
+ .then(({ response, data }) => {
+ this.organization = data
+
+ if (loading) { this.organizationLoading = false }
+ })
+ .catch((err) => {
+ console.error(err)
+ if (loading) { this.organizationLoading = false }
+ })
+ },
fetchPublicationType(publicationTypeUrl, loading) {
if (loading) this.publicationTypeLoading = true
@@ -703,6 +740,10 @@ export default {
publicationTypeStore.setPublicationTypeItem(this.publicationType)
navigationStore.setSelected('publicationType')
},
+ goToOrganization() {
+ organizationStore.setOrganizationItem(this.organization)
+ navigationStore.setSelected('organizations')
+ },
goToCatalogi() {
catalogiStore.setCatalogiItem(this.catalogi)
navigationStore.setSelected('catalogi')