Skip to content
This repository was archived by the owner on May 29, 2026. It is now read-only.
2 changes: 1 addition & 1 deletion appinfo/info.xml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ Create a [bug report](https://github.com/OpenRegister/.github/issues/new/choose)

Create a [feature request](https://github.com/OpenRegister/.github/issues/new/choose)
]]></description>
<version>0.1.14</version>
<version>0.1.17</version>
<licence>agpl</licence>
<author mail="info@conduction.nl" homepage="https://www.conduction.nl/">Conduction</author>
<namespace>OpenRegister</namespace>
Expand Down
2 changes: 2 additions & 0 deletions appinfo/routes.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,7 @@
'routes' => [
['name' => 'dashboard#page', 'url' => '/', 'verb' => 'GET'],
['name' => 'registers#objects', 'url' => '/api/registers-objects/{register}/{schema}', 'verb' => 'GET'],
['name' => 'objects#auditTrails', 'url' => '/api/audit-trails/{id}', 'verb' => 'GET'],

],
];
31 changes: 29 additions & 2 deletions lib/Controller/ObjectsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use OCA\OpenRegister\Service\SearchService;
use OCA\OpenRegister\Db\ObjectEntity;
use OCA\OpenRegister\Db\ObjectEntityMapper;
use OCA\OpenRegister\Db\AuditTrailMapper;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http\TemplateResponse;
use OCP\AppFramework\Http\JSONResponse;
Expand All @@ -25,7 +26,8 @@ public function __construct(
$appName,
IRequest $request,
private readonly IAppConfig $config,
private readonly ObjectEntityMapper $objectEntityMapper
private readonly ObjectEntityMapper $objectEntityMapper,
private readonly AuditTrailMapper $auditTrailMapper
)
{
parent::__construct($appName, $request);
Expand Down Expand Up @@ -64,12 +66,21 @@ public function index(ObjectService $objectService, SearchService $searchService
{
$filters = $this->request->getParams();
$fieldsToSearch = ['uuid', 'register', 'schema'];
$extend = ['schema', 'register'];

$searchParams = $searchService->createMySQLSearchParams(filters: $filters);
$searchConditions = $searchService->createMySQLSearchConditions(filters: $filters, fieldsToSearch: $fieldsToSearch);
$filters = $searchService->unsetSpecialQueryParams(filters: $filters);

return new JSONResponse(['results' => $this->objectEntityMapper->findAll(filters: $filters, searchConditions: $searchConditions, searchParams: $searchParams)]);
// @todo: figure out how to use extend here
$results = $this->objectEntityMapper->findAll(filters: $filters);

// We dont want to return the entity, but the object (and kant reley on the normal serilzier)
foreach ($results as $key => $result) {
$results[$key] = $result->getObjectArray();
}

return new JSONResponse(['results' => $results]);
}

/**
Expand Down Expand Up @@ -162,4 +173,20 @@ public function destroy(int $id): JSONResponse

return new JSONResponse([]);
}

/**
* Retrieves a list of logs for an object
*
* This method returns a JSON response containing the logs for a specific object.
*
* @NoAdminRequired
* @NoCSRFRequired
*
* @param string $id The ID of the object to delete
* @return JSONResponse An empty JSON response
*/
public function auditTrails(int $id): JSONResponse
{
return new JSONResponse($this->auditTrailMapper->findAll(filters: ['object' => $id]));
}
}
89 changes: 89 additions & 0 deletions lib/Db/AuditTrail.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
<?php

namespace OCA\OpenRegister\Db;

use DateTime;
use JsonSerializable;
use OCP\AppFramework\Db\Entity;

class AuditTrail extends Entity implements JsonSerializable
{
protected ?string $uuid = null;
protected ?int $schema = null;
protected ?int $register = null;
protected ?int $object = null;
protected ?string $action = null;
protected ?array $changed = null;
protected ?string $user = null;
protected ?string $userName = null;
protected ?string $session = null;
protected ?string $request = null;
protected ?string $ipAddress = null;
protected ?string $version = null;
protected ?DateTime $created = null;

public function __construct() {
$this->addType(fieldName: 'uuid', type: 'string');
$this->addType(fieldName: 'schema', type: 'integer');
$this->addType(fieldName: 'register', type: 'integer');
$this->addType(fieldName: 'object', type: 'integer');
$this->addType(fieldName: 'action', type: 'string');
$this->addType(fieldName: 'changed', type: 'json');
$this->addType(fieldName: 'user', type: 'string');
$this->addType(fieldName: 'userName', type: 'string');
$this->addType(fieldName: 'session', type: 'string');
$this->addType(fieldName: 'request', type: 'string');
$this->addType(fieldName: 'ipAddress', type: 'string');
$this->addType(fieldName: 'version', type: 'string');
$this->addType(fieldName: 'created', type: 'datetime');
}

public function getJsonFields(): array
{
return array_keys(
array_filter($this->getFieldTypes(), function ($field) {
return $field === 'json';
})
);
}

public function hydrate(array $object): self
{
$jsonFields = $this->getJsonFields();

foreach ($object as $key => $value) {
if (in_array($key, $jsonFields) === true && $value === []) {
$value = null;
}

$method = 'set'.ucfirst($key);

try {
$this->$method($value);
} catch (\Exception $exception) {
}
}

return $this;
}

public function jsonSerialize(): array
{
return [
'id' => $this->id,
'uuid' => $this->uuid,
'schema' => $this->schema,
'register' => $this->register,
'object' => $this->object,
'action' => $this->action,
'changed' => $this->changed,
'user' => $this->user,
'userName' => $this->userName,
'session' => $this->session,
'request' => $this->request,
'ipAddress' => $this->ipAddress,
'version' => $this->version,
'created' => isset($this->created) ? $this->created->format('c') : null
];
}
}
75 changes: 75 additions & 0 deletions lib/Db/AuditTrailMapper.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
<?php

namespace OCA\OpenRegister\Db;

use OCA\OpenRegister\Db\Log;
use OCP\AppFramework\Db\Entity;
use OCP\AppFramework\Db\QBMapper;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\IDBConnection;
use Symfony\Component\Uid\Uuid;

class AuditTrailMapper extends QBMapper
{
public function __construct(IDBConnection $db)
{
parent::__construct($db, 'openregister_audit_trails');
}

public function find(int $id): Log
{
$qb = $this->db->getQueryBuilder();

$qb->select('*')
->from('openregister_audit_trails')
->where(
$qb->expr()->eq('id', $qb->createNamedParameter($id, IQueryBuilder::PARAM_INT))
);

return $this->findEntity(query: $qb);
}

public function findAll(?int $limit = null, ?int $offset = null, ?array $filters = [], ?array $searchConditions = [], ?array $searchParams = []): array
{
$qb = $this->db->getQueryBuilder();

$qb->select('*')
->from('openregister_audit_trails')
->setMaxResults($limit)
->setFirstResult($offset);

foreach ($filters as $filter => $value) {
if ($value === 'IS NOT NULL') {
$qb->andWhere($qb->expr()->isNotNull($filter));
} elseif ($value === 'IS NULL') {
$qb->andWhere($qb->expr()->isNull($filter));
} else {
$qb->andWhere($qb->expr()->eq($filter, $qb->createNamedParameter($value)));
}
}

if (!empty($searchConditions)) {
$qb->andWhere('(' . implode(' OR ', $searchConditions) . ')');
foreach ($searchParams as $param => $value) {
$qb->setParameter($param, $value);
}
}

return $this->findEntities(query: $qb);
}

public function createFromArray(array $object): Log
{
$log = new Log();
$log->hydrate(object: $object);

// Set uuid if not provided
if ($log->getUuid() === null) {
$log->setUuid(Uuid::v4());
}

return $this->insert(entity: $log);
}

// We dont need update as we dont change the log
}
29 changes: 18 additions & 11 deletions lib/Db/ObjectEntity.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ class ObjectEntity extends Entity implements JsonSerializable
protected ?string $schema = null;
protected ?string $version = null;
protected ?array $object = [];
protected ?string $textRepresentation = null;
protected ?DateTime $updated = null;
protected ?DateTime $created = null;

Expand All @@ -22,6 +23,7 @@ public function __construct() {
$this->addType(fieldName:'schema', type: 'string');
$this->addType(fieldName: 'version', type: 'string');
$this->addType(fieldName:'object', type: 'json');
$this->addType(fieldName:'textRepresentation', type: 'text');
$this->addType(fieldName:'updated', type: 'datetime');
$this->addType(fieldName:'created', type: 'datetime');
}
Expand Down Expand Up @@ -62,17 +64,22 @@ public function hydrate(array $object): self

public function jsonSerialize(): array
{
// return [
// 'id' => $this->id,
// 'uuid' => $this->uuid,
// 'version' => $this->version,
// 'register' => $this->register,
// 'schema' => $this->schema,
// 'object' => $this->object,
// 'updated' => isset($this->updated) ? $this->updated->format('c') : null,
// 'created' => isset($this->created) ? $this->created->format('c') : null
// ];

return $this->object;
}

public function getObjectArray(): array
{
return [
'id' => $this->id,
'uuid' => $this->uuid,
'version' => $this->version,
'register' => $this->register,
'schema' => $this->schema,
'object' => $this->object,
'textRepresentation' => $this->textRepresentation,
'updated' => isset($this->updated) ? $this->updated->format('c') : null,
'created' => isset($this->created) ? $this->created->format('c') : null
];
}

}
13 changes: 8 additions & 5 deletions lib/Db/ObjectEntityMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,22 +30,25 @@ public function __construct(IDBConnection $db, MySQLJsonService $mySQLJsonServic
}

