      1: <?php
      2: /**
      3:  * Copyright 2012-2014 Rackspace US, Inc.
      4:  *
      5:  * Licensed under the Apache License, Version 2.0 (the "License");
      6:  * you may not use this file except in compliance with the License.
      7:  * You may obtain a copy of the License at
      8:  *
      9:  * http://www.apache.org/licenses/LICENSE-2.0
     10:  *
     11:  * Unless required by applicable law or agreed to in writing, software
     12:  * distributed under the License is distributed on an "AS IS" BASIS,
     13:  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14:  * See the License for the specific language governing permissions and
     15:  * limitations under the License.
     16:  */
     17:
     18: namespace OpenCloud\ObjectStore;
     19:
     20: use Guzzle\Http\EntityBody;
     21: use OpenCloud\Common\Constants\Header;
     22: use OpenCloud\Common\Constants\Mime;
     23: use OpenCloud\Common\Exceptions;
     24: use OpenCloud\Common\Exceptions\InvalidArgumentError;
     25: use OpenCloud\Common\Http\Client;
     26: use OpenCloud\Common\Http\Message\Formatter;
     27: use OpenCloud\Common\Service\ServiceBuilder;
     28: use OpenCloud\ObjectStore\Constants\UrlType;
     29: use OpenCloud\ObjectStore\Resource\Container;
     30: use OpenCloud\ObjectStore\Upload\ContainerMigration;
     31:
     32: /**
     33:  * The ObjectStore (Cloud Files) service.
     34:  */
     35: class Service extends AbstractService
     36: {
     37:     const DEFAULT_NAME = 'cloudFiles';
     38:     const DEFAULT_TYPE = 'object-store';
     39:
     40:     /**
     41:      * This holds the associated CDN service (for Rackspace public cloud)
     42:      * or is NULL otherwise. The existence of an object here is
     43:      * indicative that the CDN service is available.
     44:      */
     45:     private $cdnService;
     46:
     47:     public function __construct(Client $client, $type = null, $name = null, $region = null, $urlType = null)
     48:     {
     49:         parent::__construct($client, $type, $name, $region, $urlType);
     50:
     51:         try {
     52:             $this->cdnService = ServiceBuilder::factory($client, 'OpenCloud\ObjectStore\CDNService', array(
     53:                 'region' => $region
     54:             ));
     55:         } catch (Exceptions\EndpointError $e) {
     56:         }
     57:     }
     58:
     59:     /**
     60:      * @return CDNService
     61:      */
     62:     public function getCdnService()
     63:     {
     64:         return $this->cdnService;
     65:     }
     66:
     67:     /**
     68:      * @param $data
     69:      * @return Container
     70:      */
     71:     public function getContainer($data = null)
     72:     {
     73:         return new Container($this, $data);
     74:     }
     75:
     76:     /**
     77:      * Create a container for this service.
     78:      *
     79:      * @param       $name     The name of the container
     80:      * @param array $metadata Additional (optional) metadata to associate with the container
     81:      * @return bool|static
     82:      */
     83:     public function createContainer($name, array $metadata = array())
     84:     {
     85:         $this->checkContainerName($name);
     86:
     87:         $containerHeaders = Container::stockHeaders($metadata);
     88:
     89:         $response = $this->getClient()
     90:             ->put($this->getUrl($name), $containerHeaders)
     91:             ->send();
     92:
     93:         if ($response->getStatusCode() == 201) {
     94:             return Container::fromResponse($response, $this);
     95:         }
     96:
     97:         return false;
     98:     }
     99:
    100:     /**
    101:      * Check the validity of a potential container name.
    102:      *
    103:      * @param $name
    104:      * @return bool
    105:      * @throws \OpenCloud\Common\Exceptions\InvalidArgumentError
    106:      */
    107:     public function checkContainerName($name)
    108:     {
    109:         if (strlen($name) == 0) {
    110:             $error = 'Container name cannot be blank';
    111:         }
    112:
    113:         if (strpos($name, '/') !== false) {
    114:             $error = 'Container name cannot contain "/"';
    115:         }
    116:
    117:         if (strlen($name) > self::MAX_CONTAINER_NAME_LENGTH) {
    118:             $error = 'Container name is too long';
    119:         }
    120:
    121:         if (isset($error)) {
    122:             throw new InvalidArgumentError($error);
    123:         }
    124:
    125:         return true;
    126:     }
    127:
    128:     /**
    129:      * Perform a bulk extraction, expanding an archive file. If the $path is an empty string, containers will be
    130:      * auto-created accordingly, and files in the archive that do not map to any container (files in the base directory)
    131:      * will be ignored. You can create up to 1,000 new containers per extraction request. Also note that only regular
    132:      * files will be uploaded. Empty directories, symlinks, and so on, will not be uploaded.
    133:      *
    134:      * @param        $path        The path to the archive being extracted
    135:      * @param        $archive     The contents of the archive (either string or stream)
    136:      * @param string $archiveType The type of archive you're using {@see \OpenCloud\ObjectStore\Constants\UrlType}
    137:      * @return \Guzzle\Http\Message\Response
    138:      * @throws Exception\BulkOperationException
    139:      * @throws \OpenCloud\Common\Exceptions\InvalidArgumentError
    140:      */
    141:     public function bulkExtract($path = '', $archive, $archiveType = UrlType::TAR_GZ)
    142:     {
    143:         $entity = EntityBody::factory($archive);
    144:
    145:         $acceptableTypes = array(
    146:             UrlType::TAR,
    147:             UrlType::TAR_GZ,
    148:             UrlType::TAR_BZ2
    149:         );
    150:
    151:         if (!in_array($archiveType, $acceptableTypes)) {
    152:             throw new InvalidArgumentError(sprintf(
    153:                 'The archive type must be one of the following: [%s]. You provided [%s].',
    154:                 implode($acceptableTypes, ','),
    155:                 print_r($archiveType, true)
    156:             ));
    157:         }
    158:
    159:         $url = $this->getUrl()->addPath($path)->setQuery(array('extract-archive' => $archiveType));
    160:         $response = $this->getClient()->put($url, array(Header::CONTENT_TYPE => ''), $entity)->send();
    161:
    162:         $body = Formatter::decode($response);
    163:
    164:         if (!empty($body->Errors)) {
    165:             throw new Exception\BulkOperationException((array) $body->Errors);
    166:         }
    167:
    168:         return $response;
    169:     }
    170:
    171:     /**
    172:      * This method will delete multiple objects or containers from their account with a single request.
    173:      *
    174:      * @param array $paths  A two-dimensional array of paths:
    175:      *                      array('container_a/file_1', 'container_b/file_78', 'container_c/file_40582')
    176:      * @return \Guzzle\Http\Message\Response
    177:      * @throws Exception\BulkOperationException
    178:      */
    179:     public function bulkDelete(array $paths)
    180:     {
    181:         $entity = EntityBody::factory(implode(PHP_EOL, $paths));
    182:
    183:         $url = $this->getUrl()->setQuery(array('bulk-delete' => true));
    184:
    185:         $response = $this->getClient()
    186:             ->delete($url, array(Header::CONTENT_TYPE => Mime::TEXT), $entity)
    187:             ->send();
    188:
    189:         try {
    190:             $body = Formatter::decode($response);
    191:             if (!empty($body->Errors)) {
    192:                 throw new Exception\BulkOperationException((array) $body->Errors);
    193:             }
    194:         } catch (Exceptions\JsonError $e) {
    195:         }
    196:
    197:         return $response;
    198:     }
    199:
    200:     /**
    201:      * Allows files to be transferred from one container to another.
    202:      *
    203:      * @param Container $old Where you're moving files from
    204:      * @param Container $new Where you're moving files to
    205:      * @return array    Of PUT responses
    206:      */
    207:     public function migrateContainer(Container $old, Container $new, array $options = array())
    208:     {
    209:         $migration = ContainerMigration::factory($old, $new, $options);
    210:
    211:         return $migration->transfer();
    212:     }
    213: }
    214: