feat: Add file status tracking to CDokumente entity; implement beforeSave hook for hash calculation and status determination

This commit is contained in:
2026-03-03 08:17:51 +01:00
parent ba986a32fe
commit ec25089ab4
7 changed files with 103 additions and 6 deletions

View File

@@ -0,0 +1,60 @@
<?php
namespace Espo\Custom\Hooks\CDokumente;
use Espo\ORM\Entity;
use Espo\Core\Utils\Util;
class CDokumente extends \Espo\Core\Hooks\Base
{
public function beforeSave(Entity $entity, array $options = [])
{
// Nur berechnen, wenn die Datei geändert wurde oder neu
if (!$entity->isNew() && !$entity->isAttributeChanged('dokument')) {
return;
}
$dokument = $entity->get('dokument');
if (!$dokument) {
return;
}
if (is_object($dokument)) {
$attachment = $dokument;
} else {
$attachment = $this->getEntityManager()->getEntity('Attachment', $dokument);
}
if (!$attachment) {
return;
}
$filePath = 'data/upload/' . $attachment->get('id');
if (!file_exists($filePath)) {
return;
}
// Berechne neue Hashes
$newMd5 = hash_file('md5', $filePath);
$newSha256 = hash_file('sha256', $filePath);
// Setze Hashes
$entity->set('md5sum', $newMd5);
$entity->set('sha256', $newSha256);
// Bestimme Status
if ($entity->isNew()) {
$entity->set('fileStatus', 'new');
} else {
$oldMd5 = $entity->getFetched('md5sum');
$oldSha256 = $entity->getFetched('sha256');
if ($oldMd5 !== $newMd5 || $oldSha256 !== $newSha256) {
$entity->set('fileStatus', 'changed');
} else {
$entity->set('fileStatus', 'unchanged');
}
}
}
}

View File

@@ -11,6 +11,7 @@
"xaiId": "x.AI ID", "xaiId": "x.AI ID",
"xaiCollections": "x.AI Collections", "xaiCollections": "x.AI Collections",
"xaiSyncStatus": "Sync-Status", "xaiSyncStatus": "Sync-Status",
"fileStatus": "Datei-Status",
"contactsvmhdokumente": "Freigegebene Nutzer", "contactsvmhdokumente": "Freigegebene Nutzer",
"vmhMietverhltnisesDokumente": "Mietverhältnisse", "vmhMietverhltnisesDokumente": "Mietverhältnisse",
"vmhErstgespraechsdokumente": "Erstgespräche", "vmhErstgespraechsdokumente": "Erstgespräche",
@@ -41,7 +42,8 @@
"syncStatus": "Status der Synchronisation: pending_sync = Warte auf Sync, clean = erfolgreich, unclean = Abweichungen, failed = Fehler", "syncStatus": "Status der Synchronisation: pending_sync = Warte auf Sync, clean = erfolgreich, unclean = Abweichungen, failed = Fehler",
"xaiId": "Eindeutige ID für x.AI Synchronisation", "xaiId": "Eindeutige ID für x.AI Synchronisation",
"xaiCollections": "Liste der x.AI Collections für dieses Dokument", "xaiCollections": "Liste der x.AI Collections für dieses Dokument",
"xaiSyncStatus": "Status der x.AI Synchronisation: pending_sync = Warte auf Sync, clean = erfolgreich, unclean = Abweichungen, failed = Fehler" "xaiSyncStatus": "Status der x.AI Synchronisation: pending_sync = Warte auf Sync, clean = erfolgreich, unclean = Abweichungen, failed = Fehler",
"fileStatus": "Status der Datei: new = neu hochgeladen, changed = geändert, unchanged = unverändert"
}, },
"options": { "options": {
"syncStatus": { "syncStatus": {
@@ -55,6 +57,11 @@
"clean": "Synchronisiert", "clean": "Synchronisiert",
"unclean": "Abweichungen", "unclean": "Abweichungen",
"failed": "Fehlgeschlagen" "failed": "Fehlgeschlagen"
},
"fileStatus": {
"new": "Neu",
"changed": "Geändert",
"unchanged": "Unverändert"
} }
} }
} }