/**
* Find an object by ID
* Find an object by ID or UUID
*
* @param int $id The ID of the object to find
* @param int|string $idOrUuid The ID or UUID of the object to find
* @return ObjectEntity The ObjectEntity
*/
public function find(int $id): ObjectEntity
public function find($idOrUuid): ObjectEntity
{
$qb = $this->db->getQueryBuilder();

$qb->select('*')
->from('openregister_objects')
->where(
$qb->expr()->eq('id', $qb->createNamedParameter($id, IQueryBuilder::PARAM_INT))
$qb->expr()->orX(
$qb->expr()->eq('id', $qb->createNamedParameter($idOrUuid, IQueryBuilder::PARAM_INT)),
$qb->expr()->eq('uuid', $qb->createNamedParameter($idOrUuid, IQueryBuilder::PARAM_STR))
)
);

return $this->findEntity(query: $qb);
return $this->findEntity($qb);
}

/**
Expand Down
41 changes: 25 additions & 16 deletions lib/Migration/Version1Date20241019205009.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,6 @@
use OCP\Migration\IOutput;
use OCP\Migration\SimpleMigrationStep;

/**
* FIXME Auto-generated migration step: Please modify to your needs!
*/
class Version1Date20241019205009 extends SimpleMigrationStep {

/**
Expand All @@ -38,26 +35,38 @@ public function changeSchema(IOutput $output, Closure $schemaClosure, array $opt
/** @var ISchemaWrapper $schema */
$schema = $schemaClosure();

// Update the abilities table to add the base column
// Update the openregister_sources table
$table = $schema->getTable('openregister_sources');
$table->addColumn(name: 'uuid', typeName: Types::STRING, options: ['notnull' => true, 'length' => 255]);
$table->addColumn(name: 'version', typeName: Types::STRING, options: ['notnull' => true, 'length' => 255, 'default' => '0.0.1']);
$table->addIndex(['uuid'], 'openregister_sources_uuid_index');
if (!$table->hasColumn('uuid')) {
$table->addColumn(name: 'uuid', typeName: Types::STRING, options: ['notnull' => true, 'length' => 255]);
$table->addIndex(['uuid'], 'openregister_sources_uuid_index');
}
if (!$table->hasColumn('version')) {
$table->addColumn(name: 'version', typeName: Types::STRING, options: ['notnull' => true, 'length' => 255, 'default' => '0.0.1']);
}

// Update the abilities table to add the base column
// Update the openregister_schemas table
$table = $schema->getTable('openregister_schemas');
$table->addColumn(name: 'uuid', typeName: Types::STRING, options: ['notnull' => true, 'length' => 255]);
$table->addIndex(['uuid'], 'openregister_sources_uuid_index');
if (!$table->hasColumn('uuid')) {
$table->addColumn(name: 'uuid', typeName: Types::STRING, options: ['notnull' => true, 'length' => 255]);
$table->addIndex(['uuid'], 'openregister_schemas_uuid_index');
}

// Update the abilities table to add the base column
// Update the openregister_registers table
$table = $schema->getTable('openregister_registers');
$table->addColumn(name: 'uuid', typeName: Types::STRING, options: ['notnull' => true, 'length' => 255]);
$table->addColumn(name: 'version', typeName: Types::STRING, options: ['notnull' => true, 'length' => 255, 'default' => '0.0.1']);
$table->addIndex(['uuid'], 'openregister_sources_uuid_index');
if (!$table->hasColumn('uuid')) {
$table->addColumn(name: 'uuid', typeName: Types::STRING, options: ['notnull' => true, 'length' => 255]);
$table->addIndex(['uuid'], 'openregister_registers_uuid_index');
}
if (!$table->hasColumn('version')) {
$table->addColumn(name: 'version', typeName: Types::STRING, options: ['notnull' => true, 'length' => 255, 'default' => '0.0.1']);
}

// Update the abilities table to add the base column
// Update the openregister_objects table
$table = $schema->getTable('openregister_objects');
$table->addColumn(name: 'version', typeName: Types::STRING, options: ['notnull' => true, 'length' => 255, 'default' => '0.0.1']);
if (!$table->hasColumn('version')) {
$table->addColumn(name: 'version', typeName: Types::STRING, options: ['notnull' => true, 'length' => 255, 'default' => '0.0.1']);
}

return $schema;
}
Expand Down
Loading