Skip to content

Commit

Permalink
Implement attribute mappings (CO-1759)
Browse files Browse the repository at this point in the history
  • Loading branch information
Benn Oshrin committed Jun 23, 2019
1 parent 8fcd59a commit e5ad581
Show file tree
Hide file tree
Showing 28 changed files with 2,423 additions and 51 deletions.
37 changes: 36 additions & 1 deletion app/config/schema/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"comment": "columns with names matching those defined here will by default inherit these properties",

"columns": {
"attribute_map_id": { "type": "integer", "foreignkey": { "table": "attribute_maps", "column": "id" } },
"description": { "type": "string", "size": 128 },
"id": { "type": "integer", "autoincrement": true, "primarykey": true },
"matchgrid_id": { "type": "integer", "foreignkey": { "table": "matchgrids", "column": "id" }, "notnull": true },
Expand Down Expand Up @@ -68,6 +69,39 @@
"changelog": false
},

"attribute_maps": {
"columns": {
"id": {},
"matchgrid_id": {},
"name": {},
"description": {}
},
"indexes": {
"attribute_maps_i1": {
"columns": [ "matchgrid_id" ]
}
},
"changelog": false
},

"attribute_mappings": {
"columns": {
"id": {},
"attribute_map_id": {},
"query": { "type": "string", "size": 80 },
"value": { "type": "string", "size": 80 }
},
"indexes": {
"attribute_mappings_i1": {
"columns": [ "attribute_map_id" ]
},
"attribute_mappings_i2": {
"columns": [ "query" ]
}
},
"changelog": false
},

