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/src/Controller/AppController.php
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
343 lines (283 sloc)
12.8 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 Match App 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; | |
use Cake\Controller\Controller; | |
use Cake\Datasource\Exception; | |
use Cake\Datasource\Exception\RecordNotFoundException; | |
use Cake\Event\EventInterface; | |
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; | |
// If set, the current primary link. | |
protected $cur_pl = null; | |
/** | |
* Initialization callback. | |
* | |
* @since COmanage Match v1.0.0 | |
*/ | |
public function initialize(): void { | |
parent::initialize(); | |
// Load Components used by most or all controllers | |
$this->loadComponent('RequestHandler', [ | |
// As of Cake v3.6.7 need to disable this to suppress v4.0.0 deprecation warnings. | |
'enableBeforeRedirect' => false | |
]); | |
$this->loadComponent('Flash'); | |
$this->loadComponent('Auth', [ | |
// We want to use isAuthorized in each controller for request authorization | |
'authorize' => [ | |
'Controller' | |
], | |
// This corresponds to EnvAuthenticate | |
'authenticate' => [ | |
'Env' | |
], | |
]); | |
/* | |
* Enable the following components for recommended CakePHP security settings. | |
* see https://book.cakephp.org/3.0/en/controllers/components/security.html | |
*/ | |
$this->loadComponent('Security'); | |
// CSRF Protection is enabled via in Middleware via Application.php. | |
// This is the COmanage AuthorizationComponent, not to be confused with | |
// Cake's AuthComponent, or the use of Controller Authorization. | |
$this->loadComponent('Authorization'); | |
} | |
/** | |
* Callback run prior to the request action. | |
* | |
* @since COmanage Match v1.0.0 | |
* @param \Cake\Event\EventInterface $event Cake Event | |
*/ | |
public function beforeFilter(EventInterface $event) { | |
parent::beforeFilter($event); | |
// Determine the timezone | |
$this->setTZ(); | |
// Determine the requested Matchgrid | |
$this->setMatchgrid(); | |
} | |
/** | |
* Callback run prior to the view rendering. | |
* | |
* @since COmanage Match v1.0.0 | |
* @param \Cake\Event\EventInterface $event Cake Event | |
*/ | |
public function beforeRender(EventInterface $event) { | |
parent::beforeRender($event); | |
// The current user, if authenticated | |
$curUser = $this->request->getSession()->read('Auth.User'); | |
$this->set('vv_user', $curUser); | |
// The current Matchgrid, as determined in beforeFilter() | |
$mgid = null; | |
if($this->cur_mg) { | |
$mgid = $this->cur_mg->id; | |
} | |
// Available Matchgrids | |
$this->Matchgrids = $this->fetchTable('Matchgrids'); | |
$this->set('vv_matchgrids', $this->Matchgrids->find('all')->find('activeMatchGrids')->order(['table_name' => 'ASC'])->toArray()); | |
// The set of menu permissions, so the layout knows what to render | |
if(isset($this->Authorization) && $curUser) { | |
// Ordinarily $this->Authorization will be set, but under certain error conditions | |
// it won't, which will prevent error messages from rendering | |
$this->set('vv_menu_permissions', | |
$this->Authorization->menuPermissions($this->request->getSession()->read('Auth.User.username'), $mgid)); | |
} | |
} | |
/** | |
* 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) { | |
// Did we already figure this out? (But only if $lookup) | |
if($lookup && isset($this->cur_pl['linkvalue'])) { | |
return $this->cur_pl; | |
} | |
// $this->name = Models | |
$modelsName = $this->name; | |
// $modelName = Model | |
$modelName = \Cake\Utility\Inflector::singularize($this->name); | |
$this->cur_pl = []; | |
// PrimaryLinkTrait | |
if(method_exists($this->$modelsName, "getPrimaryLink") | |
&& $this->$modelsName->getPrimaryLink()) { | |
$this->cur_pl['linkattr'] = $this->$modelsName->getPrimaryLink(); | |
$this->set('vv_primary_link', $this->cur_pl['linkattr']); | |
if($lookup) { | |
// Try to find a value | |
if($this->request->is('get')) { | |
// If this action allows unkeyed, asserted primary link IDs, check the query | |
// string (eg: 'add' or 'index' allow matchgrid_id to be passed in) | |
if($this->$modelsName->allowUnkeyedPrimaryLink($this->request->getParam('action')) | |
&& $this->request->getQuery($this->cur_pl['linkattr'])) { | |
$this->cur_pl['linkvalue'] = $this->request->getQuery($this->cur_pl['linkattr']); | |
} elseif($this->$modelsName->allowLookupPrimaryLink($this->request->getParam('action'))) { | |
// Try to map the requested object ID | |
$param = (int)$this->request->getParam('pass.0'); | |
if(!empty($param)) { | |
$this->cur_pl['linkvalue'] = $this->$modelsName->calculatePrimaryLinkId($param); | |
} | |
} | |
} elseif($this->request->is('post') || $this->request->is('put')) { | |
// Look in the data for the primary link ID | |
if(!empty($this->request->getData($this->cur_pl['linkattr']))) { | |
$this->cur_pl['linkvalue'] = $this->request->getData($this->cur_pl['linkattr']); | |
} elseif(!empty($this->request->getData($modelName . "." . $this->cur_pl['linkattr']))) { | |
$this->cur_pl['linkvalue'] = $this->request->getData($modelName . "." . $this->cur_pl['linkattr']); | |
} elseif($this->$modelsName->allowUnkeyedPrimaryLink($this->request->getParam('action')) | |
&& $this->request->getQuery($this->cur_pl['linkattr'])) { | |
// If this action allows unkeyed, asserted primary link IDs, check the query | |
// string (eg: 'add' or 'index' allow matchgrid_id to be passed in). Note we | |
// do this even though we're in post/put. | |
$this->cur_pl['linkvalue'] = $this->request->getQuery($this->cur_pl['linkattr']); | |
} elseif($this->$modelsName->allowLookupPrimaryLink($this->request->getParam('action'))) { | |
// Try to map the requested object ID (this is probably a delete, so no attribute in post body) | |
$param = (int)$this->request->getParam('pass.0'); | |
if(!empty($param)) { | |
$this->cur_pl['linkvalue'] = $this->$modelsName->calculatePrimaryLinkId($param); | |
} | |
} | |
} | |
if(empty($this->cur_pl['linkvalue']) && !$this->$modelsName->allowEmptyPrimaryLink()) { | |
throw new \RuntimeException(__('match.er.primary_link', [ $this->cur_pl['linkattr'] ])); | |
} | |
} | |
if(!empty($this->cur_pl['linkvalue'])) { | |
// Look up the link value to find the related entity | |
$linkModelName = $this->$modelsName->getPrimaryLinkTableName(); | |
$linkModel = $this->getTableLocator()->get($linkModelName); | |
$this->set('vv_primary_link_model', $linkModelName); | |
$this->set('vv_primary_link_obj', $linkModel->findById($this->cur_pl['linkvalue'])->firstOrFail()); | |
} | |
} | |
return $this->cur_pl; | |
} | |
/** | |
* Determine the (requested) current Matchgrid and make it available to the | |
* rest of the application. | |
* | |
* @since COmanage Match v1.0.0 | |
* @throws Cake\Datasource\Exception\RecordNotFoundException | |
* @throws \InvalidArgumentException | |
*/ | |
protected function setMatchgrid() { | |
// Note: TierApiController overrides this. | |
// $this->name = Models | |
$modelsName = $this->name; | |
// $modelName = Model | |
$modelName = \Cake\Utility\Inflector::singularize($this->name); | |
if(!method_exists($this->$modelsName, "requiresMatchgrid") | |
|| !$this->$modelsName->requiresMatchgrid()) { | |
// Nothing to do, matchgrid not required by this model/controller | |
return; | |
} | |
// Not all models have matchgrid as their primary link. This will also | |
// trigger setting of the viewVar for breadcrumbs and anything else. | |
// PrimaryLinkTrait | |
$link = $this->getPrimaryLink(true); | |
// Try to find the requested matchgrid | |
$mgid = null; | |
// If this action allows unkeyed, asserted primary link IDs, check the query | |
// string (eg: 'add' or 'index' allow matchgrid_id to be passed in), and | |
// possibly dereference it if the primary key is not matchgrid_id. | |
if($this->$modelsName->allowUnkeyedPrimaryLink($this->request->getParam('action'))) { | |
if($link['linkattr'] == 'matchgrid_id') { | |
// Simply accept the passed matchgrid ID | |
if($this->request->is('get')) { | |
$mgid = $this->request->getQuery('matchgrid_id'); | |
} elseif($this->request->is('post') || $this->request->is('put')) { | |
if(!empty($this->request->getData('matchgrid_id'))) { | |
$mgid = $this->request->getData('matchgrid_id'); | |
} elseif(!empty($this->request->getData($modelName . ".matchgrid_id"))) { | |
$mgid = $this->request->getData($modelName . ".matchgrid_id"); | |
} elseif($this->name == 'MatchgridRecords' | |
&& !empty($this->request->getQuery('matchgrid_id'))) { | |
// As a special case for MatchgridRecords, we accept the matchgrid_id | |
// as a get parameter even though the action is post/put, since this | |
// is how it is provided for a delete action. | |
$mgid = $this->request->getQuery('matchgrid_id'); | |
} | |
} | |
} else { | |
// We already have the primary link object in a viewvar | |
$ViewBuilder = $this->viewBuilder(); | |
$plObj = $ViewBuilder->getVar('vv_primary_link_obj'); | |
if(!empty($plObj->matchgrid_id)) { | |
$mgid = $plObj->matchgrid_id; | |
} | |
} | |
} elseif($this->$modelsName->allowLookupPrimaryLink($this->request->getParam('action'))) { | |
// Try to map the requested object ID | |
$param = (int)$this->request->getParam('pass.0'); | |
if(!empty($param)) { | |
$mgid = $this->$modelsName->calculateMatchgridId($param); | |
} | |
} | |
if(!$mgid && !$this->$modelsName->allowEmptyMatchgrid()) { | |
// If we get this far without a Matchgrid ID, something went wrong. | |
throw new \RuntimeException(__('match.er.mgid')); | |
} | |
if($mgid) { | |
$this->Matchgrids = $this->fetchTable('Matchgrids'); | |
// This throws Cake\Datasource\Exception\RecordNotFoundException which | |
// we just let pass up the stack. | |
$this->cur_mg = $this->Matchgrids->findById($mgid)->firstOrFail(); | |
$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); | |
} | |
} | |
} |