diff --git a/app/resources/locales/en_US/default.po b/app/resources/locales/en_US/default.po index 4a6ba8e4..f9e95f59 100644 --- a/app/resources/locales/en_US/default.po +++ b/app/resources/locales/en_US/default.po @@ -288,6 +288,9 @@ msgstr "When this value is selected, {0} cannot be empty" msgid "match.er.input.invalid" msgstr "Invalid character found" +msgid "match.er.mg.active" +msgstr "This matchgrid is in Active status and cannot be deleted" + msgid "match.er.mg.inuse" msgstr "A Matchgrid with table name {0} already exists" @@ -571,6 +574,9 @@ msgid "match.home.welcome" msgstr "Welcome to {0}." ### Informational Messages +msgid "match.in.delete.matchgrid.info" +msgstr "The database table for this Matchgrid currently exists. You may delete it, or leave it in place. If you delete it, the table CAN NOT BE RESTORED except via database backups. Do you want to delete the physical Matchgrid table ({0})?" + msgid "match.in.matchgrid.display" msgstr "Display all records associated with this Matchgrid." @@ -638,6 +644,15 @@ msgstr "Delete" msgid "match.op.delete.confirm" msgstr "Are you sure you wish to delete this record ({0})?" +msgid "match.op.delete.matchgrid" +msgstr "Delete Matchgrid \"{0}\"" + +msgid "match.op.delete.matchgrid.leave" +msgstr "Leave the table {0} in the database" + +msgid "match.op.delete.matchgrid.remove" +msgstr "Remove the table {0} from the database" + msgid "match.op.display" msgstr "Display" diff --git a/app/src/Controller/MatchgridsController.php b/app/src/Controller/MatchgridsController.php index 550ebdf7..fc9ae378 100644 --- a/app/src/Controller/MatchgridsController.php +++ b/app/src/Controller/MatchgridsController.php @@ -31,6 +31,7 @@ use \Cake\Log\Log; use \Cake\Event\EventInterface; + use \App\Lib\Enum\PermissionEnum; use \App\Lib\Enum\MatchgridActionEnum; @@ -77,6 +78,64 @@ public function build(string $id) { return $this->redirect(['action' => 'manage', $id]); } + /** + * Handle a delete action for a Matchgrid object. + * + * @since COmanage Match v1.1.0 + * @param Integer $id Object ID + */ + + public function delete($id) { + // Deleting a Matchgrid is a bit more complicated than other objects. + + try { + // First, we'll pull the requested object. + $obj = $this->Matchgrids->findById($id)->firstOrFail(); + + // We manually apply the status check rule here, since otherwise + // it won't get applied until we actually try to remove the Matchgrid, + // which could be _after_ we remove the table, below, which is confusing. + $err = $this->Matchgrids->ruleIsActive($obj, []); + + if(is_string($err)) { + throw new \Exception($err); + } + + // See if the Matchgrid table exists. + if(!$this->Matchgrids->tableExists($obj)) { + // Just execute the delete + parent::delete($id); + } + + // Determine what the admin wants to do with the matchgrid table. + // The first time through this will be empty, so we provide the form. + $action = $this->request->getData('remove'); + + if($action === 'leave') { + // Leave the Matchgrid table in place + parent::delete($id); + } elseif($action === 'remove') { + // Remove the Matchgrid table, then execute the standard delete + $this->Matchgrids->removeTable($obj); + parent::delete($id); + } else { + // The table exists, require the admin to indicate whether or + // not the table should also be deleted. Set some view variables. + + $this->set('vv_title', __('match.op.delete.matchgrid', [$obj->table_name])); + $this->set('vv_matchgrid', $obj->prefixed_table_name); + } + } + catch(\Exception $e) { + // findById throws Cake\Datasource\Exception\RecordNotFoundException + + $this->Flash->error($e->getMessage()); + return $this->generateRedirect(); + } + + // If we get here, we need to render the form for table deletion + } + /** * Authorization for this Controller, called by Auth component * - postcondition: $vv_permissions set with calculated permissions for this Controller @@ -286,7 +345,7 @@ public function reconcile(string $id) { $this->request->getSession()->read('Auth.User.username')); } } - + $MatchService->disconnectDatabase(); // Redirect back to list of pending requests diff --git a/app/src/Controller/StandardController.php b/app/src/Controller/StandardController.php index ea6b8743..62ed4173 100644 --- a/app/src/Controller/StandardController.php +++ b/app/src/Controller/StandardController.php @@ -98,19 +98,16 @@ public function delete($id) { try { $obj = $this->$modelsName->findById($id)->firstOrFail(); - if($this->$modelsName->delete($obj)) { - // Use the display field to generate the flash message - - $field = $this->$modelsName->getDisplayField(); - - if(!empty($obj->$field)) { - $this->Flash->success(__('match.rs.deleted.a', [$obj->$field])); - } else { - $this->Flash->success(__('match.rs.deleted')); - } + $this->$modelsName->deleteOrFail($obj); + + // Use the display field to generate the flash message + + $field = $this->$modelsName->getDisplayField(); + + if(!empty($obj->$field)) { + $this->Flash->success(__('match.rs.deleted.a', [$obj->$field])); } else { - // It's hard to get a specific failure reason to render... - $this->Flash->error(__('match.er.delete')); + $this->Flash->success(__('match.rs.deleted')); } } catch(\Exception $e) { diff --git a/app/src/Lib/Match/MatchgridBuilder.php b/app/src/Lib/Match/MatchgridBuilder.php index 903d329f..104ad593 100644 --- a/app/src/Lib/Match/MatchgridBuilder.php +++ b/app/src/Lib/Match/MatchgridBuilder.php @@ -202,4 +202,49 @@ protected function configToSchema( $stmt = $dbc->executeQuery($sql); } } + + /** + * Remove the Matchgrid table. + * + * @since COmanage Match v1.1.0 + * @param EntityInterface $Matchgrid Matchgrid Object + * @return bool True on success + */ + + public function removeTable(EntityInterface $Matchgrid) { + // Connect to the database + $dbc = $this->connectDatabase(); + + // We just construct the drop statement here since it's simple and the + // table_name is constrained from having any special characters + $sql = "DROP TABLE " . $Matchgrid->prefixed_table_name; + + // $stmt just returns the query string so we don't bother examining it + $stmt = $dbc->executeQuery($sql); + + $this->disconnectDatabase(); + + return true; + } + + /** + * Determine if the Matchgrid table exists. + * + * @since COmanage Match v1.1.0 + * @param EntityInterface $Matchgrid Matchgrid Object + * @return bool True if the Matchgrid table exists, false otherwise + */ + + public function tableExists(EntityInterface $Matchgrid) { + // Connect to the database + $dbc = $this->connectDatabase(); + + $sm = $dbc->getSchemaManager(); + + $ret = $sm->tablesExist($Matchgrid->prefixed_table_name); + + $this->disconnectDatabase(); + + return $ret; + } } diff --git a/app/src/Model/Table/MatchgridsTable.php b/app/src/Model/Table/MatchgridsTable.php index 9911a4ad..260f6eb4 100644 --- a/app/src/Model/Table/MatchgridsTable.php +++ b/app/src/Model/Table/MatchgridsTable.php @@ -29,6 +29,7 @@ namespace App\Model\Table; +use \Cake\Datasource\EntityInterface; use \Cake\Event\EventInterface; use \Cake\ORM\Query; use \Cake\ORM\RulesChecker; @@ -151,6 +152,14 @@ public function buildRules(RulesChecker $rules): RulesChecker { 'isUnique', ['errorField' => 'table_name']); + // A Matchgrid cannot be deleted if it is in Active status. + // Similar to COs in Registry PE, this requires two steps to delete a CO + // reducing the likelihood of accidentally deleting a CO. + // Note this rule is also called manually in MatchgridsController::delete. + $rules->addDelete([$this, 'ruleIsActive'], + 'isActive', + ['errorField' => 'status']); + return $rules; } @@ -203,6 +212,39 @@ public function getMatchgridConfig($id) { ]]); } + /** + * Remove the Matchgrid table. + * + * @since COmanage Match v1.1.0 + * @param EntityInterface $Matchgrid Matchgrid entity + * @return bool True on success + */ + + public function removeTable(EntityInterface $Matchgrid) { + $Builder = new MatchgridBuilder(); + + return $Builder->removeTable($Matchgrid); + } + + /** + * Application Rule to determine if the current entity is not Active. + * + * @param Entity $entity Entity to be validated + * @param array $options Application rule options + * + * @return bool|string true if the Rule check passes, false otherwise + * @since COmanage Match v1.1.0 + */ + + public function ruleIsActive($entity, array $options): bool|string { + // We want negative logic since we want to fail if the record is Active + if($entity->status === StatusEnum::Active) { + return __('match.er.mg.active'); + } + + return true; + } + /** * Application Rule to determine if the current entity is uniquely named * (case insensitive). @@ -234,6 +276,20 @@ public function ruleIsUnique($entity, array $options): bool|string { return true; } + /** + * Determine if the Matchgrid table exists. + * + * @since COmanage Match v1.1.0 + * @param EntityInterface $Matchgrid Matchgrid entity + * @return bool True if the Matchgrid table exists, false otherwise + */ + + public function tableExists(EntityInterface $Matchgrid) { + $Builder = new MatchgridBuilder(); + + return $Builder->tableExists($Matchgrid); + } + /** * Set validation rules. * diff --git a/app/templates/Matchgrids/delete.php b/app/templates/Matchgrids/delete.php new file mode 100644 index 00000000..5a5990a7 --- /dev/null +++ b/app/templates/Matchgrids/delete.php @@ -0,0 +1,47 @@ + +
= __('match.in.delete.matchgrid.info', [$vv_matchgrid]); ?>
+ +Form->create(); //$vv_delete_matchgrid_form); + print $this->Form->radio('remove', + [ + ['value' => 'leave', 'text' => __('match.op.delete.matchgrid.leave', [$vv_matchgrid])], + ['value' => 'remove', 'text' => __('match.op.delete.matchgrid.remove', [$vv_matchgrid])] + ]); + print $this->Form->submit(__('match.op.confirm')); + print $this->Form->end();