diff --git a/.env.php b/.env.php index 3fe3d27584..c64de62de6 100644 --- a/.env.php +++ b/.env.php @@ -1,7 +1,7 @@ __DIR__.'/uploads', - 'SESSION_FILESTORE' => __DIR__.'/app/storage/sessions', + 'FS_REPO' => 'Local', + 'FS_LOCAL_ENDPOINT' => __DIR__.'/uploads', 'LOG_FILESTORE' => __DIR__.'/app/storage/logs/laravel.log', ]; \ No newline at end of file diff --git a/.gitignore b/.gitignore index 6d5d112397..e7fbeabd4d 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,8 @@ composer.phar .DS_Store Thumbs.db +.phpintel +.env.*.php /app/config/ht2 /app/config/production diff --git a/app/commands/FileRepositoryCommand.php b/app/commands/FileRepositoryCommand.php new file mode 100644 index 0000000000..17a692489f --- /dev/null +++ b/app/commands/FileRepositoryCommand.php @@ -0,0 +1,43 @@ +option('from'); + $to_var = $this->argument('to'); + $from_repo = FileFactory::createRepo($from_var); + $to_repo = FileFactory::createRepo($to_var); + $files = $from_repo->index([]); + + $count = 0; + foreach ($files as $file) { + $path = $file['path']; + if ($file['type'] === 'file' && !$to_repo->has($path, [])) { + $count += 1; + $to_repo->update($path, ['content' => $from_repo->show($path, [])], []); + echo "Migrated '{$path}' from '{$from_var}' to '{$to_var}'.".PHP_EOL; + } + } + + echo "Migrated $count files and ignored ".(count($files) - $count).".".PHP_EOL; + } + + protected function getArguments() { + return [ + ['to', InputArgument::REQUIRED, 'The repository to migrate to.'] + ]; + } + + protected function getOptions() { + return [ + ['from', 'f', InputOption::VALUE_OPTIONAL, 'The repository to migrate from.', 'Local'], + ]; + } + +} diff --git a/app/config/session.php b/app/config/session.php index 21d4621675..699f734cdb 100644 --- a/app/config/session.php +++ b/app/config/session.php @@ -46,7 +46,7 @@ | */ - 'files' => Helpers::getEnvVar('SESSION_FILESTORE'), + 'files' => null, /* |-------------------------------------------------------------------------- diff --git a/app/controllers/xapi/DocumentController.php b/app/controllers/xapi/DocumentController.php index f1a106b597..945e92c45a 100644 --- a/app/controllers/xapi/DocumentController.php +++ b/app/controllers/xapi/DocumentController.php @@ -1,8 +1,8 @@ content, 200, $headers); default: - return \Response::download( - $document->getFilePath(), - $document->content, - $headers - ); + $stream = FileFactory::create()->stream($document->getFilePath(), []); + return \Response::stream(function () use ($stream) { + while (!feof($stream)) { + echo fread($stream, 8192); flush();ob_flush(); + } + fclose($stream); + }, 200, $headers); } } } diff --git a/app/controllers/xapi/StatementIndexController.php b/app/controllers/xapi/StatementIndexController.php index fe1cf85e99..fe5eab1fba 100644 --- a/app/controllers/xapi/StatementIndexController.php +++ b/app/controllers/xapi/StatementIndexController.php @@ -49,17 +49,21 @@ public function index($options) { // Defines the content type and body of the response. if ($opts['attachments'] === true) { $content_type = 'multipart/mixed; boundary='.static::BOUNDARY; - $body = $this->makeAttachmentsResult($statements, $count, $opts); + $body = function () use ($statements, $count, $opts) { + return $this->makeAttachmentsResult($statements, $count, $opts); + }; } else { $content_type = 'application/json;'; - $body = $this->makeStatementsResult($statements, $count, $opts); + $body = function () use ($statements, $count, $opts) { + return $this->makeStatementsResult($statements, $count, $opts); + }; } // Creates the response. - return \Response::make($body, 200, [ + return \Response::stream($body, 200, [ 'Content-Type' => $content_type, 'X-Experience-API-Consistent-Through' => Helpers::getCurrentDate() - ]);; + ]); } /** @@ -83,7 +87,7 @@ private function makeStatementsResult(array $statements, $count, array $opts) { 'statements' => $statements ]; - return json_encode($statement_result); + $this->emit(json_encode($statement_result)); } /** @@ -96,24 +100,37 @@ private function makeAttachmentsResult(array $statements, $count, array $opts) { $boundary = static::BOUNDARY; $eol = static::EOL; $content_type = 'multipart/mixed; boundary='.$boundary; - $statement_result = "Content-Type:application/json$eol$eol".$this->makeStatementsResult( + + $this->emit("--$boundary$eol"); + $this->emit("Content-Type:application/json$eol$eol"); + $this->makeStatementsResult( $statements, $count, $opts ); - return "--$boundary$eol".implode( - "$eol--$boundary$eol", - array_merge([$statement_result], array_map(function ($attachment) use ($eol, $boundary) { - return ( + $attachments = $this->statements->getAttachments($statements, $opts); + foreach ($attachments as $attachment) { + $this->emit( + "$eol--$boundary$eol". 'Content-Type:'.$attachment->content_type.$eol. 'Content-Transfer-Encoding:binary'.$eol. 'X-Experience-API-Hash:'.$attachment->hash. - $eol.$eol. - $attachment->content + $eol.$eol ); - }, $this->statements->getAttachments($statements, $opts))) - )."$eol--$boundary--"; + while (!feof($attachment->content)) { + $this->emit(fread($attachment->content, 8192)); + } + fclose($attachment->content); + } + + $this->emit("$eol--$boundary--"); + } + + private function emit($value) { + echo $value; + flush(); + ob_flush(); } private function getMoreLink($count, $limit, $offset) { diff --git a/app/database/migrations/2015_03_31_130813_attachment_rename.php b/app/database/migrations/2015_03_31_130813_attachment_rename.php index 107ea96dc0..fc65edcebe 100644 --- a/app/database/migrations/2015_03_31_130813_attachment_rename.php +++ b/app/database/migrations/2015_03_31_130813_attachment_rename.php @@ -7,7 +7,7 @@ class AttachmentRename extends Migration { public function up() { - $uploads = Helpers::getEnvVar('LOCAL_FILESTORE'); + $uploads = Helpers::getEnvVar('FS_LOCAL_ENDPOINT'); $LRSs = $this->getDirectores($uploads); // Gets the attachments. diff --git a/app/database/migrations/2015_08_12_150203_ConsistentForeignKeyNames.php b/app/database/migrations/2015_08_12_150203_ConsistentForeignKeyNames.php index 9d43bf4426..8027673441 100644 --- a/app/database/migrations/2015_08_12_150203_ConsistentForeignKeyNames.php +++ b/app/database/migrations/2015_08_12_150203_ConsistentForeignKeyNames.php @@ -5,63 +5,56 @@ class ConsistentForeignKeyNames extends Migration { public function up() { - $this->chunkKeyRenaming(new DocumentAPI, 'lrs', 'lrs_id'); - $this->chunkKeyRenaming(new Statement, 'lrs._id', 'lrs_id'); - $this->chunkKeyRenaming(new Lrs, 'owner._id', 'owner_id'); - $this->chunkKeyRenaming(new Report, 'lrs', 'lrs_id'); - $this->chunkKeyRenaming(new Export, 'lrs', 'lrs_id'); - } - - public function down() { - $this->chunkMongoIdRemoval(new DocumentAPI, 'lrs', 'lrs_id'); - $this->chunkMongoIdRemoval(new Statement, 'lrs._id', 'lrs_id'); - $this->chunkMongoIdRemoval(new Lrs, 'owner._id', 'owner_id'); - $this->chunkMongoIdRemoval(new Report, 'lrs', 'lrs_id'); - $this->chunkMongoIdRemoval(new Export, 'lrs', 'lrs_id'); - } + $db = \DB::getMongoDB(); - private function chunkKeyRenaming($model, $old_name, $new_name) { - $this->chunkModelMigration($model, $this->renameKey($old_name, $new_name)); - } + Lrs::get()->each(function (Lrs $lrs) use ($db) { + $convertToMongoId = function ($value) { + return new \MongoId($value); + }; + $this->changeForeignKey($db->statements, 'lrs._id', 'lrs_id', $lrs->_id, $convertToMongoId); + $this->changeForeignKey($db->documentapi, 'lrs', 'lrs_id', $lrs->_id, $convertToMongoId); + $this->changeForeignKey($db->reports, 'lrs', 'lrs_id', $lrs->_id, $convertToMongoId); + $this->changeForeignKey($db->exports, 'lrs', 'lrs_id', $lrs->_id, $convertToMongoId); - private function chunkMongoIdRemoval($model, $old_name, $new_name) { - $this->chunkModelMigration($model, $this->removeMongoId($old_name, $new_name)); - } + $lrs->owner_id = $convertToMongoId($lrs->owner['_id']); + $lrs->save(); - private function removeMongoId($old_name, $new_name) { - return function ($model) use ($old_name, $new_name) { - $value = $model->$new_name; - $model = $this->setKey($model, explode('.', $old_name), 0, (string) $value); - $model->save(); - }; - } + echo 'Models for "'.$lrs->title.'" converted.'.PHP_EOL; + }); - private function setKey($model, $keys, $key_index, $value) { - if ($key_index < count($keys) - 1) { - $model->{$keys[$key_index]} = (object) []; - $this->setKey($model->{$keys[$key_index]}, $keys, $key_index + 1, $value); - } else { - $model->{$keys[$key_index]} = $value; - } - return $model; - } + echo 'All finished, hopefully!'.PHP_EOL; + } - private function renameKey($old_name, $new_name) { - return function ($model) use ($old_name, $new_name) { - $value = array_reduce(explode('.', $old_name), function ($value, $key) { - return is_object($value) ? $value->{$key} : $value[$key]; - }, $model); - $model->$new_name = new \MongoId($value); - $model->save(); - }; + private function changeForeignKey($collection, $old_key, $new_key, $old_value, $modifier) { + $collection->update([ + $old_key => $old_value + ], [ + '$set' => [ + $new_key => $modifier($old_value) + ] + ], [ + 'multiple' => true + ]); } - private function chunkModelMigration($model, Callable $migration) { - $model->chunk(1000, function ($models) use ($migration) { - foreach ($models as $model){ - $migration($model); - } - echo count($models) . ' converted.'.PHP_EOL; + public function down() { + $db = \DB::getMongoDB(); + + Lrs::get()->each(function (Lrs $lrs) use ($db) { + $convertToString = function ($value) { + return (string) $value; + }; + $this->changeForeignKey($db->statements, 'lrs_id', 'lrs._id', $lrs->_id, $convertToString); + $this->changeForeignKey($db->documentapi, 'lrs_id', 'lrs', $lrs->_id, $convertToString); + $this->changeForeignKey($db->reports, 'lrs_id', 'lrs', $lrs->_id, $convertToString); + $this->changeForeignKey($db->exports, 'lrs_id', 'lrs', $lrs->_id, $convertToString); + + $lrs->owner = [ + '_id' => $convertToString($lrs->owner_id) + ]; + $lrs->save(); + + echo 'Models for "'.$lrs->title.'" converted.'.PHP_EOL; }); echo 'All finished, hopefully!'.PHP_EOL; diff --git a/app/locker/repository/Document/EloquentDocumentRepository.php b/app/locker/repository/Document/EloquentDocumentRepository.php index 9e887de0e7..be1f73f151 100644 --- a/app/locker/repository/Document/EloquentDocumentRepository.php +++ b/app/locker/repository/Document/EloquentDocumentRepository.php @@ -387,7 +387,6 @@ public function storeActivityDoc( $options, $data, $updated, $method ){ $document->updated_at = new Carbon($updated); $document->setContent( $data['content_info'], $method ); //set the content for the document - if( $document->save() ){ return $document; } diff --git a/app/locker/repository/File/Factory.php b/app/locker/repository/File/Factory.php new file mode 100644 index 0000000000..175273155d --- /dev/null +++ b/app/locker/repository/File/Factory.php @@ -0,0 +1,28 @@ + 'LocalFlyRepository', + 'Rackspace' => 'RackspaceFlyRepository', + ]; + $repo = ucfirst(strtolower($repo)); + $conf = function ($var) { + return Helpers::getEnvVar($var); + }; + + if (isset($repos[$repo])) { + $selected_repo = 'Locker\Repository\File\\'.$repos[$repo]; + return new $selected_repo($conf); + } else { + throw new \Exception('Valid `FS_REPO` not specified in ".env.'.\App::environment().'.php". Valid values include: "'.implode('", "', array_keys($repos)).'". You provided "'.$repo.'".'); + } + } +} \ No newline at end of file diff --git a/app/locker/repository/File/FlyRepository.php b/app/locker/repository/File/FlyRepository.php new file mode 100644 index 0000000000..15dd502430 --- /dev/null +++ b/app/locker/repository/File/FlyRepository.php @@ -0,0 +1,46 @@ +filesystem = new Filesystem($adapter); + } + + public function index(array $opts) { + return $this->filesystem->listContents('.', true); + } + + public function show($id, array $opts) { + return $this->filesystem->read($id); + } + + public function destroy($id, array $opts) { + $this->filesystem->delete($id); + return true; + } + + public function store(array $data, array $opts) { + throw new \BadMethodCallException(); + } + + public function update($id, array $data, array $opts) { + $this->filesystem->put($id, $data['content']); + return $data; + } + + public function stream($id, array $opts) { + $stream = $this->filesystem->readStream($id); + fseek($stream, 0); + return $stream; + } + + public function has($id, array $opts) { + return $this->filesystem->has($id); + } + +} diff --git a/app/locker/repository/File/LocalFlyRepository.php b/app/locker/repository/File/LocalFlyRepository.php new file mode 100644 index 0000000000..af3c7d9575 --- /dev/null +++ b/app/locker/repository/File/LocalFlyRepository.php @@ -0,0 +1,11 @@ +constructFileSystem($adapter); + } + +} diff --git a/app/locker/repository/File/RackspaceFlyRepository.php b/app/locker/repository/File/RackspaceFlyRepository.php new file mode 100644 index 0000000000..1a9d681639 --- /dev/null +++ b/app/locker/repository/File/RackspaceFlyRepository.php @@ -0,0 +1,18 @@ + $conf('FS_RACK_USERNAME'), + 'apiKey' => $conf('FS_RACK_API_KEY'), + ]); + $store = $client->objectStoreService('cloudFiles', $conf('FS_RACK_REGION')); + $container = $store->getContainer($conf('FS_RACK_CONTAINER')); + $adapter = new RackspaceAdapter($container); + $this->constructFileSystem($adapter); + } + +} diff --git a/app/locker/repository/File/Repository.php b/app/locker/repository/File/Repository.php new file mode 100644 index 0000000000..2bacfa02a8 --- /dev/null +++ b/app/locker/repository/File/Repository.php @@ -0,0 +1,4 @@ + 'Local', + 'FS_LOCAL_ENDPOINT' => __DIR__.'/uploads', +``` + +If you're migrating from another file repository (i.e. "Rackspace"), you'll need to run the command below. + +```shell +php artisan ll:file-repo Local -f Rackspace +``` + +### Rackspace +Your .env.local.php file will need to include `OpenCloud\Rackspace` (below ` 'Rackspace', + 'FS_RACK_ENDPOINT' => Rackspace::UK_IDENTITY_ENDPOINT, + 'FS_RACK_USERNAME' => 'YOUR USERNAME', + 'FS_RACK_API_KEY' => 'YOUR API KEY', + 'FS_RACK_REGION' => 'LON', + 'FS_RACK_CONTAINER' => 'YOUR CONTAINER', +``` + +If you're migrating from another file repository (i.e. "Local"), you'll need to run the command below. + +```shell +php artisan ll:file-repo Rackspace -f Local +``` + +### Useful Development Links +- http://docs.rackspace.com/sdks/api/php/class-OpenCloud.Rackspace.html +- http://flysystem.thephpleague.com/api/ +- https://github.com/thephpleague/flysystem-aws-s3-v2/issues/3 +- https://github.com/thephpleague/flysystem-rackspace/issues/7 \ No newline at end of file diff --git a/app/locker/repository/RepositoryServiceProvider.php b/app/locker/repository/RepositoryServiceProvider.php index 7ca0616e94..7589be40e0 100644 --- a/app/locker/repository/RepositoryServiceProvider.php +++ b/app/locker/repository/RepositoryServiceProvider.php @@ -49,4 +49,4 @@ public function register(){ } -} \ No newline at end of file +} diff --git a/app/locker/repository/Statement/FileAttacher.php b/app/locker/repository/Statement/FileAttacher.php index c48cffd3be..9ad87a5ebc 100644 --- a/app/locker/repository/Statement/FileAttacher.php +++ b/app/locker/repository/Statement/FileAttacher.php @@ -1,8 +1,8 @@ getDir($opts); - if (!is_dir($dir) && count($attachments > 0) && !empty($attachments)) { - mkdir($dir, 0775, true); - } foreach ($attachments as $attachment) { if (!in_array($attachment->hash, $hashes)) throw new Exceptions\Exception( @@ -28,8 +25,8 @@ public function store(array $attachments, array $hashes, StoreOptions $opts) { 'This file type cannot be supported' ); - $file = $attachment->hash.'.'.$ext; - file_put_contents($dir.$file, $attachment->content); + $filename = $attachment->hash.'.'.$ext; + FileFactory::create()->update($dir.$filename, ['content' => $attachment->content], []); } } @@ -50,7 +47,7 @@ public function index(array $statements, IndexOptions $opts) { return (object) [ 'content_type' => $attachment->contentType, 'hash' => $attachment->sha2, - 'content' => file_get_contents($dir.$filename) + 'content' => FileFactory::create()->stream($dir.$filename, []) ]; }, isset($statement->attachments) ? $statement->attachments : [])); } @@ -73,6 +70,6 @@ private function getExt($content_type) { * @return String */ private function getDir(Options $opts) { - return Helpers::getEnvVar('LOCAL_FILESTORE').'/'.$opts->getOpt('lrs_id').'/attachments/'; + return $opts->getOpt('lrs_id').'/attachments/'; } } diff --git a/app/models/DocumentAPI.php b/app/models/DocumentAPI.php index 87b45c99fb..1f88dfe7e8 100644 --- a/app/models/DocumentAPI.php +++ b/app/models/DocumentAPI.php @@ -7,8 +7,9 @@ use Jenssegers\Mongodb\Model as Eloquent; use Symfony\Component\HttpFoundation\File\UploadedFile; use Locker\Repository\Document\FileTypes; -use \Locker\Helpers\Helpers as Helpers; -use \Locker\Helpers\Exceptions as Exceptions; +use Locker\Helpers\Helpers as Helpers; +use Locker\Helpers\Exceptions as Exceptions; +use Locker\Repository\File\Factory as FileFactory; class DocumentAPI extends Eloquent { protected $collection = 'documentapi'; @@ -38,12 +39,6 @@ private function putContent($content, $contentType) { } } - private function ammendmentError() { - return new Exceptions\Exception( - "Cannot amend existing {$this->contentType} document with a string" - ); - } - private function postContent($content, $contentType) { if( $this->exists ){ @@ -94,13 +89,19 @@ private function saveDocument($content, $contentType) { $origname = $content->getClientOriginalName(); $parts = pathinfo($origname); $filename = Str::slug(Str::lower($parts['filename'])).'-'.time().'.'.$parts['extension']; - $content->move($dir, $filename); + + // Stores the file in a temporary location before writing it to the FileRepository. + $tmp_location = __DIR__.'/../../uploads/tmp'; + $content->move($tmp_location, $filename); + $data = file_get_contents($tmp_location.'/'.$filename); + FileFactory::create()->update($dir.$filename, ['content' => $data], []); + unlink($tmp_location.'/'.$filename); } else { $ext = array_search($contentType, FileTypes::getMap()); $filename = time().'_'.mt_rand(0,1000).($ext !== false ? '.'.$ext : ''); - $size = file_put_contents($dir.$filename, $content); + $size = FileFactory::create()->update($dir.$filename, ['content' => $content], []); if ($size === false) throw new Exceptions\Exception('There was an issue saving the content'); } @@ -136,12 +137,7 @@ public function setContent( $content_info, $method){ } public function getContentDir(){ - $dir = Helpers::getEnvVar('LOCAL_FILESTORE').'/'.$this->lrs_id.'/documents/'; - if( !file_exists($dir) ){ - mkdir( $dir, 0774, true ); - } - - return $dir; + return $this->lrs_id.'/documents/'; } public function getFilePath(){ diff --git a/app/start/artisan.php b/app/start/artisan.php index d8ef9049d2..5df5f86041 100644 --- a/app/start/artisan.php +++ b/app/start/artisan.php @@ -14,3 +14,4 @@ Artisan::add(new ConvertTimestamp); Artisan::add(new ReportMigrateCommand); Artisan::add(new StatementMigrateCommand); +Artisan::add(new FileRepositoryCommand); diff --git a/app/tests/routes/OAuthTest.php b/app/tests/routes/OAuthTest.php index 0287a20bd1..ccf7017b9a 100644 --- a/app/tests/routes/OAuthTest.php +++ b/app/tests/routes/OAuthTest.php @@ -61,8 +61,9 @@ public function testAccessTokenViaClient() { public function testAccessViaToken() { $token_response = $this->requestToken($this->ll_client->api['basic_key'], $this->ll_client->api['basic_secret']); $token = json_decode($token_response->getContent())->access_token; + ob_start(); $response = $this->requestApi($token); - $data = $response->getContent(); + $data = ob_get_clean(); // Checks result object. $this->assertEquals('{"more":"","statements":[]}', $data); diff --git a/app/tests/routes/StatementAttachmentTest.php b/app/tests/routes/StatementAttachmentTest.php index 51d41862d1..506cb45499 100644 --- a/app/tests/routes/StatementAttachmentTest.php +++ b/app/tests/routes/StatementAttachmentTest.php @@ -85,6 +85,7 @@ public function testIndexAttachments() { ->where('statement.id', $this->statement_id) ->first()->statement; + ob_start(); $response = $this->requestStatements('GET', [ 'attachments' => true ]); @@ -93,7 +94,7 @@ public function testIndexAttachments() { $attachment['content'] .= PHP_EOL; // Checks that the content is correct. - $actual_content = str_replace("\r", "", $response->getContent()); + $actual_content = str_replace("\r", "", ob_get_clean()); $expected_content = str_replace("\r", "", $this->generateContent(json_encode([ 'more' => '', 'statements' => [$statement] @@ -120,7 +121,7 @@ private function deleteDirectory($dir) { public function tearDown() { parent::tearDown(); - $dir = Helpers::getEnvVar('LOCAL_FILESTORE').'/'.$this->lrs->_id; + $dir = Helpers::getEnvVar('FS_LOCAL_ENDPOINT').'/'.$this->lrs->_id; $this->deleteDirectory($dir); (new \Statement) ->where('lrs_id', new \MongoId($this->lrs->_id)) diff --git a/composer.json b/composer.json index 25e8986c2b..94bfd55410 100644 --- a/composer.json +++ b/composer.json @@ -43,7 +43,9 @@ "learninglocker/statementfactory": "0.0.x", "bshaffer/oauth2-server-php": "1.7.x", "bshaffer/oauth2-server-httpfoundation-bridge": "1.1.x", - "jenssegers/mongodb-session": "~1.0" + "jenssegers/mongodb-session": "~1.0", + "league/flysystem": "~1.0", + "league/flysystem-rackspace": "^1.0" }, "require-dev": { "phpunit/phpunit": "4.1.*" diff --git a/composer.lock b/composer.lock index 2aefb6ed90..2e3cb2ce76 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "5c3730d30a2f50f10a2943aa9f48ddcb", + "hash": "3737a0e9c0e7630f6bdb771cf54a1ffd", "packages": [ { "name": "andywer/js-localization", @@ -433,6 +433,101 @@ ], "time": "2015-06-29 05:42:04" }, + { + "name": "guzzle/guzzle", + "version": "v3.9.3", + "source": { + "type": "git", + "url": "https://github.com/guzzle/guzzle3.git", + "reference": "0645b70d953bc1c067bbc8d5bc53194706b628d9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/guzzle3/zipball/0645b70d953bc1c067bbc8d5bc53194706b628d9", + "reference": "0645b70d953bc1c067bbc8d5bc53194706b628d9", + "shasum": "" + }, + "require": { + "ext-curl": "*", + "php": ">=5.3.3", + "symfony/event-dispatcher": "~2.1" + }, + "replace": { + "guzzle/batch": "self.version", + "guzzle/cache": "self.version", + "guzzle/common": "self.version", + "guzzle/http": "self.version", + "guzzle/inflection": "self.version", + "guzzle/iterator": "self.version", + "guzzle/log": "self.version", + "guzzle/parser": "self.version", + "guzzle/plugin": "self.version", + "guzzle/plugin-async": "self.version", + "guzzle/plugin-backoff": "self.version", + "guzzle/plugin-cache": "self.version", + "guzzle/plugin-cookie": "self.version", + "guzzle/plugin-curlauth": "self.version", + "guzzle/plugin-error-response": "self.version", + "guzzle/plugin-history": "self.version", + "guzzle/plugin-log": "self.version", + "guzzle/plugin-md5": "self.version", + "guzzle/plugin-mock": "self.version", + "guzzle/plugin-oauth": "self.version", + "guzzle/service": "self.version", + "guzzle/stream": "self.version" + }, + "require-dev": { + "doctrine/cache": "~1.3", + "monolog/monolog": "~1.0", + "phpunit/phpunit": "3.7.*", + "psr/log": "~1.0", + "symfony/class-loader": "~2.1", + "zendframework/zend-cache": "2.*,<2.3", + "zendframework/zend-log": "2.*,<2.3" + }, + "suggest": { + "guzzlehttp/guzzle": "Guzzle 5 has moved to a new package name. The package you have installed, Guzzle 3, is deprecated." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.9-dev" + } + }, + "autoload": { + "psr-0": { + "Guzzle": "src/", + "Guzzle\\Tests": "tests/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Guzzle Community", + "homepage": "https://github.com/guzzle/guzzle/contributors" + } + ], + "description": "PHP HTTP client. This library is deprecated in favor of https://packagist.org/packages/guzzlehttp/guzzle", + "homepage": "http://guzzlephp.org/", + "keywords": [ + "client", + "curl", + "framework", + "http", + "http client", + "rest", + "web service" + ], + "time": "2015-03-18 18:23:50" + }, { "name": "ircmaxell/password-compat", "version": "v1.0.4", @@ -741,6 +836,134 @@ ], "time": "2015-02-11 20:37:15" }, + { + "name": "league/flysystem", + "version": "1.0.11", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/flysystem.git", + "reference": "c16222fdc02467eaa12cb6d6d0e65527741f6040" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/c16222fdc02467eaa12cb6d6d0e65527741f6040", + "reference": "c16222fdc02467eaa12cb6d6d0e65527741f6040", + "shasum": "" + }, + "require": { + "php": ">=5.4.0" + }, + "require-dev": { + "ext-fileinfo": "*", + "mockery/mockery": "~0.9", + "phpspec/phpspec": "^2.2", + "phpspec/prophecy-phpunit": "~1.0", + "phpunit/phpunit": "~4.1" + }, + "suggest": { + "ext-fileinfo": "Required for MimeType", + "league/flysystem-aws-s3-v2": "Allows you to use S3 storage with AWS SDK v2", + "league/flysystem-aws-s3-v3": "Allows you to use S3 storage with AWS SDK v3", + "league/flysystem-azure": "Allows you to use Windows Azure Blob storage", + "league/flysystem-cached-adapter": "Flysystem adapter decorator for metadata caching", + "league/flysystem-copy": "Allows you to use Copy.com storage", + "league/flysystem-dropbox": "Allows you to use Dropbox storage", + "league/flysystem-eventable-filesystem": "Allows you to use EventableFilesystem", + "league/flysystem-rackspace": "Allows you to use Rackspace Cloud Files", + "league/flysystem-sftp": "Allows you to use SFTP server storage via phpseclib", + "league/flysystem-webdav": "Allows you to use WebDAV storage", + "league/flysystem-ziparchive": "Allows you to use ZipArchive adapter" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Flysystem\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frank de Jonge", + "email": "info@frenky.net" + } + ], + "description": "Filesystem abstraction: Many filesystems, one API.", + "keywords": [ + "Cloud Files", + "WebDAV", + "abstraction", + "aws", + "cloud", + "copy.com", + "dropbox", + "file systems", + "files", + "filesystem", + "filesystems", + "ftp", + "rackspace", + "remote", + "s3", + "sftp", + "storage" + ], + "time": "2015-07-28 20:41:58" + }, + { + "name": "league/flysystem-rackspace", + "version": "1.0.2", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/flysystem-rackspace.git", + "reference": "d60b1b0a270d51b74a17c879de04e615ead84d38" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/flysystem-rackspace/zipball/d60b1b0a270d51b74a17c879de04e615ead84d38", + "reference": "d60b1b0a270d51b74a17c879de04e615ead84d38", + "shasum": "" + }, + "require": { + "league/flysystem": "~1.0", + "php": ">=5.4.0", + "rackspace/php-opencloud": "~1.12" + }, + "require-dev": { + "mockery/mockery": "0.9.*", + "phpunit/phpunit": "~4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Flysystem\\Rackspace\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frank de Jonge", + "email": "info@frenky.net" + } + ], + "description": "Flysystem adapter for Rackspace", + "time": "2015-06-11 14:35:30" + }, { "name": "learninglocker/statementfactory", "version": "v0.0.2", @@ -774,6 +997,33 @@ "description": "Learning Locker's classes for xAPI.", "time": "2015-05-13 14:33:50" }, + { + "name": "mikemccabe/json-patch-php", + "version": "0.1.0", + "source": { + "type": "git", + "url": "https://github.com/mikemccabe/json-patch-php.git", + "reference": "b3af30a6aec7f6467c773cd49b2d974a70f7c0d4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mikemccabe/json-patch-php/zipball/b3af30a6aec7f6467c773cd49b2d974a70f7c0d4", + "reference": "b3af30a6aec7f6467c773cd49b2d974a70f7c0d4", + "shasum": "" + }, + "type": "library", + "autoload": { + "psr-4": { + "mikemccabe\\JsonPatch\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-3.0" + ], + "description": "Produce and apply json-patch objects", + "time": "2015-01-05 21:19:54" + }, { "name": "monolog/monolog", "version": "1.16.0", @@ -1229,6 +1479,73 @@ ], "time": "2012-12-21 11:40:51" }, + { + "name": "rackspace/php-opencloud", + "version": "v1.15.0", + "source": { + "type": "git", + "url": "https://github.com/rackspace/php-opencloud.git", + "reference": "6737f9e4580106fa7b690fdad4f8016c99e2f445" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/rackspace/php-opencloud/zipball/6737f9e4580106fa7b690fdad4f8016c99e2f445", + "reference": "6737f9e4580106fa7b690fdad4f8016c99e2f445", + "shasum": "" + }, + "require": { + "guzzle/guzzle": "~3.8", + "mikemccabe/json-patch-php": "~0.1", + "php": ">=5.4", + "psr/log": "~1.0" + }, + "require-dev": { + "apigen/apigen": "~4.0", + "fabpot/php-cs-fixer": "1.0.*@dev", + "jakub-onderka/php-parallel-lint": "0.*", + "phpspec/prophecy": "~1.4", + "phpunit/phpunit": "4.3.*", + "satooshi/php-coveralls": "0.6.*@dev" + }, + "type": "library", + "autoload": { + "psr-0": { + "OpenCloud": [ + "lib/", + "tests/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Glen Campbell", + "email": "glen.campbell@rackspace.com" + }, + { + "name": "Jamie Hannaford", + "email": "jamie.hannaford@rackspace.com", + "homepage": "https://github.com/jamiehannaford" + }, + { + "name": "Shaunak Kashyap", + "email": "shaunak.kashyap@rackspace.com", + "homepage": "https://github.com/ycombinator" + } + ], + "description": "PHP SDK for Rackspace/OpenStack APIs", + "keywords": [ + "Openstack", + "nova", + "opencloud", + "rackspace", + "swift" + ], + "time": "2015-08-04 08:48:38" + }, { "name": "ramsey/array_column", "version": "1.1.3", diff --git a/q b/q deleted file mode 100644 index c4de88ad74..0000000000 --- a/q +++ /dev/null @@ -1,10 +0,0 @@ - Ryan Smith  Fixes owners. 2 hours ago - Ryan Smith  Fixes users. 2 hours ago - Ryan Smith  Refactors to lrs_id. 2 hours ago - Ryan Smith  Begins migration of foreign keys. 4 hours ago - daniel-abbey  Update VERSION 6 days ago - daniel-abbey  Merge pull request #707 from LearningLocker/issue/config 6 days ago - daniel-abbey  Merge pull request #706 from LearningLocker/feature/dailylogs2 6 days ago - daniel-abbey  Merge pull request #705 from LearningLocker/feature/password_reset 6 days ago - Ryan Smith  Only ignores php files in local config. 9 days ago - Ryan Smith  Attempts to improve config. 9 days ago \ No newline at end of file