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/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/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/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/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 0832eaaf..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); } @@ -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 6bb66f91..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); } @@ -149,8 +154,12 @@ public function updateFromArray(int $id, array $object, bool $updateVersion = tr 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/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 3c605d4c..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); } @@ -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/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 56363f96..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); } @@ -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 cfc9885d..dd729b82 100644 --- a/lib/Db/PublicationType.php +++ b/lib/Db/PublicationType.php @@ -10,6 +10,7 @@ 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; @@ -23,6 +24,7 @@ class PublicationType 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: 'description', type: 'string'); @@ -87,6 +89,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/PublicationTypeMapper.php b/lib/Db/PublicationTypeMapper.php index 1474130d..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); } @@ -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 8b0b239b..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); } @@ -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/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 09286299..05521f3f 100644 --- a/lib/Service/DirectoryService.php +++ b/lib/Service/DirectoryService.php @@ -25,7 +25,11 @@ 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 { @@ -93,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']); @@ -159,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'])) { @@ -262,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; } @@ -368,14 +373,9 @@ public function syncExternalDirectory(string $url): array // Get all current listings for this directory $currentListings = $this->objectService->getObjects( - objectType: 'listing', - filters: [ - 'directory'=>$checkUrls, - ] + objectType: 'listing' ); - - // Remove any listings without a catalog ID from the database foreach ($currentListings as $listing) { if (empty($listing['catalog'])) { @@ -395,12 +395,15 @@ public function syncExternalDirectory(string $url): array '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) { @@ -410,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 @@ -442,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, 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 1ffe4a6c..d237e181 100644 --- a/lib/Service/ObjectService.php +++ b/lib/Service/ObjectService.php @@ -29,8 +29,12 @@ 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 */ 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 9ce35df0..8c36e6b7 100644 --- a/lib/Service/ValidationService.php +++ b/lib/Service/ValidationService.php @@ -18,10 +18,13 @@ 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 { 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

+ + + + + + {% if publication.catalogi.summary|default %} + + + + + {% endif %} + {% if publication.catalogi.description|default %} + + + + + {% endif %} + {# {% if publication.catalogi.organisation|default %}#} + {# #} + {# #} + {# #} + {# #} + {# {% endif %}#} +
Titel: {{ publication.catalogi.title }}
Samenvatting: {{ publication.catalogi.summary }}
Beschrijving: {{ publication.catalogi.description }}
Organisatie: {{ publication.catalogi.organisation }}
+{% endif %} + +{% if publication.metaData|default %} +

Publicatie Type

+ + + + + + {% if publication.metaData.version|default %} + + + + + {% endif %} + {% if publication.metaData.description|default %} + + + + + {% endif %} + {# {% if publication.metaData.required|default %}#} + {# #} + {# #} + {# #} + {# #} + {# {% endif %}#} +
Titel: {{ publication.metaData.title }}
Versie: {{ publication.metaData.version }}
Beschrijving: {{ publication.metaData.description }}
Vereisten: {{ publication.metaData.required }}
+{% endif %} + +
+ + + {% if publication.reference|default %} + + + + + {% endif %} + {% if publication.summary|default %} + + + + + {% endif %} + {% if publication.description|default %} + + + + + {% endif %} + {% if publication.category|default %} + + + + + {% endif %} + {% if publication.portal|default %} + + + + + {% endif %} + {% if publication.image|default %} + + + + + {% endif %} + {# {% if publication.themes|default %}#} + {# #} + {# #} + {# #} + {# #} + {# {% endif %}#} + {% if publication.featured is defined %} + + + + + {% endif %} + {% if publication.license|default %} + + + + + {% endif %} + {% if publication.status|default %} + + + + + {% endif %} + {% if publication.published|default %} + + + + + {% endif %} + {% if publication.modified|default %} + + + + + {% endif %} +
Referentie: {{ publication.reference }}
Samenvatting: {{ publication.summary }}
Beschrijving: {{ publication.description }}
Categorie: {{ publication.category }}
Portal: {{ publication.portal }}
Foto: {{ publication.image }}
Thema's: {{ publication.themes }}
Uitgelicht: {% if publication.featured == true %}Ja{% else %}Nee{% endif %}
Licentie: {{ publication.license }}
Status: {{ publication.status }}
Gepubliceerd: {{ publication.published | date("d-m-Y H:i") }}
Gewijzigd: {{ publication.modified | date("d-m-Y H:i") }}
+ +{% if publication.data|default %} + +

Eigenschappen

+ + + + + + {% for key, value in publication.data %} + + + + + {% endfor %} +
NaamData
{{ key }}{{ value }}
+{% 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/src/entities/page/page.ts b/src/entities/page/page.ts index 1a510c98..a58e1d78 100644 --- a/src/entities/page/page.ts +++ b/src/entities/page/page.ts @@ -41,7 +41,7 @@ export class Page implements TPage { /* istanbul ignore next */ /** * Validates the page data against a schema - * @returns SafeParseReturnType containing validation result + * @return SafeParseReturnType containing validation result */ public validate(): SafeParseReturnType { // Schema validation for page data diff --git a/src/modals/page/AddPageModal.vue b/src/modals/page/AddPageModal.vue index 8f50fd34..3b7b2fc8 100644 --- a/src/modals/page/AddPageModal.vue +++ b/src/modals/page/AddPageModal.vue @@ -1,6 +1,8 @@ +