Skip to content

Release: merge development into beta#173

Open
github-actions[bot] wants to merge 198 commits intobetafrom
development
Open

Release: merge development into beta#173
github-actions[bot] wants to merge 198 commits intobetafrom
development

Conversation

@github-actions
Copy link

@github-actions github-actions bot commented Feb 6, 2026

Automated PR to sync development changes to beta for beta release.

Merging this PR will trigger the beta release workflow.

actions-user and others added 30 commits January 5, 2026 21:38
…skipIfReleaseExists

- Changed git fetch origin master to git fetch origin main
- Changed origin/master:appinfo/info.xml to origin/main:appinfo/info.xml
- Added skipIfReleaseExists: true to prevent duplicate tag errors

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Problem:
- Import summary showed 0 for created, updated, and unchanged counts
- Statistics calculation was using 'skipped' key instead of 'unchanged'
- The statistics structure only defined 'unchanged' but code incremented 'skipped'

Solution:
- Changed incrementing 'skipped' to 'unchanged' in calculateObjectStatistics()
- Now correctly tracks object state changes from ObjectService results
- Statistics will properly show created/updated/unchanged object counts

The import now correctly displays how many objects were created, updated,
or remained unchanged during the ArchiMate/GEMMA import process.
Problem:
- Import summary showing 0 for created, updated, and unchanged counts
- Code was using 'skipped' key but ObjectService returns 'unchanged'
- Summary calculation tried to sum non-existent 'skipped' field

Root Cause:
- aggregatedStats initialized with 'skipped' instead of 'unchanged'
- Code merged saveResult['skipped'] which doesn't exist
- Summary used total_objects_skipped instead of total_objects_unchanged
- Multiple fallbacks to non-existent 'skipped' field

Solution:
- Removed 'skipped' from aggregatedStats initialization
- Changed all references from 'skipped' to 'unchanged'
- Updated summary field from total_objects_skipped to total_objects_unchanged
- Removed fallback checks for non-existent 'skipped' arrays

The import now correctly displays:
- Created: Newly created objects
- Updated: Modified existing objects
- Unchanged: Existing objects with no changes
- Errors: Objects that failed validation

Statistics are calculated from ObjectService results which returns
saved, updated, unchanged, and invalid arrays.
…ing yet)

Changes made in calculateOptimizedStatistics:
- Changed 'skipped' => 0 to 'unchanged' => 0 in initialization
- Removed fallbacks to non-existent 'skipped' arrays
- Changed variable names from $wasSkipped to $wasUnchanged
- Fixed summary calculation to use 'unchanged' instead of 'skipped'
- Added debug logging

Issue: API still returns 'skipped' instead of 'unchanged' even though
the code has been updated and container restarted. There appears to be
something overriding or caching the statistics structure between the
return statement and the JSON response.
…anged'

Problem:
- Import statistics showed 'skipped' instead of 'unchanged'
- Frontend expected 'unchanged' key but backend returned 'skipped'
- This caused mismatch between API response and frontend display

Solution:
- Changed all references from 'skipped' to 'unchanged' in calculateOptimizedStatistics()
- Updated initialization, variable names ($wasSkipped -> $wasUnchanged)
- Fixed summary calculation to use 'unchanged' instead of 'skipped'
- Added debug logging for troubleshooting

Note: The statistics now correctly return 'unchanged' key. The counts
showing 0 is a separate issue related to object matching/categorization
that needs investigation (objects processed: 8802, but stats all 0).
PROBLEM:
- ArchiMate import showed 0 for created/updated/unchanged despite processing 8802 objects
- Custom statistics calculation (calculateOptimizedStatistics) wasn't working

SOLUTION:
- Added buildStatisticsFromSaveResult() to use ObjectService statistics directly
- Removed complex object matching logic that was failing
- Simple approach: count arrays from lastSaveResult (saved/updated/unchanged)
- Statistics now correctly populated from OpenRegister ObjectService

RESULT:
- ArchiMate import now shows correct statistics (e.g., updated: 8802)
- Uses 'unchanged' instead of 'skipped' for consistency
- Depends on OpenRegister fixes committed separately
PROBLEM:
- Organisation page API calls included _extend[]=@self.schema
- This caused unnecessary schema data to be loaded
- Should only extend contactpersonen for organisations

SOLUTION:
- Updated object store to check object type before setting default _extend
- For type='organisatie': default _extend is 'contactpersonen'
- For other types: default _extend remains '@self.schema'
- Applied to all three locations: _constructApiUrl, fetchCollection, fetchObject

