diff --git a/app/config/schema/schema.json b/app/config/schema/schema.json index 4b0880a7b..de84a8e43 100644 --- a/app/config/schema/schema.json +++ b/app/config/schema/schema.json @@ -521,8 +521,8 @@ "history_records": { "columns": { "id": {}, - "action": { "type": "string", "size": 4 }, - "comment": { "type": "string", "size": 256 }, + "action": {}, + "comment": {}, "person_id": {}, "person_role_id": {}, "external_identity_id": {}, @@ -542,6 +542,62 @@ } }, + "message_templates": { + "columns": { + "id": {}, + "co_id": {}, + "description": {}, + "status": {}, + "context": {}, + "format": { "type": "string", "size": 4 }, + "subject": { "type": "string", "size": 256 }, + "body_text": { "type": "text" }, + "body_html": { "type": "text" }, + "cc": { "type": "string", "size": 256 }, + "bcc": { "type": "string", "size": 256 }, + "reply_to": { "type": "string", "size": 256 } + }, + "indexes": { + "message_templates_i1": { "columns": [ "co_id" ] } + } + }, + + "notifications": { + "columns": { + "id": {}, + "subject_person_id": { "type": "integer", "foreignkey": { "table": "people", "column": "id" } }, + "subject_group_id": { "type": "integer", "foreignkey": { "table": "people", "column": "id" } }, + "actor_person_id": { "type": "integer", "foreignkey": { "table": "people", "column": "id" } }, + "recipient_person_id": { "type": "integer", "foreignkey": { "table": "people", "column": "id" } }, + "recipient_group_id": { "type": "integer", "foreignkey": { "table": "people", "column": "id" } }, + "resolver_person_id": { "type": "integer", "foreignkey": { "table": "people", "column": "id" } }, + "action": { + "comment": "revert this to use the library definition after feature-cfm31 merge", + "type": "string", "size": 4 + }, + "comment": {}, + "message_template_id": { "type": "integer", "foreignkey": { "table": "message_templates", "column": "id" } }, + "source": { "type": "text" }, + "email_subject": { "type": "string", "size": 256 }, + "email_body_text": { "type": "text" }, + "email_body_html": { "type": "text" }, + "resolution_subject": { "type": "string", "size": 256 }, + "resolution_body": { "type": "text" }, + "status": {}, + "notification_time": { "type": "datetime" }, + "resolution_time": { "type": "datetime" } + }, + "indexes": { + "notifications_i1": { "columns": [ "subject_person_id" ] }, + "notifications_i2": { "columns": [ "subject_group_id" ] }, + "notifications_i3": { "columns": [ "recipient_person_id" ] }, + "notifications_i4": { "columns": [ "recipient_group_id" ] }, + "notifications_i5": { "columns": [ "source" ] }, + "notifications_i6": { "needed": false, "columns": [ "actor_person_id" ] }, + "notifications_i7": { "needed": false, "columns": [ "resolver_person_id" ] } + } + }, + "enrollment_flows": { "columns": { "id": {}, @@ -627,62 +683,6 @@ } }, - "message_templates": { - "columns": { - "id": {}, - "co_id": {}, - "description": {}, - "status": {}, - "context": {}, - "format": { "type": "string", "size": 4 }, - "subject": { "type": "string", "size": 256 }, - "body_text": { "type": "text" }, - "body_html": { "type": "text" }, - "cc": { "type": "string", "size": 256 }, - "bcc": { "type": "string", "size": 256 }, - "reply_to": { "type": "string", "size": 256 } - }, - "indexes": { - "message_templates_i1": { "columns": [ "co_id" ] } - } - }, - - "notifications": { - "columns": { - "id": {}, - "subject_person_id": { "type": "integer", "foreignkey": { "table": "people", "column": "id" } }, - "subject_group_id": { "type": "integer", "foreignkey": { "table": "people", "column": "id" } }, - "actor_person_id": { "type": "integer", "foreignkey": { "table": "people", "column": "id" } }, - "recipient_person_id": { "type": "integer", "foreignkey": { "table": "people", "column": "id" } }, - "recipient_group_id": { "type": "integer", "foreignkey": { "table": "people", "column": "id" } }, - "resolver_person_id": { "type": "integer", "foreignkey": { "table": "people", "column": "id" } }, - "action": { - "comment": "revert this to use the library definition after feature-cfm31 merge", - "type": "string", "size": 4 - }, - "comment": {}, - "message_template_id": { "type": "integer", "foreignkey": { "table": "message_templates", "column": "id" } }, - "source": { "type": "text" }, - "email_subject": { "type": "string", "size": 256 }, - "email_body_text": { "type": "text" }, - "email_body_html": { "type": "text" }, - "resolution_subject": { "type": "string", "size": 256 }, - "resolution_body": { "type": "text" }, - "status": {}, - "notification_time": { "type": "datetime" }, - "resolution_time": { "type": "datetime" } - }, - "indexes": { - "notifications_i1": { "columns": [ "subject_person_id" ] }, - "notifications_i2": { "columns": [ "subject_group_id" ] }, - "notifications_i3": { "columns": [ "recipient_person_id" ] }, - "notifications_i4": { "columns": [ "recipient_group_id" ] }, - "notifications_i5": { "columns": [ "source" ] }, - "notifications_i6": { "needed": false, "columns": [ "actor_person_id" ] }, - "notifications_i7": { "needed": false, "columns": [ "resolver_person_id" ] } - } - }, - "jobs": { "columns": { "id": {}, diff --git a/app/src/Model/Table/IdentifiersTable.php b/app/src/Model/Table/IdentifiersTable.php index c3b6ffbe7..12dab1c5f 100644 --- a/app/src/Model/Table/IdentifiersTable.php +++ b/app/src/Model/Table/IdentifiersTable.php @@ -265,6 +265,32 @@ public function lookupPersonByLogin(int $coId, string $identifier): int { return $id->person_id; } + /** + * Lookup a Person ID from a login identifier. Only active Identifiers can + * be used for lookups. + * + * @since COmanage Registry v5.0.0 + * @param string $identifier Identifier + * @param int $coId CO ID + * @return int Person ID or null + */ + + public function lookupPersonForLogin(string $identifier, int $coId): ?int { + $id = $this->find() + ->where([ + 'Identifiers.identifier' => $identifier, + 'Identifiers.status' => SuspendableStatusEnum::Active, + 'Identifiers.login' => true, + 'Identifiers.person_id IS NOT NULL' + ]) + ->matching('People', function ($q) use ($coId) { + return $q->where(['People.co_id' => $coId]); + }) + ->firstOrFail(); + + return $id->person_id ?? null; + } + /** * Application Rule to determine if an Identifier is already in use. * @@ -273,63 +299,63 @@ public function lookupPersonByLogin(int $coId, string $identifier): int { * @param array $options Application rule options * @return boolean true if the Rule check passes, false otherwise */ - + public function ruleUniqueIdentifier($entity, $options) { // Uniqueness constraints only apply to People and Groups - + // In v4 we created a txn to ensure consistency, but it looks like Cake actually // starts a transaction, so it appears we don't actially need to do that here. - + if(!empty($entity->person_id) || !empty($entity->group_id)) { - if($entity->isNew() - || $entity->isDirty('identifier') - || $entity->isDirty('type_id')) { + if($entity->isNew() + || $entity->isDirty('identifier') + || $entity->isDirty('type_id')) { // We need the Type configuration to see if uniqueness is case insensitive $type = $this->Types->get($entity->type_id); - + // Note we specifically do NOT check status, since a Suspended Identifier // will still prevent duplicate assignment. (AR-Identifier-3) $whereClause = [ // type_id will imply CO ID, so we don't need to check it explicitly 'type_id' => $entity->type_id ]; - + if(isset($type->case_insensitive) && $type->case_insensitive) { $whereClause['LOWER(identifier)'] = strtolower($entity->identifier); } else { $whereClause['identifier'] = $entity->identifier; } - + // We need to only check Identifiers attached to the same type of Entity if(!empty($entity->person_id)) { $whereClause[] = 'person_id IS NOT NULL'; } elseif(!empty($entity->group_id)) { $whereClause[] = 'group_id IS NOT NULL'; } - + $identifier = $this->find() - ->where($whereClause) - ->epilog('FOR UPDATE') - ->first(); + ->where($whereClause) + ->epilog('FOR UPDATE') + ->first(); if(!empty($identifier)) { $inusect = !empty($identifier->person_id) ? __d('controller', 'People', 1) : __d('controller', 'Groups', 1); $inuseid = !empty($identifier->person_id) ? $identifier->person_id : $identifier->group_id; - + // If we fail in the middle of Identifier Assignment this returned message // will get lost/superceded by a rollback error $this->llog('rule', "AR-Identifier-2 Identifier " . $identifier->identifier . " is already in use on $inusect ID $inuseid"); - - return __d('error', - 'Identifiers.exists', - [$inusect, $inuseid]); + + return __d('error', + 'Identifiers.exists', + [$inusect, $inuseid]); } } } - + return true; - } - + } + /** * Perform a keyword search. * diff --git a/app/templates/Standard/index.php b/app/templates/Standard/index.php index b9e9f7f9c..b253337ae 100644 --- a/app/templates/Standard/index.php +++ b/app/templates/Standard/index.php @@ -270,11 +270,11 @@ $actionOrderDefault = $this->Menu->getMenuOrder('Default'); foreach ($rowActions as $a) { $ok = false; - if (!empty($a['controller'])) { - $tableName = Inflector::camelize($a['controller']); + if (!empty($a['controller']) && $a['controller'] != $tableName) { + $relTableName = Inflector::camelize($a['controller']); - if (isset($vv_permission_set[$entity->id][$tableName][$a['action']])) { - $ok = $vv_permission_set[$entity->id][$tableName][$a['action']]; + if (isset($vv_permission_set[$entity->id][$relTableName][$a['action']])) { + $ok = $vv_permission_set[$entity->id][$relTableName][$a['action']]; } } else { $ok = $vv_permission_set[$entity->id][$a['action']]; @@ -295,7 +295,7 @@ $actionUrl = ['action' => $a['action'], $entity->id]; $actionLabel = !empty($a['label']) ? $a['label'] : __d('operation', $a['action']); - if (!empty($a['controller'])) { + if (!empty($a['controller']) && $a['controller'] != $tableName) { // We're linking into a related controller $actionLabel = !empty($a['label']) ? $a['label'] : __d('controller', Inflector::camelize(Inflector::pluralize($a['controller'])), [99]); $actionUrl = [ @@ -386,10 +386,11 @@ } // Output the row actions if present - if($isFirstLink && !empty($rowActions)) { + if($isFirstLink && !empty($action_args['vv_actions'])) { + // todo check if needed print '
'; print '
'; - print $this->element('menuAction', $action_args); + print $this->element('menuAction', $action_args); print '
'; }