Skip to content

CFM-291_Show_the_count_of_members_on_the_members_tab #184

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
29 changes: 18 additions & 11 deletions app/src/Controller/StandardController.php
Expand Up @@ -35,7 +35,7 @@
use \Cake\Http\Exception\BadRequestException;
use \App\Lib\Enum\ProvisioningContextEnum;
use \App\Lib\Enum\SuspendableStatusEnum;
use \App\Lib\Util\StringUtilities;
use \App\Lib\Util\{StringUtilities, FunctionUtilities};

class StandardController extends AppController {
use \App\Lib\Traits\IndexQueryTrait;
Expand Down Expand Up @@ -656,13 +656,8 @@ protected function populateAutoViewVars(object $obj=null) {
case 'select':
$avvmodel = $avv['model'];
$this->$avvmodel = TableRegistry::getTableLocator()->get($avvmodel);
$query = $this->$avvmodel->find($avv['type'] == 'auxiliary' ? 'all' : 'list');

if($avv['type'] == 'auxiliary') {
$query = $this->$avvmodel->find();
} else {
$query = $this->$avvmodel->find('list');
}

if(!empty($avv['find'])) {
if($avv['find'] == 'filterPrimaryLink') {
// We're filtering the requested model, not our current model.
Expand Down Expand Up @@ -693,18 +688,30 @@ protected function populateAutoViewVars(object $obj=null) {
// Use the specified finder, if configured
$query = $query->find($avv['find']);
}
} else {
// XXX is this the best logic? maybe some relation to filterPrimaryLink?
} elseif($table->getSchema()->hasColumn('co_id')) {
// XXX is this the best logic? maybe some relation to filterPrimaryLink?
// By default, filter everything on CO ID

$avv['where']['co_id'] = $this->getCOID();
//$query = $query->where([$table->getAlias().'.co_id' => $this->getCOID()]);
}


// Where Rule. The rule will be transfered as is
if(!empty($avv['where'])) {
// Filter on the specified clause (of the form [column=>value])
$query = $query->where($avv['where']);
}

// Where rule that will be evaluated. We use the custom whereEvan key to
// distinguish from the plain where. Also it might contain more than one conditions
if(!empty($avv['whereEval'])) {
foreach ($avv['whereEval'] as $whereClauseColumn => $chainedMethodDescription) {
$calculatedValue = FunctionUtilities::dynamicChainedFunction(
$this,
$chainedMethodDescription
);
$query = $query->where([$whereClauseColumn => $calculatedValue]);
}
}

// Sort the list by display field
if(!empty($avv['model']) && method_exists($this->$avvmodel, "getDisplayField")) {
Expand Down
83 changes: 83 additions & 0 deletions app/src/Lib/Util/FunctionUtilities.php
@@ -0,0 +1,83 @@
<?php
/**
* COmanage Registry Function Utilities
*
* Portions licensed to the University Corporation for Advanced Internet
* Development, Inc. ("UCAID") under one or more contributor license agreements.
* See the NOTICE file distributed with this work for additional information
* regarding copyright ownership.
*
* UCAID licenses this file to you under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* @link https://www.internet2.edu/comanage COmanage Project
* @package registry
* @since COmanage Registry v5.0.0
* @license Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
*/

declare(strict_types = 1);

namespace App\Lib\Util;

use Cake\ORM\TableRegistry;
use \Cake\Utility\Inflector;

class FunctionUtilities {
/**
* Create a chained method call
*
* Example:
* $this->getRequest()->getQuery(name: 'group_id)
* $rootObject: $this,
* $chainedDescriptionExample => [
* // Chain of methods
* 'getRequest',
* 'getQuery' => [
* // parameter name => parameter value, We are taking advantage the named parameters feature
* 'name' =>'group_id'
* ],
* ]
*
* @param mixed $rootObj Object of the intance method we are calling
* @param array $chainedDescription Description from root to final method call.
*
* @return mixed Return the intermediate objects or the final value
* @since COmanage Registry v5.0.0
*/

public static function dynamicChainedFunction(mixed $rootObj, array $chainedDescription): mixed {
if(!empty($chainedDescription)) {
$key = key($chainedDescription);
// This is the case where we pass a function with no parameters
$funcName = null;
$params = null;
// We pass a function with an array of parameters
if( \is_int($key)) {
$funcName = array_shift($chainedDescription);
} elseif (\is_string($key)) {
$funcName = $key;
$params = array_shift($chainedDescription);
}

if(!empty($params)) {
$funcCall = $rootObj->$funcName(...$params);
} else {
$funcCall = $rootObj->$funcName();
}
$value = self::dynamicChainedFunction($funcCall, $chainedDescription);
}

return !\is_object($rootObj) ? $rootObj : $value;
}

}
23 changes: 21 additions & 2 deletions app/src/Model/Table/GroupNestingsTable.php
Expand Up @@ -77,9 +77,9 @@ public function initialize(array $config): void {
$this->setPrimaryLink('group_id');
$this->setRequiresCO(true);

$this->setEditContains(['Groups', 'TargetGroups']);
$this->setEditContains(['Groups', 'GroupMembers', 'TargetGroups']);

$this->setIndexContains(['Groups', 'TargetGroups']);
$this->setIndexContains(['Groups', 'GroupMembers', 'TargetGroups']);

$this->setPermissions([
// XXX update for couAdmins, group owners, etc
Expand All @@ -95,6 +95,25 @@ public function initialize(array $config): void {
'index' => ['platformAdmin', 'coAdmin']
]
]);

$this->setAutoViewVars([
'groupMembers' => [
'type' => 'auxiliary',
'model' => 'GroupMembers',
'whereEval' => [
// Where Clause column name
'GroupMembers.group_id' => [
// Chain of methods that will construct the whereClause condition value
// Method that accepts no parameters
'getRequest',
// Method that accepts only one parameter
// getQuery(name: 'group_id')
'getQuery' => [
'name' =>'group_id'
]
]
]
]]);
}

/**
Expand Down
6 changes: 4 additions & 2 deletions app/src/Model/Table/GroupsTable.php
Expand Up @@ -123,15 +123,17 @@ public function initialize(array $config): void {
// For an Owners Group, the group it manages owners for
'OwnersForGroup',
// For a regular group, the Owners Group
'OwnersGroup'
'OwnersGroup',
'GroupMembers'
]);

$this->setViewContains([
'Identifiers',
// For an Owners Group, the group it manages owners for
'OwnersForGroup',
// For a regular group, the Owners Group
'OwnersGroup'
'OwnersGroup',
'GroupMembers'
]);

// XXX Also used by SearchBlocks
Expand Down
2 changes: 1 addition & 1 deletion app/templates/Groups/fields.inc
Expand Up @@ -74,7 +74,7 @@ if($vv_action != 'add') {

print $this->Field->statusControl(
fieldName: 'owners_group_id',
status: $vv_obj->owners_group->name,
status: $vv_obj->owners_group->name ?? '',
link: ['url' => ['controller' => 'groups', 'action' => 'edit', $vv_obj->owners_group_id]]
);
}
Expand Down
25 changes: 22 additions & 3 deletions app/templates/element/subnavigation.php
Expand Up @@ -301,14 +301,33 @@
<li class="nav-item">
<?php
$linkClass = ($active == 'members') ? 'nav-link active' : 'nav-link';
$title = __d('controller', 'Members', [99]);
$num_group_members = 0;
// Group Members tab
if(isset($group_members)) {
$num_group_members = $this->Paginator->counter('{{count}}');
} elseif(isset($groupMembers)) {
// Group Nesting
$num_group_members = count($groupMembers);
} elseif($vv_obj?->group_members) {
// Group Properties Tab
$num_group_members = count($vv_obj->group_members);
}

$tab_title = "<span class='tab-count'>"
. "<span class='tab-count-item'>{$num_group_members}</span>"
. '</span>'
. "<span class='tab-title'>{$title}</span>";

print $this->Html->link(
__d('controller', 'Members', [99]),
$tab_title,
[ 'controller' => 'group_members',
'action' => 'index',
'?' => $linkFilter
],
['class' => $linkClass]
);
['class' => $linkClass,
'escape' => false]
);
?>
</li>
<li class="nav-item">
Expand Down
17 changes: 17 additions & 0 deletions app/webroot/css/co-base.css
Expand Up @@ -696,6 +696,9 @@ ul.form-list li.alert-banner .co-alert {
text-transform: uppercase;
padding: 1em 1.5em;
color: var(--cmg-color-link) !important;
display: flex;
flex-direction: row-reverse;
align-items: center;
}
.cm-subnav-links .nav-link {
color: var(--cmg-color-link) !important;
Expand All @@ -706,6 +709,20 @@ ul.form-list li.alert-banner .co-alert {
background-color: var(--cmg-color-body-bg);
border-color: var(--cmg-color-bg-006) var(--cmg-color-bg-006) var(--cmg-color-body-bg) var(--cmg-color-bg-006);
}
.tab-count {
display: inline-block;
margin: -4px 0 -4px 0.5em;
padding: 1px 4px;
font-size: 0.9em;
border: 1px solid var(--cmg-color-link);
border-radius: 16px;
}
.tab-count-item {
display: inline-block;
}
.nav-link.active .tab-count {
border-color: var(--cmg-color-body-txt);
}
.cm-subnav-links ul.list-inline {
margin: 0.5em 0 0 0;
font-size: 0.9em;
Expand Down