From 77eebadcfb7a1de4c4b75cdde0428677f214b76e Mon Sep 17 00:00:00 2001 From: Benn Oshrin Date: Sun, 23 Jun 2019 05:36:40 -0400 Subject: [PATCH] Implement pagination (CO-1764) --- app/src/Controller/ApiUsersController.php | 6 + app/src/Controller/AppController.php | 2 +- .../Controller/AttributeGroupsController.php | 6 + app/src/Controller/AttributesController.php | 6 + app/src/Controller/PermissionsController.php | 7 + app/src/Controller/RulesController.php | 7 + app/src/Controller/StandardController.php | 2 +- app/src/Locale/en_US/default.po | 30 ++++ app/src/Template/Element/pagination.ctp | 109 ++++++++++++ app/src/Template/Permissions/columns.inc | 2 +- app/src/Template/Standard/index.ctp | 156 ++++++++++++++---- 11 files changed, 294 insertions(+), 39 deletions(-) create mode 100644 app/src/Template/Element/pagination.ctp diff --git a/app/src/Controller/ApiUsersController.php b/app/src/Controller/ApiUsersController.php index 91ab144fa..0554915f5 100644 --- a/app/src/Controller/ApiUsersController.php +++ b/app/src/Controller/ApiUsersController.php @@ -32,6 +32,12 @@ use Cake\Routing\Router; class ApiUsersController extends StandardController { + public $paginate = [ + 'order' => [ + 'ApiUsers.username' => 'asc' + ] + ]; + /** * Callback run prior to the view rendering. * diff --git a/app/src/Controller/AppController.php b/app/src/Controller/AppController.php index 0e02ae767..c30b2c00e 100644 --- a/app/src/Controller/AppController.php +++ b/app/src/Controller/AppController.php @@ -121,7 +121,7 @@ public function beforeRender(\Cake\Event\Event $event) { // Available Matchgrids $this->loadModel('Matchgrids'); - $this->set('vv_matchgrids', $this->Matchgrids->find('list')->find('activeMatchGrids')->toArray()); + $this->set('vv_matchgrids', $this->Matchgrids->find('list')->find('activeMatchGrids')->order(['table_name' => 'ASC'])->toArray()); // The set of menu permissions, so the layout knows what to render if($this->Authorization && $curUser) { diff --git a/app/src/Controller/AttributeGroupsController.php b/app/src/Controller/AttributeGroupsController.php index 5b954b32a..9949ba460 100644 --- a/app/src/Controller/AttributeGroupsController.php +++ b/app/src/Controller/AttributeGroupsController.php @@ -30,6 +30,12 @@ namespace App\Controller; class AttributeGroupsController extends StandardController { + public $paginate = [ + 'order' => [ + 'AttributeGroups.name' => 'asc' + ] + ]; + /** * Authorization for this Controller, called by Auth component * - postcondition: $vv_permissions set with calculated permissions for this Controller diff --git a/app/src/Controller/AttributesController.php b/app/src/Controller/AttributesController.php index ea193240c..8352592a0 100644 --- a/app/src/Controller/AttributesController.php +++ b/app/src/Controller/AttributesController.php @@ -30,6 +30,12 @@ namespace App\Controller; class AttributesController extends StandardController { + public $paginate = [ + 'order' => [ + 'Attributes.name' => 'asc' + ] + ]; + /** * Authorization for this Controller, called by Auth component * - postcondition: $vv_permissions set with calculated permissions for this Controller diff --git a/app/src/Controller/PermissionsController.php b/app/src/Controller/PermissionsController.php index 52a596236..fe7dc21e1 100644 --- a/app/src/Controller/PermissionsController.php +++ b/app/src/Controller/PermissionsController.php @@ -30,6 +30,13 @@ namespace App\Controller; class PermissionsController extends StandardController { + public $paginate = [ + 'order' => [ + 'Matchgrids.table_name' => 'asc', + 'Permissions.name' => 'asc' + ] + ]; + /** * Authorization for this Controller, called by Auth component * - postcondition: $vv_permissions set with calculated permissions for this Controller diff --git a/app/src/Controller/RulesController.php b/app/src/Controller/RulesController.php index 4b9397c25..cbbaa172b 100644 --- a/app/src/Controller/RulesController.php +++ b/app/src/Controller/RulesController.php @@ -30,6 +30,13 @@ namespace App\Controller; class RulesController extends StandardController { + public $paginate = [ + 'order' => [ + 'Rules.confidence_mode' => 'asc', + 'Rules.ordr' => 'asc' + ] + ]; + /** * Authorization for this Controller, called by Auth component * - postcondition: $vv_permissions set with calculated permissions for this Controller diff --git a/app/src/Controller/StandardController.php b/app/src/Controller/StandardController.php index 6d20f08ea..cc85f3581 100644 --- a/app/src/Controller/StandardController.php +++ b/app/src/Controller/StandardController.php @@ -287,7 +287,7 @@ public function index() { $query = $this->$modelsName->find(); } - $this->set($tableName, $this->Paginator->paginate($query)); + $this->set($tableName, $this->Paginator->paginate($query, $this->paginate)); // Default index view title is model name $this->set('vv_title', __('match.ct.'.$tableName, [99])); diff --git a/app/src/Locale/en_US/default.po b/app/src/Locale/en_US/default.po index 65ae30ff3..3007b8432 100644 --- a/app/src/Locale/en_US/default.po +++ b/app/src/Locale/en_US/default.po @@ -182,6 +182,12 @@ msgstr "Invalid format" msgid "match.er.mgid" msgstr "Could not find Matchgrid ID in request" +msgid "match.er.pagenum.exceeded" +msgstr "Page number may not be larger than {0}" + +msgid "match.er.pagenum.nan" +msgstr "Page number must be an integer" + msgid "match.er.parse.json" msgstr "Unknown type {0} for key {1} (parseFromJSON)" @@ -330,6 +336,9 @@ msgstr "Welcome to {0}." msgid "match.in.matchgrids.none" msgstr "There are no matchgrids currently defined." +msgid "match.in.pagination.format" +msgstr "Page {{page}} of {{pages}}, Viewing {{start}}-{{end}} of {{count}}" + ### Menu Items msgid "match.me.platform" msgstr "Platform" @@ -356,6 +365,15 @@ msgstr "Edit" msgid "match.op.edit.a" msgstr "Edit {0}" +msgid "match.op.first" +msgstr "First" + +msgid "match.op.go" +msgstr "Go" + +msgid "match.op.last" +msgstr "Last" + msgid "match.op.login" msgstr "Login" @@ -371,6 +389,18 @@ msgstr "Manage {0}" msgid "match.op.new" msgstr "New" +msgid "match.op.next" +msgstr "Next" + +msgid "match.op.page.display" +msgstr "Display" + +msgid "match.op.page.goto" +msgstr "Go To Page" + +msgid "match.op.previous" +msgstr "Previous" + msgid "match.op.reconcile" msgstr "Reconcile Unresolved Requests" diff --git a/app/src/Template/Element/pagination.ctp b/app/src/Template/Element/pagination.ctp new file mode 100644 index 000000000..c894efb16 --- /dev/null +++ b/app/src/Template/Element/pagination.ctp @@ -0,0 +1,109 @@ + + + + diff --git a/app/src/Template/Permissions/columns.inc b/app/src/Template/Permissions/columns.inc index 9be86bcc1..1c65d1557 100644 --- a/app/src/Template/Permissions/columns.inc +++ b/app/src/Template/Permissions/columns.inc @@ -26,10 +26,10 @@ */ $indexColumns = [ + 'matchgrid_id' => [ 'type' => 'fk' ], 'username' => [ 'type' => 'link' ], - 'matchgrid_id' => [ 'type' => 'fk' ], 'permission' => [ 'type' => 'enum', 'class' => 'PermissionEnum' diff --git a/app/src/Template/Standard/index.ctp b/app/src/Template/Standard/index.ctp index 95b8464bd..23962e730 100644 --- a/app/src/Template/Standard/index.ctp +++ b/app/src/Template/Standard/index.ctp @@ -1,6 +1,6 @@ name = Models $modelsName = $this->name; // $tablename = models -$tableName = \Cake\Utility\Inflector::tableize($this->name); +$tableName = \Cake\Utility\Inflector::tableize(\Cake\Utility\Inflector::singularize($this->name)); +if($this->name == 'SystemsOfRecord') { + // There's something wacky going on with Inflector::singularize, returning 'S' instead of + // 'SystemOfRecord'. (The internal cache appears to be getting corrupt somehow, but it's + // not clear how.) As a simple but inelegant workaround, we fix the table name here. + $tableName = 'systems_of_record'; +} +// $tablefk = model_id +$tableFK = \Cake\Utility\Inflector::singularize($tableName) . "_id"; // Our default link action is edit, unless the model config overrides it $primaryAction = 'edit'; @@ -40,30 +55,41 @@ $primaryAction = 'edit'; // Read the index configuration ($indexColumns) for this model include(APP . "Template/" . $modelsName . "/columns.inc"); -// $linkFilter is used for models that belong to a specific parent model (eg: matchgrid_id) +// $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) { +function _column_key($modelsName, $c, $tz=null) { + $product = __('product.code'); + if(strpos($c, "_id", strlen($c)-3)) { // Key is of the form field_id, use .ct label instead $k = \Cake\Utility\Inflector::pluralize(substr($c, 0, strlen($c)-3)); - return __('match.ct.'.$k, [1]); + return __($product.'.ct.'.$k, [1]); } // Look for a model specific key first - $label = __('match.fd.'.$modelsName.'.'.$c); + $label = __($product.'.fd.'.$modelsName.'.'.$c); - if($label != 'match.fd.'.$modelsName.'.'.$c) { + if($label != $product.'.fd.'.$modelsName.'.'.$c) { return $label; } + if($tz) { + // If there is a timezone aware label, use that + $label = __($product.'.fd.'.$c.'.tz', [$tz]); + + if($label != $product.'.fd.'.$c.'.tz') { + return $label; + } + } + // Otherwise look for the general key - return __('match.fd.'.$c); + return __($product.'.fd.'.$c); } ?>
@@ -71,6 +97,17 @@ function _column_key($modelsName, $c) {

+ +
+ info + +
+ + - -
- info - -
- + + +
+ info + +
+ +
$cfg): ?> - + - + - + $cfg): ?> @@ -145,18 +215,20 @@ function _column_key($modelsName, $c) { Html->link( - __('match.op.edit'), + __($product.'.op.edit'), ['action' => 'edit', $entity->id], ['class' => 'editbutton'] ); } if($vv_permissions['delete']) { + // XXX this is throwing CSRF error even though delete button on edit-record page is working? + // probably because this is using Form helper, but we're outside of a form? print $this->Form->postLink( - __('match.op.delete'), + __($product.'.op.delete'), ['action' => 'delete', $entity->id], - // XXX should be configurable which field we put in, maybe displayField? - ['confirm' => __('match.op.delete.confirm', [$entity->id]), + // XXX should be configurable which field we put in, maybe displayField? + ['confirm' => __($product.'.op.delete.confirm', [$entity->id]), 'class' => 'deletebutton'] ); } @@ -164,27 +236,36 @@ function _column_key($modelsName, $c) { if(!empty($indexActions)) { // Insert additional actions as per the .inc file - if(isset($entity->status) && $entity->status == StatusEnum::Active) { + if(!isset($entity->status) || $entity->status == StatusEnum::Active) { foreach($indexActions as $a) { if($vv_permissions[ $a['action'] ]) { // If we have a .confirm text, use postLink instead - $confirmKey = 'match.op.'.$a['action'].'.confirm'; + $confirmKey = $product.'.op.'.$a['action'].'.confirm'; $confirmTxt = __($confirmKey); if($confirmTxt != $confirmKey) { // We found the localized string print $this->Form->postLink( - __('match.op.' . $a['action']), + __($product.'.op.' . $a['action']), ['action' => $a['action'], $entity->id], - // XXX should be configurable which field we put in, maybe displayField? + // XXX should be configurable which field we put in, maybe displayField? ['confirm' => __($confirmKey, [$entity->id]), 'class' => $a['class']] ); + } elseif(!empty($a['controller'])) { + // We're linking into a related controller + print $this->Html->link( + __('match.ct.' . $a['controller'], [99]), + ['controller' => $a['controller'], + 'action' => $a['action'], + $tableFK => $entity->id], + ['class' => $a['class']] + ); } else { print $this->Html->link( - __('match.op.' . $a['action']), + __($product.'.op.' . $a['action']), ['action' => $a['action'], $entity->id], ['class' => $a['class']] ); @@ -198,4 +279,7 @@ function _column_key($modelsName, $c) {
Paginator->sort($col, $label); + } else { + print $label; + } + ?>
$col) && $entity->$col) { + print __($product.'.en.'.$cfg['class'].'.1'); + } else { + print __($product.'.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': - print __('match.en.'.$cfg['class'].'.'.$entity->$col); + if($entity->$col) { + print __($product.'.en.'.$cfg['class'].'.'.$entity->$col); + } break; case 'fk': // Assuming $col is of the form foo_id, look to see if the corresponding @@ -116,7 +187,7 @@ function _column_key($modelsName, $c) { $avv = \Cake\Utility\Inflector::variable(\Cake\Utility\Inflector::pluralize($f[1])); if(!empty(${$avv}[$entity->$col])) { - // We found the viewar (eg: $foos), and it has a corresponding value + // 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 { @@ -136,7 +207,6 @@ function _column_key($modelsName, $c) { // Just echo the value print $entity->$col; break; - // XXX dates can be rendered as eg $entity->created->format(DATE_RFC850); } ?>
-
\ No newline at end of file + + +element("pagination"); \ No newline at end of file