diff --git a/app/resources/locales/en_US/field.po b/app/resources/locales/en_US/field.po index 58cb5352a..4c8afd54d 100644 --- a/app/resources/locales/en_US/field.po +++ b/app/resources/locales/en_US/field.po @@ -29,8 +29,8 @@ # When adding entries to this file, group non-model specific translations at the top, # then model specific translations alphabetically by model. -msgid "action" -msgstr "Action" +msgid "actions" +msgstr "{0,plural,=1{Action} other{Actions}}" msgid "actor" msgstr "Actor" @@ -159,6 +159,9 @@ msgstr "Plugin" msgid "postal_code" msgstr "Postal Code" +msgid "primary" +msgstr "Primary" + msgid "primary_name" msgstr "Primary Name" @@ -243,6 +246,9 @@ msgstr "Value" msgid "verified" msgstr "Verified" +msgid "unverified" +msgstr "Unverified" + msgid "ApiUsers.privileged.desc" msgstr "A privileged API user has full access to the CO. Unprivileged API users may be granted specific permissions where supported." diff --git a/app/resources/locales/en_US/information.po b/app/resources/locales/en_US/information.po index 7e6551f1e..4cd7ed3a2 100644 --- a/app/resources/locales/en_US/information.po +++ b/app/resources/locales/en_US/information.po @@ -56,3 +56,9 @@ msgstr "There are no records to display." msgid "global.title.none" msgstr "No title" + +msgid "global.value.none" +msgstr "No value" + +msgid "global.visit.link" +msgstr "Visit link" diff --git a/app/resources/locales/en_US/operation.po b/app/resources/locales/en_US/operation.po index e337e64fa..fc01b4b65 100644 --- a/app/resources/locales/en_US/operation.po +++ b/app/resources/locales/en_US/operation.po @@ -27,6 +27,9 @@ msgid "activate" msgstr "Activate" +msgid "add" +msgstr "Add" + msgid "add.a" msgstr "Add a New {0}" diff --git a/app/src/Controller/ApiV2Controller.php b/app/src/Controller/ApiV2Controller.php index 3553dca69..78c5fb7c8 100644 --- a/app/src/Controller/ApiV2Controller.php +++ b/app/src/Controller/ApiV2Controller.php @@ -280,7 +280,20 @@ public function index() { if(!empty($link->attr) && !empty($link->value)) { $query = $query->where([$this->$modelsName->getAlias().'.'.$link->attr => $link->value]); } - + + // This will produce a nested object which is very useful for vue integration + if($this->request->getQuery('extended') !== null) { + $modelContain = []; + $associations = $this->$modelsName->associations(); + foreach($associations->getByType(['BelongsTo']) as $a) { + $modelContain[] = $a->getClassName(); + } + + if(!empty($modelContain)) { + $query = $query->contain($modelContain); + } + } + if($modelsName == 'AuthenticationEvents') { // Special case for filtering on authenticated identifier. There is a // similar filter in AuthenticationEventsController::beforeFilter. diff --git a/app/src/Controller/Component/RegistryAuthComponent.php b/app/src/Controller/Component/RegistryAuthComponent.php index 1906915fc..b59f0511a 100644 --- a/app/src/Controller/Component/RegistryAuthComponent.php +++ b/app/src/Controller/Component/RegistryAuthComponent.php @@ -129,8 +129,19 @@ public function beforeFilter(EventInterface $event) { } // Perform authorization check - - if($this->getConfig('apiUser')) { + + // Do we have an authenticated user session? + + // Note we don't stuff anything into the session anymore, the only attribute + // is the username, which is actually loaded by login.php. + + $auth = $session->read('Auth'); + + // Registry UI is now a hybrid implementation of VUE and CAKEPHP MVC. + // In order to allow a logged-in user to reach out to the backend without + // the need of an API User, but just with the use of the Session, we will + // skip the API user authorization if a user Session is available. + if(empty($auth) && $this->getConfig('apiUser')) { // There are no unauthenticated API calls, so always require a valid user try { @@ -177,16 +188,9 @@ public function beforeFilter(EventInterface $event) { if($controller->getName() == 'Pages') { return true; } - - // Do we have an authenticated user session? - - // Note we don't stuff anything into the session anymore, the only attribute - // is the username, which is actually loaded by login.php. - - $auth = $session->read('Auth'); - + if(!empty($auth['external']['user'])) { - // We have a valid user name that is *authenticated* for the current request. + // We have a valid username that is *authenticated* for the current request. // Note we haven't checked authorization, but this is how the authorization // checks can get the authenticated username. $controller->set('vv_user', ['username' => $auth['external']['user']]); diff --git a/app/src/Controller/StandardController.php b/app/src/Controller/StandardController.php index a62131ff8..e3490a4ad 100644 --- a/app/src/Controller/StandardController.php +++ b/app/src/Controller/StandardController.php @@ -331,8 +331,9 @@ public function edit(string $id) { } catch(\Exception $e) { // findById throws Cake\Datasource\Exception\RecordNotFoundException - $this->Flash->error($e->getMessage()); + // XXX This redirects to an Exception page because $id is not found. + // XXX A 404 with error would be better. return $this->generateRedirect((int)$id); } @@ -724,8 +725,9 @@ public function view($id = null) { } catch(\Exception $e) { // findById throws Cake\Datasource\Exception\RecordNotFoundException - $this->Flash->error($e->getMessage()); + // XXX This redirects to an Exception page because $id is not found. + // XXX A 404 with error would be better. return $this->generateRedirect((int)$id); } @@ -742,6 +744,9 @@ public function view($id = null) { // We don't use a trait for this since each table will implement different logic $this->set('vv_title', __d('operation', 'view.ai', $table->generateDisplayField($obj))); + $this->set('vv_supertitle', $table->generateDisplayField($obj)); + // Pass the display field also into subtitle for dealing with External IDs + $this->set('vv_subtitle', $table->generateDisplayField($obj)); } else { // Default view title is the object display field $field = $table->getDisplayField(); diff --git a/app/src/Lib/Traits/PrimaryLinkTrait.php b/app/src/Lib/Traits/PrimaryLinkTrait.php index ec52aa6ca..bd02b2b60 100644 --- a/app/src/Lib/Traits/PrimaryLinkTrait.php +++ b/app/src/Lib/Traits/PrimaryLinkTrait.php @@ -46,7 +46,7 @@ trait PrimaryLinkTrait { private $unkeyedActions = ['add', 'index']; // Actions where the primary link can be obtained by looking up the record ID - private $lookupActions = ['delete', 'edit', 'view']; + private $lookupActions = ['delete', 'edit', 'canvas', 'view']; // Where to redirect on add or edit, can be 'self', 'index', or 'primaryLink' private $redirectGoal = 'index'; diff --git a/app/src/Model/Table/PeopleTable.php b/app/src/Model/Table/PeopleTable.php index f495b9ce0..4e46ac8ca 100644 --- a/app/src/Model/Table/PeopleTable.php +++ b/app/src/Model/Table/PeopleTable.php @@ -150,6 +150,7 @@ public function initialize(array $config): void { 'entity' => [ 'delete' => ['platformAdmin', 'coAdmin'], 'edit' => ['platformAdmin', 'coAdmin'], + 'canvas' => ['platformAdmin', 'coAdmin'], 'view' => ['platformAdmin', 'coAdmin'] ], // Actions that operate over a table (ie: do not require an $id) diff --git a/app/src/View/Helper/VueHelper.php b/app/src/View/Helper/VueHelper.php new file mode 100644 index 000000000..7230ba524 --- /dev/null +++ b/app/src/View/Helper/VueHelper.php @@ -0,0 +1,74 @@ + [ + 'login', + 'primary', + 'datepicker.hour', + 'unverified' + ], + 'information' => [ + 'global.value.none', + 'datepicker.hour' + ] + ]; + + /** + * Helper which will produce an array of configured locales + * + * @param string $lang The language of the locale + * + * @return array [] + * @since COmanage Registry v5.0.0 + */ + + public function locales(string $lang = 'en_US'): array { + + I18n::setLocale($lang); + + $locales = []; + foreach ($this->locales_list as $domain => $key_list) { + foreach ($key_list as $key) { + $locales[$key] = __d($domain, $key); + } + } + + return $locales; + } + +} \ No newline at end of file diff --git a/app/templates/AdHocAttributes/columns.inc b/app/templates/AdHocAttributes/columns.inc index dd2f93b61..4d5aecbec 100644 --- a/app/templates/AdHocAttributes/columns.inc +++ b/app/templates/AdHocAttributes/columns.inc @@ -41,6 +41,6 @@ $bulkActions = [ $subnav = [ 'name' => 'person', - 'active' => 'attributes', + 'active' => 'person', 'subActive' => 'ad_hoc_attributes' ]; \ No newline at end of file diff --git a/app/templates/AdHocAttributes/fields-nav.inc b/app/templates/AdHocAttributes/fields-nav.inc index 8cddfe107..9b42482f4 100644 --- a/app/templates/AdHocAttributes/fields-nav.inc +++ b/app/templates/AdHocAttributes/fields-nav.inc @@ -30,6 +30,6 @@ $topLinks = []; $subnav = [ 'name' => 'person', - 'active' => 'attributes', + 'active' => 'person', 'subActive' => 'ad_hoc_attributes' ]; \ No newline at end of file diff --git a/app/templates/Addresses/columns.inc b/app/templates/Addresses/columns.inc index 257f4b097..e5c58c121 100644 --- a/app/templates/Addresses/columns.inc +++ b/app/templates/Addresses/columns.inc @@ -45,6 +45,6 @@ $bulkActions = [ $subnav = [ 'name' => 'person', - 'active' => 'attributes', + 'active' => 'person', 'subActive' => 'addresses' ]; diff --git a/app/templates/Addresses/fields-nav.inc b/app/templates/Addresses/fields-nav.inc index 08574e5a4..86686fdf9 100644 --- a/app/templates/Addresses/fields-nav.inc +++ b/app/templates/Addresses/fields-nav.inc @@ -30,6 +30,6 @@ $topLinks = []; $subnav = [ 'name' => 'person', - 'active' => 'attributes', + 'active' => 'person', 'subActive' => 'addresses' ]; \ No newline at end of file diff --git a/app/templates/EmailAddresses/columns.inc b/app/templates/EmailAddresses/columns.inc index da915ca19..4c40977ca 100644 --- a/app/templates/EmailAddresses/columns.inc +++ b/app/templates/EmailAddresses/columns.inc @@ -41,6 +41,6 @@ $bulkActions = [ $subnav = [ 'name' => 'person', - 'active' => 'attributes', + 'active' => 'person', 'subActive' => 'email_addresses' ]; \ No newline at end of file diff --git a/app/templates/EmailAddresses/fields-nav.inc b/app/templates/EmailAddresses/fields-nav.inc index 14b677975..797d856ba 100644 --- a/app/templates/EmailAddresses/fields-nav.inc +++ b/app/templates/EmailAddresses/fields-nav.inc @@ -30,6 +30,6 @@ $topLinks = []; $subnav = [ 'name' => 'person', - 'active' => 'attributes', // default for person. 'external identities' are special cased + 'active' => 'person', // default for person. 'external identities' are special cased 'subActive' => 'email_addresses' ]; \ No newline at end of file diff --git a/app/templates/Identifiers/columns.inc b/app/templates/Identifiers/columns.inc index 48c82927c..8a27be5d2 100644 --- a/app/templates/Identifiers/columns.inc +++ b/app/templates/Identifiers/columns.inc @@ -56,6 +56,6 @@ $bulkActions = [ $subnav = [ 'name' => 'person', - 'active' => 'attributes', + 'active' => 'person', 'subActive' => 'identifiers' ]; \ No newline at end of file diff --git a/app/templates/Identifiers/fields-nav.inc b/app/templates/Identifiers/fields-nav.inc index bcacd714f..17db0dc99 100644 --- a/app/templates/Identifiers/fields-nav.inc +++ b/app/templates/Identifiers/fields-nav.inc @@ -30,6 +30,6 @@ $topLinks = []; $subnav = [ 'name' => 'person', - 'active' => 'attributes', + 'active' => 'person', 'subActive' => 'identifiers' ]; \ No newline at end of file diff --git a/app/templates/Names/columns.inc b/app/templates/Names/columns.inc index feeda0cd5..e142b78a5 100644 --- a/app/templates/Names/columns.inc +++ b/app/templates/Names/columns.inc @@ -58,6 +58,6 @@ $bulkActions = [ $subnav = [ 'name' => 'person', - 'active' => 'attributes', + 'active' => 'person', 'subActive' => 'names' ]; \ No newline at end of file diff --git a/app/templates/Names/fields-nav.inc b/app/templates/Names/fields-nav.inc index d333f8830..955afd929 100644 --- a/app/templates/Names/fields-nav.inc +++ b/app/templates/Names/fields-nav.inc @@ -30,6 +30,6 @@ $topLinks = []; $subnav = [ 'name' => 'person', - 'active' => 'attributes', + 'active' => 'person', 'subActive' => 'names' ]; \ No newline at end of file diff --git a/app/templates/People/columns.inc b/app/templates/People/columns.inc index d29f8d6a8..e64a5f37b 100644 --- a/app/templates/People/columns.inc +++ b/app/templates/People/columns.inc @@ -30,6 +30,7 @@ $indexColumns = [ 'type' => 'link', 'model' => 'primary_name', 'field' => 'full_name', + 'action' => 'edit', // XXX see comments in the controller about sorting on given vs family 'sortable' => 'PrimaryName.family' ], @@ -49,67 +50,57 @@ $rowActions = [ 'controller' => 'names', 'action' => 'index', 'icon' => 'account_box', - 'iconClass' => 'material-icons-outlined', - 'type' => 'tab' + 'iconClass' => 'material-icons-outlined' ], [ 'controller' => 'email_addresses', 'action' => 'index', 'icon' => 'email', - 'iconClass' => 'material-icons-outlined', - 'type' => 'tab' + 'iconClass' => 'material-icons-outlined' ], [ 'controller' => 'identifiers', 'action' => 'index', 'icon' => 'fingerprint', - 'type' => 'tab', 'class' => 'bottom-border' ], [ 'controller' => 'person_roles', 'action' => 'index', - 'icon' => 'emoji_people', - 'type' => 'tab' + 'icon' => 'emoji_people' ], [ 'controller' => 'external_identities', 'action' => 'index', 'icon' => 'system_update_alt', - 'type' => 'tab', 'class' => 'bottom-border' ], [ 'controller' => 'ad_hoc_attributes', 'action' => 'index', 'icon' => 'check_box', - 'iconClass' => 'material-icons-outlined', - 'type' => 'tab' + 'iconClass' => 'material-icons-outlined' ], [ 'controller' => 'addresses', 'action' => 'index', 'icon' => 'contact_mail', - 'iconClass' => 'material-icons-outlined', - 'type' => 'tab' + 'iconClass' => 'material-icons-outlined' ], [ 'controller' => 'history_records', 'action' => 'index', - 'icon' => 'history', - 'type' => 'tab' + 'icon' => 'history' ], [ 'controller' => 'telephone_numbers', 'action' => 'index', - 'icon' => 'phone', - 'type' => 'tab' + 'icon' => 'phone' ], [ 'controller' => 'urls', 'action' => 'index', - 'icon' => 'link', - 'type' => 'tab' + 'icon' => 'link' ] ]; diff --git a/app/templates/People/fields-nav.inc b/app/templates/People/fields-nav.inc index e54aef215..cf904e33d 100644 --- a/app/templates/People/fields-nav.inc +++ b/app/templates/People/fields-nav.inc @@ -44,5 +44,6 @@ $topLinks = [ $subnav = [ 'name' => 'person', - 'active' => 'properties' + 'active' => 'person', + 'subActive' => 'properties' ]; \ No newline at end of file diff --git a/app/templates/Pronouns/columns.inc b/app/templates/Pronouns/columns.inc index 3963c2db5..58767da56 100644 --- a/app/templates/Pronouns/columns.inc +++ b/app/templates/Pronouns/columns.inc @@ -45,6 +45,6 @@ $bulkActions = [ $subnav = [ 'name' => 'person', - 'active' => 'attributes', + 'active' => 'person', 'subActive' => 'pronouns' ]; \ No newline at end of file diff --git a/app/templates/Pronouns/fields-nav.inc b/app/templates/Pronouns/fields-nav.inc index ff7b40d0f..2ea808589 100644 --- a/app/templates/Pronouns/fields-nav.inc +++ b/app/templates/Pronouns/fields-nav.inc @@ -30,6 +30,6 @@ $topLinks = []; $subnav = [ 'name' => 'person', - 'active' => 'attributes', + 'active' => 'person', 'subActive' => 'pronouns' ]; \ No newline at end of file diff --git a/app/templates/Standard/add-edit-view.php b/app/templates/Standard/add-edit-view.php index 16800f0c2..7b1f8e8fc 100644 --- a/app/templates/Standard/add-edit-view.php +++ b/app/templates/Standard/add-edit-view.php @@ -61,98 +61,140 @@ // the fields (which would be more consistent with how Views render...) $flashArgs['vv_banners'] = $banners; } + +// If subnavigation is present a supertitle and the subnavigation will be placed above +// the normal page title. The flash messages will be shown up there as well. +if(!empty($subnav)) { + // Include the $flashArgs for the subnavigation element + $subnav['flashArgs'] = $flashArgs; + // Generate the subnavigation title and tabs + print $this->element('subnavigation', $subnav); +} ?> - -