View File

@@ -19,7 +19,8 @@
"syncStatus": "Sync Status", "syncStatus": "Sync Status",
"xaiId": "x.AI ID", "xaiId": "x.AI ID",
"xaiCollections": "x.AI Collections", "xaiCollections": "x.AI Collections",
"xaiSyncStatus": "Sync Status" "xaiSyncStatus": "Sync Status",
"fileStatus": "File Status"
}, },
"links": { "links": {
"contactsvmhdokumente": "Portal Users", "contactsvmhdokumente": "Portal Users",
@@ -44,7 +45,8 @@
"syncStatus": "Synchronization status: pending_sync = Waiting for sync, clean = successful, unclean = discrepancies, failed = error", "syncStatus": "Synchronization status: pending_sync = Waiting for sync, clean = successful, unclean = discrepancies, failed = error",
"xaiId": "Unique ID for x.AI synchronization", "xaiId": "Unique ID for x.AI synchronization",
"xaiCollections": "List of x.AI collections for this document", "xaiCollections": "List of x.AI collections for this document",
"xaiSyncStatus": "x.AI synchronization status: pending_sync = Waiting for sync, clean = successful, unclean = discrepancies, failed = error" "xaiSyncStatus": "x.AI synchronization status: pending_sync = Waiting for sync, clean = successful, unclean = discrepancies, failed = error",
"fileStatus": "File status: new = newly uploaded, changed = modified, unchanged = unchanged"
}, },
"options": { "options": {
"syncStatus": { "syncStatus": {
@@ -58,6 +60,11 @@
"clean": "Synchronized", "clean": "Synchronized",
"unclean": "Discrepancies", "unclean": "Discrepancies",
"failed": "Failed" "failed": "Failed"
},
"fileStatus": {
"new": "New",
"changed": "Changed",
"unchanged": "Unchanged"
} }
} }
} }

View File

@@ -63,6 +63,11 @@
{ {
"name": "sha256" "name": "sha256"
} }
],
[
{
"name": "fileStatus"
}
] ]
], ],
"dynamicLogicVisible": null, "dynamicLogicVisible": null,

View File

@@ -138,6 +138,24 @@
"default": "pending_sync", "default": "pending_sync",
"tooltip": true, "tooltip": true,
"isCustom": true "isCustom": true
},
"fileStatus": {
"type": "enum",
"required": false,
"options": [
"new",
"changed",
"unchanged"
],
"style": {
"new": "info",
"changed": "warning",
"unchanged": "success"
},
"default": "new",
"readOnly": true,
"tooltip": true,
"isCustom": true
} }
}, },
"links": { "links": {

View File

@@ -358,7 +358,7 @@ return [
0 => 'youtube.com', 0 => 'youtube.com',
1 => 'google.com' 1 => 'google.com'
], ],
'microtime' => 1772471137.489776, 'microtime' => 1772522154.570041,
'siteUrl' => 'https://crm.bitbylaw.com', 'siteUrl' => 'https://crm.bitbylaw.com',
'fullTextSearchMinLength' => 4, 'fullTextSearchMinLength' => 4,
'webSocketUrl' => 'ws://api.bitbylaw.com:5000/espocrm/ws', 'webSocketUrl' => 'ws://api.bitbylaw.com:5000/espocrm/ws',

View File

@@ -1,7 +1,7 @@
<?php <?php
return [ return [
'cacheTimestamp' => 1772471137, 'cacheTimestamp' => 1772522193,
'microtimeState' => 1772471137.62098, 'microtimeState' => 1772522193.960869,
'currencyRates' => [ 'currencyRates' => [
'EUR' => 1.0 'EUR' => 1.0
], ],