diff --git a/app/Http/Controllers/Api/APICRUDController.php b/app/Http/Controllers/Api/APICRUDController.php index 5cb924bf..44b977b5 100644 --- a/app/Http/Controllers/Api/APICRUDController.php +++ b/app/Http/Controllers/Api/APICRUDController.php @@ -111,7 +111,7 @@ abstract class APICRUDController extends JsonController * @return array */ protected function getUpdatePayload():array{ - return Input::All(); + return request()->all(); } /** @@ -131,6 +131,10 @@ abstract class APICRUDController extends JsonController protected function curateCreatePayload(array $payload):array { return $payload; } + + protected function onUpdate($id, $payload){ + return $this->service->update($id, $payload); + } /** * @param $id * @param array $payload @@ -148,7 +152,7 @@ abstract class APICRUDController extends JsonController throw $ex->setMessages($validation->messages()->toArray()); } - $entity = $this->service->update($id, $this->curateUpdatePayload($payload)); + $entity = $this->onUpdate($id, $this->curateUpdatePayload($payload)); return $this->updated(SerializerRegistry::getInstance()->getSerializer($entity, $this->serializerType())->serialize()); } diff --git a/app/Http/Controllers/Api/UserApiController.php b/app/Http/Controllers/Api/UserApiController.php index afed16c0..e465ddc7 100644 --- a/app/Http/Controllers/Api/UserApiController.php +++ b/app/Http/Controllers/Api/UserApiController.php @@ -11,6 +11,7 @@ * See the License for the specific language governing permissions and * limitations under the License. **/ + use App\Http\Controllers\APICRUDController; use App\Http\Utils\HTMLCleaner; use App\ModelSerializers\SerializerRegistry; @@ -24,11 +25,14 @@ use OAuth2\Services\ITokenService; use OpenId\Services\IUserService; use models\exceptions\EntityNotFoundException; use Utils\Services\ILogService; +use Illuminate\Http\Request as LaravelRequest; + /** * Class UserApiController * @package App\Http\Controllers\Api */ -final class UserApiController extends APICRUDController { +final class UserApiController extends APICRUDController +{ /** * @var ITokenService @@ -48,7 +52,8 @@ final class UserApiController extends APICRUDController { ILogService $log_service, IUserService $user_service, ITokenService $token_service - ){ + ) + { parent::__construct($user_repository, $user_service, $log_service); $this->token_service = $token_service; } @@ -56,26 +61,26 @@ final class UserApiController extends APICRUDController { /** * @return array */ - protected function getFilterRules():array + protected function getFilterRules(): array { return [ - 'first_name' => ['=@', '=='], - 'last_name' => ['=@', '=='], - 'full_name' => ['=@', '=='], - 'email' => ['=@', '=='], + 'first_name' => ['=@', '=='], + 'last_name' => ['=@', '=='], + 'full_name' => ['=@', '=='], + 'email' => ['=@', '=='], ]; } /** * @return array */ - protected function getFilterValidatorRules():array + protected function getFilterValidatorRules(): array { return [ - 'first_name' => 'nullable|string', - 'last_name' => 'nullable|string', - 'full_name' => 'nullable|string', - 'email' => 'nullable|string', + 'first_name' => 'nullable|string', + 'last_name' => 'nullable|string', + 'full_name' => 'nullable|string', + 'email' => 'nullable|string', ]; } @@ -83,22 +88,18 @@ final class UserApiController extends APICRUDController { * @param $id * @return mixed */ - public function unlock($id){ + public function unlock($id) + { try { $entity = $this->service->unlockUser($id); return $this->updated(SerializerRegistry::getInstance()->getSerializer($entity)->serialize()); - } - catch (ValidationException $ex1) - { + } catch (ValidationException $ex1) { Log::warning($ex1); return $this->error412(array($ex1->getMessage())); - } - catch (EntityNotFoundException $ex2) - { + } catch (EntityNotFoundException $ex2) { Log::warning($ex2); return $this->error404(array('message' => $ex2->getMessage())); - } - catch (Exception $ex) { + } catch (Exception $ex) { Log::error($ex); return $this->error500($ex); } @@ -108,28 +109,25 @@ final class UserApiController extends APICRUDController { * @param $id * @return mixed */ - public function lock($id){ + public function lock($id) + { try { $entity = $this->service->lockUser($id); return $this->updated(SerializerRegistry::getInstance()->getSerializer($entity)->serialize()); - } - catch (ValidationException $ex1) - { + } catch (ValidationException $ex1) { Log::warning($ex1); return $this->error412(array($ex1->getMessage())); - } - catch (EntityNotFoundException $ex2) - { + } catch (EntityNotFoundException $ex2) { Log::warning($ex2); return $this->error404(array('message' => $ex2->getMessage())); - } - catch (Exception $ex) { + } catch (Exception $ex) { Log::error($ex); return $this->error500($ex); } } - protected function getAllSerializerType():string{ + protected function getAllSerializerType(): string + { return SerializerRegistry::SerializerType_Private; } @@ -138,36 +136,33 @@ final class UserApiController extends APICRUDController { * @param $value * @return mixed */ - public function revokeMyToken($value){ + public function revokeMyToken($value) + { - try{ - $hint = Input::get('hint','none'); + try { + $hint = Input::get('hint', 'none'); - switch($hint){ - case 'access-token':{ - $this->token_service->revokeAccessToken($value,true); - } - break; + switch ($hint) { + case 'access-token': + { + $this->token_service->revokeAccessToken($value, true); + } + break; case 'refresh-token': - $this->token_service->revokeRefreshToken($value,true); + $this->token_service->revokeRefreshToken($value, true); break; default: - throw new Exception(sprintf("hint %s not allowed",$hint)); + throw new Exception(sprintf("hint %s not allowed", $hint)); break; } return $this->deleted(); - } - catch (ValidationException $ex1) - { + } catch (ValidationException $ex1) { Log::warning($ex1); - return $this->error412(array( $ex1->getMessage())); - } - catch (EntityNotFoundException $ex2) - { + return $this->error412(array($ex1->getMessage())); + } catch (EntityNotFoundException $ex2) { Log::warning($ex2); return $this->error404(array('message' => $ex2->getMessage())); - } - catch (Exception $ex) { + } catch (Exception $ex) { Log::error($ex); return $this->error500($ex); } @@ -179,44 +174,46 @@ final class UserApiController extends APICRUDController { protected function getUpdatePayloadValidationRules(): array { return [ - 'first_name' => 'required|string', - 'last_name' => 'required|string', - 'email' => 'required|email', - 'identifier' => 'sometimes|string', - 'bio' => 'nullable|string', - 'address1' => 'nullable|string', - 'address2' => 'nullable|string', - 'city' => 'nullable|string', - 'state' => 'nullable|string', - 'post_code' => 'nullable|string', - 'country_iso_code' => 'nullable|country_iso_alpha2_code', - 'second_email' => 'nullable|email', - 'third_email' => 'nullable|email', - 'gender' => 'nullable|string', - 'gender_specify' => 'nullable|string', - 'statement_of_interest' => 'nullable|string', - 'irc' => 'nullable|string', - 'linked_in_profile' => 'nullable|string', - 'github_user' => 'nullable|string', - 'wechat_user' => 'nullable|string', - 'twitter_name' => 'nullable|string', - 'language' => 'nullable|string', - 'birthday' => 'nullable|date_format:U', - 'password' => 'sometimes|string|min:8|confirmed', - 'email_verified' => 'nullable|boolean', - 'active' => 'nullable|boolean', - 'phone_number' => 'nullable|string', - 'company' => 'nullable|string', + 'first_name' => 'required|string', + 'last_name' => 'required|string', + 'email' => 'required|email', + 'identifier' => 'sometimes|string', + 'bio' => 'nullable|string', + 'address1' => 'nullable|string', + 'address2' => 'nullable|string', + 'city' => 'nullable|string', + 'state' => 'nullable|string', + 'post_code' => 'nullable|string', + 'country_iso_code' => 'nullable|country_iso_alpha2_code', + 'second_email' => 'nullable|email', + 'third_email' => 'nullable|email', + 'gender' => 'nullable|string', + 'gender_specify' => 'nullable|string', + 'statement_of_interest' => 'nullable|string', + 'irc' => 'nullable|string', + 'linked_in_profile' => 'nullable|string', + 'github_user' => 'nullable|string', + 'wechat_user' => 'nullable|string', + 'twitter_name' => 'nullable|string', + 'language' => 'nullable|string', + 'birthday' => 'nullable|date_format:U', + 'password' => 'sometimes|string|min:8|confirmed', + 'email_verified' => 'nullable|boolean', + 'active' => 'nullable|boolean', + 'phone_number' => 'nullable|string', + 'company' => 'nullable|string', ]; } - protected function curateUpdatePayload(array $payload):array { + protected function curateUpdatePayload(array $payload): array + { return HTMLCleaner::cleanData($payload, [ 'bio', 'statement_of_interest' ]); } - protected function curateCreatePayload(array $payload):array { + protected function curateCreatePayload(array $payload): array + { return HTMLCleaner::cleanData($payload, [ 'bio', 'statement_of_interest' ]); @@ -228,47 +225,77 @@ final class UserApiController extends APICRUDController { protected function getCreatePayloadValidationRules(): array { return [ - 'first_name' => 'required|string', - 'last_name' => 'required|string', - 'email' => 'required|email', - 'identifier' => 'sometimes|string', - 'bio' => 'nullable|string', - 'address1' => 'nullable|string', - 'address2' => 'nullable|string', - 'city' => 'nullable|string', - 'state' => 'nullable|string', - 'post_code' => 'nullable|string', - 'country_iso_code' => 'nullable|country_iso_alpha2_code', - 'second_email' => 'nullable|email', - 'third_email' => 'nullable|email', - 'gender' => 'nullable|string', - 'statement_of_interest' => 'nullable|string', - 'irc' => 'nullable|string', - 'linked_in_profile' => 'nullable|string', - 'github_user' => 'nullable|string', - 'wechat_user' => 'nullable|string', - 'twitter_name' => 'nullable|string', - 'language' => 'nullable|string', - 'birthday' => 'nullable|date_format:U', - 'password' => 'sometimes|string|min:8|confirmed', - 'email_verified' => 'nullable|boolean', - 'active' => 'nullable|boolean', - 'phone_number' => 'nullable|string', - 'company' => 'nullable|string', + 'first_name' => 'required|string', + 'last_name' => 'required|string', + 'email' => 'required|email', + 'identifier' => 'sometimes|string', + 'bio' => 'nullable|string', + 'address1' => 'nullable|string', + 'address2' => 'nullable|string', + 'city' => 'nullable|string', + 'state' => 'nullable|string', + 'post_code' => 'nullable|string', + 'country_iso_code' => 'nullable|country_iso_alpha2_code', + 'second_email' => 'nullable|email', + 'third_email' => 'nullable|email', + 'gender' => 'nullable|string', + 'statement_of_interest' => 'nullable|string', + 'irc' => 'nullable|string', + 'linked_in_profile' => 'nullable|string', + 'github_user' => 'nullable|string', + 'wechat_user' => 'nullable|string', + 'twitter_name' => 'nullable|string', + 'language' => 'nullable|string', + 'birthday' => 'nullable|date_format:U', + 'password' => 'sometimes|string|min:8|confirmed', + 'email_verified' => 'nullable|boolean', + 'active' => 'nullable|boolean', + 'phone_number' => 'nullable|string', + 'company' => 'nullable|string', ]; } + /** + * @param LaravelRequest $request * @return \Illuminate\Http\JsonResponse|mixed */ - public function updateMe(){ - if(!Auth::check()) + public function updateMe(LaravelRequest $request) + { + if (!Auth::check()) return $this->error403(); + $myId = Auth::user()->getId(); return $this->update($myId); } - protected function serializerType():string{ + /** + * @return array + */ + protected function getUpdatePayload():array{ + $payload = request()->all(); + if(isset($payload['user'])){ + return json_decode($payload['user'],true); + } + return $payload; + } + + /** + * @param $id + * @param $payload + * @return \models\utils\IEntity + */ + protected function onUpdate($id, $payload){ + $user = parent::onUpdate($id, $payload); + $file = request()->file('pic'); + if (!is_null($file)) { + $user = $this->service->updateProfilePhoto($id, $file); + } + return $user; + } + + protected function serializerType(): string + { return SerializerRegistry::SerializerType_Private; } } \ No newline at end of file diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php index 57c534f6..7d9ad205 100644 --- a/app/Http/Kernel.php +++ b/app/Http/Kernel.php @@ -29,7 +29,8 @@ class Kernel extends HttpKernel */ protected $middleware = [ \Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class, - \App\Http\Middleware\SingleAccessPoint::class + \App\Http\Middleware\SingleAccessPoint::class, + \App\Http\Middleware\ParseMultipartFormDataInputForNonPostRequests::class, ]; /** diff --git a/app/Http/Middleware/ParseMultipartFormDataInputForNonPostRequests.php b/app/Http/Middleware/ParseMultipartFormDataInputForNonPostRequests.php new file mode 100644 index 00000000..ac7b4399 --- /dev/null +++ b/app/Http/Middleware/ParseMultipartFormDataInputForNonPostRequests.php @@ -0,0 +1,56 @@ +method() == 'POST' OR $request->method() == 'GET') { + return $next($request); + } + + if (preg_match('/multipart\/form-data/', $request->headers->get('Content-Type')) or + preg_match('/multipart\/form-data/', $request->headers->get('content-type')) + ) { + $parser = new ParseMultiPartFormDataInputStream(file_get_contents('php://input')); + $params = $parser->getInput(); + $data = $params['parameters']; + $files = $params['files']; + if (count($files) > 0) { + Log::debug("ParseMultipartFormDataInputForNonPostRequests: files ".json_encode($files)); + $request->files->add($files); + } + + if (count($data) > 0) { + Log::debug("ParseMultipartFormDataInputForNonPostRequests: parameters ".json_encode($data)); + $request->request->add($data); + } + } + return $next($request); + } +} \ No newline at end of file diff --git a/app/Http/Utils/ParseMultiPartFormDataInputStream.php b/app/Http/Utils/ParseMultiPartFormDataInputStream.php index c262377f..857216ef 100644 --- a/app/Http/Utils/ParseMultiPartFormDataInputStream.php +++ b/app/Http/Utils/ParseMultiPartFormDataInputStream.php @@ -243,8 +243,12 @@ final class ParseMultiPartFormDataInputStream $val = self::boolVal($val); if(!empty($val) && is_int($val)) $val = intval($val); + if(!empty($val) && is_numeric($val)) + $val = intval($val); if(!empty($val) && is_double($val)) $val = doubleval($val); + if(!empty($val) && is_string($val)) + $val = strval($val); if (preg_match('/^(.*)\[\]$/i', $match[1], $tmp)) { $data[$tmp[1]][] = $val; } else { diff --git a/app/Services/OpenId/UserService.php b/app/Services/OpenId/UserService.php index 3ca2696e..644e7550 100644 --- a/app/Services/OpenId/UserService.php +++ b/app/Services/OpenId/UserService.php @@ -12,7 +12,6 @@ * limitations under the License. **/ use App\Events\UserEmailUpdated; -use App\Jobs\PublishUserCreated; use App\Jobs\PublishUserDeleted; use App\Jobs\PublishUserUpdated; use App\libs\Auth\Factories\UserFactory; @@ -21,9 +20,11 @@ use App\Services\AbstractService; use Auth\IUserNameGeneratorService; use Auth\Repositories\IUserRepository; use Auth\User; +use Illuminate\Http\UploadedFile; use Illuminate\Support\Facades\Config; use Illuminate\Support\Facades\Event; use Illuminate\Support\Facades\Log; +use Illuminate\Support\Facades\Storage; use models\exceptions\EntityNotFoundException; use models\exceptions\ValidationException; use models\utils\IEntity; @@ -31,6 +32,7 @@ use OpenId\Services\IUserService; use Utils\Db\ITransactionService; use Utils\Services\ILogService; use Utils\Services\IServerConfigurationService; +use App\libs\Utils\FileSystem\FileNameSanitizer; /** * Class UserService * @package Services\OpenId @@ -297,4 +299,37 @@ final class UserService extends AbstractService implements IUserService } }); } + + /** + * @inheritDoc + */ + public function updateProfilePhoto($user_id, UploadedFile $file, $max_file_size = 10485760): User + { + return $this->tx_service->transaction(function() use($user_id, $file, $max_file_size) { + $allowed_extensions = ['png', 'jpg', 'jpeg']; + + $user = $this->repository->getById($user_id); + if(is_null($user) || !$user instanceof User) + throw new EntityNotFoundException("user not found"); + $fileName = $file->getClientOriginalName(); + $fileExt = $file->extension() ?? pathinfo($fileName, PATHINFO_EXTENSION); + + if (!in_array($fileExt, $allowed_extensions)) { + throw new ValidationException(sprintf( "file does not has a valid extension (%s).", join(",", $allowed_extensions))); + } + if ($file->getSize() > $max_file_size) { + throw new ValidationException(sprintf("file exceeds max_file_size (%s MB).", ($max_file_size / 1024) / 1024)); + } + + // normalize fileName + $fileName = FileNameSanitizer::sanitize($fileName); + $path = User::getProfilePicFolder(); + + Storage::disk('swift')->putFileAs($path, $file, $fileName); + + $user->setPic($fileName); + + return $user; + }); + } } \ No newline at end of file diff --git a/app/libs/Auth/Models/User.php b/app/libs/Auth/Models/User.php index 7ba55979..d9628f05 100644 --- a/app/libs/Auth/Models/User.php +++ b/app/libs/Auth/Models/User.php @@ -21,6 +21,7 @@ use App\Events\UserEmailVerified; use Doctrine\Common\Collections\Criteria; use Illuminate\Auth\Authenticatable; use Illuminate\Support\Facades\Log; +use Illuminate\Support\Facades\Storage; use models\exceptions\ValidationException; use Models\OAuth2\ApiScope; use Models\OAuth2\Client; @@ -293,6 +294,12 @@ class User extends BaseEntity */ private $phone_number; + /** + * @ORM\Column(name="pic", type="string") + * @var string + */ + private $pic; + // relations /** @@ -798,9 +805,19 @@ class User extends BaseEntity */ public function getPic(): string { + if(!empty($this->pic)){ + return Storage::disk('swift')->url(sprintf("%s/%s", self::getProfilePicFolder(), $this->pic)); + } return $this->getGravatarUrl(); } + /** + * @param string $pic + */ + public function setPic(string $pic){ + $this->pic = $pic; + } + /** * Get either a Gravatar URL or complete image tag for a specified email address. */ @@ -1711,4 +1728,9 @@ SQL; $this->phone_number = $phone_number; } + const ProfilePicFolder = 'profile_pics'; + + public static function getProfilePicFolder():string{ + return self::ProfilePicFolder; + } } \ No newline at end of file diff --git a/app/libs/OpenId/Services/IUserService.php b/app/libs/OpenId/Services/IUserService.php index ad19134f..bc16e126 100644 --- a/app/libs/OpenId/Services/IUserService.php +++ b/app/libs/OpenId/Services/IUserService.php @@ -13,6 +13,7 @@ **/ use App\Services\IBaseService; use Auth\User; +use Illuminate\Http\UploadedFile; use models\exceptions\EntityNotFoundException; use models\exceptions\ValidationException; /** @@ -61,4 +62,14 @@ interface IUserService extends IBaseService */ public function saveProfileInfo($user_id, $show_pic, $show_full_name, $show_email, $identifier); + /** + * @param $user_id + * @param UploadedFile $file + * @param int $max_file_size + * @throws EntityNotFoundException + * @throws ValidationException + * @return User + */ + public function updateProfilePhoto($user_id, UploadedFile $file, $max_file_size = 10485760):User; + } \ No newline at end of file diff --git a/app/libs/Utils/FileSystem/FileNameSanitizer.php b/app/libs/Utils/FileSystem/FileNameSanitizer.php new file mode 100644 index 00000000..a3780b03 --- /dev/null +++ b/app/libs/Utils/FileSystem/FileNameSanitizer.php @@ -0,0 +1,40 @@ + '-', // remove whitespace + '/_/' => '-', // underscores to dashes + '/[^A-Za-z0-9+.\-]+/' => '', // remove non-ASCII chars, only allow alphanumeric plus dash and dot + '/[\-]{2,}/' => '-', // remove duplicate dashes + '/^[\.\-_]+/' => '', // Remove all leading dots, dashes or underscores + ]; + + /** + * @param string $filename + * @return string + */ + public static function sanitize(string $filename):string { + $filename = trim(Transliterator::utf8ToAscii($filename)); + foreach(self::$default_replacements as $regex => $replace) { + $filename = preg_replace($regex, $replace, $filename); + } + return $filename; + } +} \ No newline at end of file diff --git a/app/libs/Utils/FileSystem/SwiftAdapter.php b/app/libs/Utils/FileSystem/SwiftAdapter.php new file mode 100644 index 00000000..bf6e5480 --- /dev/null +++ b/app/libs/Utils/FileSystem/SwiftAdapter.php @@ -0,0 +1,357 @@ +setPathPrefix($prefix); + $this->container = $container; + } + + /** + * {@inheritdoc} + */ + public function write($path, $contents, Config $config, $size = 0) + { + $path = $this->applyPathPrefix($path); + + $data = $this->getWriteData($path, $config); + $type = 'content'; + + if (is_a($contents, 'GuzzleHttp\Psr7\Stream')) { + $type = 'stream'; + } + + $data[$type] = $contents; + + // Create large object if the stream is larger than 300 MiB (default). + if ($type === 'stream' && $size > $config->get('swiftLargeObjectThreshold', 314572800)) { + // Set the segment size to 100 MiB by default as suggested in OVH docs. + $data['segmentSize'] = $config->get('swiftSegmentSize', 104857600); + // Set segment container to the same container by default. + $data['segmentContainer'] = $config->get('swiftSegmentContainer', $this->container->name); + + $response = $this->container->createLargeObject($data); + } else { + $response = $this->container->createObject($data); + } + + return $this->normalizeObject($response); + } + + /** + * {@inheritdoc} + */ + public function writeStream($path, $resource, Config $config) + { + return $this->write($path, new Stream($resource), $config, fstat($resource)['size']); + } + + /** + * {@inheritdoc} + */ + public function update($path, $contents, Config $config) + { + return $this->write($path, $contents, $config); + } + + /** + * {@inheritdoc} + */ + public function updateStream($path, $resource, Config $config) + { + return $this->write($path, new Stream($resource), $config, fstat($resource)['size']); + } + + /** + * {@inheritdoc} + */ + public function rename($path, $newpath) + { + $object = $this->getObject($path); + $newLocation = $this->applyPathPrefix($newpath); + $destination = '/'.$this->container->name.'/'.ltrim($newLocation, '/'); + + try { + $response = $object->copy(compact('destination')); + } catch (BadResponseError $e) { + return false; + } + + $object->delete(); + + return true; + } + + /** + * {@inheritdoc} + */ + public function delete($path) + { + $object = $this->getObjectInstance($path); + + try { + $object->delete(); + } catch (BadResponseError $e) { + return false; + } + + return true; + } + + /** + * {@inheritdoc} + */ + public function deleteDir($dirname) + { + $objects = $this->container->listObjects([ + 'prefix' => $this->applyPathPrefix($dirname) + ]); + + try { + foreach ($objects as $object) { + $object->containerName = $this->container->name; + $object->delete(); + } + } catch (BadResponseError $e) { + return false; + } + + return true; + } + + /** + * {@inheritdoc} + */ + public function createDir($dirname, Config $config) + { + return ['path' => $dirname]; + } + + /** + * {@inheritdoc} + */ + public function has($path) + { + try { + $object = $this->getObject($path); + } catch (BadResponseError $e) { + $code = $e->getResponse()->getStatusCode(); + + if ($code == 404) return false; + + throw $e; + } + + return $this->normalizeObject($object); + } + + /** + * {@inheritdoc} + */ + public function read($path) + { + $object = $this->getObject($path); + $data = $this->normalizeObject($object); + + $stream = $object->download(); + $stream->rewind(); + $data['contents'] = $stream->getContents(); + + return $data; + } + + /** + * {@inheritdoc} + */ + public function readStream($path) + { + $object = $this->getObject($path); + $data = $this->normalizeObject($object); + + $stream = $object->download(); + $stream->rewind(); + $data['stream'] = StreamWrapper::getResource($stream); + + return $data; + } + + /** + * {@inheritdoc} + */ + public function listContents($directory = '', $recursive = false) + { + $location = $this->applyPathPrefix($directory); + + $objectList = $this->container->listObjects([ + 'prefix' => $directory + ]); + + $response = iterator_to_array($objectList); + + return Util::emulateDirectories(array_map([$this, 'normalizeObject'], $response)); + } + + /** + * {@inheritdoc} + */ + public function getMetadata($path) + { + $object = $this->getObject($path); + + return $this->normalizeObject($object); + } + + /** + * {@inheritdoc} + */ + public function getSize($path) + { + return $this->getMetadata($path); + } + + /** + * {@inheritdoc} + */ + public function getMimetype($path) + { + return $this->getMetadata($path); + } + + /** + * {@inheritdoc} + */ + public function getTimestamp($path) + { + return $this->getMetadata($path); + } + + /** + * Get the data properties to write or update an object. + * + * @param string $path + * @param Config $config + * + * @return array + */ + protected function getWriteData($path, $config) + { + return ['name' => $path]; + } + + /** + * Get an object instance. + * + * @param string $path + * + * @return StorageObject + */ + protected function getObjectInstance($path) + { + $location = $this->applyPathPrefix($path); + + $object = $this->container->getObject($location); + + return $object; + } + + /** + * Get an object instance and retrieve its metadata from storage. + * + * @param string $path + * + * @return StorageObject + */ + protected function getObject($path) + { + $object = $this->getObjectInstance($path); + $object->retrieve(); + + return $object; + } + + /** + * Normalize Openstack "StorageObject" object into an array + * + * @param StorageObject $object + * @return array + */ + protected function normalizeObject(StorageObject $object) + { + $name = $this->removePathPrefix($object->name); + $mimetype = explode('; ', $object->contentType); + + if ($object->lastModified instanceof \DateTimeInterface) { + $timestamp = $object->lastModified->getTimestamp(); + } else { + $timestamp = strtotime($object->lastModified); + } + + return [ + 'type' => 'file', + 'dirname' => Util::dirname($name), + 'path' => $name, + 'timestamp' => $timestamp, + 'mimetype' => reset($mimetype), + 'size' => $object->contentLength, + ]; + } + + + public function getTemporaryLink(string $path): ?string + { + return $this->getUrl($path); + } + + public function getTemporaryUrl(string $path): ?string + { + return $this->getUrl($path); + } + + public function getUrl(string $path): ?string + { + $obj = $this->container->getObject($path); + if(is_null($obj)) + return null; + return $obj->getPublicUri(); + } +} diff --git a/app/libs/Utils/FileSystem/SwiftServiceProvider.php b/app/libs/Utils/FileSystem/SwiftServiceProvider.php new file mode 100644 index 00000000..5e684483 --- /dev/null +++ b/app/libs/Utils/FileSystem/SwiftServiceProvider.php @@ -0,0 +1,85 @@ + $config["auth_url"], + 'region' => $config["region"], + ]; + + $userName = $config["user_name"] ?? null; + $userPassword = $config["api_key"] ?? null; + + if(!empty($userName) && !empty($userPassword)){ + + $configOptions['user'] = [ + 'name' => $userName, + 'password' => $userPassword, + 'domain' => ['id' => $config["user_domain"] ?? 'default'] + ]; + + $configOptions['scope' ] = [ + 'project' => [ + 'name' => $config["project_name"], + 'domain' => ['id' => $config["project_domain"] ?? 'default'] + ], + ]; + } + + $appCredentialId = $config["app_credential_id"] ?? null; + $appCredentialSecret = $config["app_credential_secret"] ?? null; + + if(!empty($appCredentialId) && !empty($appCredentialSecret)){ + $configOptions['application_credential'] = [ + 'id' => $appCredentialId, + 'secret' => $appCredentialSecret, + ]; + } + + $openstackClient = new OpenStack($configOptions); + + $container = $openstackClient->objectStoreV1()->getContainer($config["container"]); + + return new Filesystem(new SwiftAdapter($container)); + }); + } +} diff --git a/composer.json b/composer.json index 4473def2..04d7db14 100644 --- a/composer.json +++ b/composer.json @@ -11,6 +11,12 @@ ], "license": "MIT", "type": "project", + "repositories": [ + { + "type": "vcs", + "url": "https://github.com/OpenStackweb/openstack" + } + ], "require": { "php": "^7.1.3", "ext-json": "*", @@ -39,7 +45,9 @@ "sokil/php-isocodes": "^3.0", "vladimir-yuldashev/laravel-queue-rabbitmq": "v7.5.0", "zendframework/zend-crypt": "3.3.0", - "zendframework/zend-math": "3.1.1" + "zendframework/zend-math": "3.1.1", + "behat/transliterator": "^1.2", + "php-opencloud/openstack": "dev-master" }, "require-dev": { "filp/whoops": "^2.0", diff --git a/composer.lock b/composer.lock index 6af84719..fb15ca0f 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" ], - "content-hash": "0320ba04c31a757080ddb018434ada90", + "content-hash": "36cbe56b8b47426f7661fe182679a24d", "packages": [ { "name": "beberlei/doctrineextensions", @@ -60,6 +60,51 @@ ], "time": "2018-09-26T16:18:17+00:00" }, + { + "name": "behat/transliterator", + "version": "v1.3.0", + "source": { + "type": "git", + "url": "https://github.com/Behat/Transliterator.git", + "reference": "3c4ec1d77c3d05caa1f0bf8fb3aae4845005c7fc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Behat/Transliterator/zipball/3c4ec1d77c3d05caa1f0bf8fb3aae4845005c7fc", + "reference": "3c4ec1d77c3d05caa1f0bf8fb3aae4845005c7fc", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "chuyskywalker/rolling-curl": "^3.1", + "php-yaoi/php-yaoi": "^1.0", + "phpunit/phpunit": "^4.8.36|^6.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2-dev" + } + }, + "autoload": { + "psr-4": { + "Behat\\Transliterator\\": "src/Behat/Transliterator" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Artistic-1.0" + ], + "description": "String transliterator", + "keywords": [ + "i18n", + "slug", + "transliterator" + ], + "time": "2020-01-14T16:39:13+00:00" + }, { "name": "container-interop/container-interop", "version": "1.2.0", @@ -196,16 +241,16 @@ }, { "name": "doctrine/cache", - "version": "1.10.1", + "version": "1.10.2", "source": { "type": "git", "url": "https://github.com/doctrine/cache.git", - "reference": "35a4a70cd94e09e2259dfae7488afc6b474ecbd3" + "reference": "13e3381b25847283a91948d04640543941309727" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/cache/zipball/35a4a70cd94e09e2259dfae7488afc6b474ecbd3", - "reference": "35a4a70cd94e09e2259dfae7488afc6b474ecbd3", + "url": "https://api.github.com/repos/doctrine/cache/zipball/13e3381b25847283a91948d04640543941309727", + "reference": "13e3381b25847283a91948d04640543941309727", "shasum": "" }, "require": { @@ -274,20 +319,20 @@ "redis", "xcache" ], - "time": "2020-05-27T16:24:54+00:00" + "time": "2020-07-07T18:54:01+00:00" }, { "name": "doctrine/collections", - "version": "1.6.5", + "version": "1.6.7", "source": { "type": "git", "url": "https://github.com/doctrine/collections.git", - "reference": "fc0206348e17e530d09463fef07ba8968406cd6d" + "reference": "55f8b799269a1a472457bd1a41b4f379d4cfba4a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/collections/zipball/fc0206348e17e530d09463fef07ba8968406cd6d", - "reference": "fc0206348e17e530d09463fef07ba8968406cd6d", + "url": "https://api.github.com/repos/doctrine/collections/zipball/55f8b799269a1a472457bd1a41b4f379d4cfba4a", + "reference": "55f8b799269a1a472457bd1a41b4f379d4cfba4a", "shasum": "" }, "require": { @@ -339,7 +384,7 @@ "iterators", "php" ], - "time": "2020-05-25T19:24:35+00:00" + "time": "2020-07-27T17:53:49+00:00" }, { "name": "doctrine/common", @@ -520,20 +565,20 @@ }, { "name": "doctrine/event-manager", - "version": "1.1.0", + "version": "1.1.1", "source": { "type": "git", "url": "https://github.com/doctrine/event-manager.git", - "reference": "629572819973f13486371cb611386eb17851e85c" + "reference": "41370af6a30faa9dc0368c4a6814d596e81aba7f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/event-manager/zipball/629572819973f13486371cb611386eb17851e85c", - "reference": "629572819973f13486371cb611386eb17851e85c", + "url": "https://api.github.com/repos/doctrine/event-manager/zipball/41370af6a30faa9dc0368c4a6814d596e81aba7f", + "reference": "41370af6a30faa9dc0368c4a6814d596e81aba7f", "shasum": "" }, "require": { - "php": "^7.1" + "php": "^7.1 || ^8.0" }, "conflict": { "doctrine/common": "<2.9@dev" @@ -592,7 +637,7 @@ "event system", "events" ], - "time": "2019-11-10T09:48:07+00:00" + "time": "2020-05-29T18:28:51+00:00" }, { "name": "doctrine/inflector", @@ -1464,16 +1509,16 @@ }, { "name": "fideloper/proxy", - "version": "4.3.0", + "version": "4.4.0", "source": { "type": "git", "url": "https://github.com/fideloper/TrustedProxy.git", - "reference": "ec38ad69ee378a1eec04fb0e417a97cfaf7ed11a" + "reference": "9beebf48a1c344ed67c1d36bb1b8709db7c3c1a8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/fideloper/TrustedProxy/zipball/ec38ad69ee378a1eec04fb0e417a97cfaf7ed11a", - "reference": "ec38ad69ee378a1eec04fb0e417a97cfaf7ed11a", + "url": "https://api.github.com/repos/fideloper/TrustedProxy/zipball/9beebf48a1c344ed67c1d36bb1b8709db7c3c1a8", + "reference": "9beebf48a1c344ed67c1d36bb1b8709db7c3c1a8", "shasum": "" }, "require": { @@ -1514,7 +1559,7 @@ "proxy", "trusted proxy" ], - "time": "2020-02-22T01:51:47+00:00" + "time": "2020-06-23T01:36:47+00:00" }, { "name": "firebase/php-jwt", @@ -2196,6 +2241,72 @@ ], "time": "2019-01-19T21:32:55+00:00" }, + { + "name": "justinrainbow/json-schema", + "version": "5.2.10", + "source": { + "type": "git", + "url": "https://github.com/justinrainbow/json-schema.git", + "reference": "2ba9c8c862ecd5510ed16c6340aa9f6eadb4f31b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/justinrainbow/json-schema/zipball/2ba9c8c862ecd5510ed16c6340aa9f6eadb4f31b", + "reference": "2ba9c8c862ecd5510ed16c6340aa9f6eadb4f31b", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "~2.2.20||~2.15.1", + "json-schema/json-schema-test-suite": "1.2.0", + "phpunit/phpunit": "^4.8.35" + }, + "bin": [ + "bin/validate-json" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "JsonSchema\\": "src/JsonSchema/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bruno Prieto Reis", + "email": "bruno.p.reis@gmail.com" + }, + { + "name": "Justin Rainbow", + "email": "justin.rainbow@gmail.com" + }, + { + "name": "Igor Wiedler", + "email": "igor@wiedler.ch" + }, + { + "name": "Robert Schönthal", + "email": "seroscho@googlemail.com" + } + ], + "description": "A library to validate a json schema.", + "homepage": "https://github.com/justinrainbow/json-schema", + "keywords": [ + "json", + "schema" + ], + "time": "2020-05-27T16:41:55+00:00" + }, { "name": "kylekatarnls/update-helper", "version": "1.2.1", @@ -2720,16 +2831,16 @@ }, { "name": "league/flysystem", - "version": "1.0.69", + "version": "1.0.70", "source": { "type": "git", "url": "https://github.com/thephpleague/flysystem.git", - "reference": "7106f78428a344bc4f643c233a94e48795f10967" + "reference": "585824702f534f8d3cf7fab7225e8466cc4b7493" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/7106f78428a344bc4f643c233a94e48795f10967", - "reference": "7106f78428a344bc4f643c233a94e48795f10967", + "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/585824702f534f8d3cf7fab7225e8466cc4b7493", + "reference": "585824702f534f8d3cf7fab7225e8466cc4b7493", "shasum": "" }, "require": { @@ -2740,7 +2851,7 @@ "league/flysystem-sftp": "<1.0.6" }, "require-dev": { - "phpspec/phpspec": "^3.4", + "phpspec/phpspec": "^3.4 || ^4.0 || ^5.0 || ^6.0", "phpunit/phpunit": "^5.7.26" }, "suggest": { @@ -2800,7 +2911,7 @@ "sftp", "storage" ], - "time": "2020-05-18T15:13:39+00:00" + "time": "2020-07-26T07:20:36+00:00" }, { "name": "mobiledetect/mobiledetectlib", @@ -2856,16 +2967,16 @@ }, { "name": "monolog/monolog", - "version": "1.25.4", + "version": "1.25.5", "source": { "type": "git", "url": "https://github.com/Seldaek/monolog.git", - "reference": "3022efff205e2448b560c833c6fbbf91c3139168" + "reference": "1817faadd1846cd08be9a49e905dc68823bc38c0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/monolog/zipball/3022efff205e2448b560c833c6fbbf91c3139168", - "reference": "3022efff205e2448b560c833c6fbbf91c3139168", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/1817faadd1846cd08be9a49e905dc68823bc38c0", + "reference": "1817faadd1846cd08be9a49e905dc68823bc38c0", "shasum": "" }, "require": { @@ -2929,7 +3040,7 @@ "logging", "psr-3" ], - "time": "2020-05-22T07:31:27+00:00" + "time": "2020-07-23T08:35:51+00:00" }, { "name": "nesbot/carbon", @@ -2989,16 +3100,16 @@ }, { "name": "nikic/php-parser", - "version": "v4.5.0", + "version": "v4.7.0", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "53c2753d756f5adb586dca79c2ec0e2654dd9463" + "reference": "21dce06dfbf0365c6a7cc8fdbdc995926c6a9300" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/53c2753d756f5adb586dca79c2ec0e2654dd9463", - "reference": "53c2753d756f5adb586dca79c2ec0e2654dd9463", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/21dce06dfbf0365c6a7cc8fdbdc995926c6a9300", + "reference": "21dce06dfbf0365c6a7cc8fdbdc995926c6a9300", "shasum": "" }, "require": { @@ -3015,7 +3126,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "4.7-dev" } }, "autoload": { @@ -3037,7 +3148,7 @@ "parser", "php" ], - "time": "2020-06-03T07:24:19+00:00" + "time": "2020-07-25T13:18:53+00:00" }, { "name": "ocramius/package-versions", @@ -3285,6 +3396,77 @@ ], "time": "2020-05-13T13:56:11+00:00" }, + { + "name": "php-opencloud/openstack", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/OpenStackweb/openstack.git", + "reference": "eafc3de006ad505355556c0d2f3c4af2c6b5a44e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/OpenStackweb/openstack/zipball/eafc3de006ad505355556c0d2f3c4af2c6b5a44e", + "reference": "eafc3de006ad505355556c0d2f3c4af2c6b5a44e", + "shasum": "" + }, + "require": { + "guzzlehttp/guzzle": "^6.1", + "justinrainbow/json-schema": "^5.2", + "php": "~7.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^2.13", + "jakub-onderka/php-parallel-lint": "^1.0", + "php-coveralls/php-coveralls": "^2.0", + "phpunit/phpunit": "^6.5", + "psr/log": "^1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "psr-4": { + "OpenStack\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "OpenStack\\Test\\": "tests/unit/", + "OpenStack\\Integration\\": "tests/integration/" + } + }, + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Jamie Hannaford", + "email": "jamie.hannaford@rackspace.com", + "homepage": "https://github.com/jamiehannaford" + }, + { + "name": "Ha Phan", + "email": "thanhha.work@gmail.com", + "homepage": "https://github.com/haphan" + } + ], + "description": "PHP SDK for OpenStack APIs. Supports BlockStorage, Compute, Identity, Images, Networking and Metric Gnocchi", + "homepage": "https://github.com/php-opencloud/openstack", + "keywords": [ + "api", + "openstack", + "php", + "sdk" + ], + "support": { + "source": "https://github.com/OpenStackweb/openstack/tree/master" + }, + "time": "2019-02-04T18:27:20+00:00" + }, { "name": "phpseclib/phpseclib", "version": "2.0.11", @@ -4141,16 +4323,16 @@ }, { "name": "symfony/console", - "version": "v4.4.10", + "version": "v4.4.11", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "326b064d804043005526f5a0494cfb49edb59bb0" + "reference": "55d07021da933dd0d633ffdab6f45d5b230c7e02" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/326b064d804043005526f5a0494cfb49edb59bb0", - "reference": "326b064d804043005526f5a0494cfb49edb59bb0", + "url": "https://api.github.com/repos/symfony/console/zipball/55d07021da933dd0d633ffdab6f45d5b230c7e02", + "reference": "55d07021da933dd0d633ffdab6f45d5b230c7e02", "shasum": "" }, "require": { @@ -4214,24 +4396,24 @@ ], "description": "Symfony Console Component", "homepage": "https://symfony.com", - "time": "2020-05-30T20:06:45+00:00" + "time": "2020-07-06T13:18:39+00:00" }, { "name": "symfony/css-selector", - "version": "v4.4.10", + "version": "v4.4.11", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", - "reference": "afc26133a6fbdd4f8842e38893e0ee4685c7c94b" + "reference": "bf17dc9f6ce144e41f786c32435feea4d8e11dcc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/afc26133a6fbdd4f8842e38893e0ee4685c7c94b", - "reference": "afc26133a6fbdd4f8842e38893e0ee4685c7c94b", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/bf17dc9f6ce144e41f786c32435feea4d8e11dcc", + "reference": "bf17dc9f6ce144e41f786c32435feea4d8e11dcc", "shasum": "" }, "require": { - "php": "^7.1.3" + "php": ">=7.1.3" }, "type": "library", "extra": { @@ -4267,20 +4449,20 @@ ], "description": "Symfony CssSelector Component", "homepage": "https://symfony.com", - "time": "2020-03-27T16:54:36+00:00" + "time": "2020-07-05T09:39:30+00:00" }, { "name": "symfony/debug", - "version": "v4.4.10", + "version": "v4.4.11", "source": { "type": "git", "url": "https://github.com/symfony/debug.git", - "reference": "28f92d08bb6d1fddf8158e02c194ad43870007e6" + "reference": "47aa9064d75db36389692dd4d39895a0820f00f2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/debug/zipball/28f92d08bb6d1fddf8158e02c194ad43870007e6", - "reference": "28f92d08bb6d1fddf8158e02c194ad43870007e6", + "url": "https://api.github.com/repos/symfony/debug/zipball/47aa9064d75db36389692dd4d39895a0820f00f2", + "reference": "47aa9064d75db36389692dd4d39895a0820f00f2", "shasum": "" }, "require": { @@ -4324,20 +4506,20 @@ ], "description": "Symfony Debug Component", "homepage": "https://symfony.com", - "time": "2020-05-24T08:33:35+00:00" + "time": "2020-07-23T08:31:43+00:00" }, { "name": "symfony/error-handler", - "version": "v4.4.10", + "version": "v4.4.11", "source": { "type": "git", "url": "https://github.com/symfony/error-handler.git", - "reference": "0df9a23c0f9eddbb6682479fee6fd58b88add75b" + "reference": "66f151360550ec2b3273b3746febb12e6ba0348b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/error-handler/zipball/0df9a23c0f9eddbb6682479fee6fd58b88add75b", - "reference": "0df9a23c0f9eddbb6682479fee6fd58b88add75b", + "url": "https://api.github.com/repos/symfony/error-handler/zipball/66f151360550ec2b3273b3746febb12e6ba0348b", + "reference": "66f151360550ec2b3273b3746febb12e6ba0348b", "shasum": "" }, "require": { @@ -4381,20 +4563,20 @@ ], "description": "Symfony ErrorHandler Component", "homepage": "https://symfony.com", - "time": "2020-05-28T10:39:14+00:00" + "time": "2020-07-23T08:35:20+00:00" }, { "name": "symfony/event-dispatcher", - "version": "v4.4.10", + "version": "v4.4.11", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "a5370aaa7807c7a439b21386661ffccf3dff2866" + "reference": "6140fc7047dafc5abbe84ba16a34a86c0b0229b8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/a5370aaa7807c7a439b21386661ffccf3dff2866", - "reference": "a5370aaa7807c7a439b21386661ffccf3dff2866", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/6140fc7047dafc5abbe84ba16a34a86c0b0229b8", + "reference": "6140fc7047dafc5abbe84ba16a34a86c0b0229b8", "shasum": "" }, "require": { @@ -4451,24 +4633,24 @@ ], "description": "Symfony EventDispatcher Component", "homepage": "https://symfony.com", - "time": "2020-05-20T08:37:50+00:00" + "time": "2020-06-18T17:59:13+00:00" }, { "name": "symfony/event-dispatcher-contracts", - "version": "v1.1.7", + "version": "v1.1.9", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher-contracts.git", - "reference": "c43ab685673fb6c8d84220c77897b1d6cdbe1d18" + "reference": "84e23fdcd2517bf37aecbd16967e83f0caee25a7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/c43ab685673fb6c8d84220c77897b1d6cdbe1d18", - "reference": "c43ab685673fb6c8d84220c77897b1d6cdbe1d18", + "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/84e23fdcd2517bf37aecbd16967e83f0caee25a7", + "reference": "84e23fdcd2517bf37aecbd16967e83f0caee25a7", "shasum": "" }, "require": { - "php": "^7.1.3" + "php": ">=7.1.3" }, "suggest": { "psr/event-dispatcher": "", @@ -4478,6 +4660,10 @@ "extra": { "branch-alias": { "dev-master": "1.1-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" } }, "autoload": { @@ -4509,24 +4695,24 @@ "interoperability", "standards" ], - "time": "2019-09-17T09:54:03+00:00" + "time": "2020-07-06T13:19:58+00:00" }, { "name": "symfony/finder", - "version": "v4.4.10", + "version": "v4.4.11", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "5729f943f9854c5781984ed4907bbb817735776b" + "reference": "2727aa35fddfada1dd37599948528e9b152eb742" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/5729f943f9854c5781984ed4907bbb817735776b", - "reference": "5729f943f9854c5781984ed4907bbb817735776b", + "url": "https://api.github.com/repos/symfony/finder/zipball/2727aa35fddfada1dd37599948528e9b152eb742", + "reference": "2727aa35fddfada1dd37599948528e9b152eb742", "shasum": "" }, "require": { - "php": "^7.1.3" + "php": ">=7.1.3" }, "type": "library", "extra": { @@ -4558,20 +4744,20 @@ ], "description": "Symfony Finder Component", "homepage": "https://symfony.com", - "time": "2020-03-27T16:54:36+00:00" + "time": "2020-07-05T09:39:30+00:00" }, { "name": "symfony/http-foundation", - "version": "v4.4.10", + "version": "v4.4.11", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "3adfbd7098c850b02d107330b7b9deacf2581578" + "reference": "3675676b6a47f3e71d3ab10bcf53fb9239eb77e6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/3adfbd7098c850b02d107330b7b9deacf2581578", - "reference": "3adfbd7098c850b02d107330b7b9deacf2581578", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/3675676b6a47f3e71d3ab10bcf53fb9239eb77e6", + "reference": "3675676b6a47f3e71d3ab10bcf53fb9239eb77e6", "shasum": "" }, "require": { @@ -4613,20 +4799,20 @@ ], "description": "Symfony HttpFoundation Component", "homepage": "https://symfony.com", - "time": "2020-05-23T09:11:46+00:00" + "time": "2020-07-23T09:48:09+00:00" }, { "name": "symfony/http-kernel", - "version": "v4.4.10", + "version": "v4.4.11", "source": { "type": "git", "url": "https://github.com/symfony/http-kernel.git", - "reference": "81d42148474e1852a333ed7a732f2a014af75430" + "reference": "a675d2bf04a9328f164910cae6e3918b295151f3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/81d42148474e1852a333ed7a732f2a014af75430", - "reference": "81d42148474e1852a333ed7a732f2a014af75430", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/a675d2bf04a9328f164910cae6e3918b295151f3", + "reference": "a675d2bf04a9328f164910cae6e3918b295151f3", "shasum": "" }, "require": { @@ -4704,20 +4890,20 @@ ], "description": "Symfony HttpKernel Component", "homepage": "https://symfony.com", - "time": "2020-06-12T11:15:37+00:00" + "time": "2020-07-24T04:10:09+00:00" }, { "name": "symfony/mime", - "version": "v5.1.2", + "version": "v5.1.3", "source": { "type": "git", "url": "https://github.com/symfony/mime.git", - "reference": "c0c418f05e727606e85b482a8591519c4712cf45" + "reference": "149fb0ad35aae3c7637b496b38478797fa6a7ea6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mime/zipball/c0c418f05e727606e85b482a8591519c4712cf45", - "reference": "c0c418f05e727606e85b482a8591519c4712cf45", + "url": "https://api.github.com/repos/symfony/mime/zipball/149fb0ad35aae3c7637b496b38478797fa6a7ea6", + "reference": "149fb0ad35aae3c7637b496b38478797fa6a7ea6", "shasum": "" }, "require": { @@ -4767,20 +4953,20 @@ "mime", "mime-type" ], - "time": "2020-06-09T15:07:35+00:00" + "time": "2020-07-23T10:04:31+00:00" }, { "name": "symfony/polyfill-ctype", - "version": "v1.17.1", + "version": "v1.18.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "2edd75b8b35d62fd3eeabba73b26b8f1f60ce13d" + "reference": "1c302646f6efc070cd46856e600e5e0684d6b454" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/2edd75b8b35d62fd3eeabba73b26b8f1f60ce13d", - "reference": "2edd75b8b35d62fd3eeabba73b26b8f1f60ce13d", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/1c302646f6efc070cd46856e600e5e0684d6b454", + "reference": "1c302646f6efc070cd46856e600e5e0684d6b454", "shasum": "" }, "require": { @@ -4792,7 +4978,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.17-dev" + "dev-master": "1.18-dev" }, "thanks": { "name": "symfony/polyfill", @@ -4829,20 +5015,20 @@ "polyfill", "portable" ], - "time": "2020-06-06T08:46:27+00:00" + "time": "2020-07-14T12:35:20+00:00" }, { "name": "symfony/polyfill-iconv", - "version": "v1.17.1", + "version": "v1.18.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-iconv.git", - "reference": "ba6c9c18db36235b859cc29b8372d1c01298c035" + "reference": "6c2f78eb8f5ab8eaea98f6d414a5915f2e0fce36" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-iconv/zipball/ba6c9c18db36235b859cc29b8372d1c01298c035", - "reference": "ba6c9c18db36235b859cc29b8372d1c01298c035", + "url": "https://api.github.com/repos/symfony/polyfill-iconv/zipball/6c2f78eb8f5ab8eaea98f6d414a5915f2e0fce36", + "reference": "6c2f78eb8f5ab8eaea98f6d414a5915f2e0fce36", "shasum": "" }, "require": { @@ -4854,7 +5040,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.17-dev" + "dev-master": "1.18-dev" }, "thanks": { "name": "symfony/polyfill", @@ -4892,25 +5078,26 @@ "portable", "shim" ], - "time": "2020-06-06T08:46:27+00:00" + "time": "2020-07-14T12:35:20+00:00" }, { "name": "symfony/polyfill-intl-idn", - "version": "v1.17.1", + "version": "v1.18.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-idn.git", - "reference": "a57f8161502549a742a63c09f0a604997bf47027" + "reference": "bc6549d068d0160e0f10f7a5a23c7d1406b95ebe" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/a57f8161502549a742a63c09f0a604997bf47027", - "reference": "a57f8161502549a742a63c09f0a604997bf47027", + "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/bc6549d068d0160e0f10f7a5a23c7d1406b95ebe", + "reference": "bc6549d068d0160e0f10f7a5a23c7d1406b95ebe", "shasum": "" }, "require": { "php": ">=5.3.3", - "symfony/polyfill-mbstring": "^1.3", + "symfony/polyfill-intl-normalizer": "^1.10", + "symfony/polyfill-php70": "^1.10", "symfony/polyfill-php72": "^1.10" }, "suggest": { @@ -4919,7 +5106,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.17-dev" + "dev-master": "1.18-dev" }, "thanks": { "name": "symfony/polyfill", @@ -4943,6 +5130,10 @@ "name": "Laurent Bassin", "email": "laurent@bassin.info" }, + { + "name": "Trevor Rowbotham", + "email": "trevor.rowbotham@pm.me" + }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" @@ -4958,20 +5149,87 @@ "portable", "shim" ], - "time": "2020-06-06T08:46:27+00:00" + "time": "2020-07-14T12:35:20+00:00" }, { - "name": "symfony/polyfill-mbstring", - "version": "v1.17.1", + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.18.0", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "7110338d81ce1cbc3e273136e4574663627037a7" + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "37078a8dd4a2a1e9ab0231af7c6cb671b2ed5a7e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/7110338d81ce1cbc3e273136e4574663627037a7", - "reference": "7110338d81ce1cbc3e273136e4574663627037a7", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/37078a8dd4a2a1e9ab0231af7c6cb671b2ed5a7e", + "reference": "37078a8dd4a2a1e9ab0231af7c6cb671b2ed5a7e", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.18-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Intl\\Normalizer\\": "" + }, + "files": [ + "bootstrap.php" + ], + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's Normalizer class and related functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "intl", + "normalizer", + "polyfill", + "portable", + "shim" + ], + "time": "2020-07-14T12:35:20+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.18.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "a6977d63bf9a0ad4c65cd352709e230876f9904a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/a6977d63bf9a0ad4c65cd352709e230876f9904a", + "reference": "a6977d63bf9a0ad4c65cd352709e230876f9904a", "shasum": "" }, "require": { @@ -4983,7 +5241,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.17-dev" + "dev-master": "1.18-dev" }, "thanks": { "name": "symfony/polyfill", @@ -5021,20 +5279,83 @@ "portable", "shim" ], - "time": "2020-06-06T08:46:27+00:00" + "time": "2020-07-14T12:35:20+00:00" }, { - "name": "symfony/polyfill-php72", - "version": "v1.17.0", + "name": "symfony/polyfill-php70", + "version": "v1.18.0", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-php72.git", - "reference": "f048e612a3905f34931127360bdd2def19a5e582" + "url": "https://github.com/symfony/polyfill-php70.git", + "reference": "0dd93f2c578bdc9c72697eaa5f1dd25644e618d3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/f048e612a3905f34931127360bdd2def19a5e582", - "reference": "f048e612a3905f34931127360bdd2def19a5e582", + "url": "https://api.github.com/repos/symfony/polyfill-php70/zipball/0dd93f2c578bdc9c72697eaa5f1dd25644e618d3", + "reference": "0dd93f2c578bdc9c72697eaa5f1dd25644e618d3", + "shasum": "" + }, + "require": { + "paragonie/random_compat": "~1.0|~2.0|~9.99", + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.18-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Php70\\": "" + }, + "files": [ + "bootstrap.php" + ], + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 7.0+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "time": "2020-07-14T12:35:20+00:00" + }, + { + "name": "symfony/polyfill-php72", + "version": "v1.18.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php72.git", + "reference": "639447d008615574653fb3bc60d1986d7172eaae" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/639447d008615574653fb3bc60d1986d7172eaae", + "reference": "639447d008615574653fb3bc60d1986d7172eaae", "shasum": "" }, "require": { @@ -5043,7 +5364,11 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.17-dev" + "dev-master": "1.18-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" } }, "autoload": { @@ -5076,20 +5401,20 @@ "portable", "shim" ], - "time": "2020-05-12T16:47:27+00:00" + "time": "2020-07-14T12:35:20+00:00" }, { "name": "symfony/polyfill-php73", - "version": "v1.17.1", + "version": "v1.18.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php73.git", - "reference": "fa0837fe02d617d31fbb25f990655861bb27bd1a" + "reference": "fffa1a52a023e782cdcc221d781fe1ec8f87fcca" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/fa0837fe02d617d31fbb25f990655861bb27bd1a", - "reference": "fa0837fe02d617d31fbb25f990655861bb27bd1a", + "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/fffa1a52a023e782cdcc221d781fe1ec8f87fcca", + "reference": "fffa1a52a023e782cdcc221d781fe1ec8f87fcca", "shasum": "" }, "require": { @@ -5098,7 +5423,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.17-dev" + "dev-master": "1.18-dev" }, "thanks": { "name": "symfony/polyfill", @@ -5138,20 +5463,20 @@ "portable", "shim" ], - "time": "2020-06-06T08:46:27+00:00" + "time": "2020-07-14T12:35:20+00:00" }, { "name": "symfony/polyfill-php80", - "version": "v1.17.1", + "version": "v1.18.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "4a5b6bba3259902e386eb80dd1956181ee90b5b2" + "reference": "d87d5766cbf48d72388a9f6b85f280c8ad51f981" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/4a5b6bba3259902e386eb80dd1956181ee90b5b2", - "reference": "4a5b6bba3259902e386eb80dd1956181ee90b5b2", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/d87d5766cbf48d72388a9f6b85f280c8ad51f981", + "reference": "d87d5766cbf48d72388a9f6b85f280c8ad51f981", "shasum": "" }, "require": { @@ -5160,7 +5485,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.17-dev" + "dev-master": "1.18-dev" }, "thanks": { "name": "symfony/polyfill", @@ -5204,24 +5529,24 @@ "portable", "shim" ], - "time": "2020-06-06T08:46:27+00:00" + "time": "2020-07-14T12:35:20+00:00" }, { "name": "symfony/process", - "version": "v4.4.10", + "version": "v4.4.11", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "c714958428a85c86ab97e3a0c96db4c4f381b7f5" + "reference": "65e70bab62f3da7089a8d4591fb23fbacacb3479" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/c714958428a85c86ab97e3a0c96db4c4f381b7f5", - "reference": "c714958428a85c86ab97e3a0c96db4c4f381b7f5", + "url": "https://api.github.com/repos/symfony/process/zipball/65e70bab62f3da7089a8d4591fb23fbacacb3479", + "reference": "65e70bab62f3da7089a8d4591fb23fbacacb3479", "shasum": "" }, "require": { - "php": "^7.1.3" + "php": ">=7.1.3" }, "type": "library", "extra": { @@ -5253,24 +5578,24 @@ ], "description": "Symfony Process Component", "homepage": "https://symfony.com", - "time": "2020-05-30T20:06:45+00:00" + "time": "2020-07-23T08:31:43+00:00" }, { "name": "symfony/routing", - "version": "v4.4.10", + "version": "v4.4.11", "source": { "type": "git", "url": "https://github.com/symfony/routing.git", - "reference": "0f557911dde75c2a9652b8097bd7c9f54507f646" + "reference": "e103381a4c2f0731c14589041852bf979e97c7af" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/routing/zipball/0f557911dde75c2a9652b8097bd7c9f54507f646", - "reference": "0f557911dde75c2a9652b8097bd7c9f54507f646", + "url": "https://api.github.com/repos/symfony/routing/zipball/e103381a4c2f0731c14589041852bf979e97c7af", + "reference": "e103381a4c2f0731c14589041852bf979e97c7af", "shasum": "" }, "require": { - "php": "^7.1.3" + "php": ">=7.1.3" }, "conflict": { "symfony/config": "<4.2", @@ -5329,20 +5654,20 @@ "uri", "url" ], - "time": "2020-05-30T20:07:26+00:00" + "time": "2020-07-05T09:39:30+00:00" }, { "name": "symfony/serializer", - "version": "v4.4.10", + "version": "v4.4.11", "source": { "type": "git", "url": "https://github.com/symfony/serializer.git", - "reference": "a91ceee34fc690a824770085192ffdeaa4476a8c" + "reference": "0fe47b7aa55ff28b1b781e380120c0731e2d36c7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/serializer/zipball/a91ceee34fc690a824770085192ffdeaa4476a8c", - "reference": "a91ceee34fc690a824770085192ffdeaa4476a8c", + "url": "https://api.github.com/repos/symfony/serializer/zipball/0fe47b7aa55ff28b1b781e380120c0731e2d36c7", + "reference": "0fe47b7aa55ff28b1b781e380120c0731e2d36c7", "shasum": "" }, "require": { @@ -5411,20 +5736,20 @@ ], "description": "Symfony Serializer Component", "homepage": "https://symfony.com", - "time": "2020-06-01T17:29:37+00:00" + "time": "2020-07-21T15:17:25+00:00" }, { "name": "symfony/service-contracts", - "version": "v2.1.2", + "version": "v2.1.3", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "66a8f0957a3ca54e4f724e49028ab19d75a8918b" + "reference": "58c7475e5457c5492c26cc740cc0ad7464be9442" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/66a8f0957a3ca54e4f724e49028ab19d75a8918b", - "reference": "66a8f0957a3ca54e4f724e49028ab19d75a8918b", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/58c7475e5457c5492c26cc740cc0ad7464be9442", + "reference": "58c7475e5457c5492c26cc740cc0ad7464be9442", "shasum": "" }, "require": { @@ -5438,6 +5763,10 @@ "extra": { "branch-alias": { "dev-master": "2.1-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" } }, "autoload": { @@ -5469,20 +5798,20 @@ "interoperability", "standards" ], - "time": "2020-05-20T17:43:50+00:00" + "time": "2020-07-06T13:23:11+00:00" }, { "name": "symfony/translation", - "version": "v4.4.10", + "version": "v4.4.11", "source": { "type": "git", "url": "https://github.com/symfony/translation.git", - "reference": "79d3ef9096a6a6047dbc69218b68c7b7f63193af" + "reference": "a8ea9d97353294eb6783f2894ef8cee99a045822" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/79d3ef9096a6a6047dbc69218b68c7b7f63193af", - "reference": "79d3ef9096a6a6047dbc69218b68c7b7f63193af", + "url": "https://api.github.com/repos/symfony/translation/zipball/a8ea9d97353294eb6783f2894ef8cee99a045822", + "reference": "a8ea9d97353294eb6783f2894ef8cee99a045822", "shasum": "" }, "require": { @@ -5545,20 +5874,20 @@ ], "description": "Symfony Translation Component", "homepage": "https://symfony.com", - "time": "2020-05-30T20:06:45+00:00" + "time": "2020-07-23T08:31:43+00:00" }, { "name": "symfony/translation-contracts", - "version": "v2.1.2", + "version": "v2.1.3", "source": { "type": "git", "url": "https://github.com/symfony/translation-contracts.git", - "reference": "e5ca07c8f817f865f618aa072c2fe8e0e637340e" + "reference": "616a9773c853097607cf9dd6577d5b143ffdcd63" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/e5ca07c8f817f865f618aa072c2fe8e0e637340e", - "reference": "e5ca07c8f817f865f618aa072c2fe8e0e637340e", + "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/616a9773c853097607cf9dd6577d5b143ffdcd63", + "reference": "616a9773c853097607cf9dd6577d5b143ffdcd63", "shasum": "" }, "require": { @@ -5571,6 +5900,10 @@ "extra": { "branch-alias": { "dev-master": "2.1-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" } }, "autoload": { @@ -5602,20 +5935,20 @@ "interoperability", "standards" ], - "time": "2020-05-20T17:43:50+00:00" + "time": "2020-07-06T13:23:11+00:00" }, { "name": "symfony/var-dumper", - "version": "v4.4.10", + "version": "v4.4.11", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "56b3aa5eab0ac6720dcd559fd1d590ce301594ac" + "reference": "2125805a1a4e57f2340bc566c3013ca94d2722dc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/56b3aa5eab0ac6720dcd559fd1d590ce301594ac", - "reference": "56b3aa5eab0ac6720dcd559fd1d590ce301594ac", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/2125805a1a4e57f2340bc566c3013ca94d2722dc", + "reference": "2125805a1a4e57f2340bc566c3013ca94d2722dc", "shasum": "" }, "require": { @@ -5679,30 +6012,30 @@ "debug", "dump" ], - "time": "2020-05-30T20:06:45+00:00" + "time": "2020-06-24T13:34:53+00:00" }, { "name": "tijsverkoyen/css-to-inline-styles", - "version": "2.2.2", + "version": "2.2.3", "source": { "type": "git", "url": "https://github.com/tijsverkoyen/CssToInlineStyles.git", - "reference": "dda2ee426acd6d801d5b7fd1001cde9b5f790e15" + "reference": "b43b05cf43c1b6d849478965062b6ef73e223bb5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/tijsverkoyen/CssToInlineStyles/zipball/dda2ee426acd6d801d5b7fd1001cde9b5f790e15", - "reference": "dda2ee426acd6d801d5b7fd1001cde9b5f790e15", + "url": "https://api.github.com/repos/tijsverkoyen/CssToInlineStyles/zipball/b43b05cf43c1b6d849478965062b6ef73e223bb5", + "reference": "b43b05cf43c1b6d849478965062b6ef73e223bb5", "shasum": "" }, "require": { "ext-dom": "*", "ext-libxml": "*", - "php": "^5.5 || ^7.0", + "php": "^5.5 || ^7.0 || ^8.0", "symfony/css-selector": "^2.7 || ^3.0 || ^4.0 || ^5.0" }, "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0 || ^7.5" }, "type": "library", "extra": { @@ -5728,7 +6061,7 @@ ], "description": "CssToInlineStyles is a class that enables you to convert HTML-pages/files into HTML-pages/files with inline styles. This is very useful when you're sending emails.", "homepage": "https://github.com/tijsverkoyen/CssToInlineStyles", - "time": "2019-10-24T08:53:34+00:00" + "time": "2020-07-13T06:12:54+00:00" }, { "name": "vladimir-yuldashev/laravel-queue-rabbitmq", @@ -5789,21 +6122,21 @@ }, { "name": "vlucas/phpdotenv", - "version": "v2.6.5", + "version": "v2.6.6", "source": { "type": "git", "url": "https://github.com/vlucas/phpdotenv.git", - "reference": "2e977311ffb17b2f82028a9c36824647789c6365" + "reference": "e1d57f62db3db00d9139078cbedf262280701479" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/2e977311ffb17b2f82028a9c36824647789c6365", - "reference": "2e977311ffb17b2f82028a9c36824647789c6365", + "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/e1d57f62db3db00d9139078cbedf262280701479", + "reference": "e1d57f62db3db00d9139078cbedf262280701479", "shasum": "" }, "require": { "php": "^5.3.9 || ^7.0 || ^8.0", - "symfony/polyfill-ctype": "^1.16" + "symfony/polyfill-ctype": "^1.17" }, "require-dev": { "ext-filter": "*", @@ -5847,7 +6180,7 @@ "env", "environment" ], - "time": "2020-06-02T14:06:52+00:00" + "time": "2020-07-14T17:54:18+00:00" }, { "name": "zendframework/zend-code", @@ -6229,20 +6562,20 @@ }, { "name": "hamcrest/hamcrest-php", - "version": "v2.0.0", + "version": "v2.0.1", "source": { "type": "git", "url": "https://github.com/hamcrest/hamcrest-php.git", - "reference": "776503d3a8e85d4f9a1148614f95b7a608b046ad" + "reference": "8c3d0a3f6af734494ad8f6fbbee0ba92422859f3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/hamcrest/hamcrest-php/zipball/776503d3a8e85d4f9a1148614f95b7a608b046ad", - "reference": "776503d3a8e85d4f9a1148614f95b7a608b046ad", + "url": "https://api.github.com/repos/hamcrest/hamcrest-php/zipball/8c3d0a3f6af734494ad8f6fbbee0ba92422859f3", + "reference": "8c3d0a3f6af734494ad8f6fbbee0ba92422859f3", "shasum": "" }, "require": { - "php": "^5.3|^7.0" + "php": "^5.3|^7.0|^8.0" }, "replace": { "cordoval/hamcrest-php": "*", @@ -6250,14 +6583,13 @@ "kodova/hamcrest-php": "*" }, "require-dev": { - "phpunit/php-file-iterator": "1.3.3", - "phpunit/phpunit": "~4.0", - "satooshi/php-coveralls": "^1.0" + "phpunit/php-file-iterator": "^1.4 || ^2.0", + "phpunit/phpunit": "^4.8.36 || ^5.7 || ^6.5 || ^7.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-master": "2.1-dev" } }, "autoload": { @@ -6267,13 +6599,13 @@ }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD" + "BSD-3-Clause" ], "description": "This is the PHP port of Hamcrest Matchers", "keywords": [ "test" ], - "time": "2016-01-20T08:20:44+00:00" + "time": "2020-07-09T08:09:16+00:00" }, { "name": "laravel/browser-kit-testing", @@ -6326,25 +6658,25 @@ }, { "name": "mockery/mockery", - "version": "1.3.1", + "version": "1.3.2", "source": { "type": "git", "url": "https://github.com/mockery/mockery.git", - "reference": "f69bbde7d7a75d6b2862d9ca8fab1cd28014b4be" + "reference": "9b6f117dd7d36dc3858d8d8ddf9b3d584fcae283" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/mockery/mockery/zipball/f69bbde7d7a75d6b2862d9ca8fab1cd28014b4be", - "reference": "f69bbde7d7a75d6b2862d9ca8fab1cd28014b4be", + "url": "https://api.github.com/repos/mockery/mockery/zipball/9b6f117dd7d36dc3858d8d8ddf9b3d584fcae283", + "reference": "9b6f117dd7d36dc3858d8d8ddf9b3d584fcae283", "shasum": "" }, "require": { - "hamcrest/hamcrest-php": "~2.0", + "hamcrest/hamcrest-php": "^2.0.1", "lib-pcre": ">=7.0", "php": ">=5.6.0" }, "require-dev": { - "phpunit/phpunit": "~5.7.10|~6.5|~7.0|~8.0" + "phpunit/phpunit": "~5.7.10|~6.5|~7.0|~8.0|~9.0" }, "type": "library", "extra": { @@ -6387,24 +6719,24 @@ "test double", "testing" ], - "time": "2019-12-26T09:49:15+00:00" + "time": "2020-07-09T08:23:05+00:00" }, { "name": "myclabs/deep-copy", - "version": "1.9.5", + "version": "1.10.1", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "b2c28789e80a97badd14145fda39b545d83ca3ef" + "reference": "969b211f9a51aa1f6c01d1d2aef56d3bd91598e5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/b2c28789e80a97badd14145fda39b545d83ca3ef", - "reference": "b2c28789e80a97badd14145fda39b545d83ca3ef", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/969b211f9a51aa1f6c01d1d2aef56d3bd91598e5", + "reference": "969b211f9a51aa1f6c01d1d2aef56d3bd91598e5", "shasum": "" }, "require": { - "php": "^7.1" + "php": "^7.1 || ^8.0" }, "replace": { "myclabs/deep-copy": "self.version" @@ -6435,7 +6767,7 @@ "object", "object graph" ], - "time": "2020-01-17T21:11:47+00:00" + "time": "2020-06-29T13:22:24+00:00" }, { "name": "nunomaduro/collision", @@ -6605,25 +6937,25 @@ }, { "name": "phpdocumentor/reflection-common", - "version": "2.1.0", + "version": "2.2.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionCommon.git", - "reference": "6568f4687e5b41b054365f9ae03fcb1ed5f2069b" + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/6568f4687e5b41b054365f9ae03fcb1ed5f2069b", - "reference": "6568f4687e5b41b054365f9ae03fcb1ed5f2069b", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", "shasum": "" }, "require": { - "php": ">=7.1" + "php": "^7.2 || ^8.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.x-dev" + "dev-2.x": "2.x-dev" } }, "autoload": { @@ -6650,32 +6982,31 @@ "reflection", "static analysis" ], - "time": "2020-04-27T09:25:28+00:00" + "time": "2020-06-27T09:03:43+00:00" }, { "name": "phpdocumentor/reflection-docblock", - "version": "5.1.0", + "version": "5.2.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "cd72d394ca794d3466a3b2fc09d5a6c1dc86b47e" + "reference": "3170448f5769fe19f456173d833734e0ff1b84df" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/cd72d394ca794d3466a3b2fc09d5a6c1dc86b47e", - "reference": "cd72d394ca794d3466a3b2fc09d5a6c1dc86b47e", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/3170448f5769fe19f456173d833734e0ff1b84df", + "reference": "3170448f5769fe19f456173d833734e0ff1b84df", "shasum": "" }, "require": { - "ext-filter": "^7.1", - "php": "^7.2", - "phpdocumentor/reflection-common": "^2.0", - "phpdocumentor/type-resolver": "^1.0", - "webmozart/assert": "^1" + "ext-filter": "*", + "php": "^7.2 || ^8.0", + "phpdocumentor/reflection-common": "^2.2", + "phpdocumentor/type-resolver": "^1.3", + "webmozart/assert": "^1.9.1" }, "require-dev": { - "doctrine/instantiator": "^1", - "mockery/mockery": "^1" + "mockery/mockery": "~1.3.2" }, "type": "library", "extra": { @@ -6703,29 +7034,28 @@ } ], "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", - "time": "2020-02-22T12:28:44+00:00" + "time": "2020-07-20T20:05:34+00:00" }, { "name": "phpdocumentor/type-resolver", - "version": "1.2.0", + "version": "1.3.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "30441f2752e493c639526b215ed81d54f369d693" + "reference": "e878a14a65245fbe78f8080eba03b47c3b705651" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/30441f2752e493c639526b215ed81d54f369d693", - "reference": "30441f2752e493c639526b215ed81d54f369d693", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/e878a14a65245fbe78f8080eba03b47c3b705651", + "reference": "e878a14a65245fbe78f8080eba03b47c3b705651", "shasum": "" }, "require": { - "php": "^7.2", + "php": "^7.2 || ^8.0", "phpdocumentor/reflection-common": "^2.0" }, "require-dev": { - "ext-tokenizer": "^7.2", - "mockery/mockery": "~1" + "ext-tokenizer": "*" }, "type": "library", "extra": { @@ -6749,37 +7079,37 @@ } ], "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", - "time": "2020-06-19T20:22:09+00:00" + "time": "2020-06-27T10:12:23+00:00" }, { "name": "phpspec/prophecy", - "version": "v1.10.3", + "version": "1.11.1", "source": { "type": "git", "url": "https://github.com/phpspec/prophecy.git", - "reference": "451c3cd1418cf640de218914901e51b064abb093" + "reference": "b20034be5efcdab4fb60ca3a29cba2949aead160" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/451c3cd1418cf640de218914901e51b064abb093", - "reference": "451c3cd1418cf640de218914901e51b064abb093", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/b20034be5efcdab4fb60ca3a29cba2949aead160", + "reference": "b20034be5efcdab4fb60ca3a29cba2949aead160", "shasum": "" }, "require": { - "doctrine/instantiator": "^1.0.2", - "php": "^5.3|^7.0", - "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0|^5.0", - "sebastian/comparator": "^1.2.3|^2.0|^3.0|^4.0", - "sebastian/recursion-context": "^1.0|^2.0|^3.0|^4.0" + "doctrine/instantiator": "^1.2", + "php": "^7.2", + "phpdocumentor/reflection-docblock": "^5.0", + "sebastian/comparator": "^3.0 || ^4.0", + "sebastian/recursion-context": "^3.0 || ^4.0" }, "require-dev": { - "phpspec/phpspec": "^2.5 || ^3.2", - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5 || ^7.1" + "phpspec/phpspec": "^6.0", + "phpunit/phpunit": "^8.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.10.x-dev" + "dev-master": "1.11.x-dev" } }, "autoload": { @@ -6812,7 +7142,7 @@ "spy", "stub" ], - "time": "2020-03-05T15:02:03+00:00" + "time": "2020-07-08T12:44:21+00:00" }, { "name": "phpunit/php-code-coverage", @@ -7718,16 +8048,16 @@ }, { "name": "symfony/dom-crawler", - "version": "v4.4.10", + "version": "v4.4.11", "source": { "type": "git", "url": "https://github.com/symfony/dom-crawler.git", - "reference": "c18354d5a0bb84c945f6257c51b971d52f10c614" + "reference": "72b3a65ddd5052cf6d65eac6669748ed311f39bf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/c18354d5a0bb84c945f6257c51b971d52f10c614", - "reference": "c18354d5a0bb84c945f6257c51b971d52f10c614", + "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/72b3a65ddd5052cf6d65eac6669748ed311f39bf", + "reference": "72b3a65ddd5052cf6d65eac6669748ed311f39bf", "shasum": "" }, "require": { @@ -7775,27 +8105,27 @@ ], "description": "Symfony DomCrawler Component", "homepage": "https://symfony.com", - "time": "2020-05-23T00:03:06+00:00" + "time": "2020-07-23T08:31:43+00:00" }, { "name": "theseer/tokenizer", - "version": "1.1.3", + "version": "1.2.0", "source": { "type": "git", "url": "https://github.com/theseer/tokenizer.git", - "reference": "11336f6f84e16a720dae9d8e6ed5019efa85a0f9" + "reference": "75a63c33a8577608444246075ea0af0d052e452a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/11336f6f84e16a720dae9d8e6ed5019efa85a0f9", - "reference": "11336f6f84e16a720dae9d8e6ed5019efa85a0f9", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/75a63c33a8577608444246075ea0af0d052e452a", + "reference": "75a63c33a8577608444246075ea0af0d052e452a", "shasum": "" }, "require": { "ext-dom": "*", "ext-tokenizer": "*", "ext-xmlwriter": "*", - "php": "^7.0" + "php": "^7.2 || ^8.0" }, "type": "library", "autoload": { @@ -7815,24 +8145,24 @@ } ], "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", - "time": "2019-06-13T22:48:21+00:00" + "time": "2020-07-12T23:59:07+00:00" }, { "name": "webmozart/assert", - "version": "1.9.0", + "version": "1.9.1", "source": { "type": "git", "url": "https://github.com/webmozart/assert.git", - "reference": "9dc4f203e36f2b486149058bade43c851dd97451" + "reference": "bafc69caeb4d49c39fd0779086c03a3738cbb389" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozart/assert/zipball/9dc4f203e36f2b486149058bade43c851dd97451", - "reference": "9dc4f203e36f2b486149058bade43c851dd97451", + "url": "https://api.github.com/repos/webmozart/assert/zipball/bafc69caeb4d49c39fd0779086c03a3738cbb389", + "reference": "bafc69caeb4d49c39fd0779086c03a3738cbb389", "shasum": "" }, "require": { - "php": "^5.3.3 || ^7.0", + "php": "^5.3.3 || ^7.0 || ^8.0", "symfony/polyfill-ctype": "^1.8" }, "conflict": { @@ -7864,12 +8194,14 @@ "check", "validate" ], - "time": "2020-06-16T10:16:42+00:00" + "time": "2020-07-08T17:02:28+00:00" } ], "aliases": [], "minimum-stability": "dev", - "stability-flags": [], + "stability-flags": { + "php-opencloud/openstack": 20 + }, "prefer-stable": true, "prefer-lowest": false, "platform": { diff --git a/config/app.php b/config/app.php index 0ada4f7b..9beb6a40 100644 --- a/config/app.php +++ b/config/app.php @@ -162,6 +162,7 @@ return [ // Doctrine Beberlei (Query/Type) extensions install them: LaravelDoctrine\Extensions\BeberleiExtensionsServiceProvider::class, \App\Models\Utils\MySQLExtensionsServiceProvider::class, + \App\libs\Utils\FileSystem\SwiftServiceProvider::class, ], /* diff --git a/config/filesystems.php b/config/filesystems.php index 75b50022..70a83b96 100644 --- a/config/filesystems.php +++ b/config/filesystems.php @@ -54,13 +54,14 @@ return [ 'visibility' => 'public', ], - 's3' => [ - 'driver' => 's3', - 'key' => 'your-key', - 'secret' => 'your-secret', - 'region' => 'your-region', - 'bucket' => 'your-bucket', - ], + 'swift' => [ + 'driver' => 'swift', + 'auth_url' => env('CLOUD_STORAGE_AUTH_URL'), + 'region' => env('CLOUD_STORAGE_REGION'), + 'app_credential_id' => env('CLOUD_STORAGE_APP_CREDENTIAL_ID'), + 'app_credential_secret' => env('CLOUD_STORAGE_APP_CREDENTIAL_SECRET'), + 'container' => env('CLOUD_STORAGE_CONTAINER'), + ] ], diff --git a/database/migrations/Version20200803193707.php b/database/migrations/Version20200803193707.php new file mode 100644 index 00000000..1538b287 --- /dev/null +++ b/database/migrations/Version20200803193707.php @@ -0,0 +1,50 @@ +hasTable("users") && !$builder->hasColumn("users","pic") ) { + $builder->table('users', function (Table $table) { + $table->string('pic')->setNotnull(false)->setLength(512); + }); + } + } + + /** + * @param Schema $schema + */ + public function down(Schema $schema) + { + + $builder = new Builder($schema); + if($schema->hasTable("users") && $builder->hasColumn("users","pic") ) { + $builder->table('users', function (Table $table) { + $table->dropColumn('pic'); + }); + } + } +} diff --git a/public/assets/css/main.css b/public/assets/css/main.css index 96757e53..630a9350 100644 --- a/public/assets/css/main.css +++ b/public/assets/css/main.css @@ -324,6 +324,7 @@ span.help-block::before { border: 0; vertical-align: middle; border-radius: 50%; + height: 100px; } .help-block{ diff --git a/public/assets/js/admin/edit-user.js b/public/assets/js/admin/edit-user.js index 92abf564..ae2ab6a6 100644 --- a/public/assets/js/admin/edit-user.js +++ b/public/assets/js/admin/edit-user.js @@ -152,13 +152,21 @@ $(document).ready(function() { var href = $(this).attr('action'); + var data = new FormData(); + + data.append('user', JSON.stringify(user)); + + if($('#pic', form)[0].files.length > 0) + data.append('pic', $('#pic', form)[0].files[0]); + $.ajax( { type: "PUT", url: href, - data: JSON.stringify(user), - contentType: "application/json; charset=utf-8", - dataType: "json", + data: data, + cache: false, + contentType: false, + processData: false, timeout:60000, success: function (data, textStatus, jqXHR) { $('body').ajax_loader('stop'); @@ -167,7 +175,7 @@ $(document).ready(function() { type: "success", text: "User info updated successfully!", }); - $('#spam-type').val(data.spam_type); + location.reload(); }, error: function (jqXHR, textStatus, errorThrown) { $('body').ajax_loader('stop'); @@ -180,6 +188,21 @@ $(document).ready(function() { return false; }); + function readURL(input) { + if (input.files && input.files[0]) { + var reader = new FileReader(); + + reader.onload = function(e) { + $('#img-pic').attr('src', e.target.result); + } + reader.readAsDataURL(input.files[0]); + } + } + + $("#pic", form).change(function() { + readURL(this); + }); + $("#password_container").hide(); $("body").on("click", ".change-password-link", function(event){ diff --git a/public/assets/js/profile.js b/public/assets/js/profile.js index 138c1f2d..1b68fe9f 100644 --- a/public/assets/js/profile.js +++ b/public/assets/js/profile.js @@ -92,14 +92,21 @@ $(document).ready(function() { } var href = $(this).attr('action'); + var data = new FormData(); + + data.append('user', JSON.stringify(user)); + + if($('#pic', form)[0].files.length > 0) + data.append('pic', $('#pic', form)[0].files[0]); $.ajax( { type: "PUT", url: href, - data: JSON.stringify(user), - contentType: "application/json; charset=utf-8", - dataType: "json", + data: data, + cache: false, + contentType: false, + processData: false, timeout:60000, success: function (data,textStatus,jqXHR) { $('body').ajax_loader('stop'); @@ -108,7 +115,6 @@ $(document).ready(function() { type: "success", text: "User info updated successfully!", }); - }, error: function (jqXHR, textStatus, errorThrown) { $('body').ajax_loader('stop'); @@ -121,6 +127,21 @@ $(document).ready(function() { return false; }); + function readURL(input) { + if (input.files && input.files[0]) { + var reader = new FileReader(); + + reader.onload = function(e) { + $('#img-pic').attr('src', e.target.result); + } + reader.readAsDataURL(input.files[0]); + } + } + + $("#pic", form).change(function() { + readURL(this); + }); + $("#password_container").hide(); $("body").on("click", ".change-password-link", function(event){ diff --git a/resources/views/admin/edit-user.blade.php b/resources/views/admin/edit-user.blade.php index 8330eda5..7547013a 100644 --- a/resources/views/admin/edit-user.blade.php +++ b/resources/views/admin/edit-user.blade.php @@ -12,10 +12,23 @@
-
-
+ + @method('PUT') + @csrf +
+ + + + +
+ +
@@ -189,11 +202,11 @@ /> Email Verified?
-
+
- +
diff --git a/resources/views/profile.blade.php b/resources/views/profile.blade.php index 35ddf1ed..97a64168 100644 --- a/resources/views/profile.blade.php +++ b/resources/views/profile.blade.php @@ -21,23 +21,24 @@
-
- + @method('PUT') + @csrf  {!! Config::get('app.app_name') !!} Account Settings:
- + -
-

- {!! Config::get('app.app_name') !!} uses the 'Gravatar' profile picture associated with your email address. You can customise your profile pic at Gravatar.com -

-
+