diff --git a/app/resources/locales/en_US/field.po b/app/resources/locales/en_US/field.po
index 9d688eb35..28273eb27 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"
@@ -162,6 +162,9 @@ msgstr "Plugin"
msgid "postal_code"
msgstr "Postal Code"
+msgid "primary"
+msgstr "Primary"
+
msgid "primary_name"
msgstr "Primary Name"
@@ -246,6 +249,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/menu.po b/app/resources/locales/en_US/menu.po
index f8884c6bc..39f264a9a 100644
--- a/app/resources/locales/en_US/menu.po
+++ b/app/resources/locales/en_US/menu.po
@@ -28,17 +28,50 @@ msgid "co.Attributes"
msgstr "Attributes"
msgid "co.configuration"
-msgstr "Config"
+msgstr "Configuration"
+
+msgid "co.configuration.panel.az"
+msgstr "A-Z Listing"
+
+msgid "co.configuration.panel.title"
+msgstr "Configuration"
+
+msgid "co.configuration.panel.personalization"
+msgstr "Personalization"
+
+msgid "co.configuration.panel.personalization.desc"
+msgstr "Dashboards, custom text, and theming"
+
+msgid "co.configuration.panel.platform"
+msgstr "Platform-Wide Settings"
+
+msgid "co.configuration.panel.platform.desc"
+msgstr "These configurations are found only in the COmanage CO"
+
+msgid "co.configuration.panel.setup"
+msgstr "CO Setup"
+
+msgid "co.configuration.panel.setup.desc"
+msgstr "Settings for the current CO"
msgid "co.connections"
msgstr "Connections"
-msgid "co.lifecycle"
-msgstr "Lifecycle"
+msgid "co.connections.panel.title"
+msgstr "Connections"
+
+msgid "co.connections.panel.desc"
+msgstr "Connections to external services: upstream inputs and downstream outputs"
msgid "co.operations"
msgstr "Operations"
+msgid "co.operations.panel.title"
+msgstr "Operations"
+
+msgid "co.operations.panel.desc"
+msgstr "Operational activities"
+
msgid "co.groups"
msgstr "Groups"
@@ -61,7 +94,7 @@ msgid "co.people.panel.title"
msgstr "People"
msgid "co.people.panel.desc"
-msgstr "COmanage Registry is a registry for people. This section contains the basic building blocks for modeling and managing people in COmanage."
+msgstr "Model and manage people in COmanage Registry"
msgid "co.people.population"
msgstr "My Population"
@@ -72,11 +105,23 @@ msgstr "Review and manage people in your collaboration (CO Person Records)"
msgid "co.structure"
msgstr "Structure"
+msgid "co.structure.cous.desc"
+msgstr "Collaborative Organizational Units (COUs) are the primary structural objects used to allow delegation of person management within a CO. COUs attach to a Person Role and imply specific Group memberships."
+
+msgid "co.structure.depts.desc"
+msgstr "Departments represent entities within a CO or COU and can store telephone numbers, email addresses, URLs, identifiers, and sets of people."
+
+msgid "co.structure.groups.desc"
+msgstr "Groups attach to a Person. By default, any CO Person can create a new CO Group."
+
+msgid "co.structure.orgs.desc"
+msgstr "Organizations are like Departments but represent entities external to the CO."
+
msgid "co.structure.panel.title"
msgstr "Structure"
msgid "co.structure.panel.desc"
-msgstr "COmanage registry provides structures for modeling and managing your organization, including COUs, Groups, and Departments."
+msgstr "Groupings for your population"
msgid "co.structure.groups"
msgstr "Groups"
@@ -87,3 +132,9 @@ msgstr "All Groups"
msgid "co.switch"
msgstr "Switch CO"
+msgid "related.configurations"
+msgstr "Related Configurations"
+
+msgid "related.links"
+msgstr "Related Links"
+
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 7412e4dce..0ee4f1eba 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', 'pluggableLink', or 'primaryLink'
// We use null to mean "index unless we're in a plugin context, in which case pluggableLink"
diff --git a/app/src/Lib/Util/StringUtilities.php b/app/src/Lib/Util/StringUtilities.php
index 51a8e9c96..c7056b827 100644
--- a/app/src/Lib/Util/StringUtilities.php
+++ b/app/src/Lib/Util/StringUtilities.php
@@ -46,7 +46,7 @@ class StringUtilities {
public static function columnKey($modelsName, $c, $tz=null, $useCustomClMdlLabel=false): string {
if(strpos($c, "_id", strlen($c)-3)) {
// Key is of the form field_id, use .ct label instead
- $k = $this->foreignKeyToClassName($c);
+ $k = self::foreignKeyToClassName($c);
return __d('controller', $k, [1]);
}
diff --git a/app/src/Model/Table/PeopleTable.php b/app/src/Model/Table/PeopleTable.php
index 6766ad8b2..fd131ee95 100644
--- a/app/src/Model/Table/PeopleTable.php
+++ b/app/src/Model/Table/PeopleTable.php
@@ -152,6 +152,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/Dashboards/configuration.php b/app/templates/Dashboards/configuration.php
index 726f9b73c..e7ab5bec9 100644
--- a/app/templates/Dashboards/configuration.php
+++ b/app/templates/Dashboards/configuration.php
@@ -25,9 +25,16 @@
* @license Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
*/
?>
+
+
+
-
+
+
+
\ 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 18cece2b3..3f84dd449 100644
--- a/app/templates/Standard/add-edit-view.php
+++ b/app/templates/Standard/add-edit-view.php
@@ -65,115 +65,156 @@
// 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);
+}
?>
-
-
-
-
-
- = $vv_supertitle; ?>
-
- = $vv_obj->$vv_display_field; ?>
-
-
+
+
+ id;
+ }
+ ?>
+
+
+
+ element(
+ 'mveaJs',
+ [
+ 'htmlId' => 'person-canvas-' . $attr . '-js',
+ 'parentId' => $objId,
+ 'mveaType' => $attr,
+ 'entityType' => 'person'
+ ]
+ );
+ }
+ }
+ // XXX Add the DOB as its own special card.
+ ?>
-
-
- = $this->element('flash', $flashArgs); ?>
-
- = $this->element('subnavigation', $subnav); ?>
-
-
-
-
-
-
= $vv_title; ?>
-
- request->getParam('controller') == 'PersonRoles'
- || $this->request->getParam('controller') == 'ExternalIdentities'
- || $this->request->getParam('controller') == 'ExternalIdentityRoles'): ?>
-
= $vv_title; ?>
+
+
+
+
+
= $vv_title; ?>
- = $vv_title; ?>
+ request->getParam('controller') == 'PersonRoles'
+ || $this->request->getParam('controller') == 'ExternalIdentities'
+ || $this->request->getParam('controller') == 'ExternalIdentityRoles'): ?>
+ = $vv_title; ?>
+
+ = $vv_title; ?>
+
-
-
- id;
-
- foreach(($topLinks ?? []) as $t) {
- $perm = false;
+
+ id;
+
+ foreach(($topLinks ?? []) as $t) {
+ $perm = false;
- if(!empty($t['link']['controller'])) {
- // We're linking into a related model
+ if(!empty($t['link']['controller'])) {
+ // We're linking into a related model
- $linkModel = \Cake\Utility\Inflector::camelize($t['link']['controller']);
+ $linkModel = \Cake\Utility\Inflector::camelize($t['link']['controller']);
- if(isset($vv_permissions[$linkModel][ $t['link']['action'] ])) {
- $perm = $vv_permissions[$linkModel][ $t['link']['action'] ];
- }
+ if(isset($vv_permissions[$linkModel][ $t['link']['action'] ])) {
+ $perm = $vv_permissions[$linkModel][ $t['link']['action'] ];
+ }
- // Inject a link to the current object ID
- $t['link']['?'][\App\Lib\Util\StringUtilities::entityToForeignKey($vv_obj)] = $vv_obj->id;
- } else {
- $perm = $vv_permissions[ $t['link']['action'] ];
-
- // We need to inject $linkFilter, but not overwrite any existing query params
- if(!empty($t['link']['?'])) {
- $t['link']['?'] = array_merge($t['link']['?'], $linkFilter);
+ // Inject a link to the current object ID
+ $t['link']['?'][\App\Lib\Util\StringUtilities::entityToForeignKey($vv_obj)] = $vv_obj->id;
} else {
- $t['link']['?'] = $linkFilter;
+ $perm = $vv_permissions[ $t['link']['action'] ];
+
+ // We need to inject $linkFilter, but not overwrite any existing query params
+ if(!empty($t['link']['?'])) {
+ $t['link']['?'] = array_merge($t['link']['?'], $linkFilter);
+ } else {
+ $t['link']['?'] = $linkFilter;
+ }
}
- }
- if($perm) {
- $action_args['vv_actions'][] = [
- 'order' => $this->Menu->getMenuOrder($t['order']),
- 'icon' => $this->Menu->getMenuIcon($t['icon']),
- 'url' => $this->Url->build($t['link']),
- 'label' => $t['label'],
- ];
+ if($perm) {
+ $action_args['vv_actions'][] = [
+ 'order' => $this->Menu->getMenuOrder($t['order']),
+ 'icon' => $this->Menu->getMenuIcon($t['icon']),
+ 'url' => $this->Url->build($t['link']),
+ 'label' => $t['label'],
+ ];
+ }
}
- }
-
- // Delete
- if($vv_action != 'add' && !empty($vv_obj->id) && $vv_permissions['delete']) {
- $actionPostBtnArray = ['action' => 'delete', $vv_obj->id];
- $actionUrl = $this->Url->build(['action' => 'delete', $vv_obj->id]);
- $action_args['vv_actions'][] = array(
- 'order' => $this->Menu->getMenuOrder('Delete'),
- 'icon' => $this->Menu->getMenuIcon('Delete'),
- 'url' => 'javascript:void(0);',
- 'label' => __d('operation', 'delete'),
- 'class' => 'deletebutton nospin',
- 'onclick' => array(
- 'dg_bd_txt' => __d('operation', 'delete.confirm', [$vv_obj->id]),
- 'dg_post_btn_array' => $actionPostBtnArray,
- 'dg_url' => $actionUrl,
- 'dg_conf_btn' => __d('operation', 'remove'),
- 'dg_cancel_btn' => __d('operation', 'cancel'),
- 'dg_title' => __d('operation', 'remove'),
- 'dg_bd_txt_repl_str' => ''
- )
- );
- }
-
- if(!empty($action_args['vv_actions'])) {
- print '
';
- print $this->element('menuAction', $action_args);
- print '
';
- }
- ?>
-
+
+ // Delete
+ if($vv_action != 'add' && !empty($vv_obj->id) && $vv_permissions['delete']) {
+ $actionPostBtnArray = ['action' => 'delete', $vv_obj->id];
+ $actionUrl = $this->Url->build(['action' => 'delete', $vv_obj->id]);
+ $action_args['vv_actions'][] = array(
+ 'order' => $this->Menu->getMenuOrder('Delete'),
+ 'icon' => $this->Menu->getMenuIcon('Delete'),
+ 'url' => 'javascript:void(0);',
+ 'label' => __d('operation', 'delete'),
+ 'class' => 'deletebutton nospin',
+ 'onclick' => array(
+ 'dg_bd_txt' => __d('operation', 'delete.confirm', [$vv_obj->id]),
+ 'dg_post_btn_array' => $actionPostBtnArray,
+ 'dg_url' => $actionUrl,
+ 'dg_conf_btn' => __d('operation', 'remove'),
+ 'dg_cancel_btn' => __d('operation', 'cancel'),
+ 'dg_title' => __d('operation', 'remove'),
+ 'dg_bd_txt_repl_str' => ''
+ )
+ );
+ }
+
+ if(!empty($action_args['vv_actions'])) {
+ print '
';
+ print $this->element('menuAction', $action_args);
+ print '
';
+ }
+ ?>
+
+
diff --git a/app/templates/Standard/index.php b/app/templates/Standard/index.php
index f13cac037..7b7071c0d 100644
--- a/app/templates/Standard/index.php
+++ b/app/templates/Standard/index.php
@@ -67,26 +67,16 @@
if(!empty($banners)) {
$flashArgs['vv_banners'] = $banners;
}
-?>
-
-
-
-
-
- = $vv_person_name->full_name; ?>
-
- = $vv_bc_parent_obj->$vv_bc_parent_displayfield; ?>
-
-
-
-
-
- = $this->element('flash', $flashArgs); ?>
-
- = $this->element('subnavigation', $subnav); ?>
-
-
+// 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);
+}
+?>
@@ -94,7 +84,7 @@
= $vv_title; ?>
@@ -521,6 +511,8 @@
} elseif ($a == 'view') {
$linkClass .= ' row-link-view';
$readOnlyIcon = ' edit_off';
+ } else {
+ $linkClass .= ' row-link-' . $a;
}
$args = ['class' => $linkClass];
$isFirstLink = false;
diff --git a/app/templates/TelephoneNumbers/columns.inc b/app/templates/TelephoneNumbers/columns.inc
index 55289a091..1c4f56b17 100644
--- a/app/templates/TelephoneNumbers/columns.inc
+++ b/app/templates/TelephoneNumbers/columns.inc
@@ -41,6 +41,6 @@ $bulkActions = [
$subnav = [
'name' => 'person',
- 'active' => 'attributes',
+ 'active' => 'person',
'subActive' => 'telephone_numbers'
];
\ No newline at end of file
diff --git a/app/templates/TelephoneNumbers/fields-nav.inc b/app/templates/TelephoneNumbers/fields-nav.inc
index 267ae0281..e6f2507e6 100644
--- a/app/templates/TelephoneNumbers/fields-nav.inc
+++ b/app/templates/TelephoneNumbers/fields-nav.inc
@@ -30,6 +30,6 @@ $topLinks = [];
$subnav = [
'name' => 'person',
- 'active' => 'attributes',
+ 'active' => 'person',
'subActive' => 'telephone_numbers'
];
\ No newline at end of file
diff --git a/app/templates/Urls/columns.inc b/app/templates/Urls/columns.inc
index 5790bff73..08f461839 100644
--- a/app/templates/Urls/columns.inc
+++ b/app/templates/Urls/columns.inc
@@ -41,6 +41,6 @@ $bulkActions = [
$subnav = [
'name' => 'person',
- 'active' => 'attributes',
+ 'active' => 'person',
'subActive' => 'urls'
];
\ No newline at end of file
diff --git a/app/templates/Urls/fields-nav.inc b/app/templates/Urls/fields-nav.inc
index 8e2ef8f47..8e0c5364f 100644
--- a/app/templates/Urls/fields-nav.inc
+++ b/app/templates/Urls/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/element/breadcrumbs.php b/app/templates/element/breadcrumbs.php
index 5dbc9b4c1..425d36fc4 100644
--- a/app/templates/element/breadcrumbs.php
+++ b/app/templates/element/breadcrumbs.php
@@ -225,7 +225,7 @@
// XXX This is initially for api_users:generate, not clear how much this does
// or does not generalize. If we start adding more exceptions here, we should
// flip the logic and let api_users:generate declare that it wants a link back.
- if(!in_array($vv_action, ['add', 'edit', 'index', 'view'])
+ if(!in_array($vv_action, ['add', 'edit', 'index', 'canvas', 'view'])
&& !empty($vv_obj->id)
&& !empty($vv_obj->$vv_display_field)) {
$oaction = ($vv_permissions['edit']
diff --git a/app/templates/element/javascript.php b/app/templates/element/javascript.php
index 3482c912d..acfdf8f7f 100644
--- a/app/templates/element/javascript.php
+++ b/app/templates/element/javascript.php
@@ -31,7 +31,12 @@
$(function() {
// Focus any designated form element
$('.focusFirst').focus();
-
+
+ // USER PANEL
+ $('#user-panel-user-settings').click(function(e) {
+ e.stopPropagation();
+ });
+
// DESKTOP MENU DRAWER BEHAVIOR
$('#co-menu-collapse').click(function(){
$('#navigation-drawer').toggleClass('closed');
@@ -42,7 +47,13 @@
});
$('.menu-panel-toggle').click(function() {
- $(this).next('.menu-panel').toggleClass('visible');
+ panel = $(this).next('.menu-panel');
+ if($(panel).hasClass('visible')) {
+ $(panel).removeClass('visible');
+ } else {
+ $('.menu-panel').removeClass('visible');
+ $(panel).addClass('visible');
+ }
});
$('.menu-panel-close').click(function() {
@@ -150,18 +161,23 @@
placeholder: "-- Select --"
});
- // Generic row click handling for div-based rows
- $('div.linked-row').click(function(e) {
- location.href = $(this).find('a.row-link').attr('href');
- });
-
- // Generic row click handling for index-table rows
+ // Generic row click handling
// First capture mouse location to test if we're clicking or drag-selecting (for copy)
var mouseDownEvent = null;
- $('table.index-table tr').mousedown(function(e) {
+ $('table.index-table tr, .linked-row').mousedown(function(e) {
mouseDownEvent = e;
});
-
+
+ // Generic row click handling for div-and li based rows
+ $('.linked-row').click(function(e) {
+ url = $(this).find('a.row-link').attr('href');
+ if(Math.abs(e.clientX-mouseDownEvent.clientX) < 5 &&
+ Math.abs(e.clientY-mouseDownEvent.clientY < 5)) {
+ location.href = url;
+ }
+ });
+
+ // Generic row click handling for index-table rows
$('table.index-table tr').each(function(e) {
url = $(this).find('a.row-link').attr('href');
if(url != undefined && url != '') {
diff --git a/app/templates/element/menuAction.php b/app/templates/element/menuAction.php
index 18fa989e1..ee4549b6d 100644
--- a/app/templates/element/menuAction.php
+++ b/app/templates/element/menuAction.php
@@ -27,9 +27,11 @@
$actionsCount = count($vv_actions);
$actionsCountClass = $actionsCount > 0 ? ' actions-count-' . $actionsCount : '';
-$actionsMenuClass = 'field-actions-menu dropdown dropleft' . $actionsCountClass;
+$actionsMenuClass = (!empty($vv_actions_class) ? $vv_actions_class : 'field-actions-menu') . ' dropdown dropleft' . $actionsCountClass;
$actionsMenuUid = md5($vv_attr_id);
-$actionsType = isset($vv_actions_type) ? $vv_actions_type : 'row-actions';
+$actionsType = !empty($vv_actions_type) ? $vv_actions_type : 'row-actions';
+$actionsTitle = !empty($vv_actions_title) ? $vv_actions_title : '';
+$actionsIcon = !empty($vv_actions_icon) ? $vv_actions_icon : 'settings';
?>