"attribute_groups": {
"columns": {
"id": {},
Expand Down Expand Up @@ -98,7 +132,8 @@
"search_distance": { "type": "integer" },
"search_exact": { "type": "boolean" },
"search_substr_from": { "type": "integer" },
"search_substr_for": { "type": "integer" }
"search_substr_for": { "type": "integer" },
"attribute_map_id": {}
},
"indexes": {
"attributes_i1": {
Expand Down
94 changes: 94 additions & 0 deletions app/src/Controller/AppController.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,57 @@
use Cake\Datasource\Exception;
use Cake\Datasource\Exception\RecordNotFoundException;
use Cake\Event\Event;
use Cake\ORM\TableRegistry;
use InvalidArgumentException;

class AppController extends Controller {
// If set, the current requested Matchgrid ID. Note this may be *unauthenticated*
// and so should not be trusted without further authorization.
protected $cur_mg = null;

/**
* Obtain information about the Standard Object's Primary Link, if set.
* The $vv_primary_link view variable is also set.
*
* @since COmanage Match v1.0.0
* @param boolean $lookup If true, get the value of the primary link, not just the attribute
* @return array Array holding the primary link attribute, and optionally its value
* @throws \RuntimeException
*/

protected function getPrimaryLink(bool $lookup=false) {
// $this->name = Models
$modelsName = $this->name;
// $modelName = Model
$modelName = \Cake\Utility\Inflector::singularize($this->name);

$ret = [];

// PrimaryLinkTrait
if(method_exists($this->$modelsName, "getPrimaryLink")
&& $this->$modelsName->getPrimaryLink()) {
$ret['linkattr'] = $this->$modelsName->getPrimaryLink();
$this->set('vv_primary_link', $ret['linkattr']);

if($lookup) {
// Try to find a value
if($this->request->getQuery($ret['linkattr'])) {
$ret['linkvalue'] = $this->request->getQuery($ret['linkattr']);
} elseif($this->request->getData($ret['linkattr'])) {
$ret['linkvalue'] = $this->request->getData($ret['linkattr']);
} elseif($this->request->getData($modelName . "." . $ret['linkattr'])) {
$ret['linkvalue'] = $this->request->getData($modelName . "." . $ret['linkattr']);
} else {
if(!$this->$modelsName->allowEmptyPrimaryLink()) {
throw new \RuntimeException(__('match.er.primary_link', [ $ret['linkattr'] ]));
}
}
}
}

return $ret;
}

/**
* Initialization callback.
*
Expand Down Expand Up @@ -93,6 +137,9 @@ public function initialize() {

public function beforeFilter(\Cake\Event\Event $event) {
parent::beforeFilter($event);

// Determine the timezone
$this->setTZ();

// Determine the requested Matchgrid
$this->setMatchgrid();
Expand Down Expand Up @@ -184,6 +231,22 @@ protected function setMatchgrid() {
}
}

if(!$mgid) {
// Use the primary link (if found) to calculate the matchgrid ID

// PrimaryLinkTrait
$link = $this->getPrimaryLink(true);

if(!empty($link['linkvalue'])) {
$m = \Cake\Utility\Inflector::pluralize(
\Cake\Utility\Inflector::camelize(
substr($link['linkattr'], 0, strlen($link['linkattr'])-3)));

$linkModel = TableRegistry::get($m);
$mgid = $linkModel->calculateMatchgridId((int)$link['linkvalue']);
}
}

if(!$mgid && !$this->$modelsName->allowEmptyMatchgrid()) {
// If we get this far without a Matchgrid ID, something went wrong.
throw new \RuntimeException(__('match.er.mgid'));
Expand All @@ -198,4 +261,35 @@ protected function setMatchgrid() {
$this->set('vv_cur_mg', $this->cur_mg);
}
}

/**
* Determine the current timezone and make it available to the
* rest of the application.
*
* @since COmanage Match v1.0.0
*/

protected function setTZ() {
// $this->name = Models
$modelsName = $this->name;

// See if we've collected it from the browser in a previous page load. Otherwise
// use the system default. If the user set a preferred timezone, we'll catch that below.

$tz = date_default_timezone_get();

if(!empty($_COOKIE['cm_match_tz_auto'])) {
// We have an auto-detected timezone from a previous page render from the browser.
// Note we don't call date_default_timezone_set() because we still want to record
// times internally in UTC (at the expense of having to convert back and forth).
$tz = $_COOKIE['cm_match_tz_auto'];
}

$this->set('vv_tz', $tz);

if($this->$modelsName->behaviors()->has('Timezone')) {
// Tell TimezoneBehavior what the current timezone is
$this->$modelsName->setTimeZone($tz);
}
}
}
86 changes: 86 additions & 0 deletions app/src/Controller/AttributeMappingsController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
<?php
/**
* COmanage Match Attribute Mappings Controller
*
* 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 http://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)
*/

declare(strict_types = 1);

namespace App\Controller;

class AttributeMappingsController extends StandardController {
public $paginate = [
'order' => [
'AttributeMappings.query' => 'asc',
'AttributeMappings.value' => 'asc'
]
];

/**
* Handle an edit action for a Standard object.
*
* @since COmanage Match v1.0.0
*/

public function install() {
try {
$this->AttributeMappings->install((int)$this->request->getQuery('attribute_map_id'),
$this->request->getQuery('mapping'));

$this->Flash->success(__('match.rs.AttributeMappings.install'));
}
catch(Exception $e) {
$this->Flash->error($e->getMessage());
}

return $this->generateRedirect();
}

/**
* Authorization for this Controller, called by Auth component
* - postcondition: $vv_permissions set with calculated permissions for this Controller
*
* @since COmanage Match v1.0.0
* @param Array $user Array of user data
* @return Boolean True if authorized for the current action, false otherwise
*/

public function isAuthorized(Array $user) {
$platformAdmin = $this->Authorization->isPlatformAdmin($user['username']);

$mgAdmin = $this->Authorization->isMatchAdmin($user['username'], $this->mgid);

$p = [
'add' => $platformAdmin || $mgAdmin,
'delete' => $platformAdmin || $mgAdmin,
'edit' => $platformAdmin || $mgAdmin,
'index' => $platformAdmin || $mgAdmin,
'install' => $platformAdmin || $mgAdmin,
'view' => false
];

$this->set('vv_permissions', $p);
return $p[$this->request->getParam('action')];
}
}
64 changes: 64 additions & 0 deletions app/src/Controller/AttributeMapsController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<?php
/**
* COmanage Match Attribute Maps Controller
*
* 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 http://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)
*/

declare(strict_types = 1);

namespace App\Controller;

class AttributeMapsController extends StandardController {
public $paginate = [
'order' => [
'AttributeMaps.name' => 'asc'
]
];

/**
* Authorization for this Controller, called by Auth component
* - postcondition: $vv_permissions set with calculated permissions for this Controller
*
* @since COmanage Match v1.0.0
* @param Array $user Array of user data
* @return Boolean True if authorized for the current action, false otherwise
*/

public function isAuthorized(Array $user) {
$platformAdmin = $this->Authorization->isPlatformAdmin($user['username']);

$mgAdmin = $this->Authorization->isMatchAdmin($user['username'], $this->mgid);

$p = [
'add' => $platformAdmin || $mgAdmin,
'delete' => $platformAdmin || $mgAdmin,
'edit' => $platformAdmin || $mgAdmin,
'index' => $platformAdmin || $mgAdmin,
'view' => false
];

$this->set('vv_permissions', $p);
return $p[$this->request->getParam('action')];
}
}
3 changes: 2 additions & 1 deletion app/src/Controller/Component/AuthorizationComponent.php
Original file line number Diff line number Diff line change
Expand Up @@ -185,8 +185,9 @@ public function menuPermissions($username, $matchgridId=null) {
return [
// Manage configuration of the current matchgrid
'api_users' => $platformAdmin || $mgAdmin,
'attributes' => $platformAdmin || $mgAdmin,
'attribute_groups' => $platformAdmin || $mgAdmin,
'attribute_maps' => $platformAdmin || $mgAdmin,
'attributes' => $platformAdmin || $mgAdmin,
'rules' => $platformAdmin || $mgAdmin,
'systems_of_record' => $platformAdmin || $mgAdmin,
// Permissions specific to a matchgrid
Expand Down
43 changes: 0 additions & 43 deletions app/src/Controller/StandardController.php
Original file line number Diff line number Diff line change
Expand Up @@ -216,49 +216,6 @@ public function generateRedirect() {
return $this->redirect($redirect);
}

/**
* Obtain information about the Standard Object's Primary Link, if set.
* The $vv_primary_link view variable is also set.
*
* @since COmanage Match v1.0.0
* @param boolean $lookup If true, get the value of the primary link, not just the attribute
* @return array Array holding the primary link attribute, and optionally its value
* @throws \RuntimeException
*/

protected function getPrimaryLink(bool $lookup=false) {
// $this->name = Models
$modelsName = $this->name;
// $modelName = Model
$modelName = \Cake\Utility\Inflector::singularize($this->name);

$ret = [];

// PrimaryLinkTrait
if(method_exists($this->$modelsName, "getPrimaryLink")
&& $this->$modelsName->getPrimaryLink()) {
$ret['linkattr'] = $this->$modelsName->getPrimaryLink();
$this->set('vv_primary_link', $ret['linkattr']);

if($lookup) {
// Try to find a value
if($this->request->getQuery($ret['linkattr'])) {
$ret['linkvalue'] = $this->request->getQuery($ret['linkattr']);
} elseif($this->request->getData($ret['linkattr'])) {
$ret['linkvalue'] = $this->request->getData($ret['linkattr']);
} elseif($this->request->getData($modelName . "." . $ret['linkattr'])) {
$ret['linkvalue'] = $this->request->getData($modelName . "." . $ret['linkattr']);
} else {
if(!$this->$modelsName->allowEmptyPrimaryLink()) {
throw new \RuntimeException(__('match.er.primary_link', [ $ret['linkattr'] ]));
}
}
}
}

return $ret;
}

/**
* Generate an index for a set of Standard Objects.
*
Expand Down
Loading

0 comments on commit e5ad581

Please sign in to comment.