Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
59d6ad5
fix(register): seed organisations save on a fresh install
rubenvdlinde Oct 1, 2026
ff16c88
docs(openspec): widen sharing-itsm-exchange to a CMDB with two-way sync
rubenvdlinde Oct 1, 2026
7529d6d
feat(itsm): fields, flow templates, set-up and file import for the se…
rubenvdlinde Oct 1, 2026
8547225
feat(itsm): CMDB page, service desk exchange settings and service des…
rubenvdlinde Oct 1, 2026
09509ce
Merge remote-tracking branch 'origin/development' into feature/sharin…
rubenvdlinde Oct 1, 2026
4ed9a3d
feat(itsm): follow integriq's desk interface: desk context, endpoints…
rubenvdlinde Oct 1, 2026
c505509
test(connections): the itsm key is the fourth declared connection
rubenvdlinde Oct 1, 2026
8aceed6
test(itsm): live run script against the TOPdesk and ServiceNow mocks
rubenvdlinde Oct 1, 2026
21b4c53
fix(itsm): a second set-up opens a draft before editing a published f…
rubenvdlinde Oct 1, 2026
958df42
refactor(itsm): split the set-up checks, drop else and error suppress…
rubenvdlinde Oct 1, 2026
8df9065
docs(itsm): service desk exchange page; live script uses the TOPdesk …
rubenvdlinde Oct 1, 2026
fdbe64d
feat(publication): keep contacts, costs and internal judgements priva…
rubenvdlinde Oct 1, 2026
4b56861
test(publication): live check of what an anonymous reader gets; Dutch…
rubenvdlinde Oct 1, 2026
6e392df
fix(cmdb): the custom CMDB page says why it is custom, and its mount …
rubenvdlinde Oct 1, 2026
70fdcef
Merge branch 'feature/sharing-itsm-exchange' into feature/publication…
rubenvdlinde Oct 1, 2026
0437206
Merge remote-tracking branch 'origin/development' into feature/sharin…
rubenvdlinde Oct 1, 2026
97b89fc
Merge branch 'feature/sharing-itsm-exchange' into feature/publication…
rubenvdlinde Oct 1, 2026
711362e
chore(version): 0.2.4-unstable so occ upgrade runs the new BackfillMo…
rubenvdlinde Oct 1, 2026
7d330f9
fix(cmdb): the CMDB page is a dashboard with stat tiles and a cmdb-ov…
rubenvdlinde Oct 1, 2026
1d9f175
Merge branch 'feature/sharing-itsm-exchange' into feature/publication…
rubenvdlinde Oct 1, 2026
4da1d7f
fix(publication): module 0.3.6, above the 0.3.5 that cmdb-export-impo…
rubenvdlinde Oct 1, 2026
76d8845
Merge remote-tracking branch 'origin/development' into feature/public…
rubenvdlinde Oct 1, 2026
ffa6493
Merge remote-tracking branch 'origin/development' into feature/public…
rubenvdlinde Oct 1, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion appinfo/info.xml
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ Vrij en open source onder de EUPL-licentie.

