/** * @param array $resources * @param boolean $as_list * @param string|array|null $identifier * @param string|array|null $fields * @param boolean $force_wrap * * @return array */ public static function cleanResources($resources, $as_list = false, $identifier = null, $fields = null, $force_wrap = false) { // avoid single resources or already wrapped resources if (ArrayUtils::isArrayNumeric($resources)) { // may already be a simple list if (is_array(ArrayUtils::get($resources, 0))) { if (is_string($identifier)) { $identifier = explode(',', $identifier); } elseif (!is_array($identifier)) { $identifier = []; } $identifier = array_values($identifier); if ($as_list) { if (1 == count($identifier)) { $resources = array_column($resources, $identifier[0]); } else { foreach ($resources as &$resource) { $out = ''; foreach ($identifier as $idField) { if (!empty($out)) { $out .= ','; } $out .= ArrayUtils::get($resource, $idField, ''); } $resource = '(' . $out . ')'; } } } elseif (empty($fields)) { if (is_array($identifier) && !empty($identifier)) { $identifier = array_flip($identifier); foreach ($resources as &$resource) { $resource = array_intersect_key($resource, $identifier); } } } elseif (ApiOptions::FIELDS_ALL !== $fields) { if (is_string($fields)) { $fields = explode(',', $fields); } elseif (!is_array($fields)) { $fields = []; } $fields = array_flip(array_values($fields)); foreach ($resources as &$resource) { $resource = array_intersect_key($resource, $fields); } } } return static::wrapResources($resources, $force_wrap); } return $force_wrap ? static::wrapResources($resources, true) : $resources; }
/** * {@inheritdoc} */ protected function getPayloadData($key = null, $default = null) { $payload = parent::getPayloadData(); if (null !== $key && !empty($payload[$key])) { return $payload[$key]; } // $alwaysWrap = \Config::get('df.always_wrap_resources', false); $wrapper = ResourcesWrapper::getWrapper(); if (!empty($this->resource) && !empty($payload)) { // single records passed in which don't use the record wrapper, so wrap it $payload = [$wrapper => [$payload]]; } elseif (ArrayUtils::isArrayNumeric($payload)) { // import from csv, etc doesn't include a wrapper, so wrap it $payload = [$wrapper => $payload]; } if (empty($key)) { $key = $wrapper; } return ArrayUtils::get($payload, $key); }
/** * @param mixed $data * @param string $root * @param int $level * @param bool $format * * @return string */ protected static function arrayToXmlInternal($data, $root = null, $level = 1, $format = true) { $xml = null; if (ArrayUtils::isArrayNumeric($data)) { foreach ($data as $value) { $xml .= self::arrayToXmlInternal($value, $root, $level, $format); } } else { if (ArrayUtils::isArrayAssociative($data)) { if (!empty($root)) { if ($format) { $xml .= str_repeat("\t", $level - 1); } $xml .= "<{$root}>"; if ($format) { $xml .= "\n"; } } foreach ($data as $key => $value) { $xml .= self::arrayToXmlInternal($value, $key, $level + 1, $format); } if (!empty($root)) { if ($format) { $xml .= str_repeat("\t", $level - 1); } $xml .= "</{$root}>"; if ($format) { $xml .= "\n"; } } } else { if (is_array($data)) { // empty array } else { // not an array if (!empty($root)) { if ($format) { $xml .= str_repeat("\t", $level - 1); } $xml .= "<{$root}>"; if (!is_null($data)) { if (is_bool($data)) { $xml .= $data ? 'true' : 'false'; } else { if (is_int($data) || is_float($data)) { $xml .= $data; } else { if (is_string($data)) { $htmlValue = htmlspecialchars($data, ENT_QUOTES, 'UTF-8'); $xml .= $htmlValue; } } } } $xml .= "</{$root}>"; if ($format) { $xml .= "\n"; } } } } } return $xml; }
/** * If fields is not '*' (all) then remove the empty 'config' property. * * @param array $response * @param mixed $fields * * @return array */ protected static function cleanResult(array $response, $fields) { if (!is_array($fields)) { $fields = explode(',', $fields); } //config is only available when both id and type is present. Therefore only show config if id and type is there. if (ArrayUtils::get($fields, 0) !== '*' && (!in_array('type', $fields) || !in_array('id', $fields))) { $result = []; if (ArrayUtils::isArrayNumeric($response)) { foreach ($response as $r) { if (isset($r['config'])) { unset($r['config']); } $result[] = $r; } } else { foreach ($response as $k => $v) { if ('config' === $k) { unset($response[$k]); } } $result = $response; } return $result; } return $response; }
/** * Fixes supplied records to always set is_set_admin flag to true. * Encrypts passwords if it is supplied. * * @param array $records * * @return array */ protected static function fixRecords(array $records) { if (ArrayUtils::isArrayNumeric($records)) { foreach ($records as $key => $record) { ArrayUtils::set($record, 'is_sys_admin', 1); $records[$key] = $record; } } else { ArrayUtils::set($records, 'is_sys_admin', 1); } return $records; }
/** * Handles DELETE action * * @return \DreamFactory\Core\Utility\ServiceResponse * @throws BadRequestException * @throws \Exception */ protected function handleDELETE() { $deleteStorage = $this->request->getParameterAsBool('delete_storage'); $fields = $this->request->getParameter('fields'); if ($deleteStorage) { if (empty($fields)) { $fields = 'id,storage_service_id,storage_container'; } else { if ($fields !== '*') { $fields = explode(',', $fields); if (!in_array('id', $fields)) { $fields[] = 'id'; } if (!in_array('storage_service_id', $fields)) { $fields[] = 'storage_service_id'; } if (!in_array('storage_container', $fields)) { $fields[] = 'storage_container'; } $fields = implode(',', $fields); } } $this->request->setParameter('fields', $fields); } $result = parent::handleDELETE(); if ($deleteStorage) { $temp = $result; $wrapper = ResourcesWrapper::getWrapper(); if (isset($result[$wrapper])) { $temp = ResourcesWrapper::unwrapResources($temp); } if (ArrayUtils::isArrayNumeric($temp)) { foreach ($temp as $app) { static::deleteHostedAppStorage($app['id'], $app['storage_service_id'], $app['storage_container']); } } else { static::deleteHostedAppStorage($temp['id'], $temp['storage_service_id'], $temp['storage_container']); } } return $result; }
/** * @param array $data * @param bool $extract * @param bool $clean * @param bool $checkExist * * @return array */ protected function handleFolderContentFromData($data, $extract = false, $clean = false, $checkExist = false) { $out = []; if (!empty($data) && ArrayUtils::isArrayNumeric($data)) { foreach ($data as $key => $resource) { switch (ArrayUtils::get($resource, 'type')) { case 'folder': $name = ArrayUtils::get($resource, 'name', ''); $srcPath = ArrayUtils::get($resource, 'source_path'); if (!empty($srcPath)) { $srcContainer = ArrayUtils::get($resource, 'source_container', $this->container); // copy or move if (empty($name)) { $name = FileUtilities::getNameFromPath($srcPath); } $fullPathName = $this->folderPath . $name . '/'; $out[$key] = ['name' => $name, 'path' => $fullPathName, 'type' => 'folder']; try { $this->driver->copyFolder($this->container, $fullPathName, $srcContainer, $srcPath, true); $deleteSource = ArrayUtils::getBool($resource, 'delete_source'); if ($deleteSource) { $this->driver->deleteFolder($this->container, $srcPath, true); } } catch (\Exception $ex) { $out[$key]['error'] = ['message' => $ex->getMessage()]; } } else { $fullPathName = $this->folderPath . $name . '/'; $content = ArrayUtils::get($resource, 'content', ''); $isBase64 = ArrayUtils::getBool($resource, 'is_base64'); if ($isBase64) { $content = base64_decode($content); } $out[$key] = ['name' => $name, 'path' => $fullPathName, 'type' => 'folder']; try { $this->driver->createFolder($this->container, $fullPathName, $content); } catch (\Exception $ex) { $out[$key]['error'] = ['message' => $ex->getMessage()]; } } break; case 'file': $name = ArrayUtils::get($resource, 'name', ''); $srcPath = ArrayUtils::get($resource, 'source_path'); if (!empty($srcPath)) { // copy or move $srcContainer = ArrayUtils::get($resource, 'source_container', $this->container); if (empty($name)) { $name = FileUtilities::getNameFromPath($srcPath); } $fullPathName = $this->folderPath . $name; $out[$key] = ['name' => $name, 'path' => $fullPathName, 'type' => 'file']; try { $this->driver->copyFile($this->container, $fullPathName, $srcContainer, $srcPath, true); $deleteSource = ArrayUtils::getBool($resource, 'delete_source'); if ($deleteSource) { $this->driver->deleteFile($this->container, $srcPath); } } catch (\Exception $ex) { $out[$key]['error'] = ['message' => $ex->getMessage()]; } } elseif (isset($resource['content'])) { $fullPathName = $this->folderPath . $name; $out[$key] = ['name' => $name, 'path' => $fullPathName, 'type' => 'file']; $content = ArrayUtils::get($resource, 'content', ''); $isBase64 = ArrayUtils::getBool($resource, 'is_base64'); if ($isBase64) { $content = base64_decode($content); } try { $this->driver->writeFile($this->container, $fullPathName, $content); } catch (\Exception $ex) { $out[$key]['error'] = ['message' => $ex->getMessage()]; } } break; } } } return $out; }
/** * * * IMPORTANT: The representation of the data will be placed back into the original location/position in the $record * from which it was "normalized". This means that any client-side handlers will have to deal with the bogus * determinations. Just be aware. * * Below is a side-by-side comparison of record data as shown sent by or returned to the caller, and sent to an * event handler. * * REST API v1.0 Event Representation * ------------- -------------------- * Single row... Add a 'record' key and make it look like a multi-row * * array( array( * 'id' => 1, 'record' => array( * ) 0 => array( 'id' => 1, ), * ), * ), * * Multi-row... Stays the same...or gets wrapped by adding a 'record' key * * array( array( * 'record' => array( 'record' => array( * 0 => array( 'id' => 1 ), 0 => array( 'id' => 1 ), * 1 => array( 'id' => 2 ), 1 => array( 'id' => 2 ), * 2 => array( 'id' => 3 ), 2 => array( 'id' => 3 ), * ), ), * ) ) * * or... * * array( array( * 0 => array( 'id' => 1 ), 'record' => array( * 1 => array( 'id' => 2 ), 0 => array( 'id' => 1 ), * 2 => array( 'id' => 3 ), 1 => array( 'id' => 2 ), * ), 2 => array( 'id' => 3 ), * ), * ) */ protected function detectRequestMembers() { $wrapper = ResourcesWrapper::getWrapper(); // override - don't call parent class here $this->payload = $this->getPayloadData(); if (!empty($this->resource)) { if (!empty($this->resourceId)) { if (!empty($this->payload)) { // fix wrapper on posted single record if (!isset($this->payload[$wrapper])) { // single records don't use the record wrapper, so wrap it $this->payload = [$wrapper => [$this->payload]]; } } } elseif (ArrayUtils::isArrayNumeric($this->payload)) { // import from csv, etc doesn't include a wrapper, so wrap it $this->payload = [$wrapper => $this->payload]; } else { if (!empty($this->payload)) { switch ($this->request->getMethod()) { case Verbs::POST: case Verbs::PUT: case Verbs::PATCH: case Verbs::MERGE: // fix wrapper on posted single record if (!isset($this->payload[$wrapper])) { // stuff it back in for event $this->payload[$wrapper] = [$this->payload]; } break; } } } $this->options = $this->request->getParameters(); // merge in possible payload options $optionNames = [ApiOptions::LIMIT, ApiOptions::OFFSET, ApiOptions::ORDER, ApiOptions::GROUP, ApiOptions::FIELDS, ApiOptions::IDS, ApiOptions::FILTER, ApiOptions::PARAMS, ApiOptions::CONTINUES, ApiOptions::ROLLBACK]; foreach ($optionNames as $key => $value) { if (!array_key_exists($value, $this->options)) { if (array_key_exists($value, $this->payload)) { $this->options[$value] = $this->payload[$value]; } elseif (!empty($otherNames = ApiOptions::getAliases($value))) { foreach ($otherNames as $other) { if (!array_key_exists($other, $this->options)) { if (array_key_exists($other, $this->payload)) { $this->options[$value] = $this->payload[$other]; } } else { $this->options[$value] = $this->options[$other]; } } } } } // set defaults if not present if (Verbs::GET == $this->request->getMethod()) { // default for GET should be "return all fields" if (!array_key_exists(ApiOptions::FIELDS, $this->options)) { $this->options[ApiOptions::FIELDS] = '*'; } } // Add server side filtering properties $resource = $this->name . '/' . $this->resource; if (null != ($ssFilters = Session::getServiceFilters($this->getRequestedAction(), $this->parent->name, $resource))) { $this->options['ss_filters'] = $ssFilters; } } }
/** * @param $response * * @return mixed * @throws \Exception */ protected function handleInvitation($response) { try { $sendInvite = $this->request->getParameterAsBool('send_invite'); switch ($this->action) { case Verbs::POST: case Verbs::PUT: case Verbs::PATCH: case Verbs::MERGE: if ($sendInvite) { if ($response instanceof ServiceResponseInterface) { $response = $response->getContent(); } if (is_array($response)) { $records = ArrayUtils::get($response, ResourcesWrapper::DEFAULT_WRAPPER); if (ArrayUtils::isArrayNumeric($records)) { $passed = true; foreach ($records as $record) { $id = ArrayUtils::get($record, 'id'); try { static::sendInvite($id, $this->action === Verbs::POST); } catch (\Exception $e) { if (count($records) === 1) { throw $e; } else { $passed = false; Log::error('Error processing invitation for user id ' . $id . ': ' . $e->getMessage()); } } } if (!$passed) { throw new InternalServerErrorException('Not all users were created successfully. Check log for more details.'); } } else { $id = ArrayUtils::get($response, 'id'); if (empty($id)) { throw new InternalServerErrorException('Invalid user id in response.'); } static::sendInvite($id, $this->action === Verbs::POST); } } } break; } return $response; } catch (\Exception $ex) { throw $ex; } }