Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Clean up commit for search/sort (CO-1871)
Benn Oshrin committed Sep 24, 2021
1 parent 34f1777 commit ab4917a
Showing 7 changed files with 108 additions and 114 deletions.
4 changes: 3 additions & 1 deletion app/src/Controller/StandardController.php
@@ -353,11 +353,13 @@ public function index() {
$searchableAttributes = $table->getSearchableAttributes();

if(!empty($searchableAttributes)) {
foreach($searchableAttributes as $attribute) {
foreach(array_keys($searchableAttributes) as $attribute) {
if(!empty($this->request->getQuery($attribute))) {
$query = $table->whereFilter($query, $attribute, $this->request->getQuery($attribute));
}
}

$this->set('vv_searchable_attributes', $searchableAttributes);
}
}

54 changes: 46 additions & 8 deletions app/src/Lib/Traits/SearchFilterTrait.php
@@ -33,15 +33,52 @@ trait SearchFilterTrait {
// Array (and configuration) of permitted search filters
private $searchFilters = array();

/**
* Determine the UI label for the specified attribute.
*
* @since COmanage Match v1.0.0
* @param string $attribute Attribute
* @return string Label
* @todo Merge this with _column_key from index.ctp
*/

public function getLabel($attribute) {
if(isset($this->searchFilters[$attribute]['label'])
&& $this->searchFilters[$attribute]['label'] !== null) {
return $this->searchFilters[$attribute]['label'];
}

// Try to construct a label from the language key.
$l = __('match.fd.'.$attribute);

if($l != 'match.fd.'.$attribute) {
return $l;
}

// If we make it here, just return $attribute
return $attribute;
}

/**
* Obtain the set of permitted search attributes.
*
* @since COmanage Match v1.0.0
* @return array Array of permitted search attributes
* @return array Array of permitted search attributes and configuration elements needed for display
*/

public function getSearchableAttributes() {
return array_keys($this->searchFilters);
// Not every configuration element is necessary for the search form, and
// some need to be calculated, so we do that work here.

$ret = [];

foreach(array_keys($this->searchFilters) as $attr) {
$ret[ $attr ] = [
'label' => $this->getLabel($attr)
];
}

return $ret;
}

/**
@@ -50,14 +87,15 @@ public function getSearchableAttributes() {
* @since COmanage Match v1.0.0
* @param string $attribute Attribute that filtering is permitted on (database name)
* @param bool $caseSensitive Whether this attribute is case sensitive
* @param bool $substring Whether substring searching is permitted for this attrimute
* @param string $label Label for this search field, or null to autocalculate
* @param bool $substring Whether substring searching is permitted for this attribute
*/

public function setSearchFilter(string $attribute, bool $caseSensitive=false, bool $substring=true) {
$this->searchFilters[$attribute] = [
'caseSensitive' => $caseSensitive,
'substring' => $substring
];
public function setSearchFilter(string $attribute,
bool $caseSensitive=false,
string $label=null,
bool $substring=true) {
$this->searchFilters[$attribute] = compact('caseSensitive', 'label', 'substring');
}

/**
2 changes: 1 addition & 1 deletion app/src/Locale/en_US/default.po
@@ -463,7 +463,7 @@ msgid "match.fd.select"
msgstr "(Please select a value)"

msgid "match.fd.sor"
msgstr "System of Record"
msgstr "System of Record (SOR)"

msgid "match.fd.sor.abbr"
msgstr "SOR"
12 changes: 6 additions & 6 deletions app/src/Model/Table/MatchgridRecordsTable.php
@@ -71,15 +71,15 @@ public function initialize(array $config) {

$mgconfig = $Matchgrid->getMatchgridConfig($config['matchgrid_id']);

// Always permit search on SOR, SORID, and Reference ID
$this->setSearchFilter('sor', true, null, false);
$this->setSearchFilter('sorid', true, null, false);
$this->setSearchFilter('referenceid', true, null, false);

foreach($mgconfig->attributes as $attr) {
// XXX for now we permit substring on all fields since we don't have a better way to distinguish
$this->setSearchFilter($attr->name, $attr->case_sensitive, true);
$this->setSearchFilter($attr->name, $attr->case_sensitive, null, true);
}

// Also permit search on SOR, SORID, and Reference ID
$this->setSearchFilter('sor', true, false);
$this->setSearchFilter('sorid', true, false);
$this->setSearchFilter('referenceid', true, false);
}

/**
115 changes: 39 additions & 76 deletions app/src/Template/Element/search.ctp
@@ -19,49 +19,43 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*
* @link http://www.internet2.edu/comanage COmanage Project
* @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)
*/

use \Cake\Utility\Inflector;
// $this->name = Models
$modelsName = $this->name;
// $modelName = Model
$modelName = \Cake\Utility\Inflector::singularize($modelsName);

// Globals
global $cm_lang, $cm_texts;

// Get a pointer to our controller
$controller = $this->request->getParam('controller');
$req = Inflector::singularize($controller);

// Get the query string and separate the search params from the non-search params
$query = $this->request->getQueryParams();
$non_search_params = array_diff_key($query, $vv_search_fields);
$search_params = array_intersect_key($query, $vv_search_fields);
// Begin the form
print $this->Form->create($req, [
$non_search_params = array_diff_key($query, $vv_searchable_attributes);
$search_params = array_intersect_key($query, $vv_searchable_attributes);

// Begin the form
print $this->Form->create(null, [
'id' => 'top-search-form',
'type' => 'get',
'url' => ['action' => $this->request->action]
'type' => 'get'
]);

// Pass back the non-search params as hidden fields, but always exclude the page parameter
// because we need to start new searches on page one (or we're likely to end up with a 404).
if(!empty($non_search_params)) {
foreach ($non_search_params as $param => $value) {
foreach($non_search_params as $param => $value) {
if($param != 'page') {
print $this->Form->hidden(filter_var($param, FILTER_SANITIZE_SPECIAL_CHARS), array('default' => filter_var($value, FILTER_SANITIZE_SPECIAL_CHARS))) . "\n";
}
}
}

// Boolean to distinguish between search filters and sort parameters
// XXX may no longer need this
$hasActiveFilters = false;
?>

<div id="<?php print $req . ucfirst($this->request->action); ?>Search" class="top-search">
<div id="<?= $modelName . ucfirst($this->request->getParam('action')); ?>Search" class="top-search">
<fieldset onclick="event.stopPropagation();">
<legend id="top-search-toggle">
<em class="material-icons">search</em>
@@ -71,47 +65,18 @@ $hasActiveFilters = false;
<span id="top-search-active-filters">
<?php foreach($search_params as $key => $params): ?>
<?php
// Construct aria-controls string
$key_fields = explode('.', $key);
$aria_controls = $key_fields[0] . ucfirst($key_fields[1]);
// Construct aria-controls string
$aria_controls = $key;

// We have active filters - not just a sort.
$hasActiveFilters = true;
// We have active filters - not just a sort.
$hasActiveFilters = true;
?>
<button class="top-search-active-filter deletebutton spin" type="button" aria-controls="<?php print $aria_controls; ?>" title="<?= __('match.op.clear.filters',[1]);?>">
<span class="top-search-active-filter-title"><?php print $vv_search_fields[$key]['label']; ?></span>
<span class="top-search-active-filter-title">
<?= print $vv_searchable_attributes[$key]['label']; ?>
</span>
<span class="top-search-active-filter-value">
<?php
$value = $params;
// Get user friendly name from an Enumerator Class - XXX will need updating for Match if it becomes used
// XXX How should we handle dynamic Enumerator lists?
if(isset($vv_search_fields[$key]['enum'])
&& isset($cm_texts[ $cm_lang ][$vv_search_fields[$key]['enum']][$params])) {
$value = $cm_texts[ $cm_lang ][$vv_search_fields[$key]['enum']][$params];
print filter_var($value, FILTER_SANITIZE_SPECIAL_CHARS);
continue;
}
// Get user friendly name from the dropdown Select List
// XXX Currently we do not have a use case where the grouping name would create a namespace
if (isset($vv_search_fields[$key]['options'])) {
// Outside of any groups
if (isset($vv_search_fields[$key]['options'][$value])) {
print filter_var($vv_search_fields[$key]['options'][$value], FILTER_SANITIZE_SPECIAL_CHARS);
} else {
// Inside a group
foreach(array_keys($vv_search_fields[$key]['options']) as $optgroup) {
if( is_array($vv_search_fields[$key]['options'][$optgroup])
&& isset($vv_search_fields[$key]['options'][$optgroup][$value]) ) {
print filter_var($vv_search_fields[$key]['options'][$optgroup][$value], FILTER_SANITIZE_SPECIAL_CHARS);
print $this->Html->tag('span','(' . $optgroup . ')', array('class' => 'ml-1') );
break;
}
}
}
} else {
print filter_var($value, FILTER_SANITIZE_SPECIAL_CHARS);
}
?>
<?= print filter_var($search_params[$key], FILTER_SANITIZE_SPECIAL_CHARS); ?>
</span>
</button>
<?php endforeach; ?>
@@ -127,33 +92,31 @@ $hasActiveFilters = false;
<div id="top-search-fields">
<div id="top-search-fields-subgroups">
<?php
$i = 0;
$field_subgroup_columns = array();
$skippedFields = 0;
foreach($vv_search_fields as $key => $options) {
// Exclude explicitly skipped fields
if(array_key_exists('searchSkip',$options)) {
$skippedFields++;
continue;
}
$formParams = array(
'label' => !empty($options['searchLabel']) ? $options['searchLabel'] : $options['label'],
'type' => !empty($options['searchType']) ? $options['searchType'] : 'text',

foreach($vv_searchable_attributes as $key => $options) {
$formParams = [
'label' => $options['label'],
// The default type is text, but we might convert to select below
'type' => 'text',
'value' => (!empty($query[$key]) ? $query[$key] : ''),
'required' => false,
);
if(isset($options['searchEmpty'])) {
$formParams['empty'] = $options['searchEmpty'];
}
if(isset($options['searchOptions'])) {
$formParams['options'] = $options['searchOptions'];
];

if(isset($$key)) {
// If we have an AutoViewVar matching the name of this key,
// convert to a select
$formParams['type'] = 'select';
$formParams['options'] = $$key;
// XXX we don't have a use case yet to support empty
$formParams['empty'] = false;
}

print $this->Form->input($key, $formParams);
print $this->Form->control($key, $formParams);
}
?>
</div>
<?php $rebalanceColumns = ((count($vv_search_fields)-$skippedFields) % 2 != 0) ? ' class="tss-rebalance"' : ''; ?>
<?php $rebalanceColumns = ((count($vv_searchable_attributes)) % 2 != 0) ? ' class="tss-rebalance"' : ''; ?>
<div id="top-search-submit"<?php print $rebalanceColumns ?>>
<?php
$args = array();
@@ -175,4 +138,4 @@ $hasActiveFilters = false;
</fieldset>
</div>

<?php print $this->Form->end();?>
<?= $this->Form->end(); ?>
29 changes: 10 additions & 19 deletions app/src/Template/MatchgridRecords/columns.inc
@@ -19,7 +19,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*
* @link http://www.internet2.edu/comanage COmanage Project
* @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)
@@ -30,46 +30,37 @@
$forcePrimaryLink = true;

// Turn on the search/filter box for this index view
// If $enableSearch is true, search input labels will be 'label' unless overridden by 'searchLabel'.
// If $enableSearch is true, a specific field can be excluded from the search by adding 'searchSkip' => true.
$enableSearch = true;

// Note we don't need to do any special filtering, since MatchgridRecordsController will
// force the model's table only to the one associated with the matchgrid
$indexColumns = [
'id' => [
'label' => __('match.fd.id'),
'type' => 'echo',
'searchSkip' => true
'sortable' => true
],
'sor' => [
'label' => __('match.fd.sor.abbr'),
'type' => 'echo',
'searchType' => 'select',
'searchLabel' => __('match.fd.sor'),
'searchEmpty' => __('match.fd.all'),
'searchOptions' => $sor
'type' => 'echo',
'sortable' => true
],
'sorid' => [
'label' => __('match.fd.sorid.abbr'),
'type' => 'echo',
'searchType' => 'text',
'searchLabel' => __('match.fd.sorid')
'sortable' => true
],
'referenceid' => [
'label' => __('match.fd.referenceid'),
'type' => 'echo',
'searchType' => 'text'
'sortable' => true
]
];

// Add any configured attributes to the display set
foreach($attributes as $attr) {
if($attr->index_display === true) {
$indexColumns[$attr->name] = [
'label' => $attr->name,
'type' => 'echo',
'searchType' => 'text'
'label' => $attr->name,
'type' => 'echo',
'searchType' => 'text',
'sortable' => true
];
}
}
6 changes: 3 additions & 3 deletions app/src/Template/Standard/index.ctp
@@ -123,7 +123,7 @@ function _column_key($modelsName, $c, $tz=null) {
<?php foreach($indexBanners as $b): ?>
<div class="co-info-topbox">
<em class="material-icons">info</em>
<?php print $b; ?>
<?= $b; ?>
</div>
<?php endforeach; // $indexBanners ?>
<?php endif; // $indexBanners ?>
@@ -132,14 +132,14 @@ function _column_key($modelsName, $c, $tz=null) {
<?php foreach($banners as $b): ?>
<div class="co-info-topbox">
<em class="material-icons">info</em>
<?php print $b; ?>
<?= $b; ?>
</div>
<?php endforeach; // $banners ?>
<?php endif; // $banners ?>

<!-- Search block -->
<?php if(!empty($enableSearch)): ?>
<?= $this->element('search', ['vv_search_fields' => $indexColumns]); ?>
<?= $this->element('search'); ?>
<?php endif; // $enableSearch ?>

<!-- Index table -->

0 comments on commit ab4917a

Please sign in to comment.