From 08c952539366dbb10939ab0873a9ccfb1e141a4a Mon Sep 17 00:00:00 2001 From: Tim Otten Date: Mon, 8 Dec 2014 18:58:58 -0800 Subject: [PATCH] CRM-15578 - civicrm/ajax/attachment - Add endpoint for creating attachments using HTTP POST (multipart) --- CRM/Core/Page/AJAX/Attachment.php | 147 ++++++++++++++++++++++++++++++ CRM/Core/xml/Menu/Misc.xml | 5 + 2 files changed, 152 insertions(+) create mode 100644 CRM/Core/Page/AJAX/Attachment.php diff --git a/CRM/Core/Page/AJAX/Attachment.php b/CRM/Core/Page/AJAX/Attachment.php new file mode 100644 index 0000000000..7cee3e3c52 --- /dev/null +++ b/CRM/Core/Page/AJAX/Attachment.php @@ -0,0 +1,147 @@ + $file) { + if (!$config->debug && !self::isAJAX($server)) { + require_once 'api/v3/utils.php'; + $results[$key] = civicrm_api3_create_error("SECURITY ALERT: Ajax requests can only be issued by javascript clients, eg. CRM.api3().", + array( + 'IP' => $server['REMOTE_ADDR'], + 'level' => 'security', + 'referer' => $server['HTTP_REFERER'], + 'reason' => 'CSRF suspected', + ) + ); + } + elseif ($file['error']) { + $results[$key] = civicrm_api3_create_error("Upload failed (code=" . $file['error'] . ")"); + } + else { + CRM_Core_Transaction::create(TRUE) + ->run(function (CRM_Core_Transaction $tx) use ($key, $file, $post, &$results) { + // We want check_permissions=1 while creating the DB record and check_permissions=0 while moving upload, + // so split the work across two api calls. + + $params = array(); + if (isset($file['name'])) { + $params['name'] = $file['name']; + } + if (isset($file['type'])) { + $params['mime_type'] = $file['type']; + } + foreach (array('entity_table', 'entity_id', 'description') as $field) { + if (isset($post[$field])) { + $params[$field] = $post[$field]; + } + } + $params['version'] = 3; + $params['check_permissions'] = 1; + $params['content'] = ''; + $results[$key] = civicrm_api('Attachment', 'create', $params); + + if (!$results[$key]['is_error']) { + $moveParams = array( + 'id' => $results[$key]['id'], + 'version' => 3, + 'options.move-file' => $file['tmp_name'], + // note: in this second call, check_permissions==false + ); + $moveResult = civicrm_api('Attachment', 'create', $moveParams); + if ($moveResult['is_error']) { + $results[$key] = $moveResult; + $tx->rollback(); + } + } + }); + } + } + + return $results; + } + + /** + * @param array $server (like global $_SERVER) + * @return bool + */ + public static function isAJAX($server) { + return array_key_exists('HTTP_X_REQUESTED_WITH', $server) && $server['HTTP_X_REQUESTED_WITH'] == "XMLHttpRequest"; + } + + /** + * @param array $result list of API responses, keyed by file + */ + public static function sendResponse($result) { + $isError = FALSE; + foreach ($result as $item) { + $isError = $isError || $item['is_error']; + } + + if ($isError) { + $sapi_type = php_sapi_name(); + if (substr($sapi_type, 0, 3) == 'cgi') { + header("Status: 500 Internal Server Error"); + } + else { + header("HTTP/1.1 500 Internal Server Error"); + } + } + + header('Content-Type: text/javascript'); + echo json_encode(array_merge($result)); + CRM_Utils_System::civiExit(); + } +} diff --git a/CRM/Core/xml/Menu/Misc.xml b/CRM/Core/xml/Menu/Misc.xml index 789b612ab1..0dc981bfa3 100644 --- a/CRM/Core/xml/Menu/Misc.xml +++ b/CRM/Core/xml/Menu/Misc.xml @@ -80,6 +80,11 @@ CRM_Core_I18n_Form translate CiviCRM + + civicrm/ajax/attachment + CRM_Core_Page_AJAX_Attachment::attachFile + access CiviCRM;access AJAX API + civicrm/ajax/doc CRM_Utils_REST::ajaxDoc -- 2.25.1