Permalink
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
match/app/templates/Standard/index.php
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
…ew (CO-2585) (#58)
557 lines (497 sloc)
22.2 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* COmanage Standard Index Template | |
* | |
* 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 match | |
* @since COmanage Match v1.0.0 | |
* @license Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) | |
*/ | |
/** | |
* THIS FILE IS MASTERED IN THE COMMON REPOSITORY. | |
*/ | |
declare(strict_types = 1); | |
use \Cake\Utility\Inflector; | |
use \App\Lib\Enum\StatusEnum; | |
// $this->name = Models | |
$modelsName = $this->name; | |
// $tablefk = model_id | |
$tableFK = Inflector::singularize($vv_tablename) . "_id"; | |
// Do we have records for this index? This will be set to true during render if we do. | |
// Otherwise, we'll print out a "no records" message. | |
$recordsExist = false; | |
// Our default link actions, in order of preference, unless the column config overrides it | |
$linkActions = ['edit', 'view']; | |
// Read the index configuration ($indexColumns) for this model | |
include(TEMPLATES . $modelsName . "/columns.inc"); | |
// $linkFilter is used for models that belong to a specific parent model (eg: co_id) | |
$linkFilter = []; | |
if(!empty($vv_primary_link) && !empty($this->request->getQuery($vv_primary_link))) { | |
$linkFilter = ['?' => [$vv_primary_link => $this->request->getQuery($vv_primary_link)]]; | |
} | |
function _column_key($modelsName, $c, $tz=null) { | |
if(strpos($c, "_id", strlen($c)-3)) { | |
// Key is of the form field_id, use .ct label instead | |
$k = Inflector::camelize(Inflector::pluralize(substr($c, 0, strlen($c)-3))); | |
return __('match.ct.'.$k, [1]); | |
} | |
// Look for a model specific key first | |
$label = __('match.fd.'.$modelsName.'.'.$c); | |
if($label != 'match.fd.'.$modelsName.'.'.$c) { | |
return $label; | |
} | |
if($tz) { | |
// If there is a timezone aware label, use that | |
$label = __('match.fd.'.$c.'.tz', [$tz]); | |
if($label != 'match.fd.'.$c.'.tz') { | |
return $label; | |
} | |
} | |
// Otherwise look for the general key | |
return __('match.fd.'.$c); | |
} | |
// $flashArgs pass banner messages to the flash element container | |
$flashArgs = []; | |
if(!empty($indexBanners)) { | |
$flashArgs['vv_index_banners'] = $indexBanners; | |
} | |
if(!empty($banners)) { | |
$flashArgs['vv_banners'] = $banners; | |
} | |
?> | |
<?php if(!empty($subnav)): ?> | |
<div id="subnavigation"> | |
<div class="supertitle"> | |
<h1> | |
<?php | |
if(!empty($vv_primary_link_obj) | |
&& !empty($vv_primary_link_model) | |
&& $vv_primary_link_model != 'Matchgrids') { | |
print $vv_primary_link_obj->name; | |
} | |
?> | |
</h1> | |
</div> | |
<?php /* Flash Messages are placed below supertitle when subnavigation exists. */ ?> | |
<?= $this->element('flash', $flashArgs); ?> | |
<?= $this->element('subnavigation', $subnav); ?> | |
</div> | |
<?php endif; ?> | |
<div class="pageTitleContainer"> | |
<div class="pageTitle"> | |
<?php if(empty($subnav)): ?> | |
<h1><?= $vv_title; ?></h1> | |
<?php else: ?> | |
<h2><?= $vv_title; ?></h2> | |
<?php endif; ?> | |
</div> | |
<?php if($vv_permissions['add']): ?> | |
<?php | |
// topLinks menu dropdown / button listing | |
$action_args = array(); | |
$action_args['vv_attr_mdl'] = "IndexTopLinks"; | |
// Insert additional actions as per the .inc file | |
if(!empty($topLinks)) { | |
if(!isset($entity->status) || $entity->status == StatusEnum::Active) { | |
$actionOrderDefault = $this->Menu->getMenuOrder('Default'); | |
foreach($topLinks as $a) { | |
if($vv_permissions[ $a['action'] ]) { | |
$actionOrder = !empty($a['order']) ? $a['order'] : $actionOrderDefault++; | |
$actionIcon = !empty($a['icon']) ? $a['icon'] : $this->Menu->getMenuIcon('Default'); | |
$actionClass = !empty($a['class']) ? $a['class'] : ''; | |
$actionUrl = ''; | |
$actionLabel = ''; | |
$actionOnClick = []; // used for confirmation dialog | |
// Generate the link text and urls: | |
// If we have a .confirm text, we need to generate a confirm dialog box | |
$confirmKey = 'match.op.'.$a['action'].'.confirm'; | |
$confirmTxt = __($confirmKey); | |
if($confirmTxt != $confirmKey) { | |
// We found the localized string | |
$actionPostBtnArray = ['action' => $a['action']]; | |
$actionUrl = $this->Url->build(['action' => $a['action']]); | |
// XXX should be configurable which field we put in, maybe displayField? | |
$action_args['vv_actions'][] = array( | |
'order' => $actionOrder, | |
'icon' => $actionIcon, | |
'url' => 'javascript:void(0);', | |
'label' => __('match.op.' . $a['action']), | |
'class' => !empty($actionClass) ? $actionClass . ' nospin' : 'nospin', | |
'onclick' => array( | |
'dg_bd_txt' => __($confirmKey, [$entity->id]), // dialog body text | |
'dg_post_btn_array' => $actionPostBtnArray, // postButton array for building the postButton | |
'dg_url' => $actionUrl, // action url for building a unique ID | |
'dg_conf_btn' => __('match.op.confirm'), // dialog confirm button text | |
'dg_cancel_btn' => __('match.op.cancel'), // dialog cancel button text | |
'dg_title' => __('match.op.confirm'), // dialog box title | |
'dg_bd_txt_repl_str' => '' // dialog body text replacement strings | |
), | |
); | |
} elseif(!empty($a['controller'])) { | |
// We're linking into a related controller | |
$queryParams = [ $tableFK => $entity->id ]; | |
if(!empty($a['query']) && is_callable($a['query'])) { | |
$queryParams = $a['query']($vv_cur_mg->id, $entity); | |
} | |
$actionLabel = __('match.ct.' . Inflector::camelize(Inflector::pluralize($a['controller'])), [99]); | |
$actionUrl = $this->Url->build([ | |
'controller' => $a['controller'], | |
'action' => $a['action'], | |
'?' => $queryParams | |
]); | |
} else { | |
$actionLabel = __('match.op.' . $a['action']); | |
$urlAttrs = ['action' => $a['action']]; | |
$urlAttrs = array_merge_recursive($linkFilter, $urlAttrs); | |
if(!empty($a['query'])) { | |
$urlAttrs = array_merge_recursive($urlAttrs,$a['query']); | |
} | |
$actionUrl = $this->Url->build($urlAttrs); | |
} | |
// If a specific label is sent in the config, use it instead | |
if(!empty($a['label'])) { | |
$actionLabel = $a['label']; | |
} | |
// Set the action link configuration | |
$action_args['vv_actions'][] = array( | |
'order' => $actionOrder, | |
'icon' => $actionIcon, | |
'url' => $actionUrl, | |
'label' => $actionLabel, | |
'class' => $actionClass, | |
'onclick' => $actionOnClick | |
); | |
} | |
} | |
} | |
} | |
// Add | |
$addLink = array_merge($linkFilter, ['action' => 'add']); | |
// Allow model-specific overrides of the add link. If false, | |
// the override doesn't want the add link to render. | |
if(isset($addLinkFilter) && is_callable($addLinkFilter)) { | |
$addLink = $addLinkFilter($addLink, $this->request); | |
} | |
if($addLink) { | |
$action_args['vv_actions'][] = array( | |
'order' => $this->Menu->getMenuOrder('Add'), | |
'icon' => $this->Menu->getMenuIcon('Add'), | |
'url' => $this->Url->build($addLink), | |
'label' => __('match.op.add.a', __('match.ct.'.$vv_modelname, [1])) | |
); | |
} | |
// Output the topLinks | |
if(!empty($action_args['vv_actions'])) { | |
print '<div id="topLinks" class="field-actions">'; | |
print $this->element('menuAction', $action_args); | |
print '</div>'; | |
} | |
?> | |
<?php endif; ?> | |
</div> | |
<?php if(empty($subnav)): ?> | |
<?php /* Flash Messages are placed below the main title when there's no subnavigation. */ ?> | |
<?= $this->element('flash', $flashArgs); ?> | |
<?php endif; ?> | |
<!-- Search block --> | |
<?php if(!empty($enableSearch)): ?> | |
<?= $this->element('search'); ?> | |
<?php endif; // $enableSearch ?> | |
<!-- Index table --> | |
<div class="table-container"> | |
<table id="<?= $vv_tablename . '-table'; ?>"> | |
<tr> | |
<?php foreach($indexColumns as $col => $cfg): ?> | |
<th<?= !empty($cfg['cssClass']) ? ' class="' . $cfg['cssClass'] . '"' : ''; ?>><?php | |
$label = !empty($cfg['label']) ? $cfg['label'] : _column_key($modelsName, $col, $vv_tz); | |
if(isset($cfg['sortable']) && $cfg['sortable']) { | |
if(is_string($cfg['sortable'])) { | |
print $this->Paginator->sort($cfg['sortable'], $label); | |
} else { | |
print $this->Paginator->sort($col, $label); | |
} | |
} else { | |
print $label; | |
} | |
?></th> | |
<?php endforeach; ?> | |
<th class="actions"><?= __('match.fd.action'); ?></th> | |
</tr> | |
<?php foreach(${$vv_tablename} as $entity): ?> | |
<?php | |
// Normally links just require the entity ID, but sometimes we need to | |
// explicitly add the linkFilter | |
$linkArgs = [$entity->id]; | |
if(isset($forcePrimaryLink) && $forcePrimaryLink) { | |
$linkArgs = array_merge_recursive($linkArgs, $linkFilter); | |
} | |
?> | |
<tr> | |
<?php foreach($indexColumns as $col => $cfg): ?> | |
<td<?= !empty($cfg['cssClass']) ? ' class="' . $cfg['cssClass'] . '"' : ''; ?>> | |
<?php | |
switch($cfg['type']) { | |
case 'boolean': | |
if(!empty($entity->$col) && $entity->$col) { | |
print __('match.en.'.$cfg['class'].'.1'); | |
} else { | |
print __('match.en.'.$cfg['class'].'.0'); | |
} | |
break; | |
case 'datetime': | |
// XXX dates can also be rendered as eg $entity->created->format(DATE_RFC850); | |
print $this->Time->nice($entity->$col, $vv_tz); | |
break; | |
case 'enum': | |
if($entity->$col) { | |
$badge_color_class = __('match.en.'.$cfg['class'].'.'.$entity->$col . '.badge'); | |
$title = __('match.en.'.$cfg['class'].'.'.$entity->$col); | |
if(!empty($badge_color_class) && strpos($badge_color_class, '.badge') === false) { | |
print $this->Badge->badgeIt($title, $this->Badge->getBadgeColor($badge_color_class)); | |
} else { | |
print $title; | |
} | |
} | |
break; | |
case 'fk': | |
// Assuming $col is of the form foo_id, look to see if the corresponding | |
// AutoViewVar $foos is set, and if so render the lookup value instead | |
$f = null; | |
if(preg_match('/^(.*?)_id$/', $col, $f)) { | |
$avv = Inflector::variable(Inflector::pluralize($f[1])); | |
if(!empty(${$avv}[$entity->$col])) { | |
// We found the viewvar (eg: $foos), and it has a corresponding value | |
// (eg: $foos[3]), so render it | |
print ${$avv}[$entity->$col]; // XXX filter_var? | |
} else { | |
// No match, just render the value | |
print $entity->$col; | |
} | |
} else { | |
// Just print the value | |
print $entity->$col; | |
} | |
break; | |
case 'button': | |
if(!empty($entity->$col)) { | |
$buttonAttrs = []; | |
$buttonText = $entity->$col; | |
if(!empty($cfg['button']['attrs'])) { | |
$buttonAttrs = $cfg['button']['attrs']; | |
} | |
$buttonAttrs['type'] = 'button'; | |
if(!empty($cfg['button']['text']) && $cfg['button']['text'] != 'fieldVal') { | |
$buttonText = $cfg['button']['text']; | |
} | |
if(!empty($cfg['truncate']) && is_int($cfg['truncate'])) { | |
// We check for $truncate + 1 because there's no point trimming | |
// the last character if we're just going to replace it with ... | |
$buttonText = (strlen($buttonText) > $cfg['truncate'] + 1) ? substr($buttonText,0,$cfg['truncate']).'...' : $buttonText; | |
} | |
if(!empty($cfg['button']['popover'])) { | |
if($cfg['button']['popover'] == 'fieldVal') { | |
$buttonAttrs['data-bs-content'] = $entity->$col; | |
} else { | |
$buttonAttrs['data-bs-content'] = $cfg['button']['popover']; | |
} | |
$label = !empty($cfg['label']) ? $cfg['label'] : _column_key($modelsName, $col, $vv_tz); | |
$buttonAttrs['title'] = $label; | |
$buttonAttrs['data-bs-toggle'] = 'popover'; | |
$buttonAttrs['data-bs-container'] = 'body'; | |
$buttonAttrs['data-bs-placement'] = 'top'; | |
$buttonAttrs['data-bs-animation'] = 'false'; | |
} | |
print $this->Form->button($buttonText, $buttonAttrs); | |
} | |
break; | |
case 'link': | |
case 'echo': | |
default: | |
// By default our label is the column value, but it might be overridden | |
$label = $entity->$col; | |
if(!empty($cfg['model']) && !empty($cfg['field'])) { | |
$m = $cfg['model']; | |
$f = $cfg['field']; | |
if(!empty($entity->$m->$f)) { | |
$label = $entity->$m->$f; | |
} | |
} | |
$linked = false; | |
if($cfg['type'] == 'link') { | |
foreach($linkActions as $a) { | |
// Does this user have permission for this action? | |
if($vv_permissions[$a]) { | |
print $this->Html->link($label, array_merge_recursive(['action' => $a], $linkArgs)); | |
$linked = true; | |
break 2; | |
} | |
} | |
} | |
if(!$linked) { | |
// Just echo the value | |
print $label; | |
} | |
break; | |
// XXX dates can be rendered as eg $entity->created->format(DATE_RFC850); | |
} | |
?> | |
</td> | |
<?php endforeach; // $indexColumns ?> | |
<td class="actions"> | |
<?php | |
// Action list for command menu dropdown / button listing | |
$action_args = array(); | |
$action_args['vv_attr_mdl'] = "Index"; | |
$action_args['vv_attr_id'] = $entity->id; | |
// Edit | |
if($vv_permissions['edit']) { | |
$action_args['vv_actions'][] = array( | |
'order' => $this->Menu->getMenuOrder('Edit'), | |
'icon' => $this->Menu->getMenuIcon('Edit'), | |
'url' => $this->Url->build(array_merge_recursive(['action' => 'edit'], $linkArgs)), | |
'label' => __('match.op.edit') | |
); | |
} elseif($vv_permissions['view']) { | |
$action_args['vv_actions'][] = array( | |
'order' => $this->Menu->getMenuOrder('View'), | |
'icon' => $this->Menu->getMenuIcon('View'), | |
'url' => $this->Url->build(array_merge_recursive(['action' => 'view'], $linkArgs)), | |
'label' => __('match.op.view') | |
); | |
} | |
// Duplicate | |
if(isset($vv_permissions['duplicate']) && $vv_permissions['duplicate']) { | |
$action_args['vv_actions'][] = array( | |
'order' => $this->Menu->getMenuOrder('Duplicate'), | |
'icon' => $this->Menu->getMenuIcon('Duplicate'), | |
'url' => $this->Url->build(array_merge_recursive(['action' => 'duplicate'], $linkArgs)), | |
'label' => __('match.op.duplicate') | |
); | |
} | |
// Insert additional actions as per the .inc file | |
if(!empty($indexActions)) { | |
if(!isset($entity->status) || $entity->status == StatusEnum::Active) { | |
$actionOrderDefault = $this->Menu->getMenuOrder('Default'); | |
foreach($indexActions as $a) { | |
if($vv_permissions[ $a['action'] ]) { | |
$actionOrder = !empty($a['order']) ? $a['order'] : $actionOrderDefault++; | |
$actionIcon = !empty($a['icon']) ? $a['icon'] : $this->Menu->getMenuIcon('Default'); | |
$actionClass = !empty($a['class']) ? $a['class'] : ''; | |
$actionUrl = ''; | |
$actionLabel = ''; | |
$actionOnClick = []; // used for confirmation dialog | |
// Generate the link text and urls: | |
// If we have a .confirm text, we need to generate a confirm dialog box | |
$confirmKey = 'match.op.'.$a['action'].'.confirm'; | |
$confirmTxt = __($confirmKey); | |
if($confirmTxt != $confirmKey) { | |
// We found the localized string | |
$actionPostBtnArray = array_merge_recursive(['action' => $a['action']], $linkArgs); | |
$actionUrl = $this->Url->build(array_merge_recursive(['action' => $a['action']], $linkArgs)); | |
// XXX should be configurable which field we put in, maybe displayField? | |
$action_args['vv_actions'][] = array( | |
'order' => $actionOrder, | |
'icon' => $actionIcon, | |
'url' => 'javascript:void(0);', | |
'label' => __('match.op.' . $a['action']), | |
'class' => !empty($actionClass) ? $actionClass . ' nospin' : 'nospin', | |
'onclick' => array( | |
'dg_bd_txt' => __($confirmKey, [$entity->id]), // dialog body text | |
'dg_post_btn_array' => $actionPostBtnArray, // postButton array for building the postButton | |
'dg_url' => $actionUrl, // action url for building a unique ID | |
'dg_conf_btn' => __('match.op.confirm'), // dialog confirm button text | |
'dg_cancel_btn' => __('match.op.cancel'), // dialog cancel button text | |
'dg_title' => __('match.op.confirm'), // dialog box title | |
'dg_bd_txt_repl_str' => '' // dialog body text replacement strings | |
), | |
); | |
} elseif(!empty($a['controller'])) { | |
// We're linking into a related controller | |
$queryParams = [ $tableFK => $entity->id ]; | |
if(!empty($a['query']) && is_callable($a['query'])) { | |
$queryParams = $a['query']($vv_cur_mg->id, $entity); | |
} | |
$actionLabel = __('match.ct.' . Inflector::camelize(Inflector::pluralize($a['controller'])), [99]); | |
$actionUrl = $this->Url->build([ | |
'controller' => $a['controller'], | |
'action' => $a['action'], | |
'?' => $queryParams | |
]); | |
} else { | |
$actionLabel = __('match.op.' . $a['action']); | |
$actionUrl = $this->Url->build( | |
array_merge_recursive(['action' => $a['action']], $linkArgs) | |
); | |
} | |
// If a specific label is sent in the config, use it instead | |
if(!empty($a['label'])) { | |
$actionLabel = $a['label']; | |
} | |
// Set the action link configuration | |
$action_args['vv_actions'][] = array( | |
'order' => $actionOrder, | |
'icon' => $actionIcon, | |
'url' => $actionUrl, | |
'label' => $actionLabel, | |
'class' => $actionClass, | |
'onclick' => $actionOnClick | |
); | |
} | |
} | |
} | |
} | |
// Delete | |
if($vv_permissions['delete']) { | |
$actionPostBtnArray = array_merge_recursive(['action' => 'delete'], $linkArgs); | |
$actionUrl = $this->Url->build(array_merge_recursive(['action' => 'delete'], $linkArgs)); | |
$action_args['vv_actions'][] = array( | |
'order' => $this->Menu->getMenuOrder('Delete'), | |
'icon' => $this->Menu->getMenuIcon('Delete'), | |
'url' => 'javascript:void(0);', | |
'label' => __('match.op.delete'), | |
'class' => 'deletebutton nospin', | |
'onclick' => array( | |
'dg_bd_txt' => __('match.op.delete.confirm', [$entity->id]), | |
'dg_post_btn_array' => $actionPostBtnArray, | |
'dg_url' => $actionUrl, | |
'dg_conf_btn' => __('match.op.remove'), | |
'dg_cancel_btn' => __('match.op.cancel'), | |
'dg_title' => __('match.op.remove'), | |
'dg_bd_txt_repl_str' => '' | |
), | |
); | |
} | |
if(!empty($action_args['vv_actions'])) { | |
print '<div class="field-actions">'; | |
print $this->element('menuAction', $action_args); | |
print '</div>'; | |
} | |
?> | |
</td> | |
</tr> | |
<?php $recordsExist = true; ?> | |
<?php endforeach; // $$vv_tablename ?> | |
<?php if(!$recordsExist): ?> | |
<tr><td colspan="<?= count($indexColumns); ?>"><?= __('match.in.records.none') ?></td></tr> | |
<?php endif; ?> | |
</table> | |
</div> | |
<?php | |
print $this->element("pagination"); |