Refactor UpdateLastSyncFromDocuments hook to use PDO for database queries; add AI Sync fields to CDokumente layout; update microtime values in config and state files; create check_role_permissions script for role validation
This commit is contained in:
@@ -14,97 +14,78 @@ class UpdateLastSyncFromDocuments implements BeforeSave
|
|||||||
public function __construct(
|
public function __construct(
|
||||||
private \Espo\ORM\EntityManager $entityManager
|
private \Espo\ORM\EntityManager $entityManager
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public function beforeSave(Entity $entity, SaveOptions $options): void
|
public function beforeSave(Entity $entity, SaveOptions $options): void
|
||||||
{
|
{
|
||||||
// Überspringe, wenn skipHooks gesetzt ist (verhindert Loops)
|
// Überspringe, wenn skipHooks gesetzt ist (verhindert Loops)
|
||||||
if ($options->get('skipHooks')) {
|
if ($options->get('skipHooks')) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Nur wenn Entity bereits existiert (nicht bei Create)
|
// Nur wenn Entity bereits existiert (nicht bei Create)
|
||||||
if ($entity->isNew()) {
|
if ($entity->isNew()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
$pdo = $this->entityManager->getPDO();
|
||||||
|
$aktenId = $entity->getId();
|
||||||
|
|
||||||
// Hole das neueste lastSyncTimestamp aller verknüpften Dokumente
|
// Hole das neueste lastSyncTimestamp aller verknüpften Dokumente
|
||||||
$query = $this->entityManager->getQueryBuilder()
|
$stmt = $pdo->prepare(
|
||||||
->select([
|
"SELECT MAX(last_sync_timestamp) AS maxLastSync
|
||||||
'MAX:lastSyncTimestamp' => 'maxLastSync',
|
FROM c_dokumente
|
||||||
'COUNT:id' => 'dokumentCount'
|
WHERE c_akten_id = :aktenId AND deleted = 0 AND last_sync_timestamp IS NOT NULL"
|
||||||
])
|
);
|
||||||
->from('CDokumente')
|
$stmt->execute([':aktenId' => $aktenId]);
|
||||||
->where([
|
$result = $stmt->fetch(\PDO::FETCH_ASSOC);
|
||||||
'cAktenId' => $entity->getId(),
|
|
||||||
'deleted' => false,
|
|
||||||
'lastSyncTimestamp!=' => null
|
|
||||||
])
|
|
||||||
->build();
|
|
||||||
|
|
||||||
$pdoStatement = $this->entityManager->getQueryExecutor()->execute($query);
|
|
||||||
$result = $pdoStatement->fetch(\PDO::FETCH_ASSOC);
|
|
||||||
|
|
||||||
if ($result && $result['maxLastSync']) {
|
if ($result && $result['maxLastSync']) {
|
||||||
// Setze lastSync auf den neuesten Sync-Timestamp
|
|
||||||
$entity->set('lastSync', $result['maxLastSync']);
|
$entity->set('lastSync', $result['maxLastSync']);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Berechne auch syncStatus basierend auf verknüpften Dokumenten
|
// Berechne syncStatus basierend auf verknüpften Dokumenten
|
||||||
$this->updateSyncStatus($entity);
|
$this->updateSyncStatus($entity, $pdo, $aktenId);
|
||||||
|
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
// Fehler loggen, aber nicht werfen (um Save nicht zu blockieren)
|
|
||||||
$GLOBALS['log']->error('CAkten UpdateLastSyncFromDocuments Hook Error: ' . $e->getMessage());
|
$GLOBALS['log']->error('CAkten UpdateLastSyncFromDocuments Hook Error: ' . $e->getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private function updateSyncStatus(Entity $entity, \PDO $pdo, string $aktenId): void
|
||||||
* Aktualisiert syncStatus basierend auf den Status der verknüpften Dokumente
|
|
||||||
*/
|
|
||||||
private function updateSyncStatus(Entity $entity): void
|
|
||||||
{
|
{
|
||||||
$query = $this->entityManager->getQueryBuilder()
|
$stmt = $pdo->prepare(
|
||||||
->select(['syncStatus'])
|
"SELECT sync_status FROM c_dokumente
|
||||||
->from('CDokumente')
|
WHERE c_akten_id = :aktenId AND deleted = 0"
|
||||||
->where([
|
);
|
||||||
'cAktenId' => $entity->getId(),
|
$stmt->execute([':aktenId' => $aktenId]);
|
||||||
'deleted' => false
|
$rows = $stmt->fetchAll(\PDO::FETCH_ASSOC);
|
||||||
])
|
|
||||||
->build();
|
|
||||||
|
|
||||||
$pdoStatement = $this->entityManager->getQueryExecutor()->execute($query);
|
|
||||||
$rows = $pdoStatement->fetchAll(\PDO::FETCH_ASSOC);
|
|
||||||
|
|
||||||
// Wenn keine Dokumente verknüpft, setze auf "unclean"
|
|
||||||
if (empty($rows)) {
|
if (empty($rows)) {
|
||||||
$entity->set('syncStatus', 'unclean');
|
$entity->set('syncStatus', 'unclean');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prüfe, ob irgendein Dokument "new" oder "unclean" ist
|
|
||||||
$hasUnsynced = false;
|
$hasUnsynced = false;
|
||||||
$hasFailed = false;
|
$hasFailed = false;
|
||||||
|
|
||||||
foreach ($rows as $row) {
|
foreach ($rows as $row) {
|
||||||
$status = $row['syncStatus'] ?? null;
|
$status = $row['sync_status'] ?? null;
|
||||||
|
|
||||||
if ($status === 'failed') {
|
if ($status === 'failed') {
|
||||||
$hasFailed = true;
|
$hasFailed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($status === 'new' || $status === 'unclean' || $status === null || $status === '') {
|
if ($status === 'new' || $status === 'unclean' || $status === null || $status === '') {
|
||||||
$hasUnsynced = true;
|
$hasUnsynced = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setze globalen Status
|
|
||||||
if ($hasFailed) {
|
if ($hasFailed) {
|
||||||
$entity->set('syncStatus', 'failed');
|
$entity->set('syncStatus', 'failed');
|
||||||
} elseif ($hasUnsynced) {
|
} elseif ($hasUnsynced) {
|
||||||
$entity->set('syncStatus', 'unclean');
|
$entity->set('syncStatus', 'unclean');
|
||||||
} else {
|
} else {
|
||||||
// Alle Dokumente sind "synced"
|
|
||||||
$entity->set('syncStatus', 'synced');
|
$entity->set('syncStatus', 'synced');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -99,5 +99,38 @@
|
|||||||
"noteText": null,
|
"noteText": null,
|
||||||
"noteStyle": "info",
|
"noteStyle": "info",
|
||||||
"customLabel": "Advoware Sync"
|
"customLabel": "Advoware Sync"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"rows": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "aiSyncStatus"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "aiLastSync"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "aiCollectionId"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "aiFileId"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "aiSyncHash"
|
||||||
|
},
|
||||||
|
{"isFull": false}
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"dynamicLogicVisible": null,
|
||||||
|
"style": "default",
|
||||||
|
"tabBreak": false,
|
||||||
|
"hidden": false,
|
||||||
|
"noteText": null,
|
||||||
|
"noteStyle": "info",
|
||||||
|
"customLabel": "AI Sync"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
117
custom/scripts/check_role_permissions.php
Normal file
117
custom/scripts/check_role_permissions.php
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// Direct PDO connection to check role permissions
|
||||||
|
$config = include 'data/config-internal.php';
|
||||||
|
$db = $config['database'];
|
||||||
|
|
||||||
|
try {
|
||||||
|
$pdo = new PDO(
|
||||||
|
"mysql:host={$db['host']};dbname={$db['dbname']}",
|
||||||
|
$db['user'],
|
||||||
|
$db['password']
|
||||||
|
);
|
||||||
|
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||||
|
|
||||||
|
$targetRole = 'api-user-motia';
|
||||||
|
|
||||||
|
echo "=== Rolle: $targetRole ===\n\n";
|
||||||
|
|
||||||
|
// 1. Rolle in DB finden
|
||||||
|
$stmt = $pdo->prepare("SELECT id, name, data FROM role WHERE name = ?");
|
||||||
|
$stmt->execute([$targetRole]);
|
||||||
|
$role = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
if (!$role) {
|
||||||
|
echo "FEHLER: Rolle '$targetRole' nicht gefunden!\n";
|
||||||
|
|
||||||
|
// Alle Rollen auflisten
|
||||||
|
echo "\nVorhandene Rollen:\n";
|
||||||
|
$stmt = $pdo->query("SELECT id, name FROM role ORDER BY name");
|
||||||
|
foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $r) {
|
||||||
|
echo " - {$r['name']} (id: {$r['id']})\n";
|
||||||
|
}
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "Rollen-ID: {$role['id']}\n\n";
|
||||||
|
|
||||||
|
$data = json_decode($role['data'], true);
|
||||||
|
if (!$data) {
|
||||||
|
echo "FEHLER: Rollen-Daten konnten nicht geparst werden.\n";
|
||||||
|
echo "Raw data: " . substr($role['data'], 0, 500) . "\n";
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Scope-Permissions anzeigen (table-Abschnitt)
|
||||||
|
$table = $data['table'] ?? [];
|
||||||
|
|
||||||
|
echo "=== Scope-Permissions (table) ===\n";
|
||||||
|
$interesting = ['CAkten', 'CAdvowareAkten', 'CAktenCDokumente', 'CAdvowareAktenCDokumente', 'CDokumente', 'CAIKnowledge', 'CAICollection'];
|
||||||
|
echo "\nGezielte Suche nach relevanten Entitäten:\n";
|
||||||
|
echo str_repeat("-", 60) . "\n";
|
||||||
|
printf("%-40s %-10s %-10s\n", "Entität", "create", "read");
|
||||||
|
echo str_repeat("-", 60) . "\n";
|
||||||
|
|
||||||
|
foreach ($interesting as $entity) {
|
||||||
|
if (isset($table[$entity])) {
|
||||||
|
$perms = $table[$entity];
|
||||||
|
$create = $perms['create'] ?? '(n/a)';
|
||||||
|
$read = $perms['read'] ?? '(n/a)';
|
||||||
|
printf("%-40s %-10s %-10s\n", $entity, $create, $read);
|
||||||
|
} else {
|
||||||
|
printf("%-40s %-10s\n", $entity, '--- FEHLT ---');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "\n=== Alle Scope-Einträge (table) ===\n";
|
||||||
|
echo str_repeat("-", 60) . "\n";
|
||||||
|
ksort($table);
|
||||||
|
foreach ($table as $entity => $perms) {
|
||||||
|
$read = $perms['read'] ?? '-';
|
||||||
|
$create = $perms['create'] ?? '-';
|
||||||
|
$edit = $perms['edit'] ?? '-';
|
||||||
|
$delete = $perms['delete'] ?? '-';
|
||||||
|
printf("%-40s read=%-8s create=%-8s edit=%-8s delete=%-8s\n", $entity, $read, $create, $edit, $delete);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Field-Permissions anzeigen
|
||||||
|
$fieldTable = $data['fieldTable'] ?? [];
|
||||||
|
echo "\n=== Feld-Permissions (fieldTable) für relevante Entitäten ===\n";
|
||||||
|
foreach ($interesting as $entity) {
|
||||||
|
if (isset($fieldTable[$entity])) {
|
||||||
|
echo "\n $entity:\n";
|
||||||
|
foreach ($fieldTable[$entity] as $field => $perm) {
|
||||||
|
echo " $field: " . json_encode($perm) . "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. API-User → Rolle Zuordnung prüfen
|
||||||
|
echo "\n=== API-User mit Rolle '$targetRole' ===\n";
|
||||||
|
$stmt = $pdo->query("
|
||||||
|
SELECT u.id, u.user_name, u.name, u.type, u.is_active
|
||||||
|
FROM `user` u
|
||||||
|
JOIN entity_user eu ON eu.user_id = u.id
|
||||||
|
JOIN role r ON r.id = eu.entity_id AND eu.entity_type = 'Role'
|
||||||
|
WHERE r.name = " . $pdo->quote($targetRole)
|
||||||
|
);
|
||||||
|
$users = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
if ($users) {
|
||||||
|
foreach ($users as $u) {
|
||||||
|
$active = $u['is_active'] ? 'aktiv' : 'inaktiv';
|
||||||
|
echo " - {$u['user_name']} ({$u['name']}) type={$u['type']} [$active]\n";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
echo " Keine User direkt zugeordnet.\n";
|
||||||
|
// Alternativ: per teams oder direkte Rollen-ID im User
|
||||||
|
$stmt2 = $pdo->prepare("SELECT id, user_name, name, type FROM `user` WHERE is_active = 1 AND type = 'api'");
|
||||||
|
$stmt2->execute();
|
||||||
|
echo "\n Alle API-User:\n";
|
||||||
|
foreach ($stmt2->fetchAll(PDO::FETCH_ASSOC) as $u) {
|
||||||
|
echo " - {$u['user_name']} ({$u['name']}) type={$u['type']}\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception $e) {
|
||||||
|
echo "FEHLER: " . $e->getMessage() . "\n";
|
||||||
|
}
|
||||||
@@ -360,7 +360,7 @@ return [
|
|||||||
0 => 'youtube.com',
|
0 => 'youtube.com',
|
||||||
1 => 'google.com'
|
1 => 'google.com'
|
||||||
],
|
],
|
||||||
'microtime' => 1774518628.185454,
|
'microtime' => 1774526770.942727,
|
||||||
'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',
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
return [
|
return [
|
||||||
'cacheTimestamp' => 1774518628,
|
'cacheTimestamp' => 1774530224,
|
||||||
'microtimeState' => 1774518628.373828,
|
'microtimeState' => 1774530224.017606,
|
||||||
'currencyRates' => [
|
'currencyRates' => [
|
||||||
'EUR' => 1.0
|
'EUR' => 1.0
|
||||||
],
|
],
|
||||||
|
|||||||
Reference in New Issue
Block a user