From 11d4582679f5298d0000546422a6622cffccc7e7 Mon Sep 17 00:00:00 2001 From: Ioannis Igoumenos Date: Mon, 1 Apr 2024 14:39:29 +0300 Subject: [PATCH 01/21] Refactoring. Add GrouperLiteActASPerson model --- Config/Schema/schema.xml | 25 ++++++++ Controller/GrouperGroupsController.php | 25 ++++---- Model/GrouperLiteActAsPerson.php | 65 +++++++++++++++++++++ Model/GrouperLiteWidget.php | 18 +++--- View/CoGrouperLiteWidgets/display.ctp | 11 ++-- View/GrouperGroups/base.ctp | 37 ++++++------ webroot/js/autocomplete.js | 2 +- webroot/js/page/ActAsUser.js | 81 ++++++++++++++++++++++++++ 8 files changed, 218 insertions(+), 46 deletions(-) create mode 100644 Model/GrouperLiteActAsPerson.php create mode 100644 webroot/js/page/ActAsUser.js diff --git a/Config/Schema/schema.xml b/Config/Schema/schema.xml index 506f630..e978574 100644 --- a/Config/Schema/schema.xml +++ b/Config/Schema/schema.xml @@ -51,4 +51,29 @@ + + + + + + + + REFERENCES cm_co_grouper_lite_widgets(id) + + + REFERENCES cm_co_people(id) + + + REFERENCES cm_co_people(id) + + + + + + co_grouper_lite_widget_id + + + co_person_id + +
\ No newline at end of file diff --git a/Controller/GrouperGroupsController.php b/Controller/GrouperGroupsController.php index b9749a0..19b927c 100644 --- a/Controller/GrouperGroupsController.php +++ b/Controller/GrouperGroupsController.php @@ -37,21 +37,27 @@ */ class GrouperGroupsController extends GrouperLiteWidgetAppController { - public $helpers = array('Html', 'Form', 'Flash'); + public $helpers = ['Html', 'Form', 'Flash']; // Dynamic properties are deprecated, so we will define the property here private $userId = null; - public $uses = array( + public $uses = [ 'GrouperLiteWidget.GrouperGroup', 'GrouperLiteWidget.CoGrouperLiteWidget', 'Identifier', - 'CoPerson'); - - public $components = array('Flash', 'Paginator', 'RequestHandler', 'Security' => array( - 'validatePost' => false, - 'csrfUseOnce' => false - )); + 'CoPerson' + ]; + + public $components = [ + 'Flash', + 'Paginator', + 'RequestHandler', + 'Security' => [ + 'validatePost' => false, + 'csrfUseOnce' => false + ] + ]; public $name = 'GrouperGroups'; @@ -94,8 +100,7 @@ public function addSubscriber(): void /** * Overrides parent beforeFilter to verify that Session contains the correct API settings. * - * @return CakeResponse|void|null - * + * @return void */ public function beforeFilter() { diff --git a/Model/GrouperLiteActAsPerson.php b/Model/GrouperLiteActAsPerson.php new file mode 100644 index 0000000..7d2eb9c --- /dev/null +++ b/Model/GrouperLiteActAsPerson.php @@ -0,0 +1,65 @@ + [ + 'className' => 'CoPerson', + 'foreignKey' => 'act_as_co_person_id' + ], + 'ActorCoPerson' => [ + 'className' => 'CoPerson', + 'foreignKey' => 'actor_co_person_id' + ] + ]; + + // Validation rules for table elements + public $validate = [ + 'co_grouper_lite_widget_id' => [ + 'rule' => 'numeric', + 'required' => true, + 'allowEmpty' => false + ], + 'act_as_co_person_id' => [ + 'rule' => 'numeric', + 'required' => true, + 'allowEmpty' => false + ], + 'actor_co_person_id' => [ + 'rule' => 'numeric', + 'required' => true, + 'allowEmpty' => false + ], + ]; + +} \ No newline at end of file diff --git a/Model/GrouperLiteWidget.php b/Model/GrouperLiteWidget.php index bfa3d4a..5c38153 100644 --- a/Model/GrouperLiteWidget.php +++ b/Model/GrouperLiteWidget.php @@ -27,25 +27,25 @@ class GrouperLiteWidget extends AppModel { // Define class name for cake - public $name = "GrouperLiteWidget"; + public $name = 'GrouperLiteWidget'; // Required by COmanage Plugins - public $cmPluginType = "dashboardwidget"; + public $cmPluginType = 'dashboardwidget'; // Document foreign keys - public $cmPluginHasMany = array(); + public $cmPluginHasMany = []; // Association rules from this model to other models - public $belongsTo = array(); + public $belongsTo = []; - public $hasMany = array(); + public $hasMany = [ + 'GrouperLiteActAsPerson' => ['dependent' => true] + ]; // Validation rules for table elements - public $validate = array(); + public $validate = []; public function cmPluginMenus() { - return array(); + return []; } - - } \ No newline at end of file diff --git a/View/CoGrouperLiteWidgets/display.ctp b/View/CoGrouperLiteWidgets/display.ctp index 8cb6201..3d79ede 100644 --- a/View/CoGrouperLiteWidgets/display.ctp +++ b/View/CoGrouperLiteWidgets/display.ctp @@ -29,17 +29,16 @@ * @license Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) */ +$this->extend('/GrouperGroups/base'); +//echo $this->element('GrouperLiteWidget.base-styles'); + + // Figure out the widget ID so we can overwrite the dashboard's widget div $divid = $vv_config['CoGrouperLiteWidget']['co_dashboard_widget_id']; - -$plugin = filter_var($vv_codw["CoDashboardWidget"]["plugin"],FILTER_SANITIZE_SPECIAL_CHARS); +$plugin = filter_var($vv_codw['CoDashboardWidget']['plugin'], FILTER_SANITIZE_SPECIAL_CHARS); $pl = Inflector::underscore($plugin); $plcmodel = Inflector::pluralize($pl); - -$this->extend('/GrouperGroups/base'); -echo $this->element('GrouperLiteWidget.base-styles'); - $idsuffix = rand(); ?> diff --git a/View/GrouperGroups/base.ctp b/View/GrouperGroups/base.ctp index ef2c37d..5594560 100644 --- a/View/GrouperGroups/base.ctp +++ b/View/GrouperGroups/base.ctp @@ -1,39 +1,36 @@ Html->meta( - array('http-equiv' => 'Cache-Control', 'content' => 'no-cache, no-store, must-revalidate'), - null, - array('inline' => false) +print $this->Html->meta( + ['http-equiv' => 'Cache-Control', 'content' => 'no-cache, no-store, must-revalidate'], + null, + ['inline' => false] ); -echo $this->Html->meta( - array('http-equiv' => 'Pragma', 'content' => 'no-cache'), - null, - array('inline' => false) +print $this->Html->meta( + ['http-equiv' => 'Pragma', 'content' => 'no-cache'], + null, + ['inline' => false] ); -echo $this->Html->meta( - array('http-equiv' => 'Expires', 'content' => '0'), - null, - array('inline' => false) +print $this->Html->meta( + ['http-equiv' => 'Expires', 'content' => '0'], + null, + ['inline' => false] ); print $this->Html->script('GrouperLiteWidget.autocomplete.grouperplugin') . PHP_EOL; print $this->element('GrouperLiteWidget.base-styles'); print $this->Html->css('GrouperLiteWidget.co-grouper-plugin') . PHP_EOL; - -?> - -Html->addCrumb(_txt('pl.grouperlite.crumb.root'), - array( + [ 'controller' => 'grouper_groups', 'action' => 'groupmember' - ), - array('prepend' => true)); + ], + ['prepend' => true] +); ?>
- fetch('content'); ?> + fetch('content') ?>
\ No newline at end of file diff --git a/webroot/js/autocomplete.js b/webroot/js/autocomplete.js index 368fec1..6a4bed2 100644 --- a/webroot/js/autocomplete.js +++ b/webroot/js/autocomplete.js @@ -63,7 +63,7 @@ export default { type: 'GET', dataType: "json", data: { - // XXX Change the term key to any other query key that fits your needs. + // XXX Change the term key to any other query key that fit your needs. term: request.term }, success: function (data) { diff --git a/webroot/js/page/ActAsUser.js b/webroot/js/page/ActAsUser.js new file mode 100644 index 0000000..4f7f276 --- /dev/null +++ b/webroot/js/page/ActAsUser.js @@ -0,0 +1,81 @@ +import PageCount from '../pagecount.js'; +import Pagination from '../pagination.js'; +import GroupsTable from '../groups-table.js'; +import Members from '../members.js'; +import Autocomplete from '../autocomplete.js'; +import Loader from '../loader.js'; +export default { + components: { + Autocomplete, + Loader + }, + data() { + return { + loading: false, + subscribers: [], + user: {}, + rowLoading: false, + searched: false + } + }, + inject: ['api', 'txt'], + computed: { + preInfoTxt() { + return '' + this.txt.search + ':' + } + }, + methods: { + reset() { + this.searched = false + // If you are in the middle of a row action we do not want to clear + // the results because we are breaking the user experience + if(!this.rowLoading) { + this.subscribers = [] + } + }, + async disableActAs() { + + }, + async findUsersIManage(user) { + } + }, + template: /*html*/` + + +
+
+ {{ txt.noaccess }} + {{ txt.empty }} +
+
+ + + + + + + + +
+ {{ subscriber.name }} + + {{ subscriber.id }} + + +
+ + {{ txt.empty }} + +
+
+ ` +} \ No newline at end of file From a0c059abfe5fe8b955ced53c283d36f7db8e64ae Mon Sep 17 00:00:00 2001 From: Ioannis Igoumenos Date: Mon, 1 Apr 2024 15:36:11 +0300 Subject: [PATCH 02/21] remove base-css element --- View/CoGrouperLiteWidgets/display.ctp | 3 - View/Elements/base-styles.ctp | 140 -------------------------- View/Elements/empty | 0 View/GrouperGroups/base.ctp | 2 +- webroot/css/co-grouper-base.css | 126 +++++++++++++++++++++-- 5 files changed, 119 insertions(+), 152 deletions(-) delete mode 100644 View/Elements/base-styles.ctp create mode 100644 View/Elements/empty diff --git a/View/CoGrouperLiteWidgets/display.ctp b/View/CoGrouperLiteWidgets/display.ctp index 3d79ede..4a9ad29 100644 --- a/View/CoGrouperLiteWidgets/display.ctp +++ b/View/CoGrouperLiteWidgets/display.ctp @@ -30,11 +30,8 @@ */ $this->extend('/GrouperGroups/base'); -//echo $this->element('GrouperLiteWidget.base-styles'); - // Figure out the widget ID so we can overwrite the dashboard's widget div - $divid = $vv_config['CoGrouperLiteWidget']['co_dashboard_widget_id']; $plugin = filter_var($vv_codw['CoDashboardWidget']['plugin'], FILTER_SANITIZE_SPECIAL_CHARS); $pl = Inflector::underscore($plugin); diff --git a/View/Elements/base-styles.ctp b/View/Elements/base-styles.ctp deleted file mode 100644 index 48ea5e2..0000000 --- a/View/Elements/base-styles.ctp +++ /dev/null @@ -1,140 +0,0 @@ - \ No newline at end of file diff --git a/View/Elements/empty b/View/Elements/empty new file mode 100644 index 0000000..e69de29 diff --git a/View/GrouperGroups/base.ctp b/View/GrouperGroups/base.ctp index 5594560..e4805a2 100644 --- a/View/GrouperGroups/base.ctp +++ b/View/GrouperGroups/base.ctp @@ -17,7 +17,7 @@ print $this->Html->meta( ); print $this->Html->script('GrouperLiteWidget.autocomplete.grouperplugin') . PHP_EOL; -print $this->element('GrouperLiteWidget.base-styles'); +print $this->Html->css('GrouperLiteWidget.co-grouper-base') . PHP_EOL; print $this->Html->css('GrouperLiteWidget.co-grouper-plugin') . PHP_EOL; $this->Html->addCrumb(_txt('pl.grouperlite.crumb.root'), diff --git a/webroot/css/co-grouper-base.css b/webroot/css/co-grouper-base.css index 8080b7f..198212e 100644 --- a/webroot/css/co-grouper-base.css +++ b/webroot/css/co-grouper-base.css @@ -1,28 +1,138 @@ :root { - --red: #CC3333; + --red: #B32D2D; --blue: #1D7AB4; - --green: #22AA22; + --green: #1D871D; + --orange: #faa732; --teal: #1c6070; + --darkteal: #003f59; + --link: #0076a5; + --link-hover: #003f59; --danger: var(--red); --success: var(--green); --primary: var(--teal); + --warning: var(--orange); + --secondary: var(--darkteal) +} + +.text-grouper { + color: var(--primary); +} + +.text-sml { + font-size: 0.8rem; +} + +#content .material-icons.lg { + font-size: 1.2rem; +} + +.grouper .btn:not(.btn-link) { + box-shadow: 0 2px 2px 0 rgba(0, 0, 0, .14), 0 3px 1px -2px rgba(0, 0, 0, .2), 0 1px 5px 0 rgba(0, 0, 0, .12); + max-width: 220px; +} + +.grouper .btn:not([disabled]):hover { + text-decoration: none !important; + filter: brightness(1.1); } .grouper .btn.btn-primary { background-color: var(--primary); border-color: var(--primary); - box-shadow: 0 2px 2px 0 rgba(0, 0, 0, .14), 0 3px 1px -2px rgba(0, 0, 0, .2), 0 1px 5px 0 rgba(0, 0, 0, .12); } -.grouper .btn.btn-success.btn-grouper { +.grouper .btn.btn-success { background-color: var(--success); border-color: var(--success); - box-shadow: 0 2px 2px 0 rgba(0, 0, 0, .14), 0 3px 1px -2px rgba(0, 0, 0, .2), 0 1px 5px 0 rgba(0, 0, 0, .12); } -.grouper .btn.btn-danger.btn-grouper { +.grouper .btn.btn-danger { background-color: var(--danger); border-color: var(--danger); - color: white; - box-shadow: 0 2px 2px 0 rgba(0, 0, 0, .14), 0 3px 1px -2px rgba(0, 0, 0, .2), 0 1px 5px 0 rgba(0, 0, 0, .12); +} + +.grouper .btn.btn-secondary { + background-color: var(--secondary); + border-color: var(--secondary); +} + +.grouper .btn.btn-link, +.grouper a:not(.btn) { + color: var(--link); +} + +.grouper .btn.btn-link:hover, +.grouper a:not(.btn):hover { + color: var(--link-hover); +} + +#subscribers.loading .loader { + display: block; +} + +#subscribers.loading .subs, +#subscribers.error .subs { + display: none; +} + +#subscribers.error .msg { + display: block; +} + +#subscribers .loader, +#subscribers .msg { + display: none; +} + +.modal-open .ui-menu { + z-index: 2000; +} + +#grouper-loader { + pointer-events: none; + width: 100px; + display: block; + margin: 0 auto; + overflow: visible; + padding: 15px; + max-width: 100%; +} + +#grouper-loader circle { + transform-origin: center; + fill: var(--primary); + transition: ease; + animation-timing-function: ease-in-out !important; +} + +#search { + background-color: #F6F6F6; +} + +/* fade out half */ + +#grouper-loader.fade-out-half circle:nth-child(1) { + animation: fadeInHalf 0.9s -2.3s infinite reverse; +} + +#grouper-loader.fade-out-half circle:nth-child(2) { + animation: fadeInHalf 0.9s -1.3s infinite reverse; +} + +#grouper-loader.fade-out-half circle:nth-child(3) { + animation: fadeInHalf 0.9s -0.3s infinite reverse; +} + +.popover { + max-width: 400px; +} + +@keyframes fadeInHalf { + from { + opacity: 0.5; + } + + to { + opacity: 1; + } } \ No newline at end of file From 700f48c1b1dadfa5abc32f654a798dbfb7e05bfe Mon Sep 17 00:00:00 2001 From: Ioannis Igoumenos Date: Mon, 1 Apr 2024 16:57:46 +0300 Subject: [PATCH 03/21] Code improvements --- Controller/CoGrouperLiteWidgetsController.php | 2 +- View/CoGrouperLiteWidgets/display.ctp | 96 +++++++++---------- View/GrouperGroups/base.ctp | 19 ++-- View/GrouperGroups/index.ctp | 37 ++++--- webroot/css/co-grouper-base.css | 7 ++ webroot/css/co-grouper-plugin.css | 2 +- 6 files changed, 86 insertions(+), 77 deletions(-) diff --git a/Controller/CoGrouperLiteWidgetsController.php b/Controller/CoGrouperLiteWidgetsController.php index 1ea93f0..bd6e92c 100644 --- a/Controller/CoGrouperLiteWidgetsController.php +++ b/Controller/CoGrouperLiteWidgetsController.php @@ -45,7 +45,7 @@ public function display($id) { $cfg = $this->CoGrouperLiteWidget->getConfig(); $this->set('pl_grouperlite_index_url', Router::url([ - 'plugin' => "grouper_lite", + 'plugin' => 'grouper_lite', 'controller' => 'grouper_groups', 'action' => 'index', 'co' => $this->cur_co['Co']['id'], diff --git a/View/CoGrouperLiteWidgets/display.ctp b/View/CoGrouperLiteWidgets/display.ctp index 4a9ad29..2ce6518 100644 --- a/View/CoGrouperLiteWidgets/display.ctp +++ b/View/CoGrouperLiteWidgets/display.ctp @@ -33,17 +33,15 @@ $this->extend('/GrouperGroups/base'); // Figure out the widget ID so we can overwrite the dashboard's widget div $divid = $vv_config['CoGrouperLiteWidget']['co_dashboard_widget_id']; -$plugin = filter_var($vv_codw['CoDashboardWidget']['plugin'], FILTER_SANITIZE_SPECIAL_CHARS); -$pl = Inflector::underscore($plugin); -$plcmodel = Inflector::pluralize($pl); +$pl = Inflector::underscore(filter_var($vv_codw['CoDashboardWidget']['plugin'], FILTER_SANITIZE_SPECIAL_CHARS)); $idsuffix = rand(); ?> -
+
@@ -124,7 +122,9 @@ $idsuffix = rand();
-

 

+

 

diff --git a/View/GrouperGroups/base.ctp b/View/GrouperGroups/base.ctp index e4805a2..85df70d 100644 --- a/View/GrouperGroups/base.ctp +++ b/View/GrouperGroups/base.ctp @@ -20,13 +20,18 @@ print $this->Html->script('GrouperLiteWidget.autocomplete.grouperplugin') . PHP_ print $this->Html->css('GrouperLiteWidget.co-grouper-base') . PHP_EOL; print $this->Html->css('GrouperLiteWidget.co-grouper-plugin') . PHP_EOL; -$this->Html->addCrumb(_txt('pl.grouperlite.crumb.root'), - [ - 'controller' => 'grouper_groups', - 'action' => 'groupmember' - ], - ['prepend' => true] -); +if(isset($this->viewVars['vv_coid'])) { + $this->Html->addCrumb(_txt('pl.grouperlite.crumb.root'), + [ + 'plugin' => Inflector::underscore(filter_var($this->plugin, FILTER_SANITIZE_SPECIAL_CHARS)), + 'controller' => 'grouper_groups', + 'action' => 'groupmember', + 'co' => $this->viewVars['vv_coid'], + 'glid' => $this->viewVars['vv_config']['CoGrouperLiteWidget']['id'] + ], + ['prepend' => true] + ); +} ?> diff --git a/View/GrouperGroups/index.ctp b/View/GrouperGroups/index.ctp index 138e8a3..8cbfe08 100644 --- a/View/GrouperGroups/index.ctp +++ b/View/GrouperGroups/index.ctp @@ -1,10 +1,17 @@ -extend('/GrouperGroups/base'); ?> -Html->script('GrouperLiteWidget.vue-router.js') ?> -Html->addCrumb(_txt('pl.grouperlite.nav.memberships')); ?> +extend('/GrouperGroups/base'); + +// Add javascript +print $this->Html->script('GrouperLiteWidget.vue-router.js'); + +// Add Breadcrumb +$this->Html->addCrumb(_txt('pl.grouperlite.nav.memberships')); + +?>
- Html->image('GrouperLiteWidget.Grouper.jpg', array('class' => 'img-fluid mr-2', 'style' => 'height: 50px')); ?> + Html->image('GrouperLiteWidget.Grouper.jpg', ['class' => 'img-fluid mr-2', 'style' => 'height: 50px']) ?>

@@ -109,13 +116,12 @@ owner: "", manager: "", }, - columns: _txt('pl.grouperlite.table.name'), - 'role' => _txt('pl.grouperlite.table.role'), - 'description' => _txt('pl.grouperlite.table.description'), - 'status' => _txt('pl.grouperlite.table.status'), - 'action' => _txt('pl.grouperlite.table.action'), - ))) ?>, + columns: _txt('pl.grouperlite.table.name'), + 'role' => _txt('pl.grouperlite.table.role'), + 'description' => _txt('pl.grouperlite.table.description'), + 'status' => _txt('pl.grouperlite.table.status'), + 'action' => _txt('pl.grouperlite.table.action')], + JSON_THROW_ON_ERROR) ?>, }, api: { @@ -146,15 +152,6 @@ app.mount('#grouper-lite-widget'); - -
diff --git a/webroot/css/co-grouper-base.css b/webroot/css/co-grouper-base.css index 198212e..bf7bef2 100644 --- a/webroot/css/co-grouper-base.css +++ b/webroot/css/co-grouper-base.css @@ -127,6 +127,13 @@ max-width: 400px; } +.ui-autocomplete { + max-height: 240px; + overflow-y: auto; + overflow-x: hidden; + padding-right: 20px; +} + @keyframes fadeInHalf { from { opacity: 0.5; diff --git a/webroot/css/co-grouper-plugin.css b/webroot/css/co-grouper-plugin.css index 9b1cebf..2175744 100644 --- a/webroot/css/co-grouper-plugin.css +++ b/webroot/css/co-grouper-plugin.css @@ -335,4 +335,4 @@ a.list-group-item-action:hover .fa { animation: 1.2s linear infinite both loading; background-color: var(--teal); display: inline-block; -} \ No newline at end of file +} From 2911ca01bdc69db407d9a1197e5c961b7fc87b32 Mon Sep 17 00:00:00 2001 From: Ioannis Igoumenos Date: Mon, 1 Apr 2024 17:51:02 +0300 Subject: [PATCH 04/21] ui improvements --- Controller/GrouperGroupsController.php | 2 +- Lib/lang.php | 3 ++- View/CoGrouperLiteWidgets/display.ctp | 2 +- View/GrouperGroups/index.ctp | 4 ++-- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/Controller/GrouperGroupsController.php b/Controller/GrouperGroupsController.php index 19b927c..a9790da 100644 --- a/Controller/GrouperGroupsController.php +++ b/Controller/GrouperGroupsController.php @@ -144,7 +144,7 @@ public function beforeRender() { $cfg = $this->CoGrouperLiteWidget->getConfig(); $this->set('vv_config', $cfg); - $this->set('title', _txt('pl.grouperlite.title.groupmember')); + $this->set('vv_title', _txt('pl.grouperlite.title.dashboard')); $this->set('vv_is_user_owner', $this->GrouperGroup->isUserGroupOwner($this->userId ?? '', $cfg) ); // $this->set('vv_is_template_user', $this->GrouperGroup->isTemplateUser($this->userId ?? '', $cfg) ); // $this->set('vv_is_grouper_visible', $this->GrouperGroup->isGrouperVisible($this->userId ?? '', $cfg)); diff --git a/Lib/lang.php b/Lib/lang.php index d5e490b..d0dc12a 100644 --- a/Lib/lang.php +++ b/Lib/lang.php @@ -37,7 +37,7 @@ 'pl.grouperlite.dashboard.heading.groups' => 'Groups', 'pl.grouperlite.dashboard.heading.email-lists' => 'Email lists', - 'pl.grouperlite.title.root' => 'Grouper Collaborations:', + 'pl.grouperlite.title.dashboard' => 'Grouper Lite Dashboard', 'pl.grouperlite.title.groupinfo' => 'Group configuration and attributes', 'pl.grouperlite.title.groupowner' => 'Groups I manage', 'pl.grouperlite.title.groupmember' => 'My Memberships', @@ -48,6 +48,7 @@ 'pl.grouperlite.title.emaillistsinfo' => 'Email list configuration and attributes', 'pl.grouperlite.title.groupcreate' => 'Create a group', 'pl.grouperlite.title.templatecreate' => 'Create a working group', + 'pl.grouperlite.title.root' => 'Grouper Collaborations:', 'pl.grouperlite.message.flash.join-group-success' => 'You have been added to the group:', 'pl.grouperlite.message.flash.join-group-failed' => 'You are unable to join the group:', diff --git a/View/CoGrouperLiteWidgets/display.ctp b/View/CoGrouperLiteWidgets/display.ctp index 2ce6518..0cfb893 100644 --- a/View/CoGrouperLiteWidgets/display.ctp +++ b/View/CoGrouperLiteWidgets/display.ctp @@ -115,7 +115,7 @@ $idsuffix = rand();
-
+
Html->image('GrouperLiteWidget.grouper-logo.png', array('class' => 'logo-fluid')); ?> diff --git a/View/GrouperGroups/index.ctp b/View/GrouperGroups/index.ctp index 8cbfe08..8db2442 100644 --- a/View/GrouperGroups/index.ctp +++ b/View/GrouperGroups/index.ctp @@ -6,13 +6,13 @@ $this->extend('/GrouperGroups/base'); print $this->Html->script('GrouperLiteWidget.vue-router.js'); // Add Breadcrumb -$this->Html->addCrumb(_txt('pl.grouperlite.nav.memberships')); +$this->Html->addCrumb(_txt('pl.grouperlite.title.dashboard')); ?>
Html->image('GrouperLiteWidget.Grouper.jpg', ['class' => 'img-fluid mr-2', 'style' => 'height: 50px']) ?> -

+

+
+
\ No newline at end of file diff --git a/webroot/css/co-grouper-base.css b/webroot/css/co-grouper-base.css index bf7bef2..84c5a0e 100644 --- a/webroot/css/co-grouper-base.css +++ b/webroot/css/co-grouper-base.css @@ -14,6 +14,12 @@ --secondary: var(--darkteal) } +.grouper_groups .grouper-table { + display: grid; + grid-template-columns: auto 20%; + grid-gap: 1em; +} + .text-grouper { color: var(--primary); } From 2a69807f7c4cb037db257e9c0be06ab6f0e51887 Mon Sep 17 00:00:00 2001 From: Ioannis Igoumenos Date: Tue, 2 Apr 2024 12:03:20 +0300 Subject: [PATCH 06/21] added peoplepicker module --- View/Elements/actAsPeopleAutocomplete.ctp | 127 ++++++++++++++++++++++ View/Elements/empty | 0 View/GrouperGroups/index.ctp | 40 +++---- webroot/css/co-grouper-base.css | 8 +- webroot/js/autocomplete.js | 1 - webroot/js/page/ActAsUser.js | 81 -------------- 6 files changed, 149 insertions(+), 108 deletions(-) create mode 100644 View/Elements/actAsPeopleAutocomplete.ctp delete mode 100644 View/Elements/empty delete mode 100644 webroot/js/page/ActAsUser.js diff --git a/View/Elements/actAsPeopleAutocomplete.ctp b/View/Elements/actAsPeopleAutocomplete.ctp new file mode 100644 index 0000000..49ee1b7 --- /dev/null +++ b/View/Elements/actAsPeopleAutocomplete.ctp @@ -0,0 +1,127 @@ + 0 ? '?time=' . time() : ''; + + +?> + + + +
+ + +
diff --git a/View/Elements/empty b/View/Elements/empty deleted file mode 100644 index e69de29..0000000 diff --git a/View/GrouperGroups/index.ctp b/View/GrouperGroups/index.ctp index 95e10d6..fe539cd 100644 --- a/View/GrouperGroups/index.ctp +++ b/View/GrouperGroups/index.ctp @@ -8,6 +8,14 @@ print $this->Html->script('GrouperLiteWidget.vue-router.js'); // Add Breadcrumb $this->Html->addCrumb(_txt('pl.grouperlite.title.dashboard')); + +$baseUrl = $vv_config['CoGrouperLiteWidget']['grouper_url']; +$path = '/grouper/grouperUi/app/UiV2Main.index'; +$groupOperation = '?operation=UiV2Group.viewGroup&groupId='; +$grouperUrlBase = $baseUrl . $path . $groupOperation; + +$htmlId = 'act-as-picker'; +$suffix = Configure::read('debug') > 0 ? '?time=' . time() : ''; ?>
@@ -16,20 +24,12 @@ $this->Html->addCrumb(_txt('pl.grouperlite.title.dashboard'));
-
-
+
+
diff --git a/webroot/css/co-grouper-base.css b/webroot/css/co-grouper-base.css index 84c5a0e..836c05c 100644 --- a/webroot/css/co-grouper-base.css +++ b/webroot/css/co-grouper-base.css @@ -14,12 +14,6 @@ --secondary: var(--darkteal) } -.grouper_groups .grouper-table { - display: grid; - grid-template-columns: auto 20%; - grid-gap: 1em; -} - .text-grouper { color: var(--primary); } @@ -148,4 +142,4 @@ to { opacity: 1; } -} \ No newline at end of file +} diff --git a/webroot/js/autocomplete.js b/webroot/js/autocomplete.js index 6a4bed2..a60e81d 100644 --- a/webroot/js/autocomplete.js +++ b/webroot/js/autocomplete.js @@ -68,7 +68,6 @@ export default { }, success: function (data) { $("#grouper-search-container .co-loading-mini").hide(); - // If i have more data from before append at the end response( data ); }, error: function(data) { diff --git a/webroot/js/page/ActAsUser.js b/webroot/js/page/ActAsUser.js deleted file mode 100644 index 4f7f276..0000000 --- a/webroot/js/page/ActAsUser.js +++ /dev/null @@ -1,81 +0,0 @@ -import PageCount from '../pagecount.js'; -import Pagination from '../pagination.js'; -import GroupsTable from '../groups-table.js'; -import Members from '../members.js'; -import Autocomplete from '../autocomplete.js'; -import Loader from '../loader.js'; -export default { - components: { - Autocomplete, - Loader - }, - data() { - return { - loading: false, - subscribers: [], - user: {}, - rowLoading: false, - searched: false - } - }, - inject: ['api', 'txt'], - computed: { - preInfoTxt() { - return '' + this.txt.search + ':' - } - }, - methods: { - reset() { - this.searched = false - // If you are in the middle of a row action we do not want to clear - // the results because we are breaking the user experience - if(!this.rowLoading) { - this.subscribers = [] - } - }, - async disableActAs() { - - }, - async findUsersIManage(user) { - } - }, - template: /*html*/` - - -
-
- {{ txt.noaccess }} - {{ txt.empty }} -
-
- - - - - - - - -
- {{ subscriber.name }} - - {{ subscriber.id }} - - -
- - {{ txt.empty }} - -
-
- ` -} \ No newline at end of file From 6c86851bbe017eb72a49648e1c7d156f7924b3cc Mon Sep 17 00:00:00 2001 From: Ioannis Igoumenos Date: Tue, 2 Apr 2024 13:15:55 +0300 Subject: [PATCH 07/21] action act as ui improvements --- Lib/lang.php | 1 + View/Elements/actAsPeopleAutocomplete.ctp | 4 ++- View/GrouperGroups/index.ctp | 41 +++++++++++++++++++++-- webroot/css/co-grouper-plugin.css | 4 +++ webroot/js/autocomplete.js | 2 +- 5 files changed, 47 insertions(+), 5 deletions(-) diff --git a/Lib/lang.php b/Lib/lang.php index d0dc12a..22fab32 100644 --- a/Lib/lang.php +++ b/Lib/lang.php @@ -127,6 +127,7 @@ 'pl.grouperlite.action.clear' => 'Clear', 'pl.grouperlite.action.add-user' => 'Add', 'pl.grouperlite.action.remove-user' => 'Remove', + 'pl.grouperlite.action.enable-act-as' => 'Enable', 'pl.grouperlite.message.user-not-found-error' => 'Error: User not found.', 'pl.grouperlite.message.user-not-added-error' => 'Error: Unable to add user.', 'pl.grouperlite.message.user-not-removed-error' => 'Error: Unable to remove user.', diff --git a/View/Elements/actAsPeopleAutocomplete.ctp b/View/Elements/actAsPeopleAutocomplete.ctp index 49ee1b7..0bf351e 100644 --- a/View/Elements/actAsPeopleAutocomplete.ctp +++ b/View/Elements/actAsPeopleAutocomplete.ctp @@ -42,6 +42,7 @@ $suffix = Configure::read('debug') > 0 ? '?time=' . time() : ''; }, members: "", email: "", + enableActAs: "", close: "", remove: "", addUser: "", @@ -122,6 +123,7 @@ $suffix = Configure::read('debug') > 0 ? '?time=' . time() : '';
+ action="enableActAs" + icon=""/>
diff --git a/View/GrouperGroups/index.ctp b/View/GrouperGroups/index.ctp index fe539cd..bc80b10 100644 --- a/View/GrouperGroups/index.ctp +++ b/View/GrouperGroups/index.ctp @@ -62,6 +62,7 @@ $suffix = Configure::read('debug') > 0 ? '?time=' . time() : ''; routes, }) + // TODO: Create the provides only once const app = Vue.createApp({ provide: { collapsed: , @@ -168,10 +169,44 @@ $suffix = Configure::read('debug') > 0 ? '?time=' . time() : ''; Find
-
-
test
+
+ element('actAsPeopleAutocomplete', + compact('vv_config', + 'vv_coid', + 'vv_is_user_owner', + 'htmlId') + )?> +
+
+ + +
+
+
+ John Doe +
+ +
+ Identifier (I2CollabPN): nickmastoris.mastoris@at.in... +
+
+ +
- John Doe
diff --git a/webroot/css/co-grouper-plugin.css b/webroot/css/co-grouper-plugin.css index 2175744..6e8f27e 100644 --- a/webroot/css/co-grouper-plugin.css +++ b/webroot/css/co-grouper-plugin.css @@ -14,6 +14,10 @@ border-radius: .2rem; } +.btn-fit { + height: fit-content; +} + a { color: var(--primary); } diff --git a/webroot/js/autocomplete.js b/webroot/js/autocomplete.js index a60e81d..da566ce 100644 --- a/webroot/js/autocomplete.js +++ b/webroot/js/autocomplete.js @@ -120,7 +120,7 @@ export default { type="button" @click="performAction()" :disabled="isBtnDisabled"> - + {{ btnTxt }}
From 23ca0d482cd4f146b98ae236886ad1616caf266e Mon Sep 17 00:00:00 2001 From: Ioannis Igoumenos Date: Tue, 2 Apr 2024 19:26:34 +0300 Subject: [PATCH 08/21] render action sidebar if the user belongs to the appropriate group --- Controller/GrouperGroupsController.php | 13 +- .../GrouperLiteActAsPeopleController.php | 135 ++++++++++++++++++ Model/GrouperGroup.php | 25 ++++ Model/GrouperLiteActAsPerson.php | 18 ++- View/GrouperGroups/index.ctp | 6 +- 5 files changed, 193 insertions(+), 4 deletions(-) create mode 100644 Controller/GrouperLiteActAsPeopleController.php diff --git a/Controller/GrouperGroupsController.php b/Controller/GrouperGroupsController.php index a9790da..edf0bcf 100644 --- a/Controller/GrouperGroupsController.php +++ b/Controller/GrouperGroupsController.php @@ -111,7 +111,7 @@ public function beforeFilter() HttpStatusCodesEnum::HTTP_BAD_REQUEST); } $this->response->disableCache(); - $this->RequestHandler->addInputType('json', array('json_decode', true)); + $this->RequestHandler->addInputType('json', ['json_decode', true]); $this->Security->unlockedActions = [ 'removeSubscriber', @@ -125,7 +125,7 @@ public function beforeFilter() // Get the config $args = array(); - $args['conditions']['CoGrouperLiteWidget.id'] = $this->request->params["named"]["glid"]; + $args['conditions']['CoGrouperLiteWidget.id'] = $this->request->params['named']['glid']; $args['contain'] = false; $cfg = $this->CoGrouperLiteWidget->find('first', $args); // Set the config so that everybody can access it @@ -436,6 +436,14 @@ public function isAuthorized(): array|bool $this->setUserId($identifiers['Identifier']['identifier']); } + // Find if the user belongs to Group + $eligibleGroup = $cfg['CoGrouperLiteWidget']['act_as_grp_name']; + $isActAsEligibilityGroupmember = false; + + if(!empty($eligibleGroup)) { + $isActAsEligibilityGroupmember = $this->GrouperGroup->isGroupMember($this->getUserId(), $eligibleGroup, $cfg); + } + // Determine what operations this user can perform // Construct the permission set for this user, which will also be passed to the view. $p = []; @@ -459,6 +467,7 @@ public function isAuthorized(): array|bool $p['joinGroup'] = ($roles['cmadmin'] || $roles['coadmin'] || $roles['comember']); $p['leaveGroup'] = ($roles['cmadmin'] || $roles['coadmin'] || $roles['comember']); $p['groupcreatetemplate'] = ($roles['cmadmin'] || $roles['coadmin'] || $roles['comember']); + $p['actAsAction'] = $isActAsEligibilityGroupmember; $this->set('permissions', $p); diff --git a/Controller/GrouperLiteActAsPeopleController.php b/Controller/GrouperLiteActAsPeopleController.php new file mode 100644 index 0000000..d69da89 --- /dev/null +++ b/Controller/GrouperLiteActAsPeopleController.php @@ -0,0 +1,135 @@ + [ + 'validatePost' => false, + 'csrfUseOnce' => false + ] + ]; + + + public $name = 'GrouperLiteActAsPeople'; + + /** + * Overrides parent beforeFilter to verify that Session contains the correct API settings. + * + * @return void + */ + public function beforeFilter() + { + parent::beforeFilter(); + + if(empty($this->request->params['named']['glid'])) { + throw new InvalidArgumentException(_txt('er.grouperlite.glid'), + HttpStatusCodesEnum::HTTP_BAD_REQUEST); + } + $this->response->disableCache(); + $this->RequestHandler->addInputType('json', ['json_decode', true]); + + $this->Security->unlockedActions = [ + 'add', + 'edit', + 'delete' + ]; + + // Get the config + $args = array(); + $args['conditions']['CoGrouperLiteWidget.id'] = $this->request->params['named']['glid']; + $args['contain'] = false; + $cfg = $this->CoGrouperLiteWidget->find('first', $args); + // Set the config so that everybody can access it + $this->CoGrouperLiteWidget->setConfig($cfg); + } + + /** + * NOTE: All permissions will be done on the Grouper side. All Authenticated users will be able to + * use this plugin for self-admin of groups. + * + * Authorization for this Controller, called by Auth component + * - precondition: Session.Auth holds data used for authz decisions + * - postcondition: $permissions set with calculated permissions + * + * @return array|bool Permissions + * @since COmanage Registry v4.4.0 + */ + public function isAuthorized(): array|bool + { + $roles = $this->Role->calculateCMRoles(); + $cfg = $this->CoGrouperLiteWidget->getConfig(); + // Find the identifier + $args = array(); + $args['conditions']['Identifier.type'] = $cfg['CoGrouperLiteWidget']['identifier_type']; + $args['conditions']['Identifier.status'] = SuspendableStatusEnum::Active; + $args['conditions']['Identifier.co_person_id'] = $roles['copersonid']; + $args['contain'] = false; + + $identifiers = $this->Identifier->find('first', $args); + if(!empty($identifiers) + && is_array($identifiers) + && isset($identifiers['Identifier']['identifier']) + ) { + $this->setUserId($identifiers['Identifier']['identifier']); + } + + // Find if the user belongs to Group + $eligibleGroup = $cfg['CoGrouperLiteWidget']['act_as_grp_name']; + $isActAsEligibilityGroupmember = false; + + if(!empty($eligibleGroup)) { + $isActAsEligibilityGroupmember = $this->GrouperGroup->isGroupMember($this->getUserId(), $eligibleGroup, $cfg); + } + + // Determine what operations this user can perform + // Construct the permission set for this user, which will also be passed to the view. + $p = []; + + $p['add'] = $isActAsEligibilityGroupmember; + $p['delete'] = $isActAsEligibilityGroupmember; + $p['edit'] = $isActAsEligibilityGroupmember; + $p['update'] = $isActAsEligibilityGroupmember; + + $this->set('permissions', $p); + + return ($p[$this->action]); + } + + /** + * @return null + */ + public function getUserId() + { + return $this->userId; + } + + + /** + * @param null $userId + */ + private function setUserId($userId): void + { + $this->userId = $userId; + } +} \ No newline at end of file diff --git a/Model/GrouperGroup.php b/Model/GrouperGroup.php index 1468b07..6f14364 100644 --- a/Model/GrouperGroup.php +++ b/Model/GrouperGroup.php @@ -528,6 +528,31 @@ public function optinGroups(string $userId, array $cfg): array ); } + /** + * Determine if a User can use the Grouper Template to create a Working Group. + * + * @param string $userId User ID + * @param string $groupName Group Name + * @param array $cfg + * + * @return bool T for True and F for False + * @throws GrouperLiteWidgetException + * @since COmanage Registry v4.4.0 + */ + public function isGroupMember(string $userId, string $groupName, array $cfg): bool + { + $this->initApi($cfg); + + try { + $isMember = $this->grouperAPI->isMemberOfGroup($groupName, $userId); + } catch (Exception $e) { + CakeLog::write('error', __METHOD__ . ': An error occurred'); + throw $e; + } + + return (bool)$isMember; + } + /** * Determine if User can use the Grouper Template to create a Working Group. * diff --git a/Model/GrouperLiteActAsPerson.php b/Model/GrouperLiteActAsPerson.php index 7d2eb9c..4f833e4 100644 --- a/Model/GrouperLiteActAsPerson.php +++ b/Model/GrouperLiteActAsPerson.php @@ -29,6 +29,21 @@ class GrouperLiteActAsPerson extends AppModel { public $name = 'GrouperLiteActAsPerson'; + public $cmPluginHasMany = [ + "CoGrouperLiteWidget" => ["GrouperLiteActAsPerson"], + 'CoPerson' => [ + 'GrouperLiteActAsPerson' => [ + 'className' => 'GrouperLiteActAsPerson', + 'foreignKey' => 'actor_co_person_id' + ] + ], + 'CoPerson' => [ + 'GrouperLiteActAsPerson' => [ + 'className' => 'GrouperLiteActAsPerson', + 'foreignKey' => 'act_as_co_person_id' + ] + ] + ]; // Association rules from this model to other models public $belongsTo = [ @@ -40,7 +55,8 @@ class GrouperLiteActAsPerson extends AppModel 'ActorCoPerson' => [ 'className' => 'CoPerson', 'foreignKey' => 'actor_co_person_id' - ] + ], + 'CoGrouperLiteWidget' ]; // Validation rules for table elements diff --git a/View/GrouperGroups/index.ctp b/View/GrouperGroups/index.ctp index bc80b10..bf00aba 100644 --- a/View/GrouperGroups/index.ctp +++ b/View/GrouperGroups/index.ctp @@ -124,6 +124,7 @@ $suffix = Configure::read('debug') > 0 ? '?time=' . time() : ''; JSON_THROW_ON_ERROR) ?>, }, api: { + permissions: , co: , glid: , mode: "", @@ -151,10 +152,12 @@ $suffix = Configure::read('debug') > 0 ? '?time=' . time() : ''; -
+
+ \ No newline at end of file From 3d6efd2d7757b6702431f0129c2ae8c38786496a Mon Sep 17 00:00:00 2001 From: Ioannis Igoumenos Date: Tue, 2 Apr 2024 21:43:26 +0300 Subject: [PATCH 09/21] implemented ActAsPerson add action --- Config/Schema/schema.xml | 4 +- ...ntroller.php => ActAsPeopleController.php} | 76 +++++++++++++++++-- Controller/GrouperGroupsController.php | 3 + ...perLiteActAsPerson.php => ActAsPerson.php} | 30 ++++---- Model/GrouperGroup.php | 4 +- Model/GrouperLiteWidget.php | 2 +- View/Elements/actAsPeopleAutocomplete.ctp | 52 +++++++++++-- 7 files changed, 136 insertions(+), 35 deletions(-) rename Controller/{GrouperLiteActAsPeopleController.php => ActAsPeopleController.php} (59%) rename Model/{GrouperLiteActAsPerson.php => ActAsPerson.php} (74%) diff --git a/Config/Schema/schema.xml b/Config/Schema/schema.xml index e978574..9b65be3 100644 --- a/Config/Schema/schema.xml +++ b/Config/Schema/schema.xml @@ -52,7 +52,7 @@ - +
@@ -63,7 +63,7 @@ REFERENCES cm_co_people(id) - + REFERENCES cm_co_people(id) diff --git a/Controller/GrouperLiteActAsPeopleController.php b/Controller/ActAsPeopleController.php similarity index 59% rename from Controller/GrouperLiteActAsPeopleController.php rename to Controller/ActAsPeopleController.php index d69da89..eb872ff 100644 --- a/Controller/GrouperLiteActAsPeopleController.php +++ b/Controller/ActAsPeopleController.php @@ -2,19 +2,23 @@ App::uses('Validator', 'Vendor/cakephp/Validation'); App::uses('CoGrouperLite', 'GrouperLiteWidget.Model/'); +App::uses('ActAsPerson', 'GrouperLiteWidget.Model/'); App::uses('GrouperGroup', 'GrouperLiteWidget.Model/'); + App::uses('Identifier', 'Model'); -class GrouperLiteActAsPeopleController extends StandardController +class ActAsPeopleController extends GrouperLiteWidgetAppController { public $helpers = ['Html', 'Form', 'Flash']; // Dynamic properties are deprecated, so we will define the property here private $userId; + public $requires_person = true; + public $uses = [ - 'GrouperLiteWidget.GrouperLiteActAsPerson', + 'GrouperLiteWidget.ActAsPerson', 'GrouperLiteWidget.CoGrouperLiteWidget', 'GrouperLiteWidget.GrouperGroup', 'Identifier', @@ -31,7 +35,7 @@ class GrouperLiteActAsPeopleController extends StandardController ]; - public $name = 'GrouperLiteActAsPeople'; + public $name = 'ActAsPeople'; /** * Overrides parent beforeFilter to verify that Session contains the correct API settings. @@ -64,6 +68,61 @@ public function beforeFilter() $this->CoGrouperLiteWidget->setConfig($cfg); } + /** + * Add an ActAs user + * + * @since COmanage Registry v4.4.0 + */ + + public function add(): void + { + $this->layout = null; + $this->autoRender = false; + + if(!$this->request->is('restful') + && !$this->request->is('ajax')) { + throw new RuntimeException('HTTP Method Not Allowed', HttpStatusCodesEnum::HTTP_METHOD_NOT_ALLOWED); + } + + $data = $this->request->data; + + try { + $ret = $this->ActAsPerson->saveAll($data); + } + catch(Exception $e) { + $err = filter_var($e->getMessage(),FILTER_SANITIZE_SPECIAL_CHARS); + } + + if($ret) { + // Reread the data so we account for any normalizations + $args = []; + $args['conditions']['ActAsPerson.id'] = $this->ActAsPerson->id; + $args['contain'] = false; + $data = $this->ActAsPerson->find('first', $args); + $this->Api->restResultHeader(201, "Added"); + $this->set('ActAsPerson', $this->ActAsPerson->id); + $this->response->body(json_encode($data['ActAsPerson'], JSON_THROW_ON_ERROR)); + } else { + $fs = $this->ActAsPerson->invalidFields(); + + if(!empty($fs)) { + $this->Api->restResultHeader(400, 'Invalid Fields'); + $this->set('invalid_fields', $fs); + } elseif ($e + && isset(_txt('en.http.status.codes')[$e->getCode()]) ) { + $this->Api->restResultHeader($e->getCode(), _txt('en.http.status.codes')[$e->getCode()]); + if(!empty($e->getMessage())) { + $this->set('vv_error', $e->getMessage()); + } + } else { + $this->Api->restResultHeader(500, "Other Error"); + } + } + + $this->response->send(); + } + + /** * NOTE: All permissions will be done on the Grouper side. All Authenticated users will be able to * use this plugin for self-admin of groups. @@ -78,12 +137,14 @@ public function beforeFilter() public function isAuthorized(): array|bool { $roles = $this->Role->calculateCMRoles(); + $pids = $this->parsePersonID($this->request->data); + $cfg = $this->CoGrouperLiteWidget->getConfig(); // Find the identifier $args = array(); $args['conditions']['Identifier.type'] = $cfg['CoGrouperLiteWidget']['identifier_type']; $args['conditions']['Identifier.status'] = SuspendableStatusEnum::Active; - $args['conditions']['Identifier.co_person_id'] = $roles['copersonid']; + $args['conditions']['Identifier.co_person_id'] = !empty($roles['copersonid']) ? $roles['copersonid'] : $pids['copersonid']; $args['contain'] = false; $identifiers = $this->Identifier->find('first', $args); @@ -99,7 +160,8 @@ public function isAuthorized(): array|bool $isActAsEligibilityGroupmember = false; if(!empty($eligibleGroup)) { - $isActAsEligibilityGroupmember = $this->GrouperGroup->isGroupMember($this->getUserId(), $eligibleGroup, $cfg); + $isActAsEligibilityGroupmember = $this->GrouperGroup + ->isGroupMember($this->getUserId(), $eligibleGroup, $cfg); } // Determine what operations this user can perform @@ -109,7 +171,9 @@ public function isAuthorized(): array|bool $p['add'] = $isActAsEligibilityGroupmember; $p['delete'] = $isActAsEligibilityGroupmember; $p['edit'] = $isActAsEligibilityGroupmember; - $p['update'] = $isActAsEligibilityGroupmember; + $p['index'] = $isActAsEligibilityGroupmember; + $p['view'] = $isActAsEligibilityGroupmember; + $p['patch'] = $isActAsEligibilityGroupmember; $this->set('permissions', $p); diff --git a/Controller/GrouperGroupsController.php b/Controller/GrouperGroupsController.php index edf0bcf..354c425 100644 --- a/Controller/GrouperGroupsController.php +++ b/Controller/GrouperGroupsController.php @@ -149,6 +149,9 @@ public function beforeRender() { // $this->set('vv_is_template_user', $this->GrouperGroup->isTemplateUser($this->userId ?? '', $cfg) ); // $this->set('vv_is_grouper_visible', $this->GrouperGroup->isGrouperVisible($this->userId ?? '', $cfg)); $this->set('vv_coid', $this->cur_co['Co']['id']); + + $roles = $this->Role->calculateCMRoles(); + $this->set('vv_copersonid', $roles['copersonid'] ?? null); } /** diff --git a/Model/GrouperLiteActAsPerson.php b/Model/ActAsPerson.php similarity index 74% rename from Model/GrouperLiteActAsPerson.php rename to Model/ActAsPerson.php index 4f833e4..15d6299 100644 --- a/Model/GrouperLiteActAsPerson.php +++ b/Model/ActAsPerson.php @@ -1,6 +1,6 @@ ["GrouperLiteActAsPerson"], - 'CoPerson' => [ - 'GrouperLiteActAsPerson' => [ - 'className' => 'GrouperLiteActAsPerson', - 'foreignKey' => 'actor_co_person_id' + 'CoGrouperLiteWidget' => ['ActAsPerson'], + 'CoPerson' => [ + 'ActAsPerson' => [ + 'className' => 'ActAsPerson', + 'foreignKey' => 'co_person_id' ] ], - 'CoPerson' => [ - 'GrouperLiteActAsPerson' => [ - 'className' => 'GrouperLiteActAsPerson', + 'CoPerson' => [ + 'ActAsPerson' => [ + 'className' => 'ActAsPerson', 'foreignKey' => 'act_as_co_person_id' ] ] @@ -47,15 +47,11 @@ class GrouperLiteActAsPerson extends AppModel // Association rules from this model to other models public $belongsTo = [ - 'GrouperLiteWidget', 'ActAsCoPerson' => [ 'className' => 'CoPerson', 'foreignKey' => 'act_as_co_person_id' ], - 'ActorCoPerson' => [ - 'className' => 'CoPerson', - 'foreignKey' => 'actor_co_person_id' - ], + 'CoPerson', 'CoGrouperLiteWidget' ]; @@ -71,7 +67,7 @@ class GrouperLiteActAsPerson extends AppModel 'required' => true, 'allowEmpty' => false ], - 'actor_co_person_id' => [ + 'co_person_id' => [ 'rule' => 'numeric', 'required' => true, 'allowEmpty' => false diff --git a/Model/GrouperGroup.php b/Model/GrouperGroup.php index 6f14364..a8f55b8 100644 --- a/Model/GrouperGroup.php +++ b/Model/GrouperGroup.php @@ -554,9 +554,9 @@ public function isGroupMember(string $userId, string $groupName, array $cfg): bo } /** - * Determine if User can use the Grouper Template to create a Working Group. + * Determine if a User can use the Grouper Template to create a Working Group. * - * @param string $userId Id of User + * @param string $userId User ID * @return string T for True and F for False * @throws GrouperLiteWidgetException * diff --git a/Model/GrouperLiteWidget.php b/Model/GrouperLiteWidget.php index 5c38153..d5b8aa3 100644 --- a/Model/GrouperLiteWidget.php +++ b/Model/GrouperLiteWidget.php @@ -39,7 +39,7 @@ class GrouperLiteWidget extends AppModel { public $belongsTo = []; public $hasMany = [ - 'GrouperLiteActAsPerson' => ['dependent' => true] + 'ActAsPerson' => ['dependent' => true] ]; // Validation rules for table elements diff --git a/View/Elements/actAsPeopleAutocomplete.ctp b/View/Elements/actAsPeopleAutocomplete.ctp index 0bf351e..67a0f85 100644 --- a/View/Elements/actAsPeopleAutocomplete.ctp +++ b/View/Elements/actAsPeopleAutocomplete.ctp @@ -14,7 +14,6 @@ $suffix = Configure::read('debug') > 0 ? '?time=' . time() : ''; import Autocomplete from 'webroot ?>grouper_lite_widget/js/autocomplete.js'; import Loader from 'webroot ?>grouper_lite_widget/js/loader.js'; - // XXX Probably move this to comanage.js const provided = { collapsed: , optAction: "", @@ -78,6 +77,7 @@ $suffix = Configure::read('debug') > 0 ? '?time=' . time() : ''; api: { co: , glid: , + coPersonId: , mode: "", base: "webroot ?>grouper_lite_widget/grouper_groups", find: "webroot ?>grouper_lite_widget/grouper_groups/findSubscriber/co:/glid:", @@ -85,6 +85,7 @@ $suffix = Configure::read('debug') > 0 ? '?time=' . time() : ''; leave: "webroot ?>grouper_lite_widget/grouper_groups/leaveGroup/co:/glid:", remove: "webroot ?>grouper_lite_widget/grouper_groups/removeSubscriber/co:/glid:", add: "webroot ?>grouper_lite_widget/grouper_groups/addSubscriber/co:/glid:", + addActAs: "webroot ?>grouper_lite_widget/act_as_people/add/co:/glid:", group: "webroot ?>grouper_lite_widget/grouper_groups/groupSubscribers/co:/glid:", memberships: "webroot ?>grouper_lite_widget/grouper_groups/groupmemberapi/co:/glid:", managing: "webroot ?>grouper_lite_widget/grouper_groups/usermanagerapi/co:/glid:", @@ -94,13 +95,10 @@ $suffix = Configure::read('debug') > 0 ? '?time=' . time() : ''; } const app = Vue.createApp({ - provide: provided, data() { return { loading: false, - search: '', - subscribers: [], - disabled: [], + rawData: [] } }, components: { @@ -109,14 +107,54 @@ $suffix = Configure::read('debug') > 0 ? '?time=' . time() : ''; }, inject: ['txt', 'api'], methods: { - addUser({ name }) { - console.log('test add user') + async addUser(user) { + const { identifier: ident, label, value: id } = user; + this.loading = true; + + // Create form + const formData = new FormData(); + formData.append("act_as_co_person_id", id); + formData.append("co_person_id", this.api.coPersonId); + formData.append("co_grouper_lite_widget_id", this.api.glid); + + // Request URL + const urlString = window.location.protocol + "//" + window.location.host + this.api.addActAs; + const url = new URL(urlString); + + // AJAX Request + const request = new Request(url, { + headers: new Headers({ + 'X-Requested-With': 'XMLHttpRequest', + "Accept": "application/json", + }), + method: "POST", + body: formData + }); + + // Fire request + const resp = await (fetch(request).catch(error => generateFlash('Network Error', 'error'))); + + if (!resp.ok) { + generateFlash('Enabling ActAs User failed', 'error'); + let errorResponse = await resp.json(); + generateFlash(`${errorResponse.message}`, 'error'); + this.loading = false; + return + } + generateFlash('Act As User Enabled', 'success'); + this.rawData = await resp.json(); + this.loading = false; + // Force reload here }, } }); app.config.unwrapInjectedRef = true; + // For core configurations and texts globally + for (const [key, value] of Object.entries(provided)) { + app.provide( key, value); + } // Mount the component and provide a global reference for this app instance. app.mount("#-container"); From 86c469148397db53f0f830d9d5af677e8462459f Mon Sep 17 00:00:00 2001 From: Ioannis Igoumenos Date: Wed, 3 Apr 2024 13:51:05 +0300 Subject: [PATCH 10/21] Add,Delete act as user --- Controller/ActAsPeopleController.php | 94 ++++++++++++++++--- Controller/GrouperGroupsController.php | 23 ++++- Model/GrouperGroup.php | 30 ++++-- ...mplete.ctp => ActAsPeopleAutocomplete.ctp} | 9 +- View/Elements/ActionItem.ctp | 61 ++++++++++++ View/Elements/ActionSideBar.ctp | 2 + View/GrouperGroups/index.ctp | 37 ++------ webroot/css/co-grouper-base.css | 4 + 8 files changed, 209 insertions(+), 51 deletions(-) rename View/Elements/{actAsPeopleAutocomplete.ctp => ActAsPeopleAutocomplete.ctp} (97%) create mode 100644 View/Elements/ActionItem.ctp create mode 100644 View/Elements/ActionSideBar.ctp diff --git a/Controller/ActAsPeopleController.php b/Controller/ActAsPeopleController.php index eb872ff..71e5b07 100644 --- a/Controller/ActAsPeopleController.php +++ b/Controller/ActAsPeopleController.php @@ -76,8 +76,8 @@ public function beforeFilter() public function add(): void { - $this->layout = null; - $this->autoRender = false; + $this->request->allowMethod('ajax'); + $this->layout = 'ajax'; if(!$this->request->is('restful') && !$this->request->is('ajax')) { @@ -86,40 +86,110 @@ public function add(): void $data = $this->request->data; + $fullName = $data['fullname']; + unset($data['fullname']); + try { $ret = $this->ActAsPerson->saveAll($data); } catch(Exception $e) { $err = filter_var($e->getMessage(),FILTER_SANITIZE_SPECIAL_CHARS); + $this->Flash->set($err, ['key' => 'error']); } if($ret) { - // Reread the data so we account for any normalizations + $this->Api->restResultHeader(HttpStatusCodesEnum::HTTP_CREATED, 'Added'); + // I am registering a flash message. It will render after the frontend triggers a reload + $this->Flash->set("Act As {$fullName}" , ['key' => 'success']); + $args = []; $args['conditions']['ActAsPerson.id'] = $this->ActAsPerson->id; $args['contain'] = false; $data = $this->ActAsPerson->find('first', $args); - $this->Api->restResultHeader(201, "Added"); - $this->set('ActAsPerson', $this->ActAsPerson->id); - $this->response->body(json_encode($data['ActAsPerson'], JSON_THROW_ON_ERROR)); + + $resp = [ + 'ActAsPerson' => $data['ActAsPerson'] + ]; + $this->set(compact('resp')); + $this->set('_serialize', 'resp'); } else { $fs = $this->ActAsPerson->invalidFields(); if(!empty($fs)) { - $this->Api->restResultHeader(400, 'Invalid Fields'); - $this->set('invalid_fields', $fs); + $this->Api->restResultHeader(HttpStatusCodesEnum::HTTP_BAD_REQUEST, 'Invalid Fields'); + $this->Flash->set('Invalid Fields', ['key' => 'error']); + $this->set(compact('fs')); + $this->set('_serialize', 'fs'); } elseif ($e && isset(_txt('en.http.status.codes')[$e->getCode()]) ) { $this->Api->restResultHeader($e->getCode(), _txt('en.http.status.codes')[$e->getCode()]); if(!empty($e->getMessage())) { - $this->set('vv_error', $e->getMessage()); + $vv_error = $e->getMessage(); + $this->set(compact('vv_error')); + $this->set('_serialize', 'vv_error'); + $this->Flash->set($e->getMessage(), ['key' => 'error']); } } else { - $this->Api->restResultHeader(500, "Other Error"); + $this->Api->restResultHeader(HttpStatusCodesEnum::HTTP_INTERNAL_SERVER_ERROR, 'Other Error'); + $error = ErrorsEnum::Error; + $this->set(compact('error')); + $this->set('_serialize', 'error'); } } + } + + /** + * Handle an Act As Person Delete request. + * + * @since COmanage Registry v4.4.0 + */ + + public function delete():void + { + $this->layout = null; + $this->autoRender = false; + + if(!$this->request->is('delete') + || $this->request->is('ajax') + || $this->request->is('restful')) { + throw new RuntimeException('HTTP Method Not Allowed', HttpStatusCodesEnum::HTTP_METHOD_NOT_ALLOWED); + } + + if(empty($this->request->named['copersonid']) + || empty($this->request->named['act_as_copersonid'])) { + $this->log(__METHOD__ . '::message: Named Parameter missing', LOG_ERROR); + throw new BadRequestException('Named Parameter missing'); + } + + $redirect = [ + 'plugin' => 'grouper_lite_widget', + 'controller' => 'grouper_groups', + 'action' => $this->request->named['redirect_act'] ?? 'groupmember', + 'co' => $this->request->named['co'], + 'glid' => $this->request->named['glid'] + ]; + + try { + $conditions = [ + 'ActAsPerson.co_person_id' => $this->request->named['copersonid'], + 'ActAsPerson.act_as_co_person_id' => $this->request->named['act_as_copersonid'] + ]; + + $ret = $this->ActAsPerson->deleteAll($conditions, true, true); + } catch(Exception $e) { + $err = filter_var($e->getMessage(),FILTER_SANITIZE_SPECIAL_CHARS); + $this->Flash->set($err, ['key' => 'error']); + } + + // Set flash message + if($ret) { + $this->Flash->set(_txt('er.deleted-a', ['Person']) , ['key' => 'success']); + } else { + $this->Flash->set(_txt('er.delete'), ['key' => 'error']); + } - $this->response->send(); + // Redirect back to the plugin + $this->redirect($redirect); } @@ -137,6 +207,8 @@ public function add(): void public function isAuthorized(): array|bool { $roles = $this->Role->calculateCMRoles(); + $this->set('roles', $roles); + $pids = $this->parsePersonID($this->request->data); $cfg = $this->CoGrouperLiteWidget->getConfig(); diff --git a/Controller/GrouperGroupsController.php b/Controller/GrouperGroupsController.php index 354c425..6d75472 100644 --- a/Controller/GrouperGroupsController.php +++ b/Controller/GrouperGroupsController.php @@ -28,6 +28,7 @@ App::uses('Validator', 'Vendor/cakephp/Validation'); App::uses('CoGrouperLite', 'GrouperLiteWidget.Model/'); App::uses('GrouperGroup', 'GrouperLiteWidget.Model/'); +App::uses('ActAsPerson', 'GrouperLiteWidget.Model/'); App::uses('Identifier', 'Model'); /** @@ -45,6 +46,7 @@ class GrouperGroupsController extends GrouperLiteWidgetAppController public $uses = [ 'GrouperLiteWidget.GrouperGroup', 'GrouperLiteWidget.CoGrouperLiteWidget', + 'GrouperLiteWidget.ActAsPerson', 'Identifier', 'CoPerson' ]; @@ -152,6 +154,22 @@ public function beforeRender() { $roles = $this->Role->calculateCMRoles(); $this->set('vv_copersonid', $roles['copersonid'] ?? null); + $this->set('vv_picker_mode', PeoplePickerModeEnum::All); + + // Get the ActAs User Data + $co_person_id = $this->viewVars['roles']['copersonid']; + // Get the act as data from the database + $args = []; + $args['conditions']['ActAsPerson.co_person_id'] = $co_person_id; + $args['contain'] = false; + $act_as_record = $this->ActAsPerson->find('first', $args); + $this->set('vv_act_as_people', []); + if(!empty($act_as_record)) { + $act_as_person = $this->GrouperGroup->dataConstructForPicker($this->cur_co['Co']['id'], + PeoplePickerModeEnum::All, + [$act_as_record['ActAsPerson']['act_as_co_person_id']]); + $this->set('vv_act_as_people', $act_as_person); + } } /** @@ -423,12 +441,15 @@ public function index(): void public function isAuthorized(): array|bool { $roles = $this->Role->calculateCMRoles(); + $this->set('roles', $roles); + $pids = $this->parsePersonID($this->request->data); + $cfg = $this->CoGrouperLiteWidget->getConfig(); // Find the identifier $args = array(); $args['conditions']['Identifier.type'] = $cfg['CoGrouperLiteWidget']['identifier_type']; $args['conditions']['Identifier.status'] = SuspendableStatusEnum::Active; - $args['conditions']['Identifier.co_person_id'] = $roles['copersonid']; + $args['conditions']['Identifier.co_person_id'] = !empty($roles['copersonid']) ? $roles['copersonid'] : $pids['copersonid']; $args['contain'] = false; $identifiers = $this->Identifier->find('first', $args); diff --git a/Model/GrouperGroup.php b/Model/GrouperGroup.php index a8f55b8..ad83576 100644 --- a/Model/GrouperGroup.php +++ b/Model/GrouperGroup.php @@ -169,19 +169,15 @@ public function filteredMemberOfGroups(string $userId, array $cfg): array * * @return array Array of CO Person records * @since COmanage Registry v4.4.0 - * - * XXX Remove this as soon as the change is present in COmanage core - * */ public function findForPicker(int $coId, string $mode, ?string $term): array { $coPersonIds = []; $this->CoPerson = ClassRegistry::init('CoPerson'); - $this->Co = ClassRegistry::init('Co'); // jquery Autocomplete sends the search as url?term=foo if(!empty($term)) { - // Leverage model specific keyword search + // Leverage-model-specific keyword search // Note EmailAddress and Identifier don't support substring search foreach(array('Name', 'EmailAddress', 'Identifier') as $m) { @@ -197,7 +193,29 @@ public function findForPicker(int $coId, string $mode, ?string $term): array // We only do this when there are relatively small numbers of results to // avoid making a bunch of database queries early in the search. - $matches = array(); + return $this->dataConstructForPicker($coId, $term,$coPersonIds); + } + + /** + * Construct picker(like) response data based on mode and term + * + * @param integer $coId CO ID + * @param string $mode Search mode to apply filters for + * @param array $coPersonIds List of PersonIds + * + * @return array Array of CO Person records + * @since COmanage Registry v4.4.0 + */ + public function dataConstructForPicker(int $coId, string $mode, array $coPersonIds): array + { + if(empty($coPersonIds)) { + return []; + } + + $this->CoPerson = ClassRegistry::init('CoPerson'); + $this->Co = ClassRegistry::init('Co'); + + $matches = []; if(count($coPersonIds) > 100) { // We don't return large sets to avoid slow performance diff --git a/View/Elements/actAsPeopleAutocomplete.ctp b/View/Elements/ActAsPeopleAutocomplete.ctp similarity index 97% rename from View/Elements/actAsPeopleAutocomplete.ctp rename to View/Elements/ActAsPeopleAutocomplete.ctp index 67a0f85..26a951a 100644 --- a/View/Elements/actAsPeopleAutocomplete.ctp +++ b/View/Elements/ActAsPeopleAutocomplete.ctp @@ -78,7 +78,7 @@ $suffix = Configure::read('debug') > 0 ? '?time=' . time() : ''; co: , glid: , coPersonId: , - mode: "", + mode: "", base: "webroot ?>grouper_lite_widget/grouper_groups", find: "webroot ?>grouper_lite_widget/grouper_groups/findSubscriber/co:/glid:", join: "webroot ?>grouper_lite_widget/grouper_groups/joinGroup/co:/glid:", @@ -116,6 +116,7 @@ $suffix = Configure::read('debug') > 0 ? '?time=' . time() : ''; formData.append("act_as_co_person_id", id); formData.append("co_person_id", this.api.coPersonId); formData.append("co_grouper_lite_widget_id", this.api.glid); + formData.append("fullname", label); // Request URL const urlString = window.location.protocol + "//" + window.location.host + this.api.addActAs; @@ -141,9 +142,11 @@ $suffix = Configure::read('debug') > 0 ? '?time=' . time() : ''; this.loading = false; return } - generateFlash('Act As User Enabled', 'success'); + // generateFlash('Act As User Enabled', 'success'); this.rawData = await resp.json(); - this.loading = false; + console.log(this.rawData) + this.loading = false + window.location.reload(); // Force reload here }, } diff --git a/View/Elements/ActionItem.ctp b/View/Elements/ActionItem.ctp new file mode 100644 index 0000000..518ef4d --- /dev/null +++ b/View/Elements/ActionItem.ctp @@ -0,0 +1,61 @@ + + + + +
+
+
+ +
+ +
+ +
+
+ Form->create('ActAsPeople', [ + 'id' => 'ActAsPeopleDeleteForm' . $person['value'], + 'inputDefaults' => [ + 'label' => false, + 'div' => false + ], + 'url' => [ + 'plugin' => 'grouper_lite_widget', + 'controller' => 'act_as_people', + 'action' => 'delete', + 'co' => $vv_coid, + 'glid' => $vv_config['CoGrouperLiteWidget']['id'], + 'act_as_copersonid' => $person['value'], + 'copersonid' => $vv_copersonid + ], + 'type' => 'delete' + ]); + $options = [ + 'label' => 'Disable', + 'onclick' => 'javascript:handleDisable(event)', + 'class' => 'btn btn-grouper btn-fit btn-block btn-danger btn-sm m-1 text-nowrap member-del-btn', + ]; + echo $this->Form->end($options); + ?> +
diff --git a/View/Elements/ActionSideBar.ctp b/View/Elements/ActionSideBar.ctp new file mode 100644 index 0000000..a4abe2d --- /dev/null +++ b/View/Elements/ActionSideBar.ctp @@ -0,0 +1,2 @@ + 0 ? '?time=' . time() : '';
- element('actAsPeopleAutocomplete', + element('ActAsPeopleAutocomplete', compact('vv_config', 'vv_coid', 'vv_is_user_owner', 'htmlId') )?> -
- -
-
-
- John Doe -
- -
- Identifier (I2CollabPN): nickmastoris.mastoris@at.in... -
-
- - -
+ +
+ + element('ActionItem', compact('person')) ?> + +
diff --git a/webroot/css/co-grouper-base.css b/webroot/css/co-grouper-base.css index 836c05c..f501f8b 100644 --- a/webroot/css/co-grouper-base.css +++ b/webroot/css/co-grouper-base.css @@ -123,6 +123,10 @@ animation: fadeInHalf 0.9s -0.3s infinite reverse; } +.grouper_groups .person-item { + overflow-wrap: anywhere; +} + .popover { max-width: 400px; } From 9c121b4adaf50c78b2d6181be5b183e368f6554b Mon Sep 17 00:00:00 2001 From: Ioannis Igoumenos Date: Wed, 3 Apr 2024 14:06:15 +0300 Subject: [PATCH 11/21] Fix autocomplete unique id --- View/Elements/ActAsPeopleAutocomplete.ctp | 3 +-- webroot/js/autocomplete.js | 11 +++++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/View/Elements/ActAsPeopleAutocomplete.ctp b/View/Elements/ActAsPeopleAutocomplete.ctp index 26a951a..46d5180 100644 --- a/View/Elements/ActAsPeopleAutocomplete.ctp +++ b/View/Elements/ActAsPeopleAutocomplete.ctp @@ -142,12 +142,11 @@ $suffix = Configure::read('debug') > 0 ? '?time=' . time() : ''; this.loading = false; return } - // generateFlash('Act As User Enabled', 'success'); this.rawData = await resp.json(); console.log(this.rawData) this.loading = false - window.location.reload(); // Force reload here + window.location.reload(); }, } }); diff --git a/webroot/js/autocomplete.js b/webroot/js/autocomplete.js index da566ce..50703f2 100644 --- a/webroot/js/autocomplete.js +++ b/webroot/js/autocomplete.js @@ -50,6 +50,9 @@ export default { // The minimum length that i start search is 3. So we only enable the button when // the input text value has at least three characters return this.activeBtn || (this.search.length < 3) + }, + autcompleteId() { + return `autocomplete-search-container-${this.action}` } }, mounted(el) { @@ -57,7 +60,7 @@ export default { this.url = `${this.api.find}?co=${this.api.co}&mode=${this.api.mode}&page=${this.page}&limit=${this.limit}` input.autocomplete({ source: ( request, response ) => { - $("#grouper-search-container .co-loading-mini").show(); + $(`#autocomplete-search-container-${this.action} .co-loading-mini`).show(); $.ajax({ url: this.url, type: 'GET', @@ -67,11 +70,11 @@ export default { term: request.term }, success: function (data) { - $("#grouper-search-container .co-loading-mini").hide(); + $(`#autocomplete-search-container-${this.action} .co-loading-mini`).hide(); response( data ); }, error: function(data) { - $("#grouper-search-container .co-loading-mini").hide(); + $(`#autocomplete-search-container-${this.action} .co-loading-mini`).hide(); console.log('Autocomplete ajax error:', data) generateFlash('Find action failed', 'error'); } @@ -99,7 +102,7 @@ export default { }) }, template: /*html*/` -
+
From 864a2aa2ad54f36b79750a5cf141ba7b7c574c87 Mon Sep 17 00:00:00 2001 From: Ioannis Igoumenos Date: Wed, 3 Apr 2024 15:52:21 +0300 Subject: [PATCH 12/21] Fix autocomplete unique id --- webroot/js/autocomplete.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/webroot/js/autocomplete.js b/webroot/js/autocomplete.js index 50703f2..f6e05b5 100644 --- a/webroot/js/autocomplete.js +++ b/webroot/js/autocomplete.js @@ -57,6 +57,7 @@ export default { }, mounted(el) { const input = $(this.$el).find(`#${this.toKebabCase(this.action)}-input`); + const action = this.action this.url = `${this.api.find}?co=${this.api.co}&mode=${this.api.mode}&page=${this.page}&limit=${this.limit}` input.autocomplete({ source: ( request, response ) => { @@ -70,11 +71,11 @@ export default { term: request.term }, success: function (data) { - $(`#autocomplete-search-container-${this.action} .co-loading-mini`).hide(); + $(`#autocomplete-search-container-${action} .co-loading-mini`).hide(); response( data ); }, error: function(data) { - $(`#autocomplete-search-container-${this.action} .co-loading-mini`).hide(); + $(`#autocomplete-search-container-${action} .co-loading-mini`).hide(); console.log('Autocomplete ajax error:', data) generateFlash('Find action failed', 'error'); } From 8bd174c4216e5628fd61bc7852fc0104e9ed6cb5 Mon Sep 17 00:00:00 2001 From: Ioannis Igoumenos Date: Wed, 3 Apr 2024 16:35:43 +0300 Subject: [PATCH 13/21] Move code at sidebar.ctp file --- Lib/lang.php | 4 ++- View/Elements/ActAsPeopleAutocomplete.ctp | 3 ++ View/Elements/ActionSideBar.ctp | 33 ++++++++++++++++- View/GrouperGroups/index.ctp | 43 ++++++----------------- 4 files changed, 48 insertions(+), 35 deletions(-) diff --git a/Lib/lang.php b/Lib/lang.php index 22fab32..11bccc5 100644 --- a/Lib/lang.php +++ b/Lib/lang.php @@ -126,8 +126,10 @@ 'pl.grouperlite.action.close' => 'Close', 'pl.grouperlite.action.clear' => 'Clear', 'pl.grouperlite.action.add-user' => 'Add', + 'pl.grouperlite.action.act-as' => 'Act As', 'pl.grouperlite.action.remove-user' => 'Remove', - 'pl.grouperlite.action.enable-act-as' => 'Enable', + 'pl.grouperlite.action.enable' => 'Enable', + 'pl.grouperlite.action.find' => 'Find', 'pl.grouperlite.message.user-not-found-error' => 'Error: User not found.', 'pl.grouperlite.message.user-not-added-error' => 'Error: Unable to add user.', 'pl.grouperlite.message.user-not-removed-error' => 'Error: Unable to remove user.', diff --git a/View/Elements/ActAsPeopleAutocomplete.ctp b/View/Elements/ActAsPeopleAutocomplete.ctp index 46d5180..c650022 100644 --- a/View/Elements/ActAsPeopleAutocomplete.ctp +++ b/View/Elements/ActAsPeopleAutocomplete.ctp @@ -21,6 +21,9 @@ $suffix = Configure::read('debug') > 0 ? '?time=' . time() : ''; url: "", grouperUrl: "", view: "action ?>", + hasActAs: , + actAsPerson: , + permissions: , txt: { adhocHeading: "", wgHeading: "", diff --git a/View/Elements/ActionSideBar.ctp b/View/Elements/ActionSideBar.ctp index a4abe2d..3c8898d 100644 --- a/View/Elements/ActionSideBar.ctp +++ b/View/Elements/ActionSideBar.ctp @@ -1,2 +1,33 @@ - + +
+ diff --git a/View/GrouperGroups/index.ctp b/View/GrouperGroups/index.ctp index 00a2e3e..0dc3b5e 100644 --- a/View/GrouperGroups/index.ctp +++ b/View/GrouperGroups/index.ctp @@ -71,6 +71,9 @@ $suffix = Configure::read('debug') > 0 ? '?time=' . time() : ''; url: "", grouperUrl: "", view: "action ?>", + hasActAs: , + actAsPerson: , + permissions: , txt: { adhocHeading: "", wgHeading: "", @@ -124,7 +127,6 @@ $suffix = Configure::read('debug') > 0 ? '?time=' . time() : ''; JSON_THROW_ON_ERROR) ?>, }, api: { - permissions: , co: , glid: , mode: "", @@ -158,37 +160,12 @@ $suffix = Configure::read('debug') > 0 ? '?time=' . time() : '';
- + element('ActionSideBar', + compact('vv_config', + 'vv_act_as_people', + 'vv_coid', + 'vv_is_user_owner', + 'htmlId') + ) ?>
\ No newline at end of file From b0042c77bdddbe46a0673746f368ebc29658e024 Mon Sep 17 00:00:00 2001 From: Ioannis Igoumenos Date: Wed, 3 Apr 2024 18:01:12 +0300 Subject: [PATCH 14/21] Add focus on people autocomplete on collapse open --- View/Elements/ActAsPeopleAutocomplete.ctp | 2 +- View/Elements/ActionSideBar.ctp | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/View/Elements/ActAsPeopleAutocomplete.ctp b/View/Elements/ActAsPeopleAutocomplete.ctp index c650022..d147ae9 100644 --- a/View/Elements/ActAsPeopleAutocomplete.ctp +++ b/View/Elements/ActAsPeopleAutocomplete.ctp @@ -44,7 +44,7 @@ $suffix = Configure::read('debug') > 0 ? '?time=' . time() : ''; }, members: "", email: "", - enableActAs: "", + enableActAs: "", close: "", remove: "", addUser: "", diff --git a/View/Elements/ActionSideBar.ctp b/View/Elements/ActionSideBar.ctp index 3c8898d..8658819 100644 --- a/View/Elements/ActionSideBar.ctp +++ b/View/Elements/ActionSideBar.ctp @@ -1,3 +1,11 @@ + +