**Ondersteuning:** Voor ondersteuning, neem contact op via support@conduction.nl. Voor een Service Level Agreement (SLA), neem contact op via sales@conduction.nl.
]]></description>
<version>0.2.3-unstable.20260912202721</version>
<version>0.2.4-unstable.20261001120000</version>
<licence>EUPL-1.2</licence>
<author mail="info@conduction.nl" homepage="https://www.conduction.nl/">Conduction</author>
<namespace>Stackiq</namespace>
Expand Down Expand Up @@ -224,6 +224,7 @@ Vrij en open source onder de EUPL-licentie.
<step>OCA\Stackiq\Repair\InitializeSettings</step>
<step>OCA\Stackiq\Repair\MigrateContactsToNc</step>
<step>OCA\Stackiq\Repair\BackfillContractApprovalState</step>
<step>OCA\Stackiq\Repair\BackfillModuleVersionPublication</step>
<!--
Must run AFTER the register sync that adds the English columns, so it
can tell the rename case from the back-fill case. Scoped by SCHEMA,
Expand Down
6 changes: 5 additions & 1 deletion l10n/en.js
Original file line number Diff line number Diff line change
Expand Up @@ -1069,7 +1069,11 @@ OC.L10N.register(
"Created in source": "Created in source",
"The date the application was registered in the source system.": "The date the application was registered in the source system.",
"Changed in source": "Changed in source",
"The date the application was last changed in the source system.": "The date the application was last changed in the source system."
"The date the application was last changed in the source system.": "The date the application was last changed in the source system.",
"Application published": "Application published",
"The publication date of the application this version belongs to, copied from it. A version is public only while its application is.": "The publication date of the application this version belongs to, copied from it. A version is public only while its application is.",
"Application registered by": "Application registered by",
"Who registered the application this version belongs to, copied from it. A supplier's application is public, and so are its versions.": "Who registered the application this version belongs to, copied from it. A supplier's application is public, and so are its versions."
},
"nplurals=2; plural=(n != 1);"
)
6 changes: 5 additions & 1 deletion l10n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -1068,6 +1068,10 @@
"Created in source": "Created in source",
"The date the application was registered in the source system.": "The date the application was registered in the source system.",
"Changed in source": "Changed in source",
"The date the application was last changed in the source system.": "The date the application was last changed in the source system."
"The date the application was last changed in the source system.": "The date the application was last changed in the source system.",
"Application published": "Application published",
"The publication date of the application this version belongs to, copied from it. A version is public only while its application is.": "The publication date of the application this version belongs to, copied from it. A version is public only while its application is.",
"Application registered by": "Application registered by",
"Who registered the application this version belongs to, copied from it. A supplier's application is public, and so are its versions.": "Who registered the application this version belongs to, copied from it. A supplier's application is public, and so are its versions."
}
}
6 changes: 5 additions & 1 deletion l10n/nl.js
Original file line number Diff line number Diff line change
Expand Up @@ -1139,7 +1139,11 @@ OC.L10N.register(
"Created in source": "Aangemaakt in de bron",
"The date the application was registered in the source system.": "De datum waarop de applicatie in het bronsysteem is geregistreerd.",
"Changed in source": "Gewijzigd in de bron",
"The date the application was last changed in the source system.": "De datum waarop de applicatie in het bronsysteem het laatst is gewijzigd."
"The date the application was last changed in the source system.": "De datum waarop de applicatie in het bronsysteem het laatst is gewijzigd.",
"Application published": "Applicatie gepubliceerd",
"The publication date of the application this version belongs to, copied from it. A version is public only while its application is.": "De publicatiedatum van de applicatie waar deze versie bij hoort, daarvan overgenomen. Een versie is alleen openbaar zolang haar applicatie dat is.",
"Application registered by": "Applicatie geregistreerd door",
"Who registered the application this version belongs to, copied from it. A supplier's application is public, and so are its versions.": "Wie de applicatie registreerde waar deze versie bij hoort, daarvan overgenomen. De applicatie van een leverancier is openbaar, en haar versies ook."
},
"nplurals=2; plural=(n != 1);"
)
6 changes: 5 additions & 1 deletion l10n/nl.json
Original file line number Diff line number Diff line change
Expand Up @@ -1138,6 +1138,10 @@
"Created in source": "Aangemaakt in de bron",
"The date the application was registered in the source system.": "De datum waarop de applicatie in het bronsysteem is geregistreerd.",
"Changed in source": "Gewijzigd in de bron",
"The date the application was last changed in the source system.": "De datum waarop de applicatie in het bronsysteem het laatst is gewijzigd."
"The date the application was last changed in the source system.": "De datum waarop de applicatie in het bronsysteem het laatst is gewijzigd.",
"Application published": "Applicatie gepubliceerd",
"The publication date of the application this version belongs to, copied from it. A version is public only while its application is.": "De publicatiedatum van de applicatie waar deze versie bij hoort, daarvan overgenomen. Een versie is alleen openbaar zolang haar applicatie dat is.",
"Application registered by": "Applicatie geregistreerd door",
"Who registered the application this version belongs to, copied from it. A supplier's application is public, and so are its versions.": "Wie de applicatie registreerde waar deze versie bij hoort, daarvan overgenomen. De applicatie van een leverancier is openbaar, en haar versies ook."
}
}
5 changes: 5 additions & 0 deletions lib/AppInfo/Application.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
use OCA\Stackiq\Dashboard\ConceptOrganisatiesWidget;
use OCA\Stackiq\EventListener\DecisionConcludedListener;
use OCA\Stackiq\EventListener\MaintenanceRecipientsListener;
use OCA\Stackiq\EventListener\ModuleVersionPublicationListener;
use OCA\Stackiq\EventListener\ModuleComplianceSubscriber;
use OCA\Stackiq\EventListener\ModuleRegistrationSubscriber;
use OCA\Stackiq\EventListener\TestEventListener;
Expand Down Expand Up @@ -811,6 +812,10 @@ private function registerEventListeners(IRegistrationContext $context): void {
// Queue the owner resolution when a supplier announces maintenance (lifecycle-maintenance-and-supplier-roadmap).
$context->registerEventListener(ObjectCreatedEvent::class, MaintenanceRecipientsListener::class);

// A module version is public only while its application is (publication-field-rules).
$context->registerEventListener(ObjectCreatedEvent::class, ModuleVersionPublicationListener::class);
$context->registerEventListener(ObjectUpdatedEvent::class, ModuleVersionPublicationListener::class);

// Sync user profile updates into the contactpersoon mirror.
$context->registerEventListener(UserProfileUpdatedEvent::class, UserProfileUpdatedEventListener::class);

Expand Down
81 changes: 81 additions & 0 deletions lib/EventListener/ModuleVersionPublicationListener.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
<?php

/**
* Copies a module's publication onto its versions whenever either is saved.
*
* @category EventListener
* @package OCA\Stackiq\EventListener
* @author Conduction b.v. <info@conduction.nl>
* @copyright 2026 Conduction B.V.
* @license EUPL-1.2 https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
* @link https://github.com/ConductionNL/stackiq
*
* @spec openspec/changes/publication-field-rules/specs/publication-field-rules/spec.md#requirement-req-pfr-002-a-module-version-is-public-only-while-its-application-is
*
* SPDX-FileCopyrightText: 2026 Conduction B.V. <info@conduction.nl>
* SPDX-License-Identifier: EUPL-1.2
*/

declare(strict_types=1);

namespace OCA\Stackiq\EventListener;

use OCA\OpenRegister\Event\ObjectCreatedEvent;
use OCA\OpenRegister\Event\ObjectUpdatedEvent;
use OCA\Stackiq\Service\ModuleVersionPublicationService;
use OCP\EventDispatcher\Event;
use OCP\EventDispatcher\IEventListener;
use Psr\Log\LoggerInterface;
use Throwable;

/**
* Hands every created or updated object to the publication mirror.
*
* @spec openspec/changes/publication-field-rules/specs/publication-field-rules/spec.md#requirement-req-pfr-002-a-module-version-is-public-only-while-its-application-is
*
* @template-implements IEventListener<Event>
*/
class ModuleVersionPublicationListener implements IEventListener {

/**
* Constructor.
*
* @param ModuleVersionPublicationService $publication The mirror.
* @param LoggerInterface $logger The logger.
*/
public function __construct(
private readonly ModuleVersionPublicationService $publication,
private readonly LoggerInterface $logger,
) {
}//end __construct()

/**
* Handle an object event.
*
* @param Event $event The event.
*
* @return void
*
* @spec openspec/changes/publication-field-rules/specs/publication-field-rules/spec.md#requirement-req-pfr-002-a-module-version-is-public-only-while-its-application-is
*/
public function handle(Event $event): void {
$object = null;
if ($event instanceof ObjectUpdatedEvent) {
$object = $event->getNewObject();
}

if ($event instanceof ObjectCreatedEvent) {
$object = $event->getObject();
}

if ($object === null) {
return;
}

try {
$this->publication->objectSaved(object: $object);
} catch (Throwable $e) {
$this->logger->error('ModuleVersionPublicationListener: could not mirror the publication', ['error' => $e->getMessage()]);
}
}//end handle()
}//end class
106 changes: 106 additions & 0 deletions lib/Repair/BackfillModuleVersionPublication.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
<?php

/**
* Copies every module's publication onto its versions, once, on upgrade.
*
* Versions saved before publication-field-rules carry no mirrored publication,
* so for anonymous readers they read as unpublished until their module is
* saved again. That is the safe direction; this step makes the published ones
* public again without waiting for an edit. It is idempotent: a version that
* already holds its module's values is not written.
*
* @category Repair
* @package OCA\Stackiq\Repair
* @author Conduction b.v. <info@conduction.nl>
* @copyright 2026 Conduction B.V.
* @license EUPL-1.2 https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
* @link https://github.com/ConductionNL/stackiq
*
* @spec openspec/changes/publication-field-rules/specs/publication-field-rules/spec.md#requirement-req-pfr-002-a-module-version-is-public-only-while-its-application-is
*
* SPDX-FileCopyrightText: 2026 Conduction B.V. <info@conduction.nl>
* SPDX-License-Identifier: EUPL-1.2
*/

declare(strict_types=1);

namespace OCA\Stackiq\Repair;

use OCA\OpenRegister\Contract\ObjectEntityInterface;
use OCA\Stackiq\Service\ModuleVersionPublicationService;
use OCA\Stackiq\Service\SettingsService;
use OCP\App\IAppManager;
use OCP\Migration\IOutput;
use OCP\Migration\IRepairStep;
use Throwable;

/**
* Backfills the mirrored publication on module versions.
*
* @spec openspec/changes/publication-field-rules/specs/publication-field-rules/spec.md#requirement-req-pfr-002-a-module-version-is-public-only-while-its-application-is
*/
class BackfillModuleVersionPublication implements IRepairStep {

/**
* Constructor.
*
* @param IAppManager $appManager Tells whether OpenRegister is installed.
* @param SettingsService $settingsService Resolves the module schema and the object service.
* @param ModuleVersionPublicationService $publication The mirror.
*/
public function __construct(
private readonly IAppManager $appManager,
private readonly SettingsService $settingsService,
private readonly ModuleVersionPublicationService $publication,
) {
}//end __construct()

/**
* The step's name.
*
* @return string The name.
*/
public function getName(): string {
return 'Copy each application\'s publication onto its versions';
}//end getName()

/**
* Run the backfill.
*
* @param IOutput $output The output.
*
* @return void
*
* @spec openspec/changes/publication-field-rules/specs/publication-field-rules/spec.md#requirement-req-pfr-002-a-module-version-is-public-only-while-its-application-is
*/
public function run(IOutput $output): void {
if (in_array('openregister', $this->appManager->getInstalledApps(), true) === false) {
$output->info('OpenRegister not installed, so there are no versions to update.');
return;
}

$register = $this->settingsService->getRegisterIdForObjectType('module');
$schema = $this->settingsService->getSchemaIdForObjectType('module');
$objects = $this->settingsService->getObjectService();
if ($register === null || $schema === null || $objects === null) {
$output->info('The module schema is not configured yet, so there are no versions to update.');
return;
}

try {
$modules = $objects->setRegister($register)->setSchema($schema)->findAll([], false, false);
} catch (Throwable $e) {
$output->warning('Could not read the applications: ' . $e->getMessage());
return;
}

$written = 0;
foreach ((array) $modules as $module) {
if (($module instanceof ObjectEntityInterface) === true) {
$written += $this->publication->moduleSaved(module: $module);
}
}

$output->info($written . ' versions now follow their application\'s publication.');
}//end run()
}//end class
Loading
Loading