Added Profile PIC edition
( default gravatar ) Change-Id: Ib15b9c64ebfb8b21b52175d68e179de38146b765 Signed-off-by: smarcet <smarcet@gmail.com>
This commit is contained in:
parent
5a260de3d3
commit
48dfbb1664
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
];
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
<?php namespace App\Http\Middleware;
|
||||
/**
|
||||
* Copyright 2020 OpenStack Foundation
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
use Closure;
|
||||
use utils\ParseMultiPartFormDataInputStream;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
/**
|
||||
* Class ParseMultipartFormDataInputForNonPostRequests
|
||||
* @package App\Http\Middleware
|
||||
*/
|
||||
final class ParseMultipartFormDataInputForNonPostRequests
|
||||
{
|
||||
/*
|
||||
* Content-Type: multipart/form-data - only works for POST requests. All others fail, this is a bug in PHP since 2011.
|
||||
* See comments here: https://github.com/laravel/framework/issues/13457
|
||||
*
|
||||
* This middleware converts all multi-part/form-data for NON-POST requests, into a properly formatted
|
||||
* request variable for Laravel 5.6. It uses the ParseInputStream class, found here:
|
||||
* https://gist.github.com/devmycloud/df28012101fbc55d8de1737762b70348
|
||||
*/
|
||||
public function handle($request, Closure $next)
|
||||
{
|
||||
if ($request->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);
|
||||
}
|
||||
}
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
});
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
<?php namespace App\libs\Utils\FileSystem;
|
||||
/**
|
||||
* Copyright 2020 OpenStack Foundation
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
use Behat\Transliterator\Transliterator;
|
||||
/**
|
||||
* Class FileNameSanitizer
|
||||
* @package App\Services\FileSystem
|
||||
*/
|
||||
final class FileNameSanitizer
|
||||
{
|
||||
private static $default_replacements = [
|
||||
'/\s/' => '-', // 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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,357 @@
|
|||
<?php namespace App\Services\FileSystem\Swift;
|
||||
/**
|
||||
* Copyright 2020 OpenStack Foundation
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
use GuzzleHttp\Psr7\Stream;
|
||||
use GuzzleHttp\Psr7\StreamWrapper;
|
||||
use League\Flysystem\Util;
|
||||
use League\Flysystem\Config;
|
||||
use League\Flysystem\Adapter\AbstractAdapter;
|
||||
use OpenStack\Common\Error\BadResponseError;
|
||||
use OpenStack\ObjectStore\v1\Models\Container;
|
||||
use OpenStack\ObjectStore\v1\Models\StorageObject;
|
||||
use League\Flysystem\Adapter\Polyfill\NotSupportingVisibilityTrait;
|
||||
use League\Flysystem\Adapter\Polyfill\StreamedCopyTrait;
|
||||
/**
|
||||
* Class SwiftAdapter
|
||||
* @package App\Services\FileSystem\Swift
|
||||
*/
|
||||
final class SwiftAdapter extends AbstractAdapter
|
||||
{
|
||||
use StreamedCopyTrait;
|
||||
use NotSupportingVisibilityTrait;
|
||||
|
||||
/**
|
||||
* @var Container
|
||||
*/
|
||||
protected $container;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param Container $container
|
||||
* @param string $prefix
|
||||
*/
|
||||
public function __construct(Container $container, $prefix = null)
|
||||
{
|
||||
$this->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();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
<?php namespace App\libs\Utils\FileSystem;
|
||||
/**
|
||||
* Copyright 2020 OpenStack Foundation
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
use App\Services\FileSystem\Swift\SwiftAdapter;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use League\Flysystem\Filesystem;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use OpenStack\OpenStack;
|
||||
/**
|
||||
* Class SwiftServiceProvider
|
||||
* @package App\Services\FileSystem\Swift
|
||||
*/
|
||||
final class SwiftServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* Register bindings in the container.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Bootstrap any application services.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
Storage::extend('swift', function ($app, $config) {
|
||||
|
||||
$configOptions = [
|
||||
'authUrl' => $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));
|
||||
});
|
||||
}
|
||||
}
|
|
@ -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",
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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,
|
||||
],
|
||||
|
||||
/*
|
||||
|
|
|
@ -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'),
|
||||
]
|
||||
|
||||
],
|
||||
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
<?php namespace Database\Migrations;
|
||||
/**
|
||||
* Copyright 2020 OpenStack Foundation
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
use Doctrine\DBAL\Schema\Schema as Schema;
|
||||
use LaravelDoctrine\Migrations\Schema\Builder;
|
||||
use LaravelDoctrine\Migrations\Schema\Table;
|
||||
/**
|
||||
* Class Version20200803193707
|
||||
* @package Database\Migrations
|
||||
*/
|
||||
class Version20200803193707 extends AbstractMigration
|
||||
{
|
||||
/**
|
||||
* @param Schema $schema
|
||||
*/
|
||||
public function up(Schema $schema)
|
||||
{
|
||||
$builder = new Builder($schema);
|
||||
if($schema->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');
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
|
@ -324,6 +324,7 @@ span.help-block::before {
|
|||
border: 0;
|
||||
vertical-align: middle;
|
||||
border-radius: 50%;
|
||||
height: 100px;
|
||||
}
|
||||
|
||||
.help-block{
|
||||
|
|
|
@ -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){
|
||||
|
|
|
@ -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){
|
||||
|
|
|
@ -12,10 +12,23 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<form id="user-form" name="user-form" role="form"
|
||||
autocomplete="off"
|
||||
action='{!!URL::action("Api\\UserApiController@update",["id" => $user->id])!!}'>
|
||||
<div class="form-group col-xs-10 col-sm-4 col-md-6 col-lg-6">
|
||||
<form id="user-form" name="user-form"
|
||||
role="form"
|
||||
style="padding-top: 20px"
|
||||
autocomplete="off"
|
||||
enctype="multipart/form-data"
|
||||
method="post"
|
||||
action='{!!URL::action("Api\\UserApiController@update",["id" => $user->id])!!}'>
|
||||
@method('PUT')
|
||||
@csrf
|
||||
<div class="form-group col-xs-12 col-sm-12 col-md-12 col-lg-12">
|
||||
<span class="control-label col-md-2">
|
||||
<img src="{!! $user->pic !!}" class="img-circle" id="img-pic" title="Profile pic">
|
||||
</span>
|
||||
<input type="file" name="pic" id="pic"/>
|
||||
</div>
|
||||
|
||||
<div class="form-group col-xs-10 col-sm-4 col-md-6 col-lg-6">
|
||||
<label for="first_name">First name</label>
|
||||
<input autocomplete="off" class="form-control" type="text" name="first_name" id="first_name" value="{!! $user->first_name !!}" data-lpignore="true">
|
||||
</div>
|
||||
|
@ -189,11 +202,11 @@
|
|||
/> Email Verified?
|
||||
</label>
|
||||
</div>
|
||||
<div class="col-xs-10 col-sm-4 col-md-12 col-lg-12">
|
||||
<div class="col-xs-10 col-sm-4 col-md-12 col-lg-12" style="padding-bottom: 20px">
|
||||
<label for="spam-type">Spam Type</label>
|
||||
<input type="text" readonly class="form-control" id="spam-type" name="spam-type" data-lpignore="true" value="{!! $user->spam_type !!}">
|
||||
</div>
|
||||
<button type="submit" class="btn btn-default btn-lg btn-primary">Save</button>
|
||||
<button style="margin-left: 15px;" type="submit" class="btn btn-default btn-lg btn-primary">Save</button>
|
||||
<input type="hidden" name="id" id="id" value="{!! $user->id !!}"/>
|
||||
</form>
|
||||
</div>
|
||||
|
|
|
@ -21,23 +21,24 @@
|
|||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<form id="user-form" name="user-form" role="form"
|
||||
<form id="user-form" name="user-form"
|
||||
role="form"
|
||||
autocomplete="off"
|
||||
enctype="multipart/form-data"
|
||||
method="post"
|
||||
style="padding-bottom: 20px"
|
||||
action='{!!URL::action("Api\\UserApiController@updateMe") !!}'>
|
||||
|
||||
@method('PUT')
|
||||
@csrf
|
||||
<legend><span class="glyphicon glyphicon-info-sign pointable" aria-hidden="true"
|
||||
title="this information will be public on your profile page"></span> {!! Config::get('app.app_name') !!} Account Settings:
|
||||
</legend>
|
||||
|
||||
<div class="form-group col-xs-12 col-sm-12 col-md-12 col-lg-12">
|
||||
<span class="control-label col-md-2">
|
||||
<img src="{!! $user->pic !!}" class="img-circle" title="Gravatar profile pic">
|
||||
<img src="{!! $user->pic !!}" class="img-circle" id="img-pic" title="Profile pic">
|
||||
</span>
|
||||
<div class="col-md-10" style="margin-top: 5px;">
|
||||
<p class="help-block">
|
||||
{!! Config::get('app.app_name') !!} uses the 'Gravatar' profile picture associated with your email address. You can customise your profile pic at <a href="http://en.gravatar.com/" target="_blank">Gravatar.com</a>
|
||||
</p>
|
||||
</div>
|
||||
<input type="file" name="pic" id="pic"/>
|
||||
</div>
|
||||
|
||||
<div class="form-group col-xs-10 col-sm-4 col-md-6 col-lg-6">
|
||||
|
|
Loading…
Reference in New Issue