Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CRM-17867 - Api4 core patches #7725

Merged
merged 10 commits into from
Oct 11, 2016
2 changes: 1 addition & 1 deletion CRM/Admin/Page/APIExplorer.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public function run() {
->addScriptFile('civicrm', 'templates/CRM/Admin/Page/APIExplorer.js')
->addScriptFile('civicrm', 'bower_components/google-code-prettify/bin/prettify.min.js', 99)
->addStyleFile('civicrm', 'bower_components/google-code-prettify/bin/prettify.min.css', 99)
->addVars('explorer', array('max_joins' => \Civi\API\SelectQuery::MAX_JOINS));
->addVars('explorer', array('max_joins' => \Civi\API\Api3SelectQuery::MAX_JOINS));

$this->assign('operators', CRM_Core_DAO::acceptedSQLOperators());

Expand Down
163 changes: 163 additions & 0 deletions Civi/API/Api3SelectQuery.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 4.7 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2015 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
namespace Civi\API;

/**
*/
class Api3SelectQuery extends SelectQuery {

protected $apiVersion = 3;

/**
* @inheritDoc
*/
protected function buildWhereClause() {
foreach ($this->where as $key => $value) {
$table_name = NULL;
$column_name = NULL;

if (substr($key, 0, 7) == 'filter.') {
// Legacy support for old filter syntax per the test contract.
// (Convert the style to the later one & then deal with them).
$filterArray = explode('.', $key);
$value = array($filterArray[1] => $value);
$key = 'filters';
}

// Legacy support for 'filter's construct.
if ($key == 'filters') {
foreach ($value as $filterKey => $filterValue) {
if (substr($filterKey, -4, 4) == 'high') {
$key = substr($filterKey, 0, -5);
$value = array('<=' => $filterValue);
}

if (substr($filterKey, -3, 3) == 'low') {
$key = substr($filterKey, 0, -4);
$value = array('>=' => $filterValue);
}

if ($filterKey == 'is_current' || $filterKey == 'isCurrent') {
// Is current is almost worth creating as a 'sql filter' in the DAO function since several entities have the concept.
$todayStart = date('Ymd000000', strtotime('now'));
$todayEnd = date('Ymd235959', strtotime('now'));
$a = self::MAIN_TABLE_ALIAS;
$this->query->where("($a.start_date <= '$todayStart' OR $a.start_date IS NULL)
AND ($a.end_date >= '$todayEnd' OR $a.end_date IS NULL)
AND a.is_active = 1");
}
}
}
// Ignore the "options" param if it is referring to api options and not a field in this entity
if (
$key === 'options' && is_array($value)
&& !in_array(\CRM_Utils_Array::first(array_keys($value)), \CRM_Core_DAO::acceptedSQLOperators())
) {
continue;
}
$field = $this->getField($key);
if ($field) {
$key = $field['name'];
}
if (in_array($key, $this->entityFieldNames)) {
$table_name = self::MAIN_TABLE_ALIAS;
$column_name = $key;
}
elseif (($cf_id = \CRM_Core_BAO_CustomField::getKeyID($key)) != FALSE) {
list($table_name, $column_name) = $this->addCustomField($this->apiFieldSpec['custom_' . $cf_id], 'INNER');
}
elseif (strpos($key, '.')) {
$fkInfo = $this->addFkField($key, 'INNER');
if ($fkInfo) {
list($table_name, $column_name) = $fkInfo;
$this->validateNestedInput($key, $value);
}
}
// I don't know why I had to specifically exclude 0 as a key - wouldn't the others have caught it?
// We normally silently ignore null values passed in - if people want IS_NULL they can use acceptedSqlOperator syntax.
if ((!$table_name) || empty($key) || is_null($value)) {
// No valid filter field. This might be a chained call or something.
// Just ignore this for the $where_clause.
continue;
}
if (!is_array($value)) {
$this->query->where(array("`$table_name`.`$column_name` = @value"), array(
"@value" => $value,
));
}
else {
// We expect only one element in the array, of the form
// "operator" => "rhs".
$operator = \CRM_Utils_Array::first(array_keys($value));
if (!in_array($operator, \CRM_Core_DAO::acceptedSQLOperators())) {
$this->query->where(array("{$table_name}.{$column_name} = @value"), array("@value" => $value));
}
else {
$this->query->where(\CRM_Core_DAO::createSQLFilter("{$table_name}.{$column_name}", $value));
}
}
}
}

/**
* @inheritDoc
*/
protected function getFields() {
require_once 'api/v3/Generic.php';
// Call this function directly instead of using the api wrapper to force unique field names off
$apiSpec = \civicrm_api3_generic_getfields(array(
'entity' => $this->entity,
'version' => 3,
'params' => array('action' => 'get'),
), FALSE);
return $apiSpec['values'];
}

/**
* Fetch a field from the getFields list
*
* Searches by name, uniqueName, and api.aliases
*/
protected function getField($fieldName) {
if (!$fieldName) {
return NULL;
}
if (isset($this->apiFieldSpec[$fieldName])) {
return $this->apiFieldSpec[$fieldName];
}
foreach ($this->apiFieldSpec as $field) {
if (
$fieldName == \CRM_Utils_Array::value('uniqueName', $field) ||
array_search($fieldName, \CRM_Utils_Array::value('api.aliases', $field, array())) !== FALSE
) {
return $field;
}
}
return NULL;
}

}
100 changes: 72 additions & 28 deletions Civi/API/Kernel.php
Original file line number Diff line number Diff line change
Expand Up @@ -61,45 +61,39 @@ public function __construct($dispatcher, $apiProviders = array()) {
}

/**
* @deprecated
* @return array|int
* @see runSafe
*/
public function run($entity, $action, $params, $extra = NULL) {
return $this->runSafe($entity, $action, $params, $extra);
}

/**
* Parse and execute an API request. Any errors will be converted to
* normal format.
*
* @param string $entity
* Type of entities to deal with.
* @param string $action
* Create, get, delete or some special action name.
* @param array $params
* Array to be passed to API function.
* @param mixed $extra
* Who knows.
* Unused/deprecated.
*
* @return array|int
* @throws \API_Exception
*/
public function run($entity, $action, $params, $extra = NULL) {
/**
* @var $apiProvider \Civi\API\Provider\ProviderInterface|NULL
*/
$apiProvider = NULL;

// TODO Define alternative calling convention makes it easier to construct $apiRequest
// without the ambiguity of "data" vs "options"
public function runSafe($entity, $action, $params, $extra = NULL) {
$apiRequest = Request::create($entity, $action, $params, $extra);

try {
if (!is_array($params)) {
throw new \API_Exception('Input variable `params` is not an array', 2000);
}

$this->boot();
$errorScope = \CRM_Core_TemporaryErrorScope::useException();

list($apiProvider, $apiRequest) = $this->resolve($apiRequest);
$this->authorize($apiProvider, $apiRequest);
$apiRequest = $this->prepare($apiProvider, $apiRequest);
$result = $apiProvider->invoke($apiRequest);

$apiResponse = $this->respond($apiProvider, $apiRequest, $result);
$apiResponse = $this->runRequest($apiRequest);
return $this->formatResult($apiRequest, $apiResponse);
}
catch (\Exception $e) {
$this->dispatcher->dispatch(Events::EXCEPTION, new ExceptionEvent($e, $apiProvider, $apiRequest, $this));
$this->dispatcher->dispatch(Events::EXCEPTION, new ExceptionEvent($e, NULL, $apiRequest, $this));

if ($e instanceof \PEAR_Exception) {
$err = $this->formatPearException($e, $apiRequest);
Expand All @@ -125,7 +119,8 @@ public function run($entity, $action, $params, $extra = NULL) {
* @param array $params
* Array to be passed to function.
* @param mixed $extra
* Who knows.
* Unused/deprecated.
*
* @return bool
* TRUE if authorization would succeed.
* @throws \Exception
Expand All @@ -146,12 +141,61 @@ public function runAuthorize($entity, $action, $params, $extra = NULL) {
}

/**
* Bootstrap - Load basic dependencies.
* Execute an API request.
*
* The request must be in canonical format. Exceptions will be propagated out.
*
* @param $apiRequest
* @return array
* @throws \API_Exception
* @throws \Civi\API\Exception\NotImplementedException
* @throws \Civi\API\Exception\UnauthorizedException
*/
public function boot() {
require_once 'api/v3/utils.php';
public function runRequest($apiRequest) {
$this->boot($apiRequest);
$errorScope = \CRM_Core_TemporaryErrorScope::useException();

list($apiProvider, $apiRequest) = $this->resolve($apiRequest);
$this->authorize($apiProvider, $apiRequest);
$apiRequest = $this->prepare($apiProvider, $apiRequest);
$result = $apiProvider->invoke($apiRequest);

return $this->respond($apiProvider, $apiRequest, $result);
}

/**
* Bootstrap - Load basic dependencies and sanity-check inputs.
*
* @param \Civi\API\V4\Action|array $apiRequest
* @throws \API_Exception
*/
public function boot($apiRequest) {
require_once 'api/Exception.php';
_civicrm_api3_initialize();

if (!is_array($apiRequest['params'])) {
throw new \API_Exception('Input variable `params` is not an array', 2000);
}
switch ($apiRequest['version']) {
case 2:
case 3:
require_once 'api/v3/utils.php';
_civicrm_api3_initialize();
break;

case 4:
// nothing to do
break;

default:
throw new \API_Exception('Unknown api version', 2000);
}
}

/**
* @param $apiRequest
* @throws \API_Exception
*/
protected function validate($apiRequest) {
}

/**
Expand Down
Loading