CHANGES:
- src/store/modules/object.js: Added type check in _extend defaults
- Lines 786, 828, 906: Added ternary to check type === 'organisatie'

RESULT:
- Organisation API calls now use: _extend[]=contactpersonen
- No more unnecessary @self.schema extension for organisations
PROBLEM:
- API calls to audit-trails and files endpoints included _extend[]=@self.schema
- These sub-resources don't need schema data
- Caused unnecessary data loading and potential errors

EXAMPLES:
- /api/objects/.../audit-trails?_extend[]=@self.schema (bad)
- /api/objects/.../files?_extend[]=@self.schema (bad)

SOLUTION:
- Added check for sub-resource actions before applying default _extend
- Skip _extend for: logs, audit-trails, files, publish, depublish, unlock, lock, revert
- Only apply _extend for main object collection/fetch calls

CHANGES:
- src/store/modules/object.js line 787-789:
  - Added isSubResource check for action types
  - Set defaultExtend to null for sub-resources
  - Only apply type-specific extends for main object calls

RESULT:
✅ Audit-trails: no _extend parameter
✅ Files: no _extend parameter
✅ Main objects: still get appropriate _extend
✅ Organisations: still get contactpersonen extend
PROBLEM:
- Frontend was forcing _source: 'database' for organisations, contactpersonen, moduleVersie
- This hardcoded decision should be made by the backend, not the frontend
- Frontend shouldn't dictate which data source to use

CODE REMOVED:
Lines 834-836 (fetchCollection):
- ...((type === 'organisatie' || type === 'contactpersoon' || type === 'moduleVersie') && !params._source
-     ? { _source: 'database' }
-     : {})

Lines 912-914 (fetchObject):
- ...(type === 'organisatie' && !params._source
-     ? { _source: 'database' }
-     : {})

Also removed debug logging that referenced the forced source parameter

RATIONALE:
- Backend should determine optimal data source based on:
  - Data freshness requirements
  - Index availability
  - Query complexity
  - Performance considerations
- Frontend can still pass _source if explicitly needed via params
- This makes the architecture cleaner and more flexible

RESULT:
✅ Frontend no longer forces _source parameter
✅ Backend has full control over data source selection
✅ Explicit _source in params still works if needed
✅ Cleaner separation of concerns
PROBLEM:
- When activating an organisation, the PATCH request was sending the entire @self object
- This included unnecessary metadata: id, slug, name, description, uri, version, register, schema, etc.
- PATCH should only send fields that are actually changing

OLD CODE (lines 141-145):
patchData['@self'] = {
    ...organisatie['@self'],  // Spreading entire @self with all metadata
    owner: organisatieUuid,
    organisation: organisatieUuid,
}

This resulted in payload like:
{
    "status": "Actief",
    "@self": {
        "id": "...",
        "slug": null,
        "name": "...",
        "description": 6180,
        "uri": "...",
        "version": null,
        "register": "2",
        "schema": "24",
        "owner": "...",
        "organisation": "...",
        // ... all other @self fields
    }
}

NEW CODE (lines 141-148):
patchData.owner = organisatieUuid
patchData.organisation = organisatieUuid

This results in clean payload:
{
    "status": "Actief",
    "owner": "6b339919-e948-4080-8a7a-83dc8dd6aae7",
    "organisation": "6b339919-e948-4080-8a7a-83dc8dd6aae7"
}

BENEFITS:
✅ Smaller payload size
✅ Only sends what's actually changing
✅ Follows REST PATCH best practices
✅ Reduces risk of unintended side effects
✅ More efficient API calls
- organisatieType: changed required to false, added default "{{ type }}"
- moduleType: changed default from "Applicatie" to "{{ type }}"

This allows old forms that only send the generic 'type' field to
automatically populate the more specific type fields.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The contact person schema uses 'e-mailadres' as the property name,
but the services were only checking for 'email'. This caused user
account creation to fail because the email was not found.

- ContactpersoonService: Check both email and e-mailadres in
  processContactpersoon(), handleRoleChanges(), handleContactDeletion()
- OrganizationSyncService: Check both email and e-mailadres in
  processContactPerson() and log messages
- ContactPersonHandler: Update log message consistency

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
rubenvdlinde and others added 30 commits February 22, 2026 20:26
Multiple bugs caused contact persons to lose their organisatie field
(the link back to their organisation) when an organisation was activated:

1. The organisatie field was permanently destroyed by unset() calls
   before saveObject(). Now saved before unset and restored after save
   at all 4 locations.

2. processRelatedContactPersons could not find contacts linked via
   inversedBy (org→contact) because it only searched by the contact's
   organisatie field (which was null). Added fallback that looks up
   contacts from the org's contactpersonen UUID array.

3. Related contacts now get their organisatie field set before being
   passed to processSpecificContactPerson, ensuring user creation
   proceeds correctly.

4. updateContactpersoonObjectOwner now restores a missing organisatie
   field as a safety net.

5. Downgraded debug-only critical log levels to info.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Leverancier organisations registered by gemeenten are no longer publicly
visible. Public users now only see organisations that are: registered by
a Leverancier, of type Gemeente, or of type Samenwerking. Aanbod-beheerders
only see their own organisation. Gebruik-beheerders retain full access.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The dienst schema had two properties with title "Diensttype": `type`
and `dienstType`, both with table.default=true, causing a duplicate
column in the diensten table. Removed `dienstType` and made `type`
the facetable field with aggregated=false to prevent cross-schema
facet mixing.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replaces empty placeholder content blocks (ph1, ph2) with actual
seed data for the quote section and 3 content blocks on the front page.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
ModuleVersionService was never registered as a service, causing
ModuleComplianceSubscriber to silently fail when trying to create
default 1.0.0 versions for new modules. The service was properly
implemented but the container registration was missing.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace existing license (Apache-2.0/AGPL) with EUPL-1.2 across all
metadata files: LICENSE, appinfo/info.xml, composer.json, package.json.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…5, #373, #377, #394)

Remove empty rollen enum array and add Gebruik-raadpleger to items enum.
Add table.default=true to diensten and standaardVersies schemas for
beheer table visibility. Add authorization.read=['authenticated'] to
contactpersonen, e-mailadres, and telefoonnummer properties.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… #434, #352)

Replace saveObject with ObjectEntityMapper.update() in registration
flow to avoid validation cascades. Add email subaddress sanitization
for username generation. Make ObjectModal support 12 object types via
generic config. Add metadata hydration after user profile sync.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…schema

moduleB now only references modules (removed oneOf with element).
buitengemeentelijkVoorziening is now visible and shown in default table view.
The UI keeps a single merged dropdown that maps to the correct field based on type.

Resolves ambiguity from #433 where moduleB accepted both modules and elements.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
v0.1.139 already exists as a stable release in the Nextcloud appstore,
causing the nightly upload to fail with HTTP 400.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The Nextcloud appstore only accepts a fixed set of licence identifiers
(agpl, AGPL-3.0-or-later, MIT, Apache-2.0, etc). EUPL-1.2 is not in
the accepted list, causing all appstore uploads to fail with HTTP 400
since Feb 26.

The actual LICENSE file remains EUPL-1.2 — this only changes the
appstore metadata identifier. EUPL 1.2 has an explicit compatibility
clause with AGPL-3.0.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
OrganizationSyncService: add null-coalescing fallbacks for missing
usersCreated/usersUpdated keys in sync result arrays.
OrganisatieService: reduce catch block logging to only error message
and UUID instead of full object dump with stack trace.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
All sourceUrl and configuration references in the register JSON were
using github.com/blob/ URLs which return HTML pages, not raw JSON.
Converted to raw.githubusercontent.com for proper JSON fetching by
the ConfigurationCheckJob cronjob.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The sync cronjob no longer needs user/org context since all ObjectService
calls now explicitly pass _rbac: false and _multitenancy: false. This is
correct because sync is a system-level operation.

Changes:
- OrganizationContactSyncJob: removed CronjobContextTrait usage and
  context warnings, simplified to direct service call
- OrganizationSyncService: added _rbac: false, _multitenancy: false to
  4 ObjectService calls that were using defaults
- CronjobContextTrait: marked as @deprecated
- SettingsService: marked 6 cronjob config methods as @deprecated
- SettingsController: marked 4 cronjob config endpoints as @deprecated

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add vimeo/psalm ^5.26, phpstan/phpstan ^1.10
- Add nextcloud/coding-standard, phpcsstandards/phpcsextra, nextcloud/ocp
- Create phpstan.neon config
- Add phpcs-custom-sniffs/ (named parameters enforcement)
- Rewrite phpcs.xml to target lib/ + add custom sniff reference
- Add phpmetrics:violations, phpcs:output, phpstan scripts
- Fix psalm script to use ./vendor/bin/psalm
- Add code-quality.yml GitHub Actions workflow (blocks PRs on phpcs+phpmd)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…rvice backup

Refactor all function calls to use named arguments across controllers,
services, event listeners, background jobs, and settings to comply with
the Conduction PHPCS coding standard. Removes the leftover
ArchiMateService_backup.php file.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The NC organisation UUID from objectData['organisation'] (RBAC system field)
is different from the register organisatie object UUID. When an NC org hasn't
been synced to a register object yet, the lookup fails with "Object not found".

This is expected behaviour, not an error — the caller already handles the
empty string return value gracefully. Downgrade from error to warning and
simplify the log context.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Fixed PHPCS violations in 32 lib/ files (all at 0 errors). Changes include:
- Added declare(strict_types=1) to all files missing it
- Fixed property and method doc blocks (short descriptions, @param/@return order)
- Replaced operator ! with === false/true explicit comparisons
- Replaced implicit true comparisons with === true
- Added named parameters to function calls
- Expanded inline ternary IFs to if/else blocks
- Fixed inline comments to end with periods
- Added end-of-block comments (//end method())
- Fixed file doc comment formatting

Large service classes intentionally skipped (too many violations for this pass):
ArchiMateService (517), ArchiMateExportService (183), ArchiMateImportService (1174),
SettingsService (527), SoftwareCatalogueService (319), OrganizationSyncService (291),
ContactPersonHandler (252).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds a complete Docusaurus 3.7 setup matching the pipelinq pattern:
- docusaurus/ with config, custom.css (OWC green theme), homepage
- docs/FEATURES.md with feature overview
- .github/workflows/documentation.yml deploying to softwarecatalog.app on push to development
- Blue hexagon app-store.svg as navbar logo

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Named parameter 'appName:' breaks on Nextcloud versions where the concrete
URLGenerator still uses '$app' as the parameter name.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add tests/bootstrap-unit.php with OCP class autoloader (no Nextcloud bootstrap)
- Update .github/workflows/code-quality.yml to run PHPUnit unit tests on every PR
  (uses --bootstrap tests/bootstrap-unit.php to skip Integration test setup)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add Psalm (Static Analysis) step to code-quality.yml php-quality job

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Fix 4 InvalidNamedArgument bugs (wrong param names in method calls):
  - AangebodenGebruikService: query → baseQuery in addQueryFilters()
  - OrganisatieService: userData → user, organisationData → organization
  - HierarchyHandler: manager → managerUsername in setUserManager()
  - OrganisatieService: createOrganisation → name in createOrganisation()
- Fix UndefinedThisPropertyFetch: replace non-existent $this->_userManager
  with $this->_container->get(IUserManager::class) in SoftwareCatalogueService
- Fix named args in Application.php DI container registrations
- Fix named args in SoftwareCatalogEventListener and ContactpersoonService
- Update psalm.xml: add suppressions for all OCA\OpenRegister\* and missing
  OCP\* runtime classes (ICache, IGroup, ISession, IWidget, IRepairStep, etc.)
- Remove phpro/grumphp from require-dev (conflicts with vimeo/psalm via
  amphp/amp dependency chain); update composer.lock accordingly

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Removed InvalidArgument, InvalidReturnType, InvalidReturnStatement,
InvalidCast, InvalidMethodCall suppressors from psalm.xml — exposing 5
real bugs that were previously hidden:

- ArchiMateExportService::createSectionFolder(): return type was
  SimpleXMLElement but function can return null; fixed to ?SimpleXMLElement
- ArchiMateExportService::extractModelMetadata(): return can be
  ArrayAccess; cast to (array) to satisfy array return type
- ContactPersonHandler::assignUserGroups(): docblock said @return void
  but function signature and body return string; fixed docblock

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Tested all remaining suppressors — removed those with 0 false positives:
- ImplementedReturnTypeMismatch (0 errors without it)
- NullableReturnStatement (0 errors without it)

Confirmed as legitimate suppressors (tested, all false positives):
- TypeDoesNotContainNull/Type, RedundantCondition: constant feature-flag patterns
- InvalidArrayOffset/Access, EmptyArrayAccess: Psalm inference cascade from OCA\OpenRegister unknowns
- UnusedVariable, NoValue: SimpleXMLElement::addChild() side-effect patterns

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants