diff --git a/.env.example b/.env.example index db3e5175b..bbad988d2 100644 --- a/.env.example +++ b/.env.example @@ -1,46 +1,52 @@ -APP_NAME=Laravel +APP_NAME="PixelFed Test" APP_ENV=local APP_KEY= APP_DEBUG=true APP_URL=http://localhost +ADMIN_DOMAIN="localhost" +APP_DOMAIN="localhost" + LOG_CHANNEL=stack DB_CONNECTION=mysql DB_HOST=127.0.0.1 DB_PORT=3306 -DB_DATABASE=homestead -DB_USERNAME=homestead -DB_PASSWORD=secret +DB_DATABASE= +DB_USERNAME= +DB_PASSWORD= BROADCAST_DRIVER=log -CACHE_DRIVER=file -SESSION_DRIVER=file +CACHE_DRIVER=redis +SESSION_DRIVER=redis SESSION_LIFETIME=120 -QUEUE_DRIVER=sync +QUEUE_DRIVER=redis REDIS_HOST=127.0.0.1 REDIS_PASSWORD=null REDIS_PORT=6379 -MAIL_DRIVER=smtp +MAIL_DRIVER=log MAIL_HOST=smtp.mailtrap.io MAIL_PORT=2525 MAIL_USERNAME=null MAIL_PASSWORD=null MAIL_ENCRYPTION=null +MAIL_FROM_ADDRESS="pixelfed@example.com" +MAIL_FROM_NAME="Pixelfed" -PUSHER_APP_ID= -PUSHER_APP_KEY= -PUSHER_APP_SECRET= -PUSHER_APP_CLUSTER=mt1 - -SESSION_DOMAIN=".pixelfed.dev" +SESSION_DOMAIN="${APP_DOMAIN}" SESSION_SECURE_COOKIE=true API_BASE="/api/1/" API_SEARCH="/api/search" OPEN_REGISTRATION=true +RECAPTCHA_ENABLED=false +ENFORCE_EMAIL_VERIFICATION=true + +MAX_PHOTO_SIZE=15000 +MAX_CAPTION_LENGTH=150 +MAX_ALBUM_LENGTH=4 MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}" MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}" diff --git a/README.md b/README.md index c6ea6e002..fe8ad87a8 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,76 @@ -# PixelFed -Federated Image Sharing +# PixelFed: Federated Image Sharing -> This project is still in active development and not yet ready for use. \ No newline at end of file +PixelFed is a federated social image sharing platform, similar to Instagram. +Federation is done using the [ActivityPub](https://activitypub.rocks/) protocol, +which is used by [Mastodon](http://joinmastodon.org/), [PeerTube](https://joinpeertube.org/en/), +[Pleroma](https://pleroma.social/), and more. Through ActivityPub PixelFed can share +and interact with these platforms, as well as other instances of PixelFed. + +**_Please note this is alpha software, not recommended for production use, +and federation is not supported yet._** + +PixelFed is very early into the development stage. If you would like to have a +permanent instance with minimal breakage, **do not use this software until +there is a stable release**. The following setup instructions are intended for +testing and development. + +## Requirements + - PHP >= 7.1.3 (7.2+ recommended for stable version) + - MySQL, Postgres (MariaDB and sqlite are not supported yet) + - Redis + - Composer + - GD or ImageMagick + - OpenSSL PHP Extension + - PDO PHP Extension + - Mbstring PHP Extension + - Tokenizer PHP Extension + - XML PHP Extension + - Ctype PHP Extension + - JSON PHP Extension + - JpegOptim + - Optipng + - Pngquant 2 + - SVGO + - Gifsicle + +## Installation + +This guide assumes you have NGINX/Apache installed, along with the dependencies. +Those will not be covered in these early docs. + +```bash +git clone https://github.com/dansup/pixelfed.git +cd pixelfed +composer install +cp .env.example .env +``` + +**Edit .env file with proper values** + +```bash +php artisan key:generate +``` + +```bash +php artisan storage:link +php artisan migrate +php artisan horizon +php artisan serve --host=localhost --port=80 +``` + +Check your browser at http://localhost + +## Communication + +The ways you can communicate on the project are below. Before interacting, please +read through the [Code Of Conduct](CODE_OF_CONDUCT.md). + +* IRC: #pixelfed on irc.freenode.net ([#freenode_#pixelfed:matrix.org through +Matrix](https://matrix.to/#/#freenode_#pixelfed:matrix.org) +* Project on Mastodon: [@pixelfed@mastodon.social](https://mastodon.social/@pixelfed) +* E-mail: [hello@pixelfed.org](mailto:hello@pixelfed.org) + +## Support + +The lead maintainer is on Patreon! You can become a Patron at +https://www.patreon.com/dansup diff --git a/app/AccountLog.php b/app/AccountLog.php new file mode 100644 index 000000000..f5ccedd9a --- /dev/null +++ b/app/AccountLog.php @@ -0,0 +1,10 @@ +settings)) { + $settings = new UserSetting; + $settings->user_id = $user->id; + $settings->save(); + } + } +} diff --git a/app/Hashtag.php b/app/Hashtag.php index 497bc94ee..6314accc8 100644 --- a/app/Hashtag.php +++ b/app/Hashtag.php @@ -6,7 +6,7 @@ use Illuminate\Database\Eloquent\Model; class Hashtag extends Model { - protected $fillable = ['name','slug']; + public $fillable = ['name','slug']; public function posts() { diff --git a/app/Http/Controllers/AccountController.php b/app/Http/Controllers/AccountController.php index 9f03ef728..5e66d8271 100644 --- a/app/Http/Controllers/AccountController.php +++ b/app/Http/Controllers/AccountController.php @@ -18,19 +18,47 @@ class AccountController extends Controller public function notifications(Request $request) { $this->validate($request, [ - 'page' => 'nullable|min:1|max:3' + 'page' => 'nullable|min:1|max:3', + 'a' => 'nullable|alpha_dash', ]); $profile = Auth::user()->profile; + $action = $request->input('a'); $timeago = Carbon::now()->subMonths(6); - $notifications = Notification::whereProfileId($profile->id) - ->whereDate('created_at', '>', $timeago) - ->orderBy('id','desc') - ->take(30) - ->simplePaginate(); + if($action && in_array($action, ['comment', 'follow', 'mention'])) { + $notifications = Notification::whereProfileId($profile->id) + ->whereAction($action) + ->whereDate('created_at', '>', $timeago) + ->orderBy('id','desc') + ->simplePaginate(30); + } else { + $notifications = Notification::whereProfileId($profile->id) + ->whereDate('created_at', '>', $timeago) + ->orderBy('id','desc') + ->simplePaginate(30); + } return view('account.activity', compact('profile', 'notifications')); } + public function followingActivity(Request $request) + { + $this->validate($request, [ + 'page' => 'nullable|min:1|max:3', + 'a' => 'nullable|alpha_dash', + ]); + $profile = Auth::user()->profile; + $action = $request->input('a'); + $timeago = Carbon::now()->subMonths(1); + $following = $profile->following->pluck('id'); + $notifications = Notification::whereIn('actor_id', $following) + ->where('profile_id', '!=', $profile->id) + ->whereDate('created_at', '>', $timeago) + ->orderBy('notifications.id','desc') + ->simplePaginate(30); + + return view('account.following', compact('profile', 'notifications')); + } + public function verifyEmail(Request $request) { return view('account.verify_email'); @@ -38,10 +66,19 @@ class AccountController extends Controller public function sendVerifyEmail(Request $request) { - if(EmailVerification::whereUserId(Auth::id())->count() !== 0) { - return redirect()->back()->with('status', 'A verification email has already been sent! Please check your email.'); + $timeLimit = Carbon::now()->subDays(1)->toDateTimeString(); + $recentAttempt = EmailVerification::whereUserId(Auth::id()) + ->where('created_at', '>', $timeLimit)->count(); + $exists = EmailVerification::whereUserId(Auth::id())->count(); + + if($recentAttempt == 1 && $exists == 1) { + return redirect()->back()->with('error', 'A verification email has already been sent recently. Please check your email, or try again later.'); + } elseif ($recentAttempt == 0 && $exists !== 0) { + // Delete old verification and send new one. + EmailVerification::whereUserId(Auth::id())->delete(); } + $user = User::whereNull('email_verified_at')->find(Auth::id()); $utoken = hash('sha512', $user->id); $rtoken = str_random(40); @@ -60,14 +97,15 @@ class AccountController extends Controller public function confirmVerifyEmail(Request $request, $userToken, $randomToken) { - $verify = EmailVerification::where(DB::raw('BINARY user_token'), $userToken) - ->where(DB::raw('BINARY random_token'), $randomToken) + $verify = EmailVerification::where('user_token', $userToken) + ->where('random_token', $randomToken) ->firstOrFail(); + if(Auth::id() === $verify->user_id) { $user = User::find(Auth::id()); $user->email_verified_at = Carbon::now(); $user->save(); - return redirect('/timeline'); + return redirect('/'); } } @@ -95,4 +133,5 @@ class AccountController extends Controller } return $notifications; } + } diff --git a/app/Http/Controllers/Api/BaseApiController.php b/app/Http/Controllers/Api/BaseApiController.php new file mode 100644 index 000000000..dd78b16f3 --- /dev/null +++ b/app/Http/Controllers/Api/BaseApiController.php @@ -0,0 +1,71 @@ +middleware('auth'); + $this->fractal = new Fractal\Manager(); + $this->fractal->setSerializer(new ArraySerializer()); + } + + public function accounts(Request $request, $id) + { + $profile = Profile::findOrFail($id); + $resource = new Fractal\Resource\Item($profile, new AccountTransformer); + $res = $this->fractal->createData($resource)->toArray(); + return response()->json($res, 200, [], JSON_PRETTY_PRINT); + } + + public function accountFollowers(Request $request, $id) + { + $profile = Profile::findOrFail($id); + $followers = $profile->followers; + $resource = new Fractal\Resource\Collection($followers, new AccountTransformer); + $res = $this->fractal->createData($resource)->toArray(); + return response()->json($res, 200, [], JSON_PRETTY_PRINT); + } + + public function accountFollowing(Request $request, $id) + { + $profile = Profile::findOrFail($id); + $following = $profile->following; + $resource = new Fractal\Resource\Collection($following, new AccountTransformer); + $res = $this->fractal->createData($resource)->toArray(); + return response()->json($res, 200, [], JSON_PRETTY_PRINT); + } + + public function accountStatuses(Request $request, $id) + { + $profile = Profile::findOrFail($id); + $statuses = $profile->statuses()->orderBy('id', 'desc')->paginate(20); + $resource = new Fractal\Resource\Collection($statuses, new StatusTransformer); + $res = $this->fractal->createData($resource)->toArray(); + return response()->json($res, 200, [], JSON_PRETTY_PRINT); + } + + + public function followSuggestions(Request $request) + { + $followers = Auth::user()->profile->recommendFollowers(); + $resource = new Fractal\Resource\Collection($followers, new AccountTransformer); + $res = $this->fractal->createData($resource)->toArray(); + return response()->json($res); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/ApiController.php b/app/Http/Controllers/ApiController.php index a596346bc..e28dc3d95 100644 --- a/app/Http/Controllers/ApiController.php +++ b/app/Http/Controllers/ApiController.php @@ -2,16 +2,13 @@ namespace App\Http\Controllers; -use Auth; -use App\Like; +use Auth, Cache; +use App\{Like, Status}; use Illuminate\Http\Request; +use App\Http\Controllers\Api\BaseApiController; -class ApiController extends Controller +class ApiController extends BaseApiController { - public function __construct() - { - $this->middleware('auth'); - } public function hydrateLikes(Request $request) { @@ -21,12 +18,18 @@ class ApiController extends Controller ]); $profile = Auth::user()->profile; - - $likes = Like::whereProfileId($profile->id) + $res = Cache::remember('api:like-ids:user:'.$profile->id, 1440, function() use ($profile) { + return Like::whereProfileId($profile->id) ->orderBy('id', 'desc') ->take(1000) ->pluck('status_id'); + }); - return response()->json($likes); + return response()->json($res); + } + + public function loadMoreComments(Request $request) + { + return; } } diff --git a/app/Http/Controllers/Auth/LoginController.php b/app/Http/Controllers/Auth/LoginController.php index 83f844da6..af596e02c 100644 --- a/app/Http/Controllers/Auth/LoginController.php +++ b/app/Http/Controllers/Auth/LoginController.php @@ -2,6 +2,7 @@ namespace App\Http\Controllers\Auth; +use App\{AccountLog, User}; use App\Http\Controllers\Controller; use Illuminate\Foundation\Auth\AuthenticatesUsers; @@ -25,7 +26,7 @@ class LoginController extends Controller * * @var string */ - protected $redirectTo = '/home'; + protected $redirectTo = '/'; /** * Create a new controller instance. @@ -56,4 +57,25 @@ class LoginController extends Controller $this->validate($request, $rules); } + + /** + * The user has been authenticated. + * + * @param \Illuminate\Http\Request $request + * @param mixed $user + * @return mixed + */ + protected function authenticated($request, $user) + { + $log = new AccountLog; + $log->user_id = $user->id; + $log->item_id = $user->id; + $log->item_type = 'App\User'; + $log->action = 'auth.login'; + $log->message = 'Account Login'; + $log->link = null; + $log->ip_address = $request->ip(); + $log->user_agent = $request->userAgent(); + $log->save(); + } } diff --git a/app/Http/Controllers/AvatarController.php b/app/Http/Controllers/AvatarController.php index afb893059..c0fd06cf0 100644 --- a/app/Http/Controllers/AvatarController.php +++ b/app/Http/Controllers/AvatarController.php @@ -3,8 +3,91 @@ namespace App\Http\Controllers; use Illuminate\Http\Request; +use Auth, Log, Storage; +use App\Avatar; +use App\Jobs\AvatarPipeline\AvatarOptimize; class AvatarController extends Controller { - // + public function __construct() + { + return $this->middleware('auth'); + } + + public function store(Request $request) + { + $this->validate($request, [ + 'avatar' => 'required|mimes:jpeg,png|max:1000' + ]); + try { + $user = Auth::user(); + $file = $request->file('avatar'); + $path = $this->getPath($user, $file); + $dir = $path['root']; + $name = $path['name']; + $public = $path['storage']; + $currentAvatar = storage_path('app/'.$user->profile->avatar->media_path); + $loc = $request->file('avatar')->storeAs($public, $name); + + $avatar = Avatar::whereProfileId($user->profile->id)->firstOrFail(); + $opath = $avatar->media_path; + $avatar->media_path = "$public/$name"; + $avatar->thumb_path = null; + $avatar->change_count = ++$avatar->change_count; + $avatar->last_processed_at = null; + $avatar->save(); + + AvatarOptimize::dispatch($user->profile, $currentAvatar); + } catch (Exception $e) { + } + return redirect()->back()->with('status', 'Avatar updated successfully. It may take a few minutes to update across the site.'); + } + + public function getPath($user, $file) + { + $basePath = storage_path('app/public/avatars'); + $this->checkDir($basePath); + + $id = $user->profile->id; + $path = $this->buildPath($id); + $dir = storage_path('app/'.$path); + $this->checkDir($dir); + $name = 'avatar.' . $file->guessExtension(); + $res = ['root' => 'storage/app/' . $path, 'name' => $name, 'storage' => $path]; + + return $res; + } + + public function checkDir($path) + { + if(!is_dir($path)) { + mkdir($path); + } + } + + public function buildPath($id) + { + $padded = str_pad($id, 12, 0, STR_PAD_LEFT); + $parts = str_split($padded, 3); + foreach($parts as $k => $part) { + if($k == 0) { + $prefix = storage_path('app/public/avatars/'.$parts[0]); + $this->checkDir($prefix); + } + if($k == 1) { + $prefix = storage_path('app/public/avatars/'.$parts[0].'/'.$parts[1]); + $this->checkDir($prefix); + } + if($k == 2) { + $prefix = storage_path('app/public/avatars/'.$parts[0].'/'.$parts[1].'/'.$parts[2]); + $this->checkDir($prefix); + } + if($k == 3) { + $avatarpath = 'public/avatars/'.$parts[0].'/'.$parts[1].'/'.$parts[2].'/'.$parts[3]; + $prefix = storage_path('app/'.$avatarpath); + $this->checkDir($prefix); + } + } + return $avatarpath; + } } diff --git a/app/Http/Controllers/BookmarkController.php b/app/Http/Controllers/BookmarkController.php index 17c788d04..fb374dbcc 100644 --- a/app/Http/Controllers/BookmarkController.php +++ b/app/Http/Controllers/BookmarkController.php @@ -16,23 +16,27 @@ class BookmarkController extends Controller public function store(Request $request) { $this->validate($request, [ - 'item' => 'required|integer|min:1' + 'item' => 'required|integer|min:1' ]); $profile = Auth::user()->profile; $status = Status::findOrFail($request->input('item')); $bookmark = Bookmark::firstOrCreate( - ['status_id' => $status->id], ['profile_id' => $profile->id] + ['status_id' => $status->id], ['profile_id' => $profile->id] ); + if(!$bookmark->wasRecentlyCreated) { + $bookmark->delete(); + } + if($request->ajax()) { $response = ['code' => 200, 'msg' => 'Bookmark saved!']; - } else { + } else { $response = redirect()->back(); - } + } - return $response; - } + return $response; + } } diff --git a/app/Http/Controllers/CommentController.php b/app/Http/Controllers/CommentController.php index 70a7825bd..2cfb4f4cc 100644 --- a/app/Http/Controllers/CommentController.php +++ b/app/Http/Controllers/CommentController.php @@ -18,6 +18,14 @@ class CommentController extends Controller return view('status.reply', compact('user', 'status')); } + public function showAll(Request $request, $username, int $id) + { + $user = Profile::whereUsername($username)->firstOrFail(); + $status = Status::whereProfileId($user->id)->findOrFail($id); + $replies = Status::whereInReplyToId($id)->paginate(40); + return view('status.comments', compact('user', 'status', 'replies')); + } + public function store(Request $request) { if(Auth::check() === false) { abort(403); } diff --git a/app/Http/Controllers/DiscoverController.php b/app/Http/Controllers/DiscoverController.php index 8a64c9379..dc3692baa 100644 --- a/app/Http/Controllers/DiscoverController.php +++ b/app/Http/Controllers/DiscoverController.php @@ -15,17 +15,44 @@ class DiscoverController extends Controller public function home() { - $following = Follower::whereProfileId(Auth::user()->profile->id)->pluck('following_id'); - $people = Profile::inRandomOrder()->where('id', '!=', Auth::user()->profile->id)->whereNotIn('id', $following)->take(3)->get(); - $posts = Status::whereHas('media')->where('profile_id', '!=', Auth::user()->profile->id)->whereNotIn('profile_id', $following)->orderBy('created_at', 'desc')->take('21')->get(); + $pid = Auth::user()->profile->id; + + $following = Follower::whereProfileId($pid) + ->pluck('following_id'); + + $people = Profile::inRandomOrder() + ->where('id', '!=', $pid) + ->whereNotIn('id', $following) + ->take(3) + ->get(); + + $posts = Status::whereHas('media') + ->where('profile_id', '!=', $pid) + ->whereNotIn('profile_id', $following) + ->orderBy('created_at', 'desc') + ->simplePaginate(21); + return view('discover.home', compact('people', 'posts')); } public function showTags(Request $request, $hashtag) { - $tag = Hashtag::whereSlug($hashtag)->firstOrFail(); - $posts = $tag->posts()->has('media')->orderBy('id','desc')->paginate(12); - $count = $tag->posts()->has('media')->orderBy('id','desc')->count(); - return view('discover.tags.show', compact('tag', 'posts', 'count')); + $this->validate($request, [ + 'page' => 'nullable|integer|min:1|max:10' + ]); + + $tag = Hashtag::with('posts') + ->withCount('posts') + ->whereSlug($hashtag) + ->firstOrFail(); + + $posts = $tag->posts() + ->whereIsNsfw(false) + ->whereVisibility('public') + ->has('media') + ->orderBy('id','desc') + ->simplePaginate(12); + + return view('discover.tags.show', compact('tag', 'posts')); } } diff --git a/app/Http/Controllers/FederationController.php b/app/Http/Controllers/FederationController.php index 29711945d..8c2e1ce94 100644 --- a/app/Http/Controllers/FederationController.php +++ b/app/Http/Controllers/FederationController.php @@ -2,8 +2,9 @@ namespace App\Http\Controllers; -use Auth; +use Auth, Cache; use App\Profile; +use Carbon\Carbon; use League\Fractal; use Illuminate\Http\Request; use App\Util\Lexer\Nickname; @@ -13,15 +14,26 @@ use App\Transformer\ActivityPub\{ ProfileTransformer }; use App\Jobs\RemoteFollowPipeline\RemoteFollowPipeline; +use App\Jobs\InboxPipeline\InboxWorker; class FederationController extends Controller { public function authCheck() { if(!Auth::check()) { - abort(403); + return abort(403); } - return; + } + + public function authorizeFollow(Request $request) + { + $this->authCheck(); + $this->validate($request, [ + 'acct' => 'required|string|min:3|max:255' + ]); + $acct = $request->input('acct'); + $nickname = Nickname::normalizeProfileUrl($acct); + return view('federation.authorizefollow', compact('acct', 'nickname')); } public function remoteFollow() @@ -64,61 +76,58 @@ class FederationController extends Controller public function nodeinfo() { - $res = [ - 'metadata' => [ - 'nodeName' => config('app.name'), - 'software' => [ - 'homepage' => 'https://pixelfed.org', - 'github' => 'https://github.com/pixelfed', - 'follow' => 'https://mastodon.social/@pixelfed' - ], - /* - TODO: Custom Features for Trending - 'customFeatures' => [ - 'trending' => [ - 'description' => 'Trending API for federated discovery', - 'api' => [ - 'url' => null, - 'docs' => null - ], + $res = Cache::remember('api:nodeinfo', 60, function() { + return [ + 'metadata' => [ + 'nodeName' => config('app.name'), + 'software' => [ + 'homepage' => 'https://pixelfed.org', + 'github' => 'https://github.com/pixelfed', + 'follow' => 'https://mastodon.social/@pixelfed' ], ], - */ - ], - 'openRegistrations' => config('pixelfed.open_registration'), - 'protocols' => [ - 'activitypub' - ], - 'services' => [ - 'inbound' => [], - 'outbound' => [] - ], - 'software' => [ - 'name' => 'pixelfed', - 'version' => config('pixelfed.version') - ], - 'usage' => [ - 'localPosts' => \App\Status::whereLocal(true)->count(), - 'users' => [ - 'total' => \App\User::count() - ] - ], - 'version' => '2.0' - ]; - - return response()->json($res); + 'openRegistrations' => config('pixelfed.open_registration'), + 'protocols' => [ + 'activitypub' + ], + 'services' => [ + 'inbound' => [], + 'outbound' => [] + ], + 'software' => [ + 'name' => 'pixelfed', + 'version' => config('pixelfed.version') + ], + 'usage' => [ + 'localPosts' => \App\Status::whereLocal(true)->whereHas('media')->count(), + 'localComments' => \App\Status::whereLocal(true)->whereNotNull('in_reply_to_id')->count(), + 'users' => [ + 'total' => \App\User::count(), + 'activeHalfyear' => \App\User::where('updated_at', '>', Carbon::now()->subMonths(6)->toDateTimeString())->count(), + 'activeMonth' => \App\User::where('updated_at', '>', Carbon::now()->subMonths(1)->toDateTimeString())->count(), + ] + ], + 'version' => '2.0' + ]; + }); + return response()->json($res, 200, [], JSON_PRETTY_PRINT); } public function webfinger(Request $request) { - $this->validate($request, ['resource'=>'required']); - $resource = $request->input('resource'); - $parsed = Nickname::normalizeProfileUrl($resource); - $username = $parsed['username']; - $user = Profile::whereUsername($username)->firstOrFail(); - $webfinger = (new Webfinger($user))->generate(); - return response()->json($webfinger); + $this->validate($request, ['resource'=>'required|string|min:3|max:255']); + + $hash = hash('sha512', $request->input('resource')); + + $webfinger = Cache::remember('api:webfinger:'.$hash, 1440, function() use($request) { + $resource = $request->input('resource'); + $parsed = Nickname::normalizeProfileUrl($resource); + $username = $parsed['username']; + $user = Profile::whereUsername($username)->firstOrFail(); + return (new Webfinger($user))->generate(); + }); + return response()->json($webfinger, 200, [], JSON_PRETTY_PRINT); } public function userOutbox(Request $request, $username) @@ -135,4 +144,20 @@ class FederationController extends Controller return response()->json($res['data']); } + public function userInbox(Request $request, $username) + { + if(config('pixelfed.activitypub_enabled') == false) { + abort(403); + } + $mimes = [ + 'application/activity+json', + 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"' + ]; + if(!in_array($request->header('Content-Type'), $mimes)) { + abort(500, 'Invalid request'); + } + $profile = Profile::whereUsername($username)->firstOrFail(); + InboxWorker::dispatch($request, $profile, $request->all()); + } + } diff --git a/app/Http/Controllers/ImportDataController.php b/app/Http/Controllers/ImportDataController.php deleted file mode 100644 index 1b0a12e63..000000000 --- a/app/Http/Controllers/ImportDataController.php +++ /dev/null @@ -1,10 +0,0 @@ -likes()->whereProfileId($profile->id)->count() !== 0) { $like = Like::whereProfileId($profile->id)->whereStatusId($status->id)->firstOrFail(); - $like->delete(); + $like->forceDelete(); $count--; } else { $like = new Like; @@ -35,9 +35,15 @@ class LikeController extends Controller $like->status_id = $status->id; $like->save(); $count++; + LikePipeline::dispatch($like); } - LikePipeline::dispatch($like); + $likes = Like::whereProfileId($profile->id) + ->orderBy('id', 'desc') + ->take(1000) + ->pluck('status_id'); + + Cache::put('api:like-ids:user:'.$profile->id, $likes, 1440); if($request->ajax()) { $response = ['code' => 200, 'msg' => 'Like saved', 'count' => $count]; diff --git a/app/Http/Controllers/ProfileController.php b/app/Http/Controllers/ProfileController.php index 4e25bd236..cfc589ba1 100644 --- a/app/Http/Controllers/ProfileController.php +++ b/app/Http/Controllers/ProfileController.php @@ -18,17 +18,22 @@ class ProfileController extends Controller public function show(Request $request, $username) { $user = Profile::whereUsername($username)->firstOrFail(); + $settings = User::whereUsername($username)->firstOrFail()->settings; $mimes = [ 'application/activity+json', - 'application/ld+json', 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"' ]; if(in_array($request->header('accept'), $mimes) && config('pixelfed.activitypub_enabled')) { return $this->showActivityPub($request, $user); } - + if($user->is_private == true) { + $can_access = $this->privateProfileCheck($user); + if($can_access !== true) { + abort(403); + } + } // TODO: refactor this mess $owner = Auth::check() && Auth::id() === $user->user_id; $is_following = ($owner == false && Auth::check()) ? $user->followedBy(Auth::user()->profile) : false; @@ -36,11 +41,26 @@ class ProfileController extends Controller $timeline = $user->statuses() ->whereHas('media') ->whereNull('in_reply_to_id') - ->orderBy('id','desc') + ->orderBy('created_at','desc') ->withCount(['comments', 'likes']) ->simplePaginate(21); - return view('profile.show', compact('user', 'owner', 'is_following', 'is_admin', 'timeline')); + return view('profile.show', compact('user', 'settings', 'owner', 'is_following', 'is_admin', 'timeline')); + } + + protected function privateProfileCheck(Profile $profile) + { + if(Auth::check() === false) { + return false; + } + + $follower_ids = (array) $profile->followers()->pluck('followers.profile_id'); + $pid = Auth::user()->profile->id; + if(!in_array($pid, $follower_ids) && $pid !== $profile->id) { + return false; + } + + return true; } public function showActivityPub(Request $request, $user) @@ -89,11 +109,12 @@ class ProfileController extends Controller abort(403); } $user = Auth::user()->profile; + $settings = User::whereUsername($username)->firstOrFail()->settings; $owner = true; $following = false; $timeline = $user->bookmarks()->withCount(['likes','comments'])->orderBy('created_at','desc')->simplePaginate(10); $is_following = ($owner == false && Auth::check()) ? $user->followedBy(Auth::user()->profile) : false; $is_admin = is_null($user->domain) ? $user->user->is_admin : false; - return view('profile.show', compact('user', 'owner', 'following', 'timeline', 'is_following', 'is_admin')); + return view('profile.show', compact('user', 'settings', 'owner', 'following', 'timeline', 'is_following', 'is_admin')); } } diff --git a/app/Http/Controllers/ReportController.php b/app/Http/Controllers/ReportController.php index c775d9377..832cb88c9 100644 --- a/app/Http/Controllers/ReportController.php +++ b/app/Http/Controllers/ReportController.php @@ -2,12 +2,25 @@ namespace App\Http\Controllers; +use Auth; use Illuminate\Http\Request; +use App\{Avatar, Profile, Report, Status, User}; class ReportController extends Controller { + protected $profile; + + public function __construct() + { + $this->middleware('auth'); + } + public function showForm(Request $request) { + $this->validate($request, [ + 'type' => 'required|alpha_dash', + 'id' => 'required|integer|min:1' + ]); return view('report.form'); } @@ -35,4 +48,92 @@ class ReportController extends Controller { return view('report.spam.profile'); } + + public function sensitiveCommentForm(Request $request) + { + return view('report.sensitive.comment'); + } + + public function sensitivePostForm(Request $request) + { + return view('report.sensitive.post'); + } + + public function sensitiveProfileForm(Request $request) + { + return view('report.sensitive.profile'); + } + + public function abusiveCommentForm(Request $request) + { + return view('report.abusive.comment'); + } + + public function abusivePostForm(Request $request) + { + return view('report.abusive.post'); + } + + public function abusiveProfileForm(Request $request) + { + return view('report.abusive.profile'); + } + + public function formStore(Request $request) + { + $this->validate($request, [ + 'report' => 'required|alpha_dash', + 'type' => 'required|alpha_dash', + 'id' => 'required|integer|min:1', + 'msg' => 'nullable|string|max:150' + ]); + + $profile = Auth::user()->profile; + $reportType = $request->input('report'); + $object_id = $request->input('id'); + $object_type = $request->input('type'); + $msg = $request->input('msg'); + $object = null; + $types = ['spam', 'sensitive', 'abusive']; + + if(!in_array($reportType, $types)) { + return redirect('/timeline')->with('error', 'Invalid report type'); + } + + switch ($object_type) { + case 'post': + $object = Status::findOrFail($object_id); + $object_type = 'App\Status'; + $exists = Report::whereUserId(Auth::id()) + ->whereObjectId($object->id) + ->whereObjectType('App\Status') + ->count(); + break; + + default: + return redirect('/timeline')->with('error', 'Invalid report type'); + break; + } + + if($exists !== 0) { + return redirect('/timeline')->with('error', 'You have already reported this!'); + } + + if($object->profile_id == $profile->id) { + return redirect('/timeline')->with('error', 'You cannot report your own content!'); + } + + $report = new Report; + $report->profile_id = $profile->id; + $report->user_id = Auth::id(); + $report->object_id = $object->id; + $report->object_type = $object_type; + $report->reported_profile_id = $object->profile_id; + $report->type = $request->input('report'); + $report->message = $request->input('msg'); + $report->save(); + + return redirect('/timeline')->with('status', 'Report successfully sent!'); + } + } diff --git a/app/Http/Controllers/SettingsController.php b/app/Http/Controllers/SettingsController.php index 4beb45418..f3a8415cb 100644 --- a/app/Http/Controllers/SettingsController.php +++ b/app/Http/Controllers/SettingsController.php @@ -3,19 +3,28 @@ namespace App\Http\Controllers; use Illuminate\Http\Request; -use App\{Profile, User}; -use Auth; +use App\{AccountLog, Media, Profile, User}; +use Auth, DB; +use App\Util\Lexer\PrettyNumber; class SettingsController extends Controller { public function __construct() { - return $this->middleware('auth'); + $this->middleware('auth'); } public function home() { - return view('settings.home'); + $id = Auth::user()->profile->id; + $storage = []; + $used = Media::whereProfileId($id)->sum('size'); + $storage['limit'] = config('pixelfed.max_account_size') * 1024; + $storage['used'] = $used; + $storage['percentUsed'] = ceil($storage['used'] / $storage['limit'] * 100); + $storage['limitPretty'] = PrettyNumber::size($storage['limit']); + $storage['usedPretty'] = PrettyNumber::size($storage['used']); + return view('settings.home', compact('storage')); } public function homeUpdate(Request $request) @@ -89,6 +98,34 @@ class SettingsController extends Controller return view('settings.avatar'); } + public function accessibility() + { + $settings = Auth::user()->settings; + return view('settings.accessibility', compact('settings')); + } + + public function accessibilityStore(Request $request) + { + $settings = Auth::user()->settings; + $fields = [ + 'compose_media_descriptions', + 'reduce_motion', + 'optimize_screen_reader', + 'high_contrast_mode', + 'video_autoplay' + ]; + foreach($fields as $field) { + $form = $request->input($field); + if($form == 'on') { + $settings->{$field} = true; + } else { + $settings->{$field} = false; + } + $settings->save(); + } + return redirect(route('settings.accessibility'))->with('status', 'Settings successfully updated!'); + } + public function notifications() { return view('settings.notifications'); @@ -96,12 +133,61 @@ class SettingsController extends Controller public function privacy() { - return view('settings.privacy'); + $settings = Auth::user()->settings; + $is_private = Auth::user()->profile->is_private; + $settings['is_private'] = (bool) $is_private; + return view('settings.privacy', compact('settings')); + } + + public function privacyStore(Request $request) + { + $settings = Auth::user()->settings; + $profile = Auth::user()->profile; + $fields = [ + 'is_private', + 'crawlable', + ]; + foreach($fields as $field) { + $form = $request->input($field); + if($field == 'is_private') { + if($form == 'on') { + $profile->{$field} = true; + $settings->show_guests = false; + $settings->show_discover = false; + $profile->save(); + } else { + $profile->{$field} = false; + $profile->save(); + } + } elseif($field == 'crawlable') { + if($form == 'on') { + $settings->{$field} = false; + } else { + $settings->{$field} = true; + } + } else { + if($form == 'on') { + $settings->{$field} = true; + } else { + $settings->{$field} = false; + } + } + $settings->save(); + } + return redirect(route('settings.privacy'))->with('status', 'Settings successfully updated!'); } public function security() { - return view('settings.security'); + $sessions = DB::table('sessions') + ->whereUserId(Auth::id()) + ->limit(20) + ->get(); + $activity = AccountLog::whereUserId(Auth::id()) + ->orderBy('created_at','desc') + ->limit(50) + ->get(); + return view('settings.security', compact('sessions', 'activity')); } public function applications() @@ -121,7 +207,7 @@ class SettingsController extends Controller public function dataImportInstagram() { - return view('settings.import.ig'); + return view('settings.import.instagram.home'); } public function developers() diff --git a/app/Http/Controllers/SiteController.php b/app/Http/Controllers/SiteController.php index 31603176b..ebe315966 100644 --- a/app/Http/Controllers/SiteController.php +++ b/app/Http/Controllers/SiteController.php @@ -2,11 +2,42 @@ namespace App\Http\Controllers; -use App; +use App, Auth, Cache; use Illuminate\Http\Request; +use App\{Follower, Profile, Status, User}; +use App\Util\Lexer\PrettyNumber; class SiteController extends Controller { + + public function home() + { + if(Auth::check()) { + return $this->homeTimeline(); + } else { + return $this->homeGuest(); + } + } + + public function homeGuest() + { + return view('welcome'); + } + + public function homeTimeline() + { + // TODO: Use redis for timelines + $following = Follower::whereProfileId(Auth::user()->profile->id)->pluck('following_id'); + $following->push(Auth::user()->profile->id); + $timeline = Status::whereIn('profile_id', $following) + ->whereHas('media') + ->orderBy('id','desc') + ->withCount(['comments', 'likes', 'shares']) + ->simplePaginate(20); + $type = 'personal'; + return view('timeline.template', compact('timeline', 'type')); + } + public function changeLocale(Request $request, $locale) { if(!App::isLocale($locale)) { @@ -15,4 +46,20 @@ class SiteController extends Controller App::setLocale($locale); return redirect()->back(); } + + public function about() + { + $res = Cache::remember('site:page:about', 15, function() { + $statuses = Status::whereHas('media') + ->whereNull('in_reply_to_id') + ->whereNull('reblog_of_id') + ->count(); + $statusCount = PrettyNumber::convert($statuses); + $userCount = PrettyNumber::convert(User::count()); + $remoteCount = PrettyNumber::convert(Profile::whereNotNull('remote_url')->count()); + $adminContact = User::whereIsAdmin(true)->first(); + return view('site.about')->with(compact('statusCount', 'userCount', 'remoteCount', 'adminContact'))->render(); + }); + return $res; + } } diff --git a/app/Http/Controllers/StatusController.php b/app/Http/Controllers/StatusController.php index 67f204cdf..651d632d5 100644 --- a/app/Http/Controllers/StatusController.php +++ b/app/Http/Controllers/StatusController.php @@ -13,93 +13,147 @@ class StatusController extends Controller { public function show(Request $request, $username, int $id) { - $user = Profile::whereUsername($username)->firstOrFail(); - $status = Status::whereProfileId($user->id) - ->withCount(['likes', 'comments', 'media']) - ->findOrFail($id); - if(!$status->media_path && $status->in_reply_to_id) { - return redirect($status->url()); - } - return view('status.show', compact('user', 'status')); + $user = Profile::whereUsername($username)->firstOrFail(); + $status = Status::whereProfileId($user->id) + ->withCount(['likes', 'comments', 'media']) + ->findOrFail($id); + if(!$status->media_path && $status->in_reply_to_id) { + return redirect($status->url()); + } + $replies = Status::whereInReplyToId($status->id)->simplePaginate(30); + return view('status.show', compact('user', 'status', 'replies')); + } + + public function compose() + { + if(Auth::check() == false) + { + abort(403); + } + return view('status.compose'); } public function store(Request $request) { - if(Auth::check() == false) - { - abort(403); - } + if(Auth::check() == false) + { + abort(403); + } - $user = Auth::user(); + $user = Auth::user(); - $this->validate($request, [ - 'photo.*' => 'required|mimes:jpeg,png,bmp,gif|max:' . config('pixelfed.max_photo_size'), - 'caption' => 'string|max:' . config('pixelfed.max_caption_length'), - 'cw' => 'nullable|string', - 'filter_class' => 'nullable|string', - 'filter_name' => 'nullable|string', - ]); + $size = Media::whereUserId($user->id)->sum('size') / 1000; + $limit = (int) config('pixelfed.max_account_size'); + if($size >= $limit) { + return redirect()->back()->with('error', 'You have exceeded your storage limit. Please click here for more info.'); + } - if(count($request->file('photo')) > config('pixelfed.max_album_length')) { - return redirect()->back()->with('error', 'Too many files, max limit per post: ' . config('pixelfed.max_album_length')); - } + $this->validate($request, [ + 'photo.*' => 'required|mimes:jpeg,png,bmp,gif|max:' . config('pixelfed.max_photo_size'), + 'caption' => 'string|max:' . config('pixelfed.max_caption_length'), + 'cw' => 'nullable|string', + 'filter_class' => 'nullable|string', + 'filter_name' => 'nullable|string', + ]); - $cw = $request->filled('cw') && $request->cw == 'on' ? true : false; - $monthHash = hash('sha1', date('Y') . date('m')); - $userHash = hash('sha1', $user->id . (string) $user->created_at); - $profile = $user->profile; + if(count($request->file('photo')) > config('pixelfed.max_album_length')) { + return redirect()->back()->with('error', 'Too many files, max limit per post: ' . config('pixelfed.max_album_length')); + } + $cw = $request->filled('cw') && $request->cw == 'on' ? true : false; + $monthHash = hash('sha1', date('Y') . date('m')); + $userHash = hash('sha1', $user->id . (string) $user->created_at); + $profile = $user->profile; - $status = new Status; - $status->profile_id = $profile->id; - $status->caption = strip_tags($request->caption); - $status->is_nsfw = $cw; + $status = new Status; + $status->profile_id = $profile->id; + $status->caption = strip_tags($request->caption); + $status->is_nsfw = $cw; - $status->save(); + $status->save(); - $photos = $request->file('photo'); - $order = 1; - foreach ($photos as $k => $v) { - $storagePath = "public/m/{$monthHash}/{$userHash}"; - $path = $v->store($storagePath); - $media = new Media; - $media->status_id = $status->id; - $media->profile_id = $profile->id; - $media->user_id = $user->id; - $media->media_path = $path; - $media->size = $v->getClientSize(); - $media->mime = $v->getClientMimeType(); - $media->filter_class = $request->input('filter_class'); - $media->filter_name = $request->input('filter_name'); - $media->order = $order; - $media->save(); - ImageOptimize::dispatch($media); - $order++; - } + $photos = $request->file('photo'); + $order = 1; + foreach ($photos as $k => $v) { + $storagePath = "public/m/{$monthHash}/{$userHash}"; + $path = $v->store($storagePath); + $media = new Media; + $media->status_id = $status->id; + $media->profile_id = $profile->id; + $media->user_id = $user->id; + $media->media_path = $path; + $media->size = $v->getClientSize(); + $media->mime = $v->getClientMimeType(); + $media->filter_class = $request->input('filter_class'); + $media->filter_name = $request->input('filter_name'); + $media->order = $order; + $media->save(); + ImageOptimize::dispatch($media); + $order++; + } - NewStatusPipeline::dispatch($status); + NewStatusPipeline::dispatch($status); - // TODO: Send to subscribers - - return redirect($status->url()); + // TODO: Send to subscribers + + return redirect($status->url()); } public function delete(Request $request) { - if(!Auth::check()) { - abort(403); - } + if(!Auth::check()) { + abort(403); + } - $this->validate($request, [ - 'type' => 'required|string', - 'item' => 'required|integer|min:1' - ]); + $this->validate($request, [ + 'type' => 'required|string', + 'item' => 'required|integer|min:1' + ]); - $status = Status::findOrFail($request->input('item')); + $status = Status::findOrFail($request->input('item')); - if($status->profile_id === Auth::user()->profile->id || Auth::user()->is_admin == true) { - StatusDelete::dispatch($status); - } + if($status->profile_id === Auth::user()->profile->id || Auth::user()->is_admin == true) { + StatusDelete::dispatch($status); + } - return redirect(Auth::user()->url()); + return redirect(Auth::user()->url()); + } + + public function storeShare(Request $request) + { + $this->validate($request, [ + 'item' => 'required|integer', + ]); + + $profile = Auth::user()->profile; + $status = Status::withCount('shares')->findOrFail($request->input('item')); + + $count = $status->shares_count; + + $exists = Status::whereProfileId(Auth::user()->profile->id) + ->whereReblogOfId($status->id) + ->count(); + if($exists !== 0) { + $shares = Status::whereProfileId(Auth::user()->profile->id) + ->whereReblogOfId($status->id) + ->get(); + foreach($shares as $share) { + $share->delete(); + $count--; + } + } else { + $share = new Status; + $share->profile_id = $profile->id; + $share->reblog_of_id = $status->id; + $share->save(); + $count++; + } + + if($request->ajax()) { + $response = ['code' => 200, 'msg' => 'Share saved', 'count' => $count]; + } else { + $response = redirect($status->url()); + } + + return $response; } } diff --git a/app/Http/Controllers/TimelineController.php b/app/Http/Controllers/TimelineController.php index c49b98b3b..7424e3db6 100644 --- a/app/Http/Controllers/TimelineController.php +++ b/app/Http/Controllers/TimelineController.php @@ -18,24 +18,23 @@ class TimelineController extends Controller // TODO: Use redis for timelines $following = Follower::whereProfileId(Auth::user()->profile->id)->pluck('following_id'); $following->push(Auth::user()->profile->id); - $timeline = Status::whereHas('media') - ->whereNull('in_reply_to_id') - ->whereIn('profile_id', $following) + $timeline = Status::whereIn('profile_id', $following) ->orderBy('id','desc') - ->withCount(['comments', 'likes']) - ->simplePaginate(10); - return view('timeline.personal', compact('timeline')); + ->simplePaginate(20); + $type = 'personal'; + return view('timeline.template', compact('timeline', 'type')); } public function local() { // TODO: Use redis for timelines + // $timeline = Timeline::build()->local(); $timeline = Status::whereHas('media') ->whereNull('in_reply_to_id') ->orderBy('id','desc') - ->withCount(['comments', 'likes']) - ->simplePaginate(10); - return view('timeline.public', compact('timeline')); + ->simplePaginate(20); + $type = 'local'; + return view('timeline.template', compact('timeline', 'type')); } } diff --git a/app/Http/Middleware/EmailVerificationCheck.php b/app/Http/Middleware/EmailVerificationCheck.php index 04ee1fb1b..b9ff791dd 100644 --- a/app/Http/Middleware/EmailVerificationCheck.php +++ b/app/Http/Middleware/EmailVerificationCheck.php @@ -18,7 +18,7 @@ class EmailVerificationCheck if($request->user() && config('pixelfed.enforce_email_verification') && is_null($request->user()->email_verified_at) && - !$request->is('i/verify-email') && !$request->is('login') && + !$request->is('i/verify-email') && !$request->is('log*') && !$request->is('i/confirm-email/*') ) { return redirect('/i/verify-email'); diff --git a/app/ImportJob.php b/app/ImportJob.php new file mode 100644 index 000000000..dc0e1cdaa --- /dev/null +++ b/app/ImportJob.php @@ -0,0 +1,10 @@ +profile = $profile; + $this->current = $current; + } + + /** + * Execute the job. + * + * @return void + */ + public function handle() + { + $avatar = $this->profile->avatar; + $file = storage_path("app/$avatar->media_path"); + + try { + $img = Intervention::make($file)->orientate(); + $img->fit(200, 200, function ($constraint) { + $constraint->upsize(); + }); + $quality = config('pixelfed.image_quality'); + $img->save($file, $quality); + + $avatar = Avatar::whereProfileId($this->profile->id)->firstOrFail(); + $avatar->thumb_path = $avatar->media_path; + $avatar->change_count = ++$avatar->change_count; + $avatar->last_processed_at = Carbon::now(); + $avatar->save(); + $this->deleteOldAvatar($avatar->media_path, $this->current); + } catch (Exception $e) { + + } + } + + protected function deleteOldAvatar($new, $current) + { + if(storage_path('app/' . $new) == $current) { + return; + } + if(is_file($current)) { + @unlink($current); + } + } +} diff --git a/app/Jobs/InboxPipeline/InboxWorker.php b/app/Jobs/InboxPipeline/InboxWorker.php new file mode 100644 index 000000000..db65c3580 --- /dev/null +++ b/app/Jobs/InboxPipeline/InboxWorker.php @@ -0,0 +1,43 @@ +request = $request; + $this->profile = $profile; + $this->payload = $payload; + } + + /** + * Execute the job. + * + * @return void + */ + public function handle() + { + (new Inbox($this->request, $this->profile, $this->payload))->handle(); + } + +} diff --git a/app/Jobs/InboxPipeline/SharedInboxWorker.php b/app/Jobs/InboxPipeline/SharedInboxWorker.php new file mode 100644 index 000000000..dcc0db282 --- /dev/null +++ b/app/Jobs/InboxPipeline/SharedInboxWorker.php @@ -0,0 +1,41 @@ +request = $request; + $this->payload = $payload; + } + + /** + * Execute the job. + * + * @return void + */ + public function handle() + { + (new Inbox($this->request, null, $this->payload))->handleSharedInbox(); + } +} diff --git a/app/Jobs/LikePipeline/LikePipeline.php b/app/Jobs/LikePipeline/LikePipeline.php index 9d53fd8b1..8eccd726f 100644 --- a/app/Jobs/LikePipeline/LikePipeline.php +++ b/app/Jobs/LikePipeline/LikePipeline.php @@ -37,6 +37,11 @@ class LikePipeline implements ShouldQueue $status = $this->like->status; $actor = $this->like->actor; + if($status->url !== null) { + // Ignore notifications to remote statuses + return; + } + $exists = Notification::whereProfileId($status->profile_id) ->whereActorId($actor->id) ->whereAction('like') diff --git a/app/Jobs/StatusPipeline/StatusEntityLexer.php b/app/Jobs/StatusPipeline/StatusEntityLexer.php index c9dff4d59..48b95d9a5 100644 --- a/app/Jobs/StatusPipeline/StatusEntityLexer.php +++ b/app/Jobs/StatusPipeline/StatusEntityLexer.php @@ -2,7 +2,7 @@ namespace App\Jobs\StatusPipeline; -use Cache; +use DB, Cache; use App\{ Hashtag, Media, @@ -68,12 +68,14 @@ class StatusEntityLexer implements ShouldQueue public function storeEntities() { - $status = $this->status; $this->storeHashtags(); $this->storeMentions(); - $status->rendered = $this->autolink; - $status->entities = json_encode($this->entities); - $status->save(); + DB::transaction(function () { + $status = $this->status; + $status->rendered = $this->autolink; + $status->entities = json_encode($this->entities); + $status->save(); + }); } public function storeHashtags() @@ -82,17 +84,15 @@ class StatusEntityLexer implements ShouldQueue $status = $this->status; foreach($tags as $tag) { - $slug = str_slug($tag); - - $htag = Hashtag::firstOrCreate( - ['name' => $tag], - ['slug' => $slug] - ); - - StatusHashtag::firstOrCreate( - ['status_id' => $status->id], - ['hashtag_id' => $htag->id] - ); + DB::transaction(function () use ($status, $tag) { + $slug = str_slug($tag); + $hashtag = Hashtag::firstOrCreate( + ['name' => $tag, 'slug' => $slug] + ); + StatusHashtag::firstOrCreate( + ['status_id' => $status->id, 'hashtag_id' => $hashtag->id] + ); + }); } } @@ -102,18 +102,20 @@ class StatusEntityLexer implements ShouldQueue $status = $this->status; foreach($mentions as $mention) { - $mentioned = Profile::whereUsername($mention)->first(); + $mentioned = Profile::whereUsername($mention)->firstOrFail(); if(empty($mentioned) || !isset($mentioned->id)) { continue; } - $m = new Mention; - $m->status_id = $status->id; - $m->profile_id = $mentioned->id; - $m->save(); - - MentionPipeline::dispatch($status, $m); + DB::transaction(function () use ($status, $mentioned) { + $m = new Mention; + $m->status_id = $status->id; + $m->profile_id = $mentioned->id; + $m->save(); + + MentionPipeline::dispatch($status, $m); + }); } } diff --git a/app/Media.php b/app/Media.php index 7c9138965..7ac547f32 100644 --- a/app/Media.php +++ b/app/Media.php @@ -23,4 +23,11 @@ class Media extends Model $url = Storage::url($path); return url($url); } + + public function thumbnailUrl() + { + $path = $this->thumbnail_path; + $url = Storage::url($path); + return url($url); + } } diff --git a/app/Observer/UserObserver.php b/app/Observer/UserObserver.php index e91042830..6f2a1dfca 100644 --- a/app/Observer/UserObserver.php +++ b/app/Observer/UserObserver.php @@ -2,7 +2,7 @@ namespace App\Observers; -use App\{Profile, User}; +use App\{Profile, User, UserSetting}; use App\Jobs\AvatarPipeline\CreateAvatar; class UserObserver @@ -36,6 +36,12 @@ class UserObserver CreateAvatar::dispatch($profile); } + + if(empty($user->settings)) { + $settings = new UserSetting; + $settings->user_id = $user->id; + $settings->save(); + } } } \ No newline at end of file diff --git a/app/Profile.php b/app/Profile.php index 009ed2cbf..4192da0f8 100644 --- a/app/Profile.php +++ b/app/Profile.php @@ -2,7 +2,7 @@ namespace App; -use Storage; +use Auth, Storage; use App\Util\Lexer\PrettyNumber; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; @@ -29,6 +29,15 @@ class Profile extends Model } public function url($suffix = '') + { + if($this->remote_url) { + return $this->remote_url; + } else { + return url($this->username . $suffix); + } + } + + public function localUrl($suffix = '') { return url($this->username . $suffix); } @@ -124,4 +133,40 @@ class Profile extends Model $url = url(Storage::url($this->avatar->media_path ?? 'public/avatars/default.png')); return $url; } + + public function statusCount() + { + return $this->statuses()->whereHas('media')->count(); + } + + public function recommendFollowers() + { + $follows = $this->following()->pluck('followers.id'); + $following = $this->following() + ->orderByRaw('rand()') + ->take(3) + ->pluck('following_id'); + $following->push(Auth::id()); + $following = Follower::whereNotIn('profile_id', $follows) + ->whereNotIn('following_id', $following) + ->whereNotIn('following_id', $follows) + ->whereIn('profile_id', $following) + ->orderByRaw('rand()') + ->limit(3) + ->pluck('following_id'); + $recommended = []; + foreach($following as $follow) { + $recommended[] = Profile::findOrFail($follow); + } + + return $recommended; + } + + public function keyId() + { + if($this->remote_url) { + return; + } + return $this->permalink('#main-key'); + } } diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php index fca6152c3..d05aed739 100644 --- a/app/Providers/EventServiceProvider.php +++ b/app/Providers/EventServiceProvider.php @@ -16,6 +16,9 @@ class EventServiceProvider extends ServiceProvider 'App\Events\Event' => [ 'App\Listeners\EventListener', ], + 'auth.login' => [ + 'App\Events\AuthLoginEvent', + ], ]; /** diff --git a/app/Report.php b/app/Report.php index a57f84acb..879a8df36 100644 --- a/app/Report.php +++ b/app/Report.php @@ -6,5 +6,33 @@ use Illuminate\Database\Eloquent\Model; class Report extends Model { - // + public function url() + { + return url('/i/admin/reports/show/' . $this->id); + } + + public function reporter() + { + return $this->belongsTo(Profile::class, 'profile_id'); + } + + public function reported() + { + $class = $this->object_type; + switch ($class) { + case 'App\Status': + $column = 'id'; + break; + + default: + $column = 'id'; + break; + } + return (new $class())->where($column, $this->object_id)->firstOrFail(); + } + + public function reportedUser() + { + return $this->belongsTo(Profile::class, 'reported_profile_id', 'id'); + } } diff --git a/app/ReportComment.php b/app/ReportComment.php new file mode 100644 index 000000000..5d364e1e9 --- /dev/null +++ b/app/ReportComment.php @@ -0,0 +1,10 @@ +id; + $username = $this->profile->username; + $path = config('app.url') . "/p/{$username}/{$id}{$suffix}"; + return url($path); + } + public function editUrl() { return $this->url() . '/edit'; @@ -71,15 +79,42 @@ class Status extends Model return $this->hasMany(Like::class); } + public function liked() : bool + { + $profile = Auth::user()->profile; + return Like::whereProfileId($profile->id)->whereStatusId($this->id)->count(); + } + public function comments() { return $this->hasMany(Status::class, 'in_reply_to_id'); } + public function bookmarked() + { + if(!Auth::check()) { + return 0; + } + $profile = Auth::user()->profile; + return Bookmark::whereProfileId($profile->id)->whereStatusId($this->id)->count(); + } + + public function shares() + { + return $this->hasMany(Status::class, 'reblog_of_id'); + } + + public function shared() : bool + { + $profile = Auth::user()->profile; + return Status::whereProfileId($profile->id)->whereReblogOfId($this->id)->count(); + } + public function parent() { - if(!empty($this->in_reply_to_id)) { - return Status::findOrFail($this->in_reply_to_id); + $parent = $this->in_reply_to_id ?? $this->reblog_of_id; + if(!empty($parent)) { + return Status::findOrFail($parent); } } @@ -100,6 +135,23 @@ class Status extends Model ); } + public function mentions() + { + return $this->hasManyThrough( + Profile::class, + Mention::class, + 'status_id', + 'id', + 'id', + 'profile_id' + ); + } + + public function reportUrl() + { + return route('report.form') . "?type=post&id={$this->id}"; + } + public function toActivityStream() { $media = $this->media; @@ -133,4 +185,9 @@ class Status extends Model return "{$actorName} " . __('notification.commented'); } + + public function recentComments() + { + return $this->comments()->orderBy('created_at','desc')->take(3); + } } diff --git a/app/StatusHashtag.php b/app/StatusHashtag.php index 7ceac0564..3d15b2036 100644 --- a/app/StatusHashtag.php +++ b/app/StatusHashtag.php @@ -6,5 +6,5 @@ use Illuminate\Database\Eloquent\Model; class StatusHashtag extends Model { - protected $fillable = ['status_id', 'hashtag_id']; + public $fillable = ['status_id', 'hashtag_id']; } diff --git a/app/Transformer/Api/AccountTransformer.php b/app/Transformer/Api/AccountTransformer.php new file mode 100644 index 000000000..1f95c8136 --- /dev/null +++ b/app/Transformer/Api/AccountTransformer.php @@ -0,0 +1,33 @@ + $profile->id, + 'username' => $profile->username, + 'acct' => $profile->username, + 'display_name' => $profile->name, + 'locked' => (bool) $profile->is_private, + 'created_at' => $profile->created_at->format('c'), + 'followers_count' => $profile->followerCount(), + 'following_count' => $profile->followingCount(), + 'statuses_count' => $profile->statusCount(), + 'note' => $profile->bio, + 'url' => $profile->url(), + 'avatar' => $profile->avatarUrl(), + 'avatar_static' => $profile->avatarUrl(), + 'header' => '', + 'header_static' => '', + 'moved' => null, + 'fields' => null, + 'bot' => null + ]; + } +} \ No newline at end of file diff --git a/app/Transformer/Api/ApplicationTransformer.php b/app/Transformer/Api/ApplicationTransformer.php new file mode 100644 index 000000000..a0fefcaa5 --- /dev/null +++ b/app/Transformer/Api/ApplicationTransformer.php @@ -0,0 +1,16 @@ + '', + 'website' => null + ]; + } +} \ No newline at end of file diff --git a/app/Transformer/Api/HashtagTransformer.php b/app/Transformer/Api/HashtagTransformer.php new file mode 100644 index 000000000..417cc9850 --- /dev/null +++ b/app/Transformer/Api/HashtagTransformer.php @@ -0,0 +1,18 @@ + $hashtag->name, + 'url' => $hashtag->url(), + ]; + } +} \ No newline at end of file diff --git a/app/Transformer/Api/MediaTransformer.php b/app/Transformer/Api/MediaTransformer.php new file mode 100644 index 000000000..959bae65b --- /dev/null +++ b/app/Transformer/Api/MediaTransformer.php @@ -0,0 +1,24 @@ + $media->id, + 'type' => 'image', + 'url' => $media->url(), + 'remote_url' => null, + 'preview_url' => $media->thumbnailUrl(), + 'text_url' => null, + 'meta' => null, + 'description' => null + ]; + } +} \ No newline at end of file diff --git a/app/Transformer/Api/MentionTransformer.php b/app/Transformer/Api/MentionTransformer.php new file mode 100644 index 000000000..1d0580afe --- /dev/null +++ b/app/Transformer/Api/MentionTransformer.php @@ -0,0 +1,19 @@ + $profile->id, + 'url' => $profile->url(), + 'username' => $profile->username, + 'acct' => $profile->username, + ]; + } +} \ No newline at end of file diff --git a/app/Transformer/Api/StatusTransformer.php b/app/Transformer/Api/StatusTransformer.php new file mode 100644 index 000000000..ad5129a91 --- /dev/null +++ b/app/Transformer/Api/StatusTransformer.php @@ -0,0 +1,69 @@ + $status->id, + 'uri' => $status->url(), + 'url' => $status->url(), + 'in_reply_to_id' => $status->in_reply_to_id, + 'in_reply_to_account_id' => $status->in_reply_to_profile_id, + + // TODO: fixme + 'reblog' => null, + + 'content' => "

$status->rendered

", + 'created_at' => $status->created_at->format('c'), + 'emojis' => [], + 'reblogs_count' => $status->shares()->count(), + 'favourites_count' => $status->likes()->count(), + 'reblogged' => $status->shared(), + 'favourited' => $status->liked(), + 'muted' => null, + 'sensitive' => (bool) $status->is_nsfw, + 'spoiler_text' => '', + 'visibility' => $status->visibility, + 'application' => null, + 'language' => null, + 'pinned' => null + ]; + } + + public function includeAccount(Status $status) + { + $account = $status->profile; + return $this->item($account, new AccountTransformer); + } + + public function includeMentions(Status $status) + { + $mentions = $status->mentions; + return $this->collection($mentions, new MentionTransformer); + } + + public function includeMediaAttachments(Status $status) + { + $media = $status->media; + return $this->collection($media, new MediaTransformer); + } + + public function includeTags(Status $status) + { + $tags = $status->hashtags; + return $this->collection($tags, new HashtagTransformer); + } +} \ No newline at end of file diff --git a/app/User.php b/app/User.php index 38edc3e9d..a3fb76fb7 100644 --- a/app/User.php +++ b/app/User.php @@ -15,7 +15,7 @@ class User extends Authenticatable * * @var array */ - protected $dates = ['deleted_at']; + protected $dates = ['deleted_at', 'email_verified_at']; /** * The attributes that are mass assignable. @@ -44,4 +44,9 @@ class User extends Authenticatable { return url(config('app.url') . '/' . $this->username); } + + public function settings() + { + return $this->hasOne(UserSetting::class); + } } diff --git a/app/UserFilter.php b/app/UserFilter.php new file mode 100644 index 000000000..071f2eeb4 --- /dev/null +++ b/app/UserFilter.php @@ -0,0 +1,10 @@ +setBaseName($path, $thumbnail, $img->extension); $newPath = storage_path('app/'.$converted['path']); - - $img->save($newPath, 75); + + $quality = config('pixelfed.image_quality'); + $img->save($newPath, $quality); if(!$thumbnail) { $media->orientation = $orientation; diff --git a/app/Util/Webfinger/Webfinger.php b/app/Util/Webfinger/Webfinger.php index ecb760f1b..cae3108a1 100644 --- a/app/Util/Webfinger/Webfinger.php +++ b/app/Util/Webfinger/Webfinger.php @@ -31,13 +31,9 @@ class Webfinger { public function generateAliases() { - $host = parse_url(config('app.url'), PHP_URL_HOST); - $username = $this->user->username; - $url = $this->user->url(); - $this->aliases = [ - 'acct:'.$username.'@'.$host, - $url + $this->user->url(), + $this->user->permalink() ]; return $this; } @@ -55,24 +51,12 @@ class Webfinger { [ 'rel' => 'http://schemas.google.com/g/2010#updates-from', 'type' => 'application/atom+xml', - 'href' => url("/users/{$user->username}.atom") + 'href' => $user->permalink('.atom') ], [ 'rel' => 'self', 'type' => 'application/activity+json', 'href' => $user->permalink() - ], - [ - 'rel' => 'magic-public-key', - 'href' => null//$user->public_key - ], - [ - 'rel' => 'salmon', - 'href' => $user->permalink('/salmon') - ], - [ - 'rel' => 'http://ostatus.org/schema/1.0/subscribe', - 'href' => url('/main/ostatussub?profile={uri}') ] ]; return $this; diff --git a/composer.json b/composer.json index d81329d87..579100823 100644 --- a/composer.json +++ b/composer.json @@ -6,18 +6,23 @@ "type": "project", "require": { "php": "^7.1.3", - "99designs/http-signatures-guzzlehttp": "^2.0", + "beyondcode/laravel-self-diagnosis": "^0.4.0", "bitverse/identicon": "^1.1", "doctrine/dbal": "^2.7", "fideloper/proxy": "^4.0", "greggilbert/recaptcha": "dev-master", "intervention/image": "^2.4", - "kitetail/zttp": "^0.3.0", + "pixelfed/zttp": "^0.4", "laravel/framework": "5.6.*", "laravel/horizon": "^1.2", + "laravel/passport": "^6.0", "laravel/tinker": "^1.0", - "league/fractal": "^0.17.0", + "moontoast/math": "^1.1", "phpseclib/phpseclib": "~2.0", + "pixelfed/dotenv-editor": "^2.0", + "pixelfed/fractal": "^0.18.0", + "pixelfed/google2fa-laravel": "^2.0", + "pixelfed/http-signatures-guzzlehttp": "^4.0", "predis/predis": "^1.1", "spatie/laravel-backup": "^5.0.0", "spatie/laravel-image-optimizer": "^1.1", @@ -25,6 +30,7 @@ }, "require-dev": { "barryvdh/laravel-debugbar": "^3.1", + "beyondcode/laravel-er-diagram-generator": "^0.2.2", "filp/whoops": "^2.0", "fzaninotto/faker": "^1.4", "mockery/mockery": "^1.0", diff --git a/composer.lock b/composer.lock index 9056e0a30..ddb5c89d6 100644 --- a/composer.lock +++ b/composer.lock @@ -4,39 +4,44 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "b64eb9ff898e0b4a99b05c054bd55212", + "content-hash": "809fc8bb7dad5bf08e689baca3a214cc", "packages": [ { - "name": "99designs/http-signatures", - "version": "4.0.0", + "name": "beyondcode/laravel-self-diagnosis", + "version": "0.4.1", "source": { "type": "git", - "url": "https://github.com/99designs/http-signatures-php.git", - "reference": "acb9d2e4f4661de9445fa5930b49a259bfd6175b" + "url": "https://github.com/beyondcode/laravel-self-diagnosis.git", + "reference": "b330a934bf2fe65b92d9adadc6e7d46313322ec0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/99designs/http-signatures-php/zipball/acb9d2e4f4661de9445fa5930b49a259bfd6175b", - "reference": "acb9d2e4f4661de9445fa5930b49a259bfd6175b", + "url": "https://api.github.com/repos/beyondcode/laravel-self-diagnosis/zipball/b330a934bf2fe65b92d9adadc6e7d46313322ec0", + "reference": "b330a934bf2fe65b92d9adadc6e7d46313322ec0", "shasum": "" }, "require": { - "paragonie/random_compat": "^1.0|^2.0", - "php": ">=5.5", - "psr/http-message": "^1.0" + "illuminate/support": "5.2.*|5.3.*|5.4.*|5.5.*|5.6.*", + "php": "^7.1", + "vlucas/phpdotenv": "~2.5" }, "require-dev": { - "friendsofphp/php-cs-fixer": "^1.11", - "guzzlehttp/psr7": "^1.2", - "phpunit/phpunit": "~4.8", - "symfony/http-foundation": "~2.8|~3.0", - "symfony/psr-http-message-bridge": "^1.0", - "zendframework/zend-diactoros": "^1.1" + "larapack/dd": "^1.0", + "mockery/mockery": "^1.0", + "orchestra/testbench": "~3.5", + "phpunit/phpunit": "^7.0" }, "type": "library", + "extra": { + "laravel": { + "providers": [ + "BeyondCode\\SelfDiagnosis\\SelfDiagnosisServiceProvider" + ] + } + }, "autoload": { "psr-4": { - "HttpSignatures\\": "src/" + "BeyondCode\\SelfDiagnosis\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -45,75 +50,19 @@ ], "authors": [ { - "name": "Paul Annesley", - "email": "paul@99designs.com" + "name": "Marcel Pociot", + "email": "marcel@beyondco.de", + "homepage": "https://beyondcode.de", + "role": "Developer" } ], - "description": "Sign and verify HTTP messages", + "description": "Perform various self diagnosis tests on your Laravel application.", + "homepage": "https://github.com/beyondcode/laravel-self-diagnosis", "keywords": [ - "hmac", - "http", - "https", - "signature", - "signed", - "signing" + "beyondcode", + "laravel-self-diagnosis" ], - "time": "2017-05-04T01:36:17+00:00" - }, - { - "name": "99designs/http-signatures-guzzlehttp", - "version": "2.0.1", - "source": { - "type": "git", - "url": "https://github.com/99designs/http-signatures-guzzlehttp.git", - "reference": "920ddd3cfbfae4c11a0f7c3b44699c01ae8cb203" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/99designs/http-signatures-guzzlehttp/zipball/920ddd3cfbfae4c11a0f7c3b44699c01ae8cb203", - "reference": "920ddd3cfbfae4c11a0f7c3b44699c01ae8cb203", - "shasum": "" - }, - "require": { - "99designs/http-signatures": ">=3.0.0 <5.0.0", - "guzzlehttp/guzzle": ">=6.0 <7.0.0", - "php": ">=5.5.0" - }, - "require-dev": { - "phpunit/phpunit": "~4.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Adrian Palmer", - "email": "adrian.palmer@99designs.com" - }, - { - "name": "Ruben de Vries", - "email": "ruben@rubensayshi.com" - } - ], - "description": "Sign and verify HTTP messages with Guzzle 6", - "homepage": "https://github.com/99designs/http-signatures-guzzlehttp", - "keywords": [ - "guzzle 6", - "hmac", - "http", - "https", - "signature", - "signed", - "signing" - ], - "time": "2017-05-04T02:00:20+00:00" + "time": "2018-07-09T12:33:27+00:00" }, { "name": "bitverse/identicon", @@ -156,16 +105,16 @@ }, { "name": "cakephp/chronos", - "version": "1.1.4", + "version": "1.2.2", "source": { "type": "git", "url": "https://github.com/cakephp/chronos.git", - "reference": "85bcaea6a832684b32ef54b2487b0c14a172e9e6" + "reference": "30f5b26bcf76a5e53ecc274700ad1ec49dc05567" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/cakephp/chronos/zipball/85bcaea6a832684b32ef54b2487b0c14a172e9e6", - "reference": "85bcaea6a832684b32ef54b2487b0c14a172e9e6", + "url": "https://api.github.com/repos/cakephp/chronos/zipball/30f5b26bcf76a5e53ecc274700ad1ec49dc05567", + "reference": "30f5b26bcf76a5e53ecc274700ad1ec49dc05567", "shasum": "" }, "require": { @@ -173,15 +122,15 @@ }, "require-dev": { "athletic/athletic": "~0.1", - "cakephp/cakephp-codesniffer": "~2.3", + "cakephp/cakephp-codesniffer": "^3.0", "phpbench/phpbench": "@dev", "phpstan/phpstan": "^0.6.4", - "phpunit/phpunit": "<6.0" + "phpunit/phpunit": "<6.0 || ^7.0" }, "type": "library", "autoload": { "psr-4": { - "Cake\\Chronos\\": "src" + "Cake\\Chronos\\": "src/" }, "files": [ "src/carbon_compat.php" @@ -209,7 +158,70 @@ "datetime", "time" ], - "time": "2018-01-13T12:19:50+00:00" + "time": "2018-07-11T18:51:56+00:00" + }, + { + "name": "defuse/php-encryption", + "version": "v2.2.1", + "source": { + "type": "git", + "url": "https://github.com/defuse/php-encryption.git", + "reference": "0f407c43b953d571421e0020ba92082ed5fb7620" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/defuse/php-encryption/zipball/0f407c43b953d571421e0020ba92082ed5fb7620", + "reference": "0f407c43b953d571421e0020ba92082ed5fb7620", + "shasum": "" + }, + "require": { + "ext-openssl": "*", + "paragonie/random_compat": ">= 2", + "php": ">=5.4.0" + }, + "require-dev": { + "nikic/php-parser": "^2.0|^3.0|^4.0", + "phpunit/phpunit": "^4|^5" + }, + "bin": [ + "bin/generate-defuse-key" + ], + "type": "library", + "autoload": { + "psr-4": { + "Defuse\\Crypto\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Hornby", + "email": "taylor@defuse.ca", + "homepage": "https://defuse.ca/" + }, + { + "name": "Scott Arciszewski", + "email": "info@paragonie.com", + "homepage": "https://paragonie.com" + } + ], + "description": "Secure PHP Encryption Library", + "keywords": [ + "aes", + "authenticated encryption", + "cipher", + "crypto", + "cryptography", + "encrypt", + "encryption", + "openssl", + "security", + "symmetric key cryptography" + ], + "time": "2018-07-24T23:27:56+00:00" }, { "name": "dnoegel/php-xdg-base-dir", @@ -244,74 +256,6 @@ "description": "implementation of xdg base directory specification for php", "time": "2014-10-24T07:27:01+00:00" }, - { - "name": "doctrine/annotations", - "version": "v1.6.0", - "source": { - "type": "git", - "url": "https://github.com/doctrine/annotations.git", - "reference": "c7f2050c68a9ab0bdb0f98567ec08d80ea7d24d5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/annotations/zipball/c7f2050c68a9ab0bdb0f98567ec08d80ea7d24d5", - "reference": "c7f2050c68a9ab0bdb0f98567ec08d80ea7d24d5", - "shasum": "" - }, - "require": { - "doctrine/lexer": "1.*", - "php": "^7.1" - }, - "require-dev": { - "doctrine/cache": "1.*", - "phpunit/phpunit": "^6.4" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.6.x-dev" - } - }, - "autoload": { - "psr-4": { - "Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, - { - "name": "Benjamin Eberlei", - "email": "kontakt@beberlei.de" - }, - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, - { - "name": "Jonathan Wage", - "email": "jonwage@gmail.com" - }, - { - "name": "Johannes Schmitt", - "email": "schmittjoh@gmail.com" - } - ], - "description": "Docblock Annotations Parser", - "homepage": "http://www.doctrine-project.org", - "keywords": [ - "annotations", - "docblock", - "parser" - ], - "time": "2017-12-06T07:11:42+00:00" - }, { "name": "doctrine/cache", "version": "v1.7.1", @@ -386,170 +330,33 @@ ], "time": "2017-08-25T07:02:50+00:00" }, - { - "name": "doctrine/collections", - "version": "v1.5.0", - "source": { - "type": "git", - "url": "https://github.com/doctrine/collections.git", - "reference": "a01ee38fcd999f34d9bfbcee59dbda5105449cbf" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/collections/zipball/a01ee38fcd999f34d9bfbcee59dbda5105449cbf", - "reference": "a01ee38fcd999f34d9bfbcee59dbda5105449cbf", - "shasum": "" - }, - "require": { - "php": "^7.1" - }, - "require-dev": { - "doctrine/coding-standard": "~0.1@dev", - "phpunit/phpunit": "^5.7" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.3.x-dev" - } - }, - "autoload": { - "psr-0": { - "Doctrine\\Common\\Collections\\": "lib/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, - { - "name": "Benjamin Eberlei", - "email": "kontakt@beberlei.de" - }, - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, - { - "name": "Jonathan Wage", - "email": "jonwage@gmail.com" - }, - { - "name": "Johannes Schmitt", - "email": "schmittjoh@gmail.com" - } - ], - "description": "Collections Abstraction library", - "homepage": "http://www.doctrine-project.org", - "keywords": [ - "array", - "collections", - "iterator" - ], - "time": "2017-07-22T10:37:32+00:00" - }, - { - "name": "doctrine/common", - "version": "v2.8.1", - "source": { - "type": "git", - "url": "https://github.com/doctrine/common.git", - "reference": "f68c297ce6455e8fd794aa8ffaf9fa458f6ade66" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/common/zipball/f68c297ce6455e8fd794aa8ffaf9fa458f6ade66", - "reference": "f68c297ce6455e8fd794aa8ffaf9fa458f6ade66", - "shasum": "" - }, - "require": { - "doctrine/annotations": "1.*", - "doctrine/cache": "1.*", - "doctrine/collections": "1.*", - "doctrine/inflector": "1.*", - "doctrine/lexer": "1.*", - "php": "~7.1" - }, - "require-dev": { - "phpunit/phpunit": "^5.7" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.8.x-dev" - } - }, - "autoload": { - "psr-4": { - "Doctrine\\Common\\": "lib/Doctrine/Common" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, - { - "name": "Benjamin Eberlei", - "email": "kontakt@beberlei.de" - }, - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, - { - "name": "Jonathan Wage", - "email": "jonwage@gmail.com" - }, - { - "name": "Johannes Schmitt", - "email": "schmittjoh@gmail.com" - } - ], - "description": "Common Library for Doctrine projects", - "homepage": "http://www.doctrine-project.org", - "keywords": [ - "annotations", - "collections", - "eventmanager", - "persistence", - "spl" - ], - "time": "2017-08-31T08:43:38+00:00" - }, { "name": "doctrine/dbal", - "version": "v2.7.1", + "version": "v2.8.0", "source": { "type": "git", "url": "https://github.com/doctrine/dbal.git", - "reference": "11037b4352c008373561dc6fc836834eed80c3b5" + "reference": "5140a64c08b4b607b9bedaae0cedd26f04a0e621" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/dbal/zipball/11037b4352c008373561dc6fc836834eed80c3b5", - "reference": "11037b4352c008373561dc6fc836834eed80c3b5", + "url": "https://api.github.com/repos/doctrine/dbal/zipball/5140a64c08b4b607b9bedaae0cedd26f04a0e621", + "reference": "5140a64c08b4b607b9bedaae0cedd26f04a0e621", "shasum": "" }, "require": { - "doctrine/common": "^2.7.1", + "doctrine/cache": "^1.0", + "doctrine/event-manager": "^1.0", "ext-pdo": "*", "php": "^7.1" }, "require-dev": { "doctrine/coding-standard": "^4.0", - "phpunit/phpunit": "^7.0", + "jetbrains/phpstorm-stubs": "^2018.1.2", + "phpstan/phpstan": "^0.10.1", + "phpunit/phpunit": "^7.1.2", "phpunit/phpunit-mock-objects": "!=3.2.4,!=3.2.5", - "symfony/console": "^2.0.5||^3.0", + "symfony/console": "^2.0.5|^3.0|^4.0", "symfony/phpunit-bridge": "^3.4.5|^4.0.5" }, "suggest": { @@ -561,7 +368,8 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.7.x-dev" + "dev-master": "2.8.x-dev", + "dev-develop": "3.0.x-dev" } }, "autoload": { @@ -599,7 +407,81 @@ "persistence", "queryobject" ], - "time": "2018-04-07T18:44:18+00:00" + "time": "2018-07-13T03:16:35+00:00" + }, + { + "name": "doctrine/event-manager", + "version": "v1.0.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/event-manager.git", + "reference": "a520bc093a0170feeb6b14e9d83f3a14452e64b3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/event-manager/zipball/a520bc093a0170feeb6b14e9d83f3a14452e64b3", + "reference": "a520bc093a0170feeb6b14e9d83f3a14452e64b3", + "shasum": "" + }, + "require": { + "php": "^7.1" + }, + "conflict": { + "doctrine/common": "<2.9@dev" + }, + "require-dev": { + "doctrine/coding-standard": "^4.0", + "phpunit/phpunit": "^7.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Doctrine\\Common\\": "lib/Doctrine/Common" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + }, + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com" + } + ], + "description": "Doctrine Event Manager component", + "homepage": "https://www.doctrine-project.org/projects/event-manager.html", + "keywords": [ + "event", + "eventdispatcher", + "eventmanager" + ], + "time": "2018-06-11T11:59:03+00:00" }, { "name": "doctrine/inflector", @@ -724,16 +606,16 @@ }, { "name": "dragonmantank/cron-expression", - "version": "v2.1.0", + "version": "v2.2.0", "source": { "type": "git", "url": "https://github.com/dragonmantank/cron-expression.git", - "reference": "3f00985deec8df53d4cc1e5c33619bda1ee309a5" + "reference": "92a2c3768d50e21a1f26a53cb795ce72806266c5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/3f00985deec8df53d4cc1e5c33619bda1ee309a5", - "reference": "3f00985deec8df53d4cc1e5c33619bda1ee309a5", + "url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/92a2c3768d50e21a1f26a53cb795ce72806266c5", + "reference": "92a2c3768d50e21a1f26a53cb795ce72806266c5", "shasum": "" }, "require": { @@ -769,7 +651,7 @@ "cron", "schedule" ], - "time": "2018-04-06T15:51:55+00:00" + "time": "2018-06-06T03:12:17+00:00" }, { "name": "egulias/email-validator", @@ -928,6 +810,52 @@ ], "time": "2018-02-07T20:20:57+00:00" }, + { + "name": "firebase/php-jwt", + "version": "v5.0.0", + "source": { + "type": "git", + "url": "https://github.com/firebase/php-jwt.git", + "reference": "9984a4d3a32ae7673d6971ea00bae9d0a1abba0e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/firebase/php-jwt/zipball/9984a4d3a32ae7673d6971ea00bae9d0a1abba0e", + "reference": "9984a4d3a32ae7673d6971ea00bae9d0a1abba0e", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": " 4.8.35" + }, + "type": "library", + "autoload": { + "psr-4": { + "Firebase\\JWT\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Neuman Vong", + "email": "neuman+pear@twilio.com", + "role": "Developer" + }, + { + "name": "Anant Narayanan", + "email": "anant@php.net", + "role": "Developer" + } + ], + "description": "A simple library to encode and decode JSON Web Tokens (JWT) in PHP. Should conform to the current spec.", + "homepage": "https://github.com/firebase/php-jwt", + "time": "2017-06-27T22:17:23+00:00" + }, { "name": "greggilbert/recaptcha", "version": "dev-master", @@ -1318,64 +1246,18 @@ ], "time": "2015-04-20T18:58:01+00:00" }, - { - "name": "kitetail/zttp", - "version": "v0.3.0", - "source": { - "type": "git", - "url": "https://github.com/kitetail/zttp.git", - "reference": "e788ab8fc5c0259f691e2960d17e0ddbab761c6a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/kitetail/zttp/zipball/e788ab8fc5c0259f691e2960d17e0ddbab761c6a", - "reference": "e788ab8fc5c0259f691e2960d17e0ddbab761c6a", - "shasum": "" - }, - "require": { - "guzzlehttp/guzzle": "^6.0", - "php": ">=7.0", - "tightenco/collect": "^5.4" - }, - "require-dev": { - "laravel/lumen-framework": "^5.4", - "phpunit/phpunit": "^6.0" - }, - "type": "library", - "autoload": { - "files": [ - "src/Zttp.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Adam Wathan", - "email": "adam.wathan@gmail.com" - } - ], - "description": "A developer-experience focused HTTP client, optimized for most common use cases.", - "keywords": [ - "Guzzle", - "http" - ], - "time": "2017-08-09T15:31:26+00:00" - }, { "name": "laravel/framework", - "version": "v5.6.23", + "version": "v5.6.31", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "f547f0a71a12763d1adb8493237d541c9e3a5d10" + "reference": "b11fbad84cd90d109c6d817ff3c9af428746edfd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/f547f0a71a12763d1adb8493237d541c9e3a5d10", - "reference": "f547f0a71a12763d1adb8493237d541c9e3a5d10", + "url": "https://api.github.com/repos/laravel/framework/zipball/b11fbad84cd90d109c6d817ff3c9af428746edfd", + "reference": "b11fbad84cd90d109c6d817ff3c9af428746edfd", "shasum": "" }, "require": { @@ -1501,20 +1383,20 @@ "framework", "laravel" ], - "time": "2018-05-22T14:55:57+00:00" + "time": "2018-08-08T20:56:17+00:00" }, { "name": "laravel/horizon", - "version": "v1.2.3", + "version": "v1.3.1", "source": { "type": "git", "url": "https://github.com/laravel/horizon.git", - "reference": "36ef9e2d6e09e617cf801050326a69e876ff5535" + "reference": "342c4ddf6dda7c7ed21e57566f202b96e28afb6b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/horizon/zipball/36ef9e2d6e09e617cf801050326a69e876ff5535", - "reference": "36ef9e2d6e09e617cf801050326a69e876ff5535", + "url": "https://api.github.com/repos/laravel/horizon/zipball/342c4ddf6dda7c7ed21e57566f202b96e28afb6b", + "reference": "342c4ddf6dda7c7ed21e57566f202b96e28afb6b", "shasum": "" }, "require": { @@ -1569,7 +1451,76 @@ "laravel", "queue" ], - "time": "2018-03-13T18:00:18+00:00" + "time": "2018-06-21T09:19:40+00:00" + }, + { + "name": "laravel/passport", + "version": "v6.0.6", + "source": { + "type": "git", + "url": "https://github.com/laravel/passport.git", + "reference": "83ac18d903c1446be7344464d37fa006fb6957c9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/passport/zipball/83ac18d903c1446be7344464d37fa006fb6957c9", + "reference": "83ac18d903c1446be7344464d37fa006fb6957c9", + "shasum": "" + }, + "require": { + "firebase/php-jwt": "~3.0|~4.0|~5.0", + "guzzlehttp/guzzle": "~6.0", + "illuminate/auth": "~5.6", + "illuminate/console": "~5.6", + "illuminate/container": "~5.6", + "illuminate/contracts": "~5.6", + "illuminate/database": "~5.6", + "illuminate/encryption": "~5.6", + "illuminate/http": "~5.6", + "illuminate/support": "~5.6", + "league/oauth2-server": "^7.0", + "php": ">=7.1", + "phpseclib/phpseclib": "^2.0", + "symfony/psr-http-message-bridge": "~1.0", + "zendframework/zend-diactoros": "~1.0" + }, + "require-dev": { + "mockery/mockery": "~1.0", + "phpunit/phpunit": "~6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "6.0-dev" + }, + "laravel": { + "providers": [ + "Laravel\\Passport\\PassportServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Laravel\\Passport\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "Laravel Passport provides OAuth2 server support to Laravel.", + "keywords": [ + "laravel", + "oauth", + "passport" + ], + "time": "2018-07-17T13:00:32+00:00" }, { "name": "laravel/tinker", @@ -1634,6 +1585,114 @@ ], "time": "2018-05-17T13:42:07+00:00" }, + { + "name": "lcobucci/jwt", + "version": "3.2.4", + "source": { + "type": "git", + "url": "https://github.com/lcobucci/jwt.git", + "reference": "c9704b751315d21735dc98d78d4f37bd73596da7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/lcobucci/jwt/zipball/c9704b751315d21735dc98d78d4f37bd73596da7", + "reference": "c9704b751315d21735dc98d78d4f37bd73596da7", + "shasum": "" + }, + "require": { + "ext-openssl": "*", + "php": ">=5.5" + }, + "require-dev": { + "mdanter/ecc": "~0.3.1", + "mikey179/vfsstream": "~1.5", + "phpmd/phpmd": "~2.2", + "phpunit/php-invoker": "~1.1", + "phpunit/phpunit": "~4.5", + "squizlabs/php_codesniffer": "~2.3" + }, + "suggest": { + "mdanter/ecc": "Required to use Elliptic Curves based algorithms." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.1-dev" + } + }, + "autoload": { + "psr-4": { + "Lcobucci\\JWT\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Luís Otávio Cobucci Oblonczyk", + "email": "lcobucci@gmail.com", + "role": "Developer" + } + ], + "description": "A simple library to work with JSON Web Token and JSON Web Signature", + "keywords": [ + "JWS", + "jwt" + ], + "time": "2018-08-03T11:23:50+00:00" + }, + { + "name": "league/event", + "version": "2.1.2", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/event.git", + "reference": "e4bfc88dbcb60c8d8a2939a71f9813e141bbe4cd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/event/zipball/e4bfc88dbcb60c8d8a2939a71f9813e141bbe4cd", + "reference": "e4bfc88dbcb60c8d8a2939a71f9813e141bbe4cd", + "shasum": "" + }, + "require": { + "php": ">=5.4.0" + }, + "require-dev": { + "henrikbjorn/phpspec-code-coverage": "~1.0.1", + "phpspec/phpspec": "~2.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.2-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Event\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frank de Jonge", + "email": "info@frenky.net" + } + ], + "description": "Event package", + "keywords": [ + "emitter", + "event", + "listener" + ], + "time": "2015-05-21T12:24:47+00:00" + }, { "name": "league/flysystem", "version": "1.0.45", @@ -1719,45 +1778,42 @@ "time": "2018-05-07T08:44:23+00:00" }, { - "name": "league/fractal", - "version": "0.17.0", + "name": "league/oauth2-server", + "version": "7.2.0", "source": { "type": "git", - "url": "https://github.com/thephpleague/fractal.git", - "reference": "a0b350824f22fc2fdde2500ce9d6851a3f275b0e" + "url": "https://github.com/thephpleague/oauth2-server.git", + "reference": "8184f771d43ea7305ddbb893d0daf6f0352ec5fd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/fractal/zipball/a0b350824f22fc2fdde2500ce9d6851a3f275b0e", - "reference": "a0b350824f22fc2fdde2500ce9d6851a3f275b0e", + "url": "https://api.github.com/repos/thephpleague/oauth2-server/zipball/8184f771d43ea7305ddbb893d0daf6f0352ec5fd", + "reference": "8184f771d43ea7305ddbb893d0daf6f0352ec5fd", "shasum": "" }, "require": { - "php": ">=5.4" + "defuse/php-encryption": "^2.1", + "ext-openssl": "*", + "lcobucci/jwt": "^3.2.2", + "league/event": "^2.1", + "php": ">=7.0.0", + "psr/http-message": "^1.0.1" + }, + "replace": { + "league/oauth2server": "*", + "lncd/oauth2": "*" }, "require-dev": { - "doctrine/orm": "^2.5", - "illuminate/contracts": "~5.0", - "mockery/mockery": "~0.9", - "pagerfanta/pagerfanta": "~1.0.0", - "phpunit/phpunit": "~4.0", - "squizlabs/php_codesniffer": "~1.5", - "zendframework/zend-paginator": "~2.3" - }, - "suggest": { - "illuminate/pagination": "The Illuminate Pagination component.", - "pagerfanta/pagerfanta": "Pagerfanta Paginator", - "zendframework/zend-paginator": "Zend Framework Paginator" + "phpstan/phpstan": "^0.9.2", + "phpstan/phpstan-phpunit": "^0.9.4", + "phpstan/phpstan-strict-rules": "^0.9.0", + "phpunit/phpunit": "^6.3 || ^7.0", + "zendframework/zend-diactoros": "^1.3.2" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "0.13-dev" - } - }, "autoload": { "psr-4": { - "League\\Fractal\\": "src" + "League\\OAuth2\\Server\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -1766,21 +1822,30 @@ ], "authors": [ { - "name": "Phil Sturgeon", - "email": "me@philsturgeon.uk", - "homepage": "http://philsturgeon.uk/", + "name": "Alex Bilbie", + "email": "hello@alexbilbie.com", + "homepage": "http://www.alexbilbie.com", "role": "Developer" } ], - "description": "Handle the output of complex data structures ready for API output.", - "homepage": "http://fractal.thephpleague.com/", + "description": "A lightweight and powerful OAuth 2.0 authorization and resource server library with support for all the core specification grants. This library will allow you to secure your API with OAuth and allow your applications users to approve apps that want to access their data from your API.", + "homepage": "https://oauth2.thephpleague.com/", "keywords": [ + "Authentication", "api", - "json", - "league", - "rest" + "auth", + "authorisation", + "authorization", + "oauth", + "oauth 2", + "oauth 2.0", + "oauth2", + "protect", + "resource", + "secure", + "server" ], - "time": "2017-06-12T11:04:56+00:00" + "time": "2018-06-23T16:57:59+00:00" }, { "name": "monolog/monolog", @@ -1860,6 +1925,55 @@ ], "time": "2017-06-19T01:22:40+00:00" }, + { + "name": "moontoast/math", + "version": "1.1.2", + "source": { + "type": "git", + "url": "https://github.com/ramsey/moontoast-math.git", + "reference": "c2792a25df5cad4ff3d760dd37078fc5b6fccc79" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ramsey/moontoast-math/zipball/c2792a25df5cad4ff3d760dd37078fc5b6fccc79", + "reference": "c2792a25df5cad4ff3d760dd37078fc5b6fccc79", + "shasum": "" + }, + "require": { + "ext-bcmath": "*", + "php": ">=5.3.3" + }, + "require-dev": { + "jakub-onderka/php-parallel-lint": "^0.9.0", + "phpunit/phpunit": "^4.7|>=5.0 <5.4", + "satooshi/php-coveralls": "^0.6.1", + "squizlabs/php_codesniffer": "^2.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Moontoast\\Math\\": "src/Moontoast/Math/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Ben Ramsey", + "email": "ben@benramsey.com", + "homepage": "https://benramsey.com" + } + ], + "description": "A mathematics library, providing functionality for large numbers", + "homepage": "https://github.com/ramsey/moontoast-math", + "keywords": [ + "bcmath", + "math" + ], + "time": "2017-02-16T16:54:46+00:00" + }, { "name": "nesbot/carbon", "version": "1.25.0", @@ -1915,16 +2029,16 @@ }, { "name": "nikic/php-parser", - "version": "v4.0.1", + "version": "v4.0.3", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "e4a54fa90a5cd8e8dd3fb4099942681731c5cdd3" + "reference": "bd088dc940a418f09cda079a9b5c7c478890fb8d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/e4a54fa90a5cd8e8dd3fb4099942681731c5cdd3", - "reference": "e4a54fa90a5cd8e8dd3fb4099942681731c5cdd3", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/bd088dc940a418f09cda079a9b5c7c478890fb8d", + "reference": "bd088dc940a418f09cda079a9b5c7c478890fb8d", "shasum": "" }, "require": { @@ -1962,20 +2076,82 @@ "parser", "php" ], - "time": "2018-03-25T17:35:16+00:00" + "time": "2018-07-15T17:25:16+00:00" }, { - "name": "paragonie/random_compat", - "version": "v2.0.12", + "name": "paragonie/constant_time_encoding", + "version": "v2.2.2", "source": { "type": "git", - "url": "https://github.com/paragonie/random_compat.git", - "reference": "258c89a6b97de7dfaf5b8c7607d0478e236b04fb" + "url": "https://github.com/paragonie/constant_time_encoding.git", + "reference": "eccf915f45f911bfb189d1d1638d940ec6ee6e33" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/paragonie/random_compat/zipball/258c89a6b97de7dfaf5b8c7607d0478e236b04fb", - "reference": "258c89a6b97de7dfaf5b8c7607d0478e236b04fb", + "url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/eccf915f45f911bfb189d1d1638d940ec6ee6e33", + "reference": "eccf915f45f911bfb189d1d1638d940ec6ee6e33", + "shasum": "" + }, + "require": { + "php": "^7" + }, + "require-dev": { + "phpunit/phpunit": "^6|^7", + "vimeo/psalm": "^1" + }, + "type": "library", + "autoload": { + "psr-4": { + "ParagonIE\\ConstantTime\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com", + "homepage": "https://paragonie.com", + "role": "Maintainer" + }, + { + "name": "Steve 'Sc00bz' Thomas", + "email": "steve@tobtu.com", + "homepage": "https://www.tobtu.com", + "role": "Original Developer" + } + ], + "description": "Constant-time Implementations of RFC 4648 Encoding (Base-64, Base-32, Base-16)", + "keywords": [ + "base16", + "base32", + "base32_decode", + "base32_encode", + "base64", + "base64_decode", + "base64_encode", + "bin2hex", + "encoding", + "hex", + "hex2bin", + "rfc4648" + ], + "time": "2018-03-10T19:47:49+00:00" + }, + { + "name": "paragonie/random_compat", + "version": "v2.0.17", + "source": { + "type": "git", + "url": "https://github.com/paragonie/random_compat.git", + "reference": "29af24f25bab834fcbb38ad2a69fa93b867e070d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paragonie/random_compat/zipball/29af24f25bab834fcbb38ad2a69fa93b867e070d", + "reference": "29af24f25bab834fcbb38ad2a69fa93b867e070d", "shasum": "" }, "require": { @@ -2007,10 +2183,11 @@ "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", "keywords": [ "csprng", + "polyfill", "pseudorandom", "random" ], - "time": "2018-04-04T21:24:14+00:00" + "time": "2018-07-04T16:31:37+00:00" }, { "name": "phpseclib/phpseclib", @@ -2104,6 +2281,406 @@ ], "time": "2018-04-15T16:55:05+00:00" }, + { + "name": "pixelfed/dotenv-editor", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/pixelfed/Laravel-Dotenv-Editor.git", + "reference": "b53cb2707bb856e92cf1a282b4e5ee17a45ccb2c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/pixelfed/Laravel-Dotenv-Editor/zipball/b53cb2707bb856e92cf1a282b4e5ee17a45ccb2c", + "reference": "b53cb2707bb856e92cf1a282b4e5ee17a45ccb2c", + "shasum": "" + }, + "require": { + "illuminate/config": ">=5.0", + "illuminate/container": ">=5.0", + "illuminate/support": ">=5.0", + "php": ">=5.4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "psr-4": { + "Jackiedo\\DotenvEditor\\": "src/Jackiedo/DotenvEditor" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jackie Do", + "email": "anhvudo@gmail.com" + } + ], + "description": "The .env file editor tool for Laravel 5+", + "keywords": [ + "dotenv", + "dotenv-editor", + "laravel" + ], + "time": "2018-07-17T19:38:26+00:00" + }, + { + "name": "pixelfed/fractal", + "version": "0.18.0", + "source": { + "type": "git", + "url": "https://github.com/pixelfed/fractal.git", + "reference": "faff10c9f3e3300b1571ef41926f933a9cce4782" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/pixelfed/fractal/zipball/faff10c9f3e3300b1571ef41926f933a9cce4782", + "reference": "faff10c9f3e3300b1571ef41926f933a9cce4782", + "shasum": "" + }, + "require": { + "php": ">=5.4" + }, + "require-dev": { + "doctrine/orm": "^2.5", + "illuminate/contracts": "~5.0", + "mockery/mockery": "~0.9", + "pagerfanta/pagerfanta": "~1.0.0", + "phpunit/phpunit": "^4.8.35", + "squizlabs/php_codesniffer": "~1.5", + "zendframework/zend-paginator": "~2.3" + }, + "suggest": { + "illuminate/pagination": "The Illuminate Pagination component.", + "pagerfanta/pagerfanta": "Pagerfanta Paginator", + "zendframework/zend-paginator": "Zend Framework Paginator" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "0.13-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Fractal\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Phil Sturgeon", + "email": "me@philsturgeon.uk", + "homepage": "http://philsturgeon.uk/", + "role": "Developer" + } + ], + "description": "Handle the output of complex data structures ready for API output.", + "homepage": "http://fractal.thephpleague.com/", + "keywords": [ + "api", + "json", + "league", + "rest" + ], + "time": "2018-07-01T02:30:24+00:00" + }, + { + "name": "pixelfed/google2fa", + "version": "v4.0.0", + "source": { + "type": "git", + "url": "https://github.com/pixelfed/google2fa.git", + "reference": "919ecec68074a27818451d8653029773a2391fe5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/pixelfed/google2fa/zipball/919ecec68074a27818451d8653029773a2391fe5", + "reference": "919ecec68074a27818451d8653029773a2391fe5", + "shasum": "" + }, + "require": { + "paragonie/constant_time_encoding": "~1.0|~2.0", + "paragonie/random_compat": "~1.4|~2.0", + "php": ">=5.4", + "symfony/polyfill-php56": "~1.2" + }, + "require-dev": { + "bacon/bacon-qr-code": "~1.0", + "phpunit/phpunit": "~4|~5|~6" + }, + "suggest": { + "bacon/bacon-qr-code": "Required to generate inline QR Codes." + }, + "type": "library", + "extra": { + "component": "package", + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "psr-4": { + "PragmaRX\\Google2FA\\": "src/", + "PragmaRX\\Google2FA\\Tests\\": "tests/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Antonio Carlos Ribeiro", + "email": "acr@antoniocarlosribeiro.com", + "role": "Creator & Designer" + } + ], + "description": "A One Time Password Authentication package, compatible with Google Authenticator.", + "keywords": [ + "2fa", + "Authentication", + "Two Factor Authentication", + "google2fa", + "laravel" + ], + "time": "2018-07-05T03:38:31+00:00" + }, + { + "name": "pixelfed/google2fa-laravel", + "version": "v2.0.0", + "source": { + "type": "git", + "url": "https://github.com/pixelfed/google2fa-laravel.git", + "reference": "72cfcbe2c04db1a2702925413b40fdda3743e6b5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/pixelfed/google2fa-laravel/zipball/72cfcbe2c04db1a2702925413b40fdda3743e6b5", + "reference": "72cfcbe2c04db1a2702925413b40fdda3743e6b5", + "shasum": "" + }, + "require": { + "laravel/framework": ">=5.2", + "php": ">=5.4", + "pixelfed/google2fa": "~4.0" + }, + "require-dev": { + "orchestra/testbench-browser-kit": "~3.4|~3.5|~3.6", + "phpunit/phpunit": "~5|~6|~7" + }, + "suggest": { + "bacon/bacon-qr-code": "Required to generate inline QR Codes.", + "pragmarx/recovery": "Generate recovery codes." + }, + "type": "library", + "extra": { + "component": "package", + "frameworks": [ + "Laravel" + ], + "branch-alias": { + "dev-master": "0.2-dev" + }, + "laravel": { + "providers": [ + "PragmaRX\\Google2FALaravel\\ServiceProvider" + ], + "aliases": { + "Google2FA": "PragmaRX\\Google2FALaravel\\Facade" + } + } + }, + "autoload": { + "psr-4": { + "PragmaRX\\Google2FALaravel\\": "src/", + "PragmaRX\\Google2FALaravel\\Tests\\": "tests/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Antonio Carlos Ribeiro", + "email": "acr@antoniocarlosribeiro.com", + "role": "Creator & Designer" + } + ], + "description": "A One Time Password Authentication package, compatible with Google Authenticator.", + "keywords": [ + "Authentication", + "Two Factor Authentication", + "google2fa", + "laravel" + ], + "time": "2018-07-05T03:42:05+00:00" + }, + { + "name": "pixelfed/http-signatures", + "version": "V5.3.0", + "source": { + "type": "git", + "url": "https://github.com/pixelfed/http-signatures-php.git", + "reference": "0eede979b02b868ef970d08a1cc7cb4d6cf4546a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/pixelfed/http-signatures-php/zipball/0eede979b02b868ef970d08a1cc7cb4d6cf4546a", + "reference": "0eede979b02b868ef970d08a1cc7cb4d6cf4546a", + "shasum": "" + }, + "require": { + "paragonie/random_compat": "^1.0|^2.0", + "php": ">=5.5", + "psr/http-message": "^1.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^1.11", + "guzzlehttp/psr7": "^1.2", + "phpunit/phpunit": "~4.8", + "symfony/http-foundation": "~2.8|~3.0", + "symfony/psr-http-message-bridge": "^1.0", + "zendframework/zend-diactoros": "^1.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "HttpSignatures\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Paul Annesley", + "email": "paul@99designs.com" + } + ], + "description": "Sign and verify HTTP messages", + "keywords": [ + "hmac", + "http", + "https", + "signature", + "signed", + "signing" + ], + "time": "2018-07-30T19:46:07+00:00" + }, + { + "name": "pixelfed/http-signatures-guzzlehttp", + "version": "v4.1.0", + "source": { + "type": "git", + "url": "https://github.com/pixelfed/http-signatures-guzzlehttp.git", + "reference": "39e404a79367ade78f4d6d8c7db4999b05a0f83a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/pixelfed/http-signatures-guzzlehttp/zipball/39e404a79367ade78f4d6d8c7db4999b05a0f83a", + "reference": "39e404a79367ade78f4d6d8c7db4999b05a0f83a", + "shasum": "" + }, + "require": { + "guzzlehttp/guzzle": ">=6.0 <7.0.0", + "php": ">=5.5.0", + "pixelfed/http-signatures": "^5.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Adrian Palmer", + "email": "adrian.palmer@99designs.com" + }, + { + "name": "Ruben de Vries", + "email": "ruben@rubensayshi.com" + } + ], + "description": "Sign and verify HTTP messages with Guzzle 6", + "homepage": "https://github.com/99designs/http-signatures-guzzlehttp", + "keywords": [ + "guzzle 6", + "hmac", + "http", + "https", + "signature", + "signed", + "signing" + ], + "time": "2018-07-29T01:59:53+00:00" + }, + { + "name": "pixelfed/zttp", + "version": "v0.4.1", + "source": { + "type": "git", + "url": "https://github.com/pixelfed/zttp.git", + "reference": "9a95a42716eb3e71a0a88411805737965bb77c05" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/pixelfed/zttp/zipball/9a95a42716eb3e71a0a88411805737965bb77c05", + "reference": "9a95a42716eb3e71a0a88411805737965bb77c05", + "shasum": "" + }, + "require": { + "guzzlehttp/guzzle": "^6.0", + "php": ">=7.0", + "tightenco/collect": "^5.4" + }, + "require-dev": { + "laravel/lumen-framework": "5.5.*", + "phpunit/phpunit": "^6.0" + }, + "type": "library", + "autoload": { + "files": [ + "src/Zttp.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Adam Wathan", + "email": "adam.wathan@gmail.com" + } + ], + "description": "A developer-experience focused HTTP client, optimized for most common use cases.", + "keywords": [ + "Guzzle", + "http" + ], + "time": "2018-07-30T05:04:42+00:00" + }, { "name": "predis/predis", "version": "v1.1.1", @@ -2350,16 +2927,16 @@ }, { "name": "psy/psysh", - "version": "v0.9.4", + "version": "v0.9.6", "source": { "type": "git", "url": "https://github.com/bobthecow/psysh.git", - "reference": "4d969a0e08e1e05e7207c07cb4207017ecc9a331" + "reference": "4a2ce86f199d51b6e2524214dc06835e872f4fce" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/bobthecow/psysh/zipball/4d969a0e08e1e05e7207c07cb4207017ecc9a331", - "reference": "4d969a0e08e1e05e7207c07cb4207017ecc9a331", + "url": "https://api.github.com/repos/bobthecow/psysh/zipball/4a2ce86f199d51b6e2524214dc06835e872f4fce", + "reference": "4a2ce86f199d51b6e2524214dc06835e872f4fce", "shasum": "" }, "require": { @@ -2418,25 +2995,26 @@ "interactive", "shell" ], - "time": "2018-05-22T06:48:07+00:00" + "time": "2018-06-10T17:57:20+00:00" }, { "name": "ramsey/uuid", - "version": "3.7.3", + "version": "3.8.0", "source": { "type": "git", "url": "https://github.com/ramsey/uuid.git", - "reference": "44abcdad877d9a46685a3a4d221e3b2c4b87cb76" + "reference": "d09ea80159c1929d75b3f9c60504d613aeb4a1e3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ramsey/uuid/zipball/44abcdad877d9a46685a3a4d221e3b2c4b87cb76", - "reference": "44abcdad877d9a46685a3a4d221e3b2c4b87cb76", + "url": "https://api.github.com/repos/ramsey/uuid/zipball/d09ea80159c1929d75b3f9c60504d613aeb4a1e3", + "reference": "d09ea80159c1929d75b3f9c60504d613aeb4a1e3", "shasum": "" }, "require": { - "paragonie/random_compat": "^1.0|^2.0", - "php": "^5.4 || ^7.0" + "paragonie/random_compat": "^1.0|^2.0|9.99.99", + "php": "^5.4 || ^7.0", + "symfony/polyfill-ctype": "^1.8" }, "replace": { "rhumsaa/uuid": "self.version" @@ -2444,16 +3022,17 @@ "require-dev": { "codeception/aspect-mock": "^1.0 | ~2.0.0", "doctrine/annotations": "~1.2.0", - "goaop/framework": "1.0.0-alpha.2 | ^1.0 | ^2.1", + "goaop/framework": "1.0.0-alpha.2 | ^1.0 | ~2.1.0", "ircmaxell/random-lib": "^1.1", "jakub-onderka/php-parallel-lint": "^0.9.0", "mockery/mockery": "^0.9.9", "moontoast/math": "^1.1", "php-mock/php-mock-phpunit": "^0.3|^1.1", - "phpunit/phpunit": "^4.7|^5.0", + "phpunit/phpunit": "^4.7|^5.0|^6.5", "squizlabs/php_codesniffer": "^2.3" }, "suggest": { + "ext-ctype": "Provides support for PHP Ctype functions", "ext-libsodium": "Provides the PECL libsodium extension for use with the SodiumRandomGenerator", "ext-uuid": "Provides the PECL UUID extension for use with the PeclUuidTimeGenerator and PeclUuidRandomGenerator", "ircmaxell/random-lib": "Provides RandomLib for use with the RandomLibAdapter", @@ -2498,7 +3077,7 @@ "identifier", "uuid" ], - "time": "2018-01-20T00:28:24+00:00" + "time": "2018-07-19T23:38:55+00:00" }, { "name": "spatie/db-dumper", @@ -2552,16 +3131,16 @@ }, { "name": "spatie/image-optimizer", - "version": "1.0.14", + "version": "1.1.0", "source": { "type": "git", "url": "https://github.com/spatie/image-optimizer.git", - "reference": "91603599eb29024cc9849a4a511a629ebce97850" + "reference": "1530d6cf72070068eecab150ffb73466c3806bdd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/image-optimizer/zipball/91603599eb29024cc9849a4a511a629ebce97850", - "reference": "91603599eb29024cc9849a4a511a629ebce97850", + "url": "https://api.github.com/repos/spatie/image-optimizer/zipball/1530d6cf72070068eecab150ffb73466c3806bdd", + "reference": "1530d6cf72070068eecab150ffb73466c3806bdd", "shasum": "" }, "require": { @@ -2597,20 +3176,20 @@ "image-optimizer", "spatie" ], - "time": "2018-03-07T13:42:33+00:00" + "time": "2018-06-05T07:36:17+00:00" }, { "name": "spatie/laravel-backup", - "version": "5.7.0", + "version": "5.9.3", "source": { "type": "git", "url": "https://github.com/spatie/laravel-backup.git", - "reference": "60c6fbc27c9c2cbdf3c7da90b080a63d2599cd2d" + "reference": "0c16390c9d37c58a57077129378b8d48f5c214b7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/laravel-backup/zipball/60c6fbc27c9c2cbdf3c7da90b080a63d2599cd2d", - "reference": "60c6fbc27c9c2cbdf3c7da90b080a63d2599cd2d", + "url": "https://api.github.com/repos/spatie/laravel-backup/zipball/0c16390c9d37c58a57077129378b8d48f5c214b7", + "reference": "0c16390c9d37c58a57077129378b8d48f5c214b7", "shasum": "" }, "require": { @@ -2670,26 +3249,26 @@ "laravel-backup", "spatie" ], - "time": "2018-05-11T07:06:58+00:00" + "time": "2018-08-04T11:52:51+00:00" }, { "name": "spatie/laravel-image-optimizer", - "version": "1.2.0", + "version": "1.3.1", "source": { "type": "git", "url": "https://github.com/spatie/laravel-image-optimizer.git", - "reference": "f98d1a8e90851ed0384b46f9b692297d47688a0c" + "reference": "b40f5accb41b385dcc62ca17c25283e5db11d9a3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/laravel-image-optimizer/zipball/f98d1a8e90851ed0384b46f9b692297d47688a0c", - "reference": "f98d1a8e90851ed0384b46f9b692297d47688a0c", + "url": "https://api.github.com/repos/spatie/laravel-image-optimizer/zipball/b40f5accb41b385dcc62ca17c25283e5db11d9a3", + "reference": "b40f5accb41b385dcc62ca17c25283e5db11d9a3", "shasum": "" }, "require": { "illuminate/support": "~5.5.0|~5.6.0", "php": "^7.0", - "spatie/image-optimizer": "^1.0.4" + "spatie/image-optimizer": "^1.1.0" }, "require-dev": { "orchestra/testbench": "~3.5.0|~3.6.0", @@ -2729,7 +3308,7 @@ "laravel-image-optimizer", "spatie" ], - "time": "2018-05-16T14:07:07+00:00" + "time": "2018-06-05T07:37:24+00:00" }, { "name": "spatie/laravel-partialcache", @@ -2838,16 +3417,16 @@ }, { "name": "swiftmailer/swiftmailer", - "version": "v6.0.2", + "version": "v6.1.2", "source": { "type": "git", "url": "https://github.com/swiftmailer/swiftmailer.git", - "reference": "412333372fb6c8ffb65496a2bbd7321af75733fc" + "reference": "7d760881d266d63c5e7a1155cbcf2ac656a31ca8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/412333372fb6c8ffb65496a2bbd7321af75733fc", - "reference": "412333372fb6c8ffb65496a2bbd7321af75733fc", + "url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/7d760881d266d63c5e7a1155cbcf2ac656a31ca8", + "reference": "7d760881d266d63c5e7a1155cbcf2ac656a31ca8", "shasum": "" }, "require": { @@ -2858,10 +3437,14 @@ "mockery/mockery": "~0.9.1", "symfony/phpunit-bridge": "~3.3@dev" }, + "suggest": { + "ext-intl": "Needed to support internationalized email addresses", + "true/punycode": "Needed to support internationalized email addresses, if ext-intl is not installed" + }, "type": "library", "extra": { "branch-alias": { - "dev-master": "6.0-dev" + "dev-master": "6.1-dev" } }, "autoload": { @@ -2883,26 +3466,26 @@ } ], "description": "Swiftmailer, free feature-rich PHP mailer", - "homepage": "http://swiftmailer.symfony.com", + "homepage": "https://swiftmailer.symfony.com", "keywords": [ "email", "mail", "mailer" ], - "time": "2017-09-30T22:39:41+00:00" + "time": "2018-07-13T07:04:35+00:00" }, { "name": "symfony/console", - "version": "v4.1.0", + "version": "v4.1.3", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "2d5d973bf9933d46802b01010bd25c800c87c242" + "reference": "ca80b8ced97cf07390078b29773dc384c39eee1f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/2d5d973bf9933d46802b01010bd25c800c87c242", - "reference": "2d5d973bf9933d46802b01010bd25c800c87c242", + "url": "https://api.github.com/repos/symfony/console/zipball/ca80b8ced97cf07390078b29773dc384c39eee1f", + "reference": "ca80b8ced97cf07390078b29773dc384c39eee1f", "shasum": "" }, "require": { @@ -2957,20 +3540,20 @@ ], "description": "Symfony Console Component", "homepage": "https://symfony.com", - "time": "2018-05-30T07:26:09+00:00" + "time": "2018-07-26T11:24:31+00:00" }, { "name": "symfony/css-selector", - "version": "v4.1.0", + "version": "v4.1.3", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", - "reference": "03ac71606ecb0b0ce792faa17d74cc32c2949ef4" + "reference": "2a4df7618f869b456f9096781e78c57b509d76c7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/03ac71606ecb0b0ce792faa17d74cc32c2949ef4", - "reference": "03ac71606ecb0b0ce792faa17d74cc32c2949ef4", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/2a4df7618f869b456f9096781e78c57b509d76c7", + "reference": "2a4df7618f869b456f9096781e78c57b509d76c7", "shasum": "" }, "require": { @@ -3010,20 +3593,20 @@ ], "description": "Symfony CssSelector Component", "homepage": "https://symfony.com", - "time": "2018-05-30T07:26:09+00:00" + "time": "2018-07-26T09:10:45+00:00" }, { "name": "symfony/debug", - "version": "v4.1.0", + "version": "v4.1.3", "source": { "type": "git", "url": "https://github.com/symfony/debug.git", - "reference": "449f8b00b28ab6e6912c3e6b920406143b27193b" + "reference": "9316545571f079c4dd183e674721d9dc783ce196" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/debug/zipball/449f8b00b28ab6e6912c3e6b920406143b27193b", - "reference": "449f8b00b28ab6e6912c3e6b920406143b27193b", + "url": "https://api.github.com/repos/symfony/debug/zipball/9316545571f079c4dd183e674721d9dc783ce196", + "reference": "9316545571f079c4dd183e674721d9dc783ce196", "shasum": "" }, "require": { @@ -3066,20 +3649,20 @@ ], "description": "Symfony Debug Component", "homepage": "https://symfony.com", - "time": "2018-05-16T14:33:22+00:00" + "time": "2018-07-26T11:24:31+00:00" }, { "name": "symfony/event-dispatcher", - "version": "v4.1.0", + "version": "v4.1.3", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "2391ed210a239868e7256eb6921b1bd83f3087b5" + "reference": "bfb30c2ad377615a463ebbc875eba64a99f6aa3e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/2391ed210a239868e7256eb6921b1bd83f3087b5", - "reference": "2391ed210a239868e7256eb6921b1bd83f3087b5", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/bfb30c2ad377615a463ebbc875eba64a99f6aa3e", + "reference": "bfb30c2ad377615a463ebbc875eba64a99f6aa3e", "shasum": "" }, "require": { @@ -3129,20 +3712,20 @@ ], "description": "Symfony EventDispatcher Component", "homepage": "https://symfony.com", - "time": "2018-04-06T07:35:57+00:00" + "time": "2018-07-26T09:10:45+00:00" }, { "name": "symfony/finder", - "version": "v4.1.0", + "version": "v4.1.3", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "087e2ee0d74464a4c6baac4e90417db7477dc238" + "reference": "e162f1df3102d0b7472805a5a9d5db9fcf0a8068" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/087e2ee0d74464a4c6baac4e90417db7477dc238", - "reference": "087e2ee0d74464a4c6baac4e90417db7477dc238", + "url": "https://api.github.com/repos/symfony/finder/zipball/e162f1df3102d0b7472805a5a9d5db9fcf0a8068", + "reference": "e162f1df3102d0b7472805a5a9d5db9fcf0a8068", "shasum": "" }, "require": { @@ -3178,20 +3761,20 @@ ], "description": "Symfony Finder Component", "homepage": "https://symfony.com", - "time": "2018-05-16T14:33:22+00:00" + "time": "2018-07-26T11:24:31+00:00" }, { "name": "symfony/http-foundation", - "version": "v4.1.0", + "version": "v4.1.3", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "a916c88390fb861ee21f12a92b107d51bb68af99" + "reference": "7d93e3547660ec7ee3dad1428ba42e8076a0e5f1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/a916c88390fb861ee21f12a92b107d51bb68af99", - "reference": "a916c88390fb861ee21f12a92b107d51bb68af99", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/7d93e3547660ec7ee3dad1428ba42e8076a0e5f1", + "reference": "7d93e3547660ec7ee3dad1428ba42e8076a0e5f1", "shasum": "" }, "require": { @@ -3232,20 +3815,20 @@ ], "description": "Symfony HttpFoundation Component", "homepage": "https://symfony.com", - "time": "2018-05-25T14:55:38+00:00" + "time": "2018-08-01T14:07:44+00:00" }, { "name": "symfony/http-kernel", - "version": "v4.1.0", + "version": "v4.1.3", "source": { "type": "git", "url": "https://github.com/symfony/http-kernel.git", - "reference": "b5ab9d4cdbfd369083744b6b5dfbf454e31e5f90" + "reference": "6347be5110efb27fe45ea04bf213078b67a05036" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/b5ab9d4cdbfd369083744b6b5dfbf454e31e5f90", - "reference": "b5ab9d4cdbfd369083744b6b5dfbf454e31e5f90", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/6347be5110efb27fe45ea04bf213078b67a05036", + "reference": "6347be5110efb27fe45ea04bf213078b67a05036", "shasum": "" }, "require": { @@ -3253,13 +3836,13 @@ "psr/log": "~1.0", "symfony/debug": "~3.4|~4.0", "symfony/event-dispatcher": "~4.1", - "symfony/http-foundation": "~4.1", + "symfony/http-foundation": "^4.1.1", "symfony/polyfill-ctype": "~1.8" }, "conflict": { "symfony/config": "<3.4", "symfony/dependency-injection": "<4.1", - "symfony/var-dumper": "<4.1", + "symfony/var-dumper": "<4.1.1", "twig/twig": "<1.34|<2.4,>=2" }, "provide": { @@ -3280,7 +3863,7 @@ "symfony/stopwatch": "~3.4|~4.0", "symfony/templating": "~3.4|~4.0", "symfony/translation": "~3.4|~4.0", - "symfony/var-dumper": "~4.1" + "symfony/var-dumper": "^4.1.1" }, "suggest": { "symfony/browser-kit": "", @@ -3319,29 +3902,32 @@ ], "description": "Symfony HttpKernel Component", "homepage": "https://symfony.com", - "time": "2018-05-30T12:52:34+00:00" + "time": "2018-08-01T15:30:34+00:00" }, { "name": "symfony/polyfill-ctype", - "version": "v1.8.0", + "version": "v1.9.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "7cc359f1b7b80fc25ed7796be7d96adc9b354bae" + "reference": "e3d826245268269cd66f8326bd8bc066687b4a19" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/7cc359f1b7b80fc25ed7796be7d96adc9b354bae", - "reference": "7cc359f1b7b80fc25ed7796be7d96adc9b354bae", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/e3d826245268269cd66f8326bd8bc066687b4a19", + "reference": "e3d826245268269cd66f8326bd8bc066687b4a19", "shasum": "" }, "require": { "php": ">=5.3.3" }, + "suggest": { + "ext-ctype": "For best performance" + }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.8-dev" + "dev-master": "1.9-dev" } }, "autoload": { @@ -3374,20 +3960,20 @@ "polyfill", "portable" ], - "time": "2018-04-30T19:57:29+00:00" + "time": "2018-08-06T14:22:27+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.8.0", + "version": "v1.9.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "3296adf6a6454a050679cde90f95350ad604b171" + "reference": "d0cd638f4634c16d8df4508e847f14e9e43168b8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/3296adf6a6454a050679cde90f95350ad604b171", - "reference": "3296adf6a6454a050679cde90f95350ad604b171", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/d0cd638f4634c16d8df4508e847f14e9e43168b8", + "reference": "d0cd638f4634c16d8df4508e847f14e9e43168b8", "shasum": "" }, "require": { @@ -3399,7 +3985,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.8-dev" + "dev-master": "1.9-dev" } }, "autoload": { @@ -3433,20 +4019,76 @@ "portable", "shim" ], - "time": "2018-04-26T10:06:28+00:00" + "time": "2018-08-06T14:22:27+00:00" }, { - "name": "symfony/polyfill-php72", - "version": "v1.8.0", + "name": "symfony/polyfill-php56", + "version": "v1.9.0", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-php72.git", - "reference": "a4576e282d782ad82397f3e4ec1df8e0f0cafb46" + "url": "https://github.com/symfony/polyfill-php56.git", + "reference": "7b4fc009172cc0196535b0328bd1226284a28000" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/a4576e282d782ad82397f3e4ec1df8e0f0cafb46", - "reference": "a4576e282d782ad82397f3e4ec1df8e0f0cafb46", + "url": "https://api.github.com/repos/symfony/polyfill-php56/zipball/7b4fc009172cc0196535b0328bd1226284a28000", + "reference": "7b4fc009172cc0196535b0328bd1226284a28000", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "symfony/polyfill-util": "~1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.9-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Php56\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "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 5.6+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "time": "2018-08-06T14:22:27+00:00" + }, + { + "name": "symfony/polyfill-php72", + "version": "v1.9.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php72.git", + "reference": "95c50420b0baed23852452a7f0c7b527303ed5ae" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/95c50420b0baed23852452a7f0c7b527303ed5ae", + "reference": "95c50420b0baed23852452a7f0c7b527303ed5ae", "shasum": "" }, "require": { @@ -3455,7 +4097,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.8-dev" + "dev-master": "1.9-dev" } }, "autoload": { @@ -3488,20 +4130,72 @@ "portable", "shim" ], - "time": "2018-04-26T10:06:28+00:00" + "time": "2018-08-06T14:22:27+00:00" }, { - "name": "symfony/process", - "version": "v4.1.0", + "name": "symfony/polyfill-util", + "version": "v1.9.0", "source": { "type": "git", - "url": "https://github.com/symfony/process.git", - "reference": "73445bd33b0d337c060eef9652b94df72b6b3434" + "url": "https://github.com/symfony/polyfill-util.git", + "reference": "8e15d04ba3440984d23e7964b2ee1d25c8de1581" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/73445bd33b0d337c060eef9652b94df72b6b3434", - "reference": "73445bd33b0d337c060eef9652b94df72b6b3434", + "url": "https://api.github.com/repos/symfony/polyfill-util/zipball/8e15d04ba3440984d23e7964b2ee1d25c8de1581", + "reference": "8e15d04ba3440984d23e7964b2ee1d25c8de1581", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.9-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Util\\": "" + } + }, + "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 utilities for portability of PHP codes", + "homepage": "https://symfony.com", + "keywords": [ + "compat", + "compatibility", + "polyfill", + "shim" + ], + "time": "2018-08-06T14:22:27+00:00" + }, + { + "name": "symfony/process", + "version": "v4.1.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/process.git", + "reference": "f01fc7a4493572f7f506c49dcb50ad01fb3a2f56" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/process/zipball/f01fc7a4493572f7f506c49dcb50ad01fb3a2f56", + "reference": "f01fc7a4493572f7f506c49dcb50ad01fb3a2f56", "shasum": "" }, "require": { @@ -3537,20 +4231,80 @@ ], "description": "Symfony Process Component", "homepage": "https://symfony.com", - "time": "2018-05-30T07:26:09+00:00" + "time": "2018-07-26T11:24:31+00:00" }, { - "name": "symfony/routing", - "version": "v4.1.0", + "name": "symfony/psr-http-message-bridge", + "version": "v1.0.2", "source": { "type": "git", - "url": "https://github.com/symfony/routing.git", - "reference": "180b51c66d10f09e562c9ebc395b39aacb2cf8a2" + "url": "https://github.com/symfony/psr-http-message-bridge.git", + "reference": "c2b757934f2d9681a287e662efbc27c41fe8ef86" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/routing/zipball/180b51c66d10f09e562c9ebc395b39aacb2cf8a2", - "reference": "180b51c66d10f09e562c9ebc395b39aacb2cf8a2", + "url": "https://api.github.com/repos/symfony/psr-http-message-bridge/zipball/c2b757934f2d9681a287e662efbc27c41fe8ef86", + "reference": "c2b757934f2d9681a287e662efbc27c41fe8ef86", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "psr/http-message": "~1.0", + "symfony/http-foundation": "~2.3|~3.0|~4.0" + }, + "require-dev": { + "symfony/phpunit-bridge": "~3.2|4.0" + }, + "suggest": { + "psr/http-message-implementation": "To use the HttpFoundation factory", + "zendframework/zend-diactoros": "To use the Zend Diactoros factory" + }, + "type": "symfony-bridge", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Bridge\\PsrHttpMessage\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + }, + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "description": "PSR HTTP message bridge", + "homepage": "http://symfony.com", + "keywords": [ + "http", + "http-message", + "psr-7" + ], + "time": "2017-12-19T00:31:44+00:00" + }, + { + "name": "symfony/routing", + "version": "v4.1.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/routing.git", + "reference": "6912cfebc0ea4e7a46fdd15c9bd1f427dd39ff1b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/routing/zipball/6912cfebc0ea4e7a46fdd15c9bd1f427dd39ff1b", + "reference": "6912cfebc0ea4e7a46fdd15c9bd1f427dd39ff1b", "shasum": "" }, "require": { @@ -3563,7 +4317,6 @@ }, "require-dev": { "doctrine/annotations": "~1.0", - "doctrine/common": "~2.2", "psr/log": "~1.0", "symfony/config": "~3.4|~4.0", "symfony/dependency-injection": "~3.4|~4.0", @@ -3615,20 +4368,20 @@ "uri", "url" ], - "time": "2018-05-30T07:26:09+00:00" + "time": "2018-07-26T11:24:31+00:00" }, { "name": "symfony/translation", - "version": "v4.1.0", + "version": "v4.1.3", "source": { "type": "git", "url": "https://github.com/symfony/translation.git", - "reference": "16328f5b217cebc8dd4adfe4aeeaa8c377581f5a" + "reference": "6fcd1bd44fd6d7181e6ea57a6f4e08a09b29ef65" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/16328f5b217cebc8dd4adfe4aeeaa8c377581f5a", - "reference": "16328f5b217cebc8dd4adfe4aeeaa8c377581f5a", + "url": "https://api.github.com/repos/symfony/translation/zipball/6fcd1bd44fd6d7181e6ea57a6f4e08a09b29ef65", + "reference": "6fcd1bd44fd6d7181e6ea57a6f4e08a09b29ef65", "shasum": "" }, "require": { @@ -3684,20 +4437,20 @@ ], "description": "Symfony Translation Component", "homepage": "https://symfony.com", - "time": "2018-05-30T07:26:09+00:00" + "time": "2018-07-26T11:24:31+00:00" }, { "name": "symfony/var-dumper", - "version": "v4.1.0", + "version": "v4.1.3", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "bc88ad53e825ebacc7b190bbd360781fce381c64" + "reference": "69e174f4c02ec43919380171c6f7550753299316" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/bc88ad53e825ebacc7b190bbd360781fce381c64", - "reference": "bc88ad53e825ebacc7b190bbd360781fce381c64", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/69e174f4c02ec43919380171c6f7550753299316", + "reference": "69e174f4c02ec43919380171c6f7550753299316", "shasum": "" }, "require": { @@ -3759,20 +4512,20 @@ "debug", "dump" ], - "time": "2018-04-29T07:56:09+00:00" + "time": "2018-07-26T11:24:31+00:00" }, { "name": "tightenco/collect", - "version": "v5.6.23", + "version": "v5.6.29", "source": { "type": "git", "url": "https://github.com/tightenco/collect.git", - "reference": "0954fc3ca147a7d727d807e15113daba4a08c810" + "reference": "64f281891ad887451b520ef77574b7a66f7d80e8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/tightenco/collect/zipball/0954fc3ca147a7d727d807e15113daba4a08c810", - "reference": "0954fc3ca147a7d727d807e15113daba4a08c810", + "url": "https://api.github.com/repos/tightenco/collect/zipball/64f281891ad887451b520ef77574b7a66f7d80e8", + "reference": "64f281891ad887451b520ef77574b7a66f7d80e8", "shasum": "" }, "require": { @@ -3809,7 +4562,7 @@ "collection", "laravel" ], - "time": "2018-05-22T17:57:22+00:00" + "time": "2018-07-26T22:33:08+00:00" }, { "name": "tijsverkoyen/css-to-inline-styles", @@ -3860,28 +4613,28 @@ }, { "name": "vlucas/phpdotenv", - "version": "v2.4.0", + "version": "v2.5.1", "source": { "type": "git", "url": "https://github.com/vlucas/phpdotenv.git", - "reference": "3cc116adbe4b11be5ec557bf1d24dc5e3a21d18c" + "reference": "8abb4f9aa89ddea9d52112c65bbe8d0125e2fa8e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/3cc116adbe4b11be5ec557bf1d24dc5e3a21d18c", - "reference": "3cc116adbe4b11be5ec557bf1d24dc5e3a21d18c", + "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/8abb4f9aa89ddea9d52112c65bbe8d0125e2fa8e", + "reference": "8abb4f9aa89ddea9d52112c65bbe8d0125e2fa8e", "shasum": "" }, "require": { "php": ">=5.3.9" }, "require-dev": { - "phpunit/phpunit": "^4.8 || ^5.0" + "phpunit/phpunit": "^4.8.35 || ^5.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.4-dev" + "dev-master": "2.5-dev" } }, "autoload": { @@ -3891,7 +4644,7 @@ }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause-Attribution" + "BSD-3-Clause" ], "authors": [ { @@ -3906,7 +4659,70 @@ "env", "environment" ], - "time": "2016-09-01T10:05:43+00:00" + "time": "2018-07-29T20:33:41+00:00" + }, + { + "name": "zendframework/zend-diactoros", + "version": "1.8.4", + "source": { + "type": "git", + "url": "https://github.com/zendframework/zend-diactoros.git", + "reference": "736ffa7c2bfa4a60e8a10acb316fa2ac456c5fba" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/zendframework/zend-diactoros/zipball/736ffa7c2bfa4a60e8a10acb316fa2ac456c5fba", + "reference": "736ffa7c2bfa4a60e8a10acb316fa2ac456c5fba", + "shasum": "" + }, + "require": { + "php": "^5.6 || ^7.0", + "psr/http-message": "^1.0" + }, + "provide": { + "psr/http-message-implementation": "1.0" + }, + "require-dev": { + "ext-dom": "*", + "ext-libxml": "*", + "phpunit/phpunit": "^5.7.16 || ^6.0.8 || ^7.2.7", + "zendframework/zend-coding-standard": "~1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.8.x-dev", + "dev-develop": "1.9.x-dev", + "dev-release-2.0": "2.0.x-dev" + } + }, + "autoload": { + "files": [ + "src/functions/create_uploaded_file.php", + "src/functions/marshal_headers_from_sapi.php", + "src/functions/marshal_method_from_sapi.php", + "src/functions/marshal_protocol_version_from_sapi.php", + "src/functions/marshal_uri_from_sapi.php", + "src/functions/normalize_server.php", + "src/functions/normalize_uploaded_files.php", + "src/functions/parse_cookie_header.php" + ], + "psr-4": { + "Zend\\Diactoros\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-2-Clause" + ], + "description": "PSR HTTP Message implementations", + "homepage": "https://github.com/zendframework/zend-diactoros", + "keywords": [ + "http", + "psr", + "psr-7" + ], + "time": "2018-08-01T13:47:49+00:00" } ], "packages-dev": [ @@ -3978,6 +4794,68 @@ ], "time": "2018-05-03T18:27:04+00:00" }, + { + "name": "beyondcode/laravel-er-diagram-generator", + "version": "0.2.3", + "source": { + "type": "git", + "url": "https://github.com/beyondcode/laravel-er-diagram-generator.git", + "reference": "ff3c9345b679b243ce529a860f6660f1cc8c49c9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/beyondcode/laravel-er-diagram-generator/zipball/ff3c9345b679b243ce529a860f6660f1cc8c49c9", + "reference": "ff3c9345b679b243ce529a860f6660f1cc8c49c9", + "shasum": "" + }, + "require": { + "doctrine/dbal": "~2.3", + "nikic/php-parser": "^2.0|^3.0|^4.0", + "php": "^7.1", + "phpdocumentor/graphviz": "^1.0" + }, + "require-dev": { + "larapack/dd": "^1.0", + "orchestra/testbench": "~3.5", + "phpunit/phpunit": "^7.0", + "spatie/phpunit-snapshot-assertions": "^1.3" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "BeyondCode\\ErdGenerator\\ErdGeneratorServiceProvider" + ], + "aliases": { + "ErdGenerator": "BeyondCode\\ErdGenerator\\ErdGeneratorFacade" + } + } + }, + "autoload": { + "psr-4": { + "BeyondCode\\ErdGenerator\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marcel Pociot", + "email": "marcel@beyondco.de", + "homepage": "https://beyondcode.de", + "role": "Developer" + } + ], + "description": "Generate ER diagrams from your Laravel models.", + "homepage": "https://github.com/beyondcode/laravel-er-diagram-generator", + "keywords": [ + "beyondcode", + "laravel-er-diagram-generator" + ], + "time": "2018-07-12T08:28:30+00:00" + }, { "name": "doctrine/instantiator", "version": "1.1.0", @@ -4034,16 +4912,16 @@ }, { "name": "filp/whoops", - "version": "2.1.14", + "version": "2.2.0", "source": { "type": "git", "url": "https://github.com/filp/whoops.git", - "reference": "c6081b8838686aa04f1e83ba7e91f78b7b2a23e6" + "reference": "181c4502d8f34db7aed7bfe88d4f87875b8e947a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/filp/whoops/zipball/c6081b8838686aa04f1e83ba7e91f78b7b2a23e6", - "reference": "c6081b8838686aa04f1e83ba7e91f78b7b2a23e6", + "url": "https://api.github.com/repos/filp/whoops/zipball/181c4502d8f34db7aed7bfe88d4f87875b8e947a", + "reference": "181c4502d8f34db7aed7bfe88d4f87875b8e947a", "shasum": "" }, "require": { @@ -4051,9 +4929,9 @@ "psr/log": "^1.0.1" }, "require-dev": { - "mockery/mockery": "0.9.*", + "mockery/mockery": "^0.9 || ^1.0", "phpunit/phpunit": "^4.8.35 || ^5.7", - "symfony/var-dumper": "^2.6 || ^3.0" + "symfony/var-dumper": "^2.6 || ^3.0 || ^4.0" }, "suggest": { "symfony/var-dumper": "Pretty print complex values better with var-dumper available", @@ -4062,7 +4940,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-master": "2.1-dev" } }, "autoload": { @@ -4091,20 +4969,20 @@ "throwable", "whoops" ], - "time": "2017-11-23T18:22:44+00:00" + "time": "2018-03-03T17:56:25+00:00" }, { "name": "fzaninotto/faker", - "version": "v1.7.1", + "version": "v1.8.0", "source": { "type": "git", "url": "https://github.com/fzaninotto/Faker.git", - "reference": "d3ed4cc37051c1ca52d22d76b437d14809fc7e0d" + "reference": "f72816b43e74063c8b10357394b6bba8cb1c10de" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/fzaninotto/Faker/zipball/d3ed4cc37051c1ca52d22d76b437d14809fc7e0d", - "reference": "d3ed4cc37051c1ca52d22d76b437d14809fc7e0d", + "url": "https://api.github.com/repos/fzaninotto/Faker/zipball/f72816b43e74063c8b10357394b6bba8cb1c10de", + "reference": "f72816b43e74063c8b10357394b6bba8cb1c10de", "shasum": "" }, "require": { @@ -4112,7 +4990,7 @@ }, "require-dev": { "ext-intl": "*", - "phpunit/phpunit": "^4.0 || ^5.0", + "phpunit/phpunit": "^4.8.35 || ^5.7", "squizlabs/php_codesniffer": "^1.5" }, "type": "library", @@ -4141,7 +5019,7 @@ "faker", "fixtures" ], - "time": "2017-08-15T16:48:10+00:00" + "time": "2018-07-12T10:23:15+00:00" }, { "name": "hamcrest/hamcrest-php", @@ -4320,16 +5198,16 @@ }, { "name": "myclabs/deep-copy", - "version": "1.8.0", + "version": "1.8.1", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "478465659fd987669df0bd8a9bf22a8710e5f1b6" + "reference": "3e01bdad3e18354c3dce54466b7fbe33a9f9f7f8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/478465659fd987669df0bd8a9bf22a8710e5f1b6", - "reference": "478465659fd987669df0bd8a9bf22a8710e5f1b6", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3e01bdad3e18354c3dce54466b7fbe33a9f9f7f8", + "reference": "3e01bdad3e18354c3dce54466b7fbe33a9f9f7f8", "shasum": "" }, "require": { @@ -4364,20 +5242,20 @@ "object", "object graph" ], - "time": "2018-05-29T17:25:09+00:00" + "time": "2018-06-11T23:09:50+00:00" }, { "name": "nunomaduro/collision", - "version": "v2.0.2", + "version": "v2.0.3", "source": { "type": "git", "url": "https://github.com/nunomaduro/collision.git", - "reference": "245958b02c6a9edf24627380f368333ac5413a51" + "reference": "b1f606399ae77e9479b5597cd1aa3d8ea0078176" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nunomaduro/collision/zipball/245958b02c6a9edf24627380f368333ac5413a51", - "reference": "245958b02c6a9edf24627380f368333ac5413a51", + "url": "https://api.github.com/repos/nunomaduro/collision/zipball/b1f606399ae77e9479b5597cd1aa3d8ea0078176", + "reference": "b1f606399ae77e9479b5597cd1aa3d8ea0078176", "shasum": "" }, "require": { @@ -4388,7 +5266,8 @@ }, "require-dev": { "laravel/framework": "5.6.*", - "phpunit/phpunit": "~7.0" + "phpstan/phpstan": "^0.9.2", + "phpunit/phpunit": "~7.2" }, "type": "library", "extra": { @@ -4426,26 +5305,26 @@ "php", "symfony" ], - "time": "2018-03-21T20:11:24+00:00" + "time": "2018-06-16T22:05:52+00:00" }, { "name": "phar-io/manifest", - "version": "1.0.1", + "version": "1.0.3", "source": { "type": "git", "url": "https://github.com/phar-io/manifest.git", - "reference": "2df402786ab5368a0169091f61a7c1e0eb6852d0" + "reference": "7761fcacf03b4d4f16e7ccb606d4879ca431fcf4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phar-io/manifest/zipball/2df402786ab5368a0169091f61a7c1e0eb6852d0", - "reference": "2df402786ab5368a0169091f61a7c1e0eb6852d0", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/7761fcacf03b4d4f16e7ccb606d4879ca431fcf4", + "reference": "7761fcacf03b4d4f16e7ccb606d4879ca431fcf4", "shasum": "" }, "require": { "ext-dom": "*", "ext-phar": "*", - "phar-io/version": "^1.0.1", + "phar-io/version": "^2.0", "php": "^5.6 || ^7.0" }, "type": "library", @@ -4481,20 +5360,20 @@ } ], "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", - "time": "2017-03-05T18:14:27+00:00" + "time": "2018-07-08T19:23:20+00:00" }, { "name": "phar-io/version", - "version": "1.0.1", + "version": "2.0.1", "source": { "type": "git", "url": "https://github.com/phar-io/version.git", - "reference": "a70c0ced4be299a63d32fa96d9281d03e94041df" + "reference": "45a2ec53a73c70ce41d55cedef9063630abaf1b6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phar-io/version/zipball/a70c0ced4be299a63d32fa96d9281d03e94041df", - "reference": "a70c0ced4be299a63d32fa96d9281d03e94041df", + "url": "https://api.github.com/repos/phar-io/version/zipball/45a2ec53a73c70ce41d55cedef9063630abaf1b6", + "reference": "45a2ec53a73c70ce41d55cedef9063630abaf1b6", "shasum": "" }, "require": { @@ -4528,7 +5407,48 @@ } ], "description": "Library for handling version information and constraints", - "time": "2017-03-05T17:38:23+00:00" + "time": "2018-07-08T19:19:57+00:00" + }, + { + "name": "phpdocumentor/graphviz", + "version": "1.0.4", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/GraphViz.git", + "reference": "a906a90a9f230535f25ea31caf81b2323956283f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/GraphViz/zipball/a906a90a9f230535f25ea31caf81b2323956283f", + "reference": "a906a90a9f230535f25ea31caf81b2323956283f", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "~4.0" + }, + "type": "library", + "autoload": { + "psr-0": { + "phpDocumentor": [ + "src/", + "tests/unit" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "mike.vanriel@naenius.com" + } + ], + "time": "2016-02-02T13:00:08+00:00" }, { "name": "phpdocumentor/reflection-common", @@ -4684,16 +5604,16 @@ }, { "name": "phpspec/prophecy", - "version": "1.7.6", + "version": "1.8.0", "source": { "type": "git", "url": "https://github.com/phpspec/prophecy.git", - "reference": "33a7e3c4fda54e912ff6338c48823bd5c0f0b712" + "reference": "4ba436b55987b4bf311cb7c6ba82aa528aac0a06" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/33a7e3c4fda54e912ff6338c48823bd5c0f0b712", - "reference": "33a7e3c4fda54e912ff6338c48823bd5c0f0b712", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/4ba436b55987b4bf311cb7c6ba82aa528aac0a06", + "reference": "4ba436b55987b4bf311cb7c6ba82aa528aac0a06", "shasum": "" }, "require": { @@ -4705,12 +5625,12 @@ }, "require-dev": { "phpspec/phpspec": "^2.5|^3.2", - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5" + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5 || ^7.1" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.7.x-dev" + "dev-master": "1.8.x-dev" } }, "autoload": { @@ -4743,7 +5663,7 @@ "spy", "stub" ], - "time": "2018-04-18T13:57:24+00:00" + "time": "2018-08-05T17:53:17+00:00" }, { "name": "phpunit/php-code-coverage", @@ -4810,16 +5730,16 @@ }, { "name": "phpunit/php-file-iterator", - "version": "2.0.0", + "version": "2.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "e20525b0c2945c7c317fff95660698cb3d2a53bc" + "reference": "cecbc684605bb0cc288828eb5d65d93d5c676d3c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/e20525b0c2945c7c317fff95660698cb3d2a53bc", - "reference": "e20525b0c2945c7c317fff95660698cb3d2a53bc", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cecbc684605bb0cc288828eb5d65d93d5c676d3c", + "reference": "cecbc684605bb0cc288828eb5d65d93d5c676d3c", "shasum": "" }, "require": { @@ -4853,7 +5773,7 @@ "filesystem", "iterator" ], - "time": "2018-05-28T12:13:49+00:00" + "time": "2018-06-11T11:44:00+00:00" }, { "name": "phpunit/php-text-template", @@ -4996,16 +5916,16 @@ }, { "name": "phpunit/phpunit", - "version": "7.2.2", + "version": "7.3.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "3cf0836680bf5c365c627e8566d46c9e1f544db9" + "reference": "f9b14c17860eccb440a0352a117a81eb754cff5a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/3cf0836680bf5c365c627e8566d46c9e1f544db9", - "reference": "3cf0836680bf5c365c627e8566d46c9e1f544db9", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/f9b14c17860eccb440a0352a117a81eb754cff5a", + "reference": "f9b14c17860eccb440a0352a117a81eb754cff5a", "shasum": "" }, "require": { @@ -5016,12 +5936,12 @@ "ext-mbstring": "*", "ext-xml": "*", "myclabs/deep-copy": "^1.7", - "phar-io/manifest": "^1.0.1", - "phar-io/version": "^1.0", + "phar-io/manifest": "^1.0.2", + "phar-io/version": "^2.0", "php": "^7.1", "phpspec/prophecy": "^1.7", "phpunit/php-code-coverage": "^6.0.7", - "phpunit/php-file-iterator": "^2.0", + "phpunit/php-file-iterator": "^2.0.1", "phpunit/php-text-template": "^1.2.1", "phpunit/php-timer": "^2.0", "sebastian/comparator": "^3.0", @@ -5050,7 +5970,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "7.2-dev" + "dev-master": "7.3-dev" } }, "autoload": { @@ -5076,7 +5996,7 @@ "testing", "xunit" ], - "time": "2018-06-01T07:54:27+00:00" + "time": "2018-08-07T06:44:28+00:00" }, { "name": "sebastian/code-unit-reverse-lookup", @@ -5125,16 +6045,16 @@ }, { "name": "sebastian/comparator", - "version": "3.0.0", + "version": "3.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "ed5fd2281113729f1ebcc64d101ad66028aeb3d5" + "reference": "5de4fc177adf9bce8df98d8d141a7559d7ccf6da" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/ed5fd2281113729f1ebcc64d101ad66028aeb3d5", - "reference": "ed5fd2281113729f1ebcc64d101ad66028aeb3d5", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/5de4fc177adf9bce8df98d8d141a7559d7ccf6da", + "reference": "5de4fc177adf9bce8df98d8d141a7559d7ccf6da", "shasum": "" }, "require": { @@ -5185,20 +6105,20 @@ "compare", "equality" ], - "time": "2018-04-18T13:33:00+00:00" + "time": "2018-07-12T15:12:46+00:00" }, { "name": "sebastian/diff", - "version": "3.0.0", + "version": "3.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "e09160918c66281713f1c324c1f4c4c3037ba1e8" + "reference": "366541b989927187c4ca70490a35615d3fef2dce" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/e09160918c66281713f1c324c1f4c4c3037ba1e8", - "reference": "e09160918c66281713f1c324c1f4c4c3037ba1e8", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/366541b989927187c4ca70490a35615d3fef2dce", + "reference": "366541b989927187c4ca70490a35615d3fef2dce", "shasum": "" }, "require": { @@ -5241,7 +6161,7 @@ "unidiff", "unified diff" ], - "time": "2018-02-01T13:45:15+00:00" + "time": "2018-06-10T07:54:39+00:00" }, { "name": "sebastian/environment", diff --git a/config/app.php b/config/app.php index a281d980b..b426fa531 100644 --- a/config/app.php +++ b/config/app.php @@ -151,6 +151,7 @@ return [ * Package Service Providers... */ Greggilbert\Recaptcha\RecaptchaServiceProvider::class, + Jackiedo\DotenvEditor\DotenvEditorServiceProvider::class, /* * Application Service Providers... @@ -211,6 +212,7 @@ return [ 'View' => Illuminate\Support\Facades\View::class, 'Recaptcha' => Greggilbert\Recaptcha\Facades\Recaptcha::class, + 'DotenvEditor' => Jackiedo\DotenvEditor\Facades\DotenvEditor::class, ], ]; diff --git a/config/dotenv-editor.php b/config/dotenv-editor.php new file mode 100644 index 000000000..583039c63 --- /dev/null +++ b/config/dotenv-editor.php @@ -0,0 +1,27 @@ + true, + + /* + |---------------------------------------------------------------------- + | Backup location + |---------------------------------------------------------------------- + | + | This value is used when you backup your file. This value is the sub + | path from root folder of project application. + */ + + 'backupPath' => base_path('storage/dotenv-editor/backups/') + +); diff --git a/config/horizon.php b/config/horizon.php index 0d74e01dd..68fbf60b8 100644 --- a/config/horizon.php +++ b/config/horizon.php @@ -76,7 +76,7 @@ return [ 'connection' => 'redis', 'queue' => ['default'], 'balance' => 'simple', - 'processes' => 10, + 'processes' => 20, 'tries' => 3, ], ], diff --git a/config/image-optimizer.php b/config/image-optimizer.php index 241dc199b..6e97d8eff 100644 --- a/config/image-optimizer.php +++ b/config/image-optimizer.php @@ -49,5 +49,5 @@ return [ * If set to `true` all output of the optimizer binaries will be appended to the default log. * You can also set this to a class that implements `Psr\Log\LoggerInterface`. */ - 'log_optimizer_activity' => true, + 'log_optimizer_activity' => false, ]; diff --git a/config/pixelfed.php b/config/pixelfed.php index c825643a7..ffdd5b633 100644 --- a/config/pixelfed.php +++ b/config/pixelfed.php @@ -23,7 +23,7 @@ return [ | This value is the version of your PixelFed instance. | */ - 'version' => '0.1.0', + 'version' => '0.1.4', /* |-------------------------------------------------------------------------- @@ -77,6 +77,17 @@ return [ 'activitypub_enabled' => env('ACTIVITY_PUB', false), + /* + |-------------------------------------------------------------------------- + | Account file size limit + |-------------------------------------------------------------------------- + | + | Update the max account size, the per user limit of files in KB. + | + | + */ + 'max_account_size' => env('MAX_ACCOUNT_SIZE', 1000000), + /* |-------------------------------------------------------------------------- | Photo file size limit @@ -95,7 +106,7 @@ return [ | Change the caption length limit for new local posts. | */ - 'max_caption_length' => env('MAX_CAPTION_LENGTH', 150), + 'max_caption_length' => env('MAX_CAPTION_LENGTH', 500), /* |-------------------------------------------------------------------------- @@ -116,5 +127,15 @@ return [ | */ 'enforce_email_verification' => env('ENFORCE_EMAIL_VERIFICATION', true), + + /* + |-------------------------------------------------------------------------- + | Image Quality + |-------------------------------------------------------------------------- + | + | Set the image optimization quality, must be a value between 1-100. + | + */ + 'image_quality' => (int) env('IMAGE_QUALITY', 80), ]; \ No newline at end of file diff --git a/database/migrations/2018_04_22_233721_create_web_subs_table.php b/database/migrations/2018_04_22_233721_create_web_subs_table.php new file mode 100644 index 000000000..cd91ca4d1 --- /dev/null +++ b/database/migrations/2018_04_22_233721_create_web_subs_table.php @@ -0,0 +1,36 @@ +bigIncrements('id'); + $table->bigInteger('follower_id')->unsigned()->index(); + $table->bigInteger('following_id')->unsigned()->index(); + $table->string('profile_url')->index(); + $table->timestamp('approved_at')->nullable(); + $table->unique(['follower_id', 'following_id', 'profile_url']); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('web_subs'); + } +} diff --git a/database/migrations/2018_04_26_003259_create_import_jobs_table.php b/database/migrations/2018_04_26_003259_create_import_jobs_table.php new file mode 100644 index 000000000..447deaada --- /dev/null +++ b/database/migrations/2018_04_26_003259_create_import_jobs_table.php @@ -0,0 +1,38 @@ +increments('id'); + $table->bigInteger('profile_id')->unsigned(); + $table->string('service')->default('instagram'); + $table->string('uuid')->nullable(); + $table->string('storage_path')->nullable(); + $table->tinyInteger('stage')->unsigned()->default(0); + $table->text('media_json')->nullable(); + $table->timestamp('completed_at')->nullable(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('import_jobs'); + } +} diff --git a/database/migrations/2018_06_22_062621_create_report_comments_table.php b/database/migrations/2018_06_22_062621_create_report_comments_table.php new file mode 100644 index 000000000..51594cc85 --- /dev/null +++ b/database/migrations/2018_06_22_062621_create_report_comments_table.php @@ -0,0 +1,35 @@ +increments('id'); + $table->bigInteger('report_id')->unsigned()->index(); + $table->bigInteger('profile_id')->unsigned(); + $table->bigInteger('user_id')->unsigned(); + $table->text('comment'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('report_comments'); + } +} diff --git a/database/migrations/2018_06_22_062628_create_report_logs_table.php b/database/migrations/2018_06_22_062628_create_report_logs_table.php new file mode 100644 index 000000000..74551aed1 --- /dev/null +++ b/database/migrations/2018_06_22_062628_create_report_logs_table.php @@ -0,0 +1,37 @@ +increments('id'); + $table->bigInteger('profile_id')->unsigned(); + $table->bigInteger('item_id')->unsigned()->nullable(); + $table->string('item_type')->nullable(); + $table->string('action')->nullable(); + $table->boolean('system_message')->default(false); + $table->json('metadata')->nullable(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('report_logs'); + } +} diff --git a/database/migrations/2018_07_05_010303_create_account_logs_table.php b/database/migrations/2018_07_05_010303_create_account_logs_table.php new file mode 100644 index 000000000..1d2007330 --- /dev/null +++ b/database/migrations/2018_07_05_010303_create_account_logs_table.php @@ -0,0 +1,40 @@ +bigIncrements('id'); + $table->bigInteger('user_id')->unsigned()->index(); + $table->bigInteger('item_id')->unsigned()->nullable(); + $table->string('item_type')->nullable(); + $table->string('action')->nullable(); + $table->string('message')->nullable(); + $table->string('link')->nullable(); + $table->string('ip_address')->nullable(); + $table->string('user_agent')->nullable(); + $table->json('metadata')->nullable(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('account_logs'); + } +} diff --git a/database/migrations/2018_07_12_054015_create_user_settings_table.php b/database/migrations/2018_07_12_054015_create_user_settings_table.php new file mode 100644 index 000000000..21d672000 --- /dev/null +++ b/database/migrations/2018_07_12_054015_create_user_settings_table.php @@ -0,0 +1,50 @@ +bigIncrements('id'); + $table->bigInteger('user_id')->unsigned()->unique(); + $table->string('role')->default('user'); + $table->boolean('crawlable')->default(true); + $table->boolean('show_guests')->default(true); + $table->boolean('show_discover')->default(true); + $table->boolean('public_dm')->default(false); + $table->boolean('hide_cw_search')->default(true); + $table->boolean('hide_blocked_search')->default(true); + $table->boolean('always_show_cw')->default(false); + $table->boolean('compose_media_descriptions')->default(false); + $table->boolean('reduce_motion')->default(false); + $table->boolean('optimize_screen_reader')->default(false); + $table->boolean('high_contrast_mode')->default(false); + $table->boolean('video_autoplay')->default(false); + $table->boolean('send_email_new_follower')->default(false); + $table->boolean('send_email_new_follower_request')->default(true); + $table->boolean('send_email_on_share')->default(false); + $table->boolean('send_email_on_like')->default(false); + $table->boolean('send_email_on_mention')->default(false); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('user_settings'); + } +} diff --git a/database/migrations/2018_07_15_011916_add_2fa_to_users_table.php b/database/migrations/2018_07_15_011916_add_2fa_to_users_table.php new file mode 100644 index 000000000..ad77b5c60 --- /dev/null +++ b/database/migrations/2018_07_15_011916_add_2fa_to_users_table.php @@ -0,0 +1,38 @@ +boolean('2fa_enabled')->default(false); + $table->string('2fa_secret')->nullable(); + $table->json('2fa_backup_codes')->nullable(); + $table->timestamp('2fa_setup_at')->nullable(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('users', function (Blueprint $table) { + $table->dropColumn('2fa_enabled'); + $table->dropColumn('2fa_secret'); + $table->dropColumn('2fa_backup_codes'); + $table->dropColumn('2fa_setup_at'); + }); + } +} diff --git a/database/migrations/2018_07_15_013106_create_user_filters_table.php b/database/migrations/2018_07_15_013106_create_user_filters_table.php new file mode 100644 index 000000000..ef13bc981 --- /dev/null +++ b/database/migrations/2018_07_15_013106_create_user_filters_table.php @@ -0,0 +1,41 @@ +bigIncrements('id'); + $table->bigInteger('user_id')->unsigned()->index(); + $table->bigInteger('filterable_id')->unsigned(); + $table->string('filterable_type'); + $table->string('filter_type')->default('block')->index(); + $table->unique([ + 'user_id', + 'filterable_id', + 'filterable_type', + 'filter_type' + ], 'filter_unique'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('user_filters'); + } +} diff --git a/docker-compose.yml b/docker-compose.yml index 8e9ccd4c1..861dc0b9d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -46,7 +46,7 @@ services: - "mysql-data:/var/lib/mysql" redis: - image: redis:alpine + image: redis:4-alpine volumes: - "redis-data:/data" networks: diff --git a/public/css/app.css b/public/css/app.css index bff19b1fa..4114dad8e 100644 Binary files a/public/css/app.css and b/public/css/app.css differ diff --git a/public/img/favicon.png b/public/img/favicon.png new file mode 100644 index 000000000..ef5ab6f6c Binary files /dev/null and b/public/img/favicon.png differ diff --git a/public/img/fred1.gif b/public/img/fred1.gif new file mode 100644 index 000000000..b3a7e3257 Binary files /dev/null and b/public/img/fred1.gif differ diff --git a/public/img/pixelfed-icon-black.svg b/public/img/pixelfed-icon-black.svg new file mode 100644 index 000000000..ec26b5a32 Binary files /dev/null and b/public/img/pixelfed-icon-black.svg differ diff --git a/public/img/pixelfed-icon-color.svg b/public/img/pixelfed-icon-color.svg index c3a8625bd..67eba7d8e 100644 Binary files a/public/img/pixelfed-icon-color.svg and b/public/img/pixelfed-icon-color.svg differ diff --git a/public/js/app.js b/public/js/app.js index 1d03a7288..65c2a63a6 100644 Binary files a/public/js/app.js and b/public/js/app.js differ diff --git a/public/js/timeline.js b/public/js/timeline.js index 4b14bcf4c..14cc9d76a 100644 Binary files a/public/js/timeline.js and b/public/js/timeline.js differ diff --git a/public/mix-manifest.json b/public/mix-manifest.json index 6eb24f257..17f77a698 100644 Binary files a/public/mix-manifest.json and b/public/mix-manifest.json differ diff --git a/resources/assets/js/bootstrap.js b/resources/assets/js/bootstrap.js index 4f95a589c..461ee9b50 100644 --- a/resources/assets/js/bootstrap.js +++ b/resources/assets/js/bootstrap.js @@ -1,13 +1,6 @@ - window._ = require('lodash'); window.Popper = require('popper.js').default; - -/** - * We'll load jQuery and the Bootstrap jQuery plugin which provides support - * for JavaScript based Bootstrap features such as modals and tabs. This - * code may be modified to fit the specific needs of your application. - */ - +import swal from 'sweetalert'; try { window.pixelfed = {}; window.$ = window.jQuery = require('jquery'); @@ -16,6 +9,7 @@ try { window.filesize = require('filesize'); window.typeahead = require('./lib/typeahead'); window.Bloodhound = require('./lib/bloodhound'); + window.Vue = require('vue'); require('./components/localstorage'); require('./components/likebutton'); @@ -23,45 +17,21 @@ try { require('./components/searchform'); require('./components/bookmarkform'); require('./components/statusform'); + + Vue.component( + 'follow-suggestions', + require('./components/FollowSuggestions.vue') + ); } catch (e) {} -/** - * We'll load the axios HTTP library which allows us to easily issue requests - * to our Laravel back-end. This library automatically handles sending the - * CSRF token as a header based on the value of the "XSRF" token cookie. - */ - +$('[data-toggle="tooltip"]').tooltip(); window.axios = require('axios'); window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest'; - -/** - * Next we will register the CSRF Token as a common header with Axios so that - * all outgoing HTTP requests automatically have it attached. This is just - * a simple convenience so we don't have to attach every token manually. - */ - let token = document.head.querySelector('meta[name="csrf-token"]'); - if (token) { window.axios.defaults.headers.common['X-CSRF-TOKEN'] = token.content; } else { console.error('CSRF token not found: https://laravel.com/docs/csrf#csrf-x-csrf-token'); } -/** - * Echo exposes an expressive API for subscribing to channels and listening - * for events that are broadcast by Laravel. Echo and event broadcasting - * allows your team to easily build robust real-time web applications. - */ - -// import Echo from 'laravel-echo' - -// window.Pusher = require('pusher-js'); - -// window.Echo = new Echo({ -// broadcaster: 'pusher', -// key: process.env.MIX_PUSHER_APP_KEY, -// cluster: process.env.MIX_PUSHER_APP_CLUSTER, -// encrypted: true -// }); diff --git a/resources/assets/js/components/FollowSuggestions.vue b/resources/assets/js/components/FollowSuggestions.vue new file mode 100644 index 000000000..da45954d9 --- /dev/null +++ b/resources/assets/js/components/FollowSuggestions.vue @@ -0,0 +1,50 @@ + + + + + \ No newline at end of file diff --git a/resources/assets/js/components/commentform.js b/resources/assets/js/components/commentform.js index a2aa50394..c9d4f9477 100644 --- a/resources/assets/js/components/commentform.js +++ b/resources/assets/js/components/commentform.js @@ -1,6 +1,12 @@ $(document).ready(function() { - $('.status-comment-focus').on('click', function(el) { + $('.status-card > .card-footer').each(function() { + $(this).addClass('d-none'); + }); + + $(document).on('click', '.status-comment-focus', function(el) { + var form = $(this).parents().eq(2).find('.card-footer'); + form.removeClass('d-none'); var el = $(this).parents().eq(2).find('input[name="comment"]'); el.focus(); }); @@ -31,7 +37,7 @@ $(document).ready(function() { var comment = '

' + username + ''+ reply + '1s

'; - comments.prepend(comment); + comments.append(comment); commentform.val(''); commentform.blur(); @@ -41,7 +47,5 @@ $(document).ready(function() { .catch(function (res) { }); - }); - }); \ No newline at end of file diff --git a/resources/assets/js/components/likebutton.js b/resources/assets/js/components/likebutton.js index 650f880e2..9e7206445 100644 --- a/resources/assets/js/components/likebutton.js +++ b/resources/assets/js/components/likebutton.js @@ -1,18 +1,17 @@ $(document).ready(function() { - if(!ls.get('likes')) { - axios.get('/api/v1/likes') - .then(function (res) { - ls.set('likes', res.data); - console.log(res); - }) - .catch(function (res) { - ls.set('likes', []); - }) + pixelfed.fetchLikes = () => { + axios.get('/api/v1/likes') + .then(function (res) { + ls.set('likes', res.data); + }) + .catch(function (res) { + ls.set('likes', []); + }) } - pixelfed.hydrateLikes = function() { + pixelfed.hydrateLikes = () => { var likes = ls.get('likes'); $('.like-form').each(function(i, el) { var el = $(el); @@ -20,11 +19,14 @@ $(document).ready(function() { var heart = el.find('.status-heart'); if(likes.indexOf(id) != -1) { - heart.removeClass('far fa-heart').addClass('fas fa-heart'); + heart.removeClass('text-dark').addClass('text-primary'); + } else { + heart.removeClass('text-primary').addClass('text-dark'); } }); }; + pixelfed.fetchLikes(); pixelfed.hydrateLikes(); $(document).on('submit', '.like-form', function(e) { @@ -33,6 +35,8 @@ $(document).ready(function() { var id = el.data('id'); axios.post('/i/like', {item: id}) .then(function (res) { + pixelfed.fetchLikes(); + pixelfed.hydrateLikes(); var likes = ls.get('likes'); var action = false; var counter = el.parents().eq(1).find('.like-count'); @@ -40,14 +44,14 @@ $(document).ready(function() { var heart = el.find('.status-heart'); if(likes.indexOf(id) > -1) { - heart.removeClass('fas fa-heart').addClass('far fa-heart'); + heart.removeClass('text-primary').addClass('text-dark'); likes = likes.filter(function(item) { return item !== id }); counter.text(count); action = 'unlike'; } else { - heart.removeClass('far fa-heart').addClass('fas fa-heart'); + heart.removeClass('text-dark').addClass('text-primary'); likes.push(id); counter.text(count); action = 'like'; diff --git a/resources/assets/js/components/passport/AuthorizedClients.vue b/resources/assets/js/components/passport/AuthorizedClients.vue new file mode 100644 index 000000000..11a068922 --- /dev/null +++ b/resources/assets/js/components/passport/AuthorizedClients.vue @@ -0,0 +1,107 @@ + + + + + diff --git a/resources/assets/js/components/passport/Clients.vue b/resources/assets/js/components/passport/Clients.vue new file mode 100644 index 000000000..5cd68f26d --- /dev/null +++ b/resources/assets/js/components/passport/Clients.vue @@ -0,0 +1,350 @@ + + + + + diff --git a/resources/assets/js/components/passport/PersonalAccessTokens.vue b/resources/assets/js/components/passport/PersonalAccessTokens.vue new file mode 100644 index 000000000..14541a292 --- /dev/null +++ b/resources/assets/js/components/passport/PersonalAccessTokens.vue @@ -0,0 +1,298 @@ + + + + + diff --git a/resources/assets/js/components/statusform.js b/resources/assets/js/components/statusform.js index 636afbca5..9aa1e9e94 100644 --- a/resources/assets/js/components/statusform.js +++ b/resources/assets/js/components/statusform.js @@ -1,9 +1,5 @@ $(document).ready(function() { - $('#statusForm .btn-filter-select').on('click', function(e) { - let el = $(this); - }); - pixelfed.create = {}; pixelfed.filters = {}; pixelfed.create.hasGeneratedSelect = false; @@ -78,7 +74,7 @@ $(document).ready(function() { pixelfed.create.hasGeneratedSelect = true; } - $('#fileInput').on('change', function() { + $(document).on('change', '#fileInput', function() { previewImage(this); $('#statusForm .form-filters.d-none').removeClass('d-none'); $('#statusForm .form-preview.d-none').removeClass('d-none'); @@ -88,23 +84,43 @@ $(document).ready(function() { } }); - $('#filterSelectDropdown').on('change', function() { + $(document).on('change', '#filterSelectDropdown', function() { let el = $(this); let filter = el.val(); let oldFilter = pixelfed.create.currentFilterClass; if(filter == 'none') { - $('.filterContainer').removeClass(oldFilter); - pixelfed.create.currentFilterClass = false; - pixelfed.create.currentFilterName = 'None'; - $('.form-group.form-preview .form-text').text('Current Filter: No filter selected'); - return; + $('input[name=filter_class]').val(''); + $('input[name=filter_name]').val(''); + $('.filterContainer').removeClass(oldFilter); + pixelfed.create.currentFilterClass = false; + pixelfed.create.currentFilterName = 'None'; + $('.form-group.form-preview .form-text').text('Current Filter: No filter selected'); + return; + } else { + $('.filterContainer').removeClass(oldFilter).addClass(filter); + pixelfed.create.currentFilterClass = filter; + pixelfed.create.currentFilterName = el.find(':selected').text(); + $('.form-group.form-preview .form-text').text('Current Filter: ' + pixelfed.create.currentFilterName); + $('input[name=filter_class]').val(pixelfed.create.currentFilterClass); + $('input[name=filter_name]').val(pixelfed.create.currentFilterName); + return; } - $('.filterContainer').removeClass(oldFilter).addClass(filter); - pixelfed.create.currentFilterClass = filter; - pixelfed.create.currentFilterName = el.find(':selected').text(); - $('.form-group.form-preview .form-text').text('Current Filter: ' + pixelfed.create.currentFilterName); - $('input[name=filter_class]').val(pixelfed.create.currentFilterClass); - $('input[name=filter_name]').val(pixelfed.create.currentFilterName); }); + $(document).on('keyup keydown', '#statusForm textarea[name=caption]', function() { + const el = $(this); + const len = el.val().length; + const limit = el.data('limit'); + if(len > limit) { + const diff = limit - len; + $('#statusForm .caption-counter').text(diff).addClass('text-danger'); + } else { + $('#statusForm .caption-counter').text(len).removeClass('text-danger'); + } + }); + + $(document).on('focus', '#statusForm textarea[name=caption]', function() { + const el = $(this); + el.attr('rows', '3'); + }); }); \ No newline at end of file diff --git a/resources/assets/js/lib/bloodhound.js b/resources/assets/js/lib/bloodhound.js old mode 100644 new mode 100755 index 6095f2915..3cb1c4e5f --- a/resources/assets/js/lib/bloodhound.js +++ b/resources/assets/js/lib/bloodhound.js @@ -1,18 +1,18 @@ /*! - * typeahead.js 0.11.1 + * typeahead.js 1.2.0 * https://github.com/twitter/typeahead.js - * Copyright 2013-2015 Twitter, Inc. and other contributors; Licensed MIT + * Copyright 2013-2017 Twitter, Inc. and other contributors; Licensed MIT */ (function(root, factory) { if (typeof define === "function" && define.amd) { - define("bloodhound", [ "jquery" ], function(a0) { + define([ "jquery" ], function(a0) { return root["Bloodhound"] = factory(a0); }); } else if (typeof exports === "object") { module.exports = factory(require("jquery")); } else { - root["Bloodhound"] = factory(jQuery); + root["Bloodhound"] = factory(root["jQuery"]); } })(this, function($) { var _ = function() { @@ -148,18 +148,27 @@ stringify: function(val) { return _.isString(val) ? val : JSON.stringify(val); }, + guid: function() { + function _p8(s) { + var p = (Math.random().toString(16) + "000000000").substr(2, 8); + return s ? "-" + p.substr(0, 4) + "-" + p.substr(4, 4) : p; + } + return "tt-" + _p8() + _p8(true) + _p8(true) + _p8(); + }, noop: function() {} }; }(); - var VERSION = "0.11.1"; + var VERSION = "1.2.0"; var tokenizers = function() { "use strict"; return { nonword: nonword, whitespace: whitespace, + ngram: ngram, obj: { nonword: getObjTokenizer(nonword), - whitespace: getObjTokenizer(whitespace) + whitespace: getObjTokenizer(whitespace), + ngram: getObjTokenizer(ngram) } }; function whitespace(str) { @@ -170,6 +179,19 @@ str = _.toStr(str); return str ? str.split(/\W+/) : []; } + function ngram(str) { + str = _.toStr(str); + var tokens = [], word = ""; + _.each(str.split(""), function(char) { + if (char.match(/\s+/)) { + word = ""; + } else { + tokens.push(word + char); + word += char; + } + }); + return tokens; + } function getObjTokenizer(tokenizer) { return function setKey(keys) { keys = _.isArray(keys) ? keys : [].slice.call(arguments, 0); @@ -341,9 +363,10 @@ }(); var Transport = function() { "use strict"; - var pendingRequestsCount = 0, pendingRequests = {}, maxPendingRequests = 6, sharedCache = new LruCache(10); + var pendingRequestsCount = 0, pendingRequests = {}, sharedCache = new LruCache(10); function Transport(o) { o = o || {}; + this.maxPendingRequests = o.maxPendingRequests || 6; this.cancelled = false; this.lastReq = null; this._send = o.transport; @@ -351,7 +374,7 @@ this._cache = o.cache === false ? new LruCache(0) : sharedCache; } Transport.setMaxPendingRequests = function setMaxPendingRequests(num) { - maxPendingRequests = num; + this.maxPendingRequests = num; }; Transport.resetCache = function resetCache() { sharedCache.reset(); @@ -369,7 +392,7 @@ } if (jqXhr = pendingRequests[fingerprint]) { jqXhr.done(done).fail(fail); - } else if (pendingRequestsCount < maxPendingRequests) { + } else if (pendingRequestsCount < this.maxPendingRequests) { pendingRequestsCount++; pendingRequests[fingerprint] = this._send(o).done(done).fail(fail).always(always); } else { @@ -423,6 +446,7 @@ this.identify = o.identify || _.stringify; this.datumTokenizer = o.datumTokenizer; this.queryTokenizer = o.queryTokenizer; + this.matchAnyQueryToken = o.matchAnyQueryToken; this.reset(); } _.mixin(SearchIndex.prototype, { @@ -459,7 +483,7 @@ tokens = normalizeTokens(this.queryTokenizer(query)); _.each(tokens, function(token) { var node, chars, ch, ids; - if (matches && matches.length === 0) { + if (matches && matches.length === 0 && !that.matchAnyQueryToken) { return false; } node = that.trie; @@ -471,8 +495,10 @@ ids = node[IDS].slice(0); matches = matches ? getIntersection(matches, ids) : ids; } else { - matches = []; - return false; + if (!that.matchAnyQueryToken) { + matches = []; + return false; + } } }); return matches ? _.map(unique(matches), function(id) { @@ -614,10 +640,12 @@ this.url = o.url; this.prepare = o.prepare; this.transform = o.transform; + this.indexResponse = o.indexResponse; this.transport = new Transport({ cache: o.cache, limiter: o.limiter, - transport: o.transport + transport: o.transport, + maxPendingRequests: o.maxPendingRequests }); } _.mixin(Remote.prototype, { @@ -655,7 +683,9 @@ identify: _.stringify, datumTokenizer: null, queryTokenizer: null, + matchAnyQueryToken: false, sufficient: 5, + indexRemote: false, sorter: null, local: [], prefetch: null, @@ -744,7 +774,7 @@ } else if (o.wildcard) { prepare = prepareByWildcard; } else { - prepare = idenityPrepare; + prepare = identityPrepare; } return prepare; function prepareByReplace(query, settings) { @@ -755,7 +785,7 @@ settings.url = settings.url.replace(wildcard, encodeURIComponent(query)); return settings; } - function idenityPrepare(query, settings) { + function identityPrepare(query, settings) { return settings; } } @@ -806,6 +836,7 @@ this.sorter = o.sorter; this.identify = o.identify; this.sufficient = o.sufficient; + this.indexRemote = o.indexRemote; this.local = o.local; this.remote = o.remote ? new Remote(o.remote) : null; this.prefetch = o.prefetch ? new Prefetch(o.prefetch) : null; @@ -875,6 +906,8 @@ }, search: function search(query, sync, async) { var that = this, local; + sync = sync || _.noop; + async = async || _.noop; local = this.sorter(this.index.search(query)); sync(this.remote ? local.slice() : local); if (this.remote && local.length < this.sufficient) { @@ -890,7 +923,8 @@ return that.identify(r) === that.identify(l); }) && nonDuplicates.push(r); }); - async && async(nonDuplicates); + that.indexRemote && that.add(nonDuplicates); + async(nonDuplicates); } }, all: function all() { diff --git a/resources/assets/js/lib/typeahead.js b/resources/assets/js/lib/typeahead.js old mode 100644 new mode 100755 index 2b0892897..f80bb192b --- a/resources/assets/js/lib/typeahead.js +++ b/resources/assets/js/lib/typeahead.js @@ -1,18 +1,18 @@ /*! - * typeahead.js 0.11.1 + * typeahead.js 1.2.0 * https://github.com/twitter/typeahead.js - * Copyright 2013-2015 Twitter, Inc. and other contributors; Licensed MIT + * Copyright 2013-2017 Twitter, Inc. and other contributors; Licensed MIT */ (function(root, factory) { if (typeof define === "function" && define.amd) { - define("typeahead.js", [ "jquery" ], function(a0) { + define([ "jquery" ], function(a0) { return factory(a0); }); } else if (typeof exports === "object") { module.exports = factory(require("jquery")); } else { - factory(jQuery); + factory(root["jQuery"]); } })(this, function($) { var _ = function() { @@ -148,6 +148,13 @@ stringify: function(val) { return _.isString(val) ? val : JSON.stringify(val); }, + guid: function() { + function _p8(s) { + var p = (Math.random().toString(16) + "000000000").substr(2, 8); + return s ? "-" + p.substr(0, 4) + "-" + p.substr(4, 4) : p; + } + return "tt-" + _p8() + _p8(true) + _p8(true) + _p8(); + }, noop: function() {} }; }(); @@ -189,7 +196,7 @@ function buildHtml(c) { return { wrapper: '', - menu: '
' + menu: '
' }; } function buildSelectors(classes) { @@ -264,10 +271,8 @@ } _.mixin(EventBus.prototype, { _trigger: function(type, args) { - var $e; - $e = $.Event(namespace + type); - (args = args || []).unshift($e); - this.$el.trigger.apply(this.$el, args); + var $e = $.Event(namespace + type); + this.$el.trigger.call(this.$el, $e, args || []); return $e; }, before: function(type) { @@ -384,7 +389,36 @@ tagName: "strong", className: null, wordsOnly: false, - caseSensitive: false + caseSensitive: false, + diacriticInsensitive: false + }; + var accented = { + A: "[AaªÀ-Åà-åĀ-ąǍǎȀ-ȃȦȧᴬᵃḀḁẚẠ-ảₐ℀℁℻⒜Ⓐⓐ㍱-㍴㎀-㎄㎈㎉㎩-㎯㏂㏊㏟㏿Aa]", + B: "[BbᴮᵇḂ-ḇℬ⒝Ⓑⓑ㍴㎅-㎇㏃㏈㏔㏝Bb]", + C: "[CcÇçĆ-čᶜ℀ℂ℃℅℆ℭⅭⅽ⒞Ⓒⓒ㍶㎈㎉㎝㎠㎤㏄-㏇Cc]", + D: "[DdĎďDŽ-džDZ-dzᴰᵈḊ-ḓⅅⅆⅮⅾ⒟Ⓓⓓ㋏㍲㍷-㍹㎗㎭-㎯㏅㏈Dd]", + E: "[EeÈ-Ëè-ëĒ-ěȄ-ȇȨȩᴱᵉḘ-ḛẸ-ẽₑ℡ℯℰⅇ⒠Ⓔⓔ㉐㋍㋎Ee]", + F: "[FfᶠḞḟ℉ℱ℻⒡Ⓕⓕ㎊-㎌㎙ff-fflFf]", + G: "[GgĜ-ģǦǧǴǵᴳᵍḠḡℊ⒢Ⓖⓖ㋌㋍㎇㎍-㎏㎓㎬㏆㏉㏒㏿Gg]", + H: "[HhĤĥȞȟʰᴴḢ-ḫẖℋ-ℎ⒣Ⓗⓗ㋌㍱㎐-㎔㏊㏋㏗Hh]", + I: "[IiÌ-Ïì-ïĨ-İIJijǏǐȈ-ȋᴵᵢḬḭỈ-ịⁱℐℑℹⅈⅠ-ⅣⅥ-ⅨⅪⅫⅰ-ⅳⅵ-ⅸⅺⅻ⒤Ⓘⓘ㍺㏌㏕fiffiIi]", + J: "[JjIJ-ĵLJ-njǰʲᴶⅉ⒥ⒿⓙⱼJj]", + K: "[KkĶķǨǩᴷᵏḰ-ḵK⒦Ⓚⓚ㎄㎅㎉㎏㎑㎘㎞㎢㎦㎪㎸㎾㏀㏆㏍-㏏Kk]", + L: "[LlĹ-ŀLJ-ljˡᴸḶḷḺ-ḽℒℓ℡Ⅼⅼ⒧Ⓛⓛ㋏㎈㎉㏐-㏓㏕㏖㏿flfflLl]", + M: "[MmᴹᵐḾ-ṃ℠™ℳⅯⅿ⒨Ⓜⓜ㍷-㍹㎃㎆㎎㎒㎖㎙-㎨㎫㎳㎷㎹㎽㎿㏁㏂㏎㏐㏔-㏖㏘㏙㏞㏟Mm]", + N: "[NnÑñŃ-ʼnNJ-njǸǹᴺṄ-ṋⁿℕ№⒩Ⓝⓝ㎁㎋㎚㎱㎵㎻㏌㏑Nn]", + O: "[OoºÒ-Öò-öŌ-őƠơǑǒǪǫȌ-ȏȮȯᴼᵒỌ-ỏₒ℅№ℴ⒪Ⓞⓞ㍵㏇㏒㏖Oo]", + P: "[PpᴾᵖṔ-ṗℙ⒫Ⓟⓟ㉐㍱㍶㎀㎊㎩-㎬㎰㎴㎺㏋㏗-㏚Pp]", + Q: "[Qqℚ⒬Ⓠⓠ㏃Qq]", + R: "[RrŔ-řȐ-ȓʳᴿᵣṘ-ṛṞṟ₨ℛ-ℝ⒭Ⓡⓡ㋍㍴㎭-㎯㏚㏛Rr]", + S: "[SsŚ-šſȘșˢṠ-ṣ₨℁℠⒮Ⓢⓢ㎧㎨㎮-㎳㏛㏜stSs]", + T: "[TtŢ-ťȚțᵀᵗṪ-ṱẗ℡™⒯Ⓣⓣ㉐㋏㎔㏏ſtstTt]", + U: "[UuÙ-Üù-üŨ-ųƯưǓǔȔ-ȗᵁᵘᵤṲ-ṷỤ-ủ℆⒰Ⓤⓤ㍳㍺Uu]", + V: "[VvᵛᵥṼ-ṿⅣ-Ⅷⅳ-ⅷ⒱Ⓥⓥⱽ㋎㍵㎴-㎹㏜㏞Vv]", + W: "[WwŴŵʷᵂẀ-ẉẘ⒲Ⓦⓦ㎺-㎿㏝Ww]", + X: "[XxˣẊ-ẍₓ℻Ⅸ-Ⅻⅸ-ⅻ⒳Ⓧⓧ㏓Xx]", + Y: "[YyÝýÿŶ-ŸȲȳʸẎẏẙỲ-ỹ⒴Ⓨⓨ㏉Yy]", + Z: "[ZzŹ-žDZ-dzᶻẐ-ẕℤℨ⒵Ⓩⓩ㎐-㎔Zz]" }; return function hightlight(o) { var regex; @@ -393,7 +427,7 @@ return; } o.pattern = _.isArray(o.pattern) ? o.pattern : [ o.pattern ]; - regex = getRegex(o.pattern, o.caseSensitive, o.wordsOnly); + regex = getRegex(o.pattern, o.caseSensitive, o.wordsOnly, o.diacriticInsensitive); traverse(o.node, hightlightTextNode); function hightlightTextNode(textNode) { var match, patternNode, wrapperNode; @@ -419,10 +453,17 @@ } } }; - function getRegex(patterns, caseSensitive, wordsOnly) { + function accent_replacer(chr) { + return accented[chr.toUpperCase()] || chr; + } + function getRegex(patterns, caseSensitive, wordsOnly, diacriticInsensitive) { var escapedPatterns = [], regexStr; for (var i = 0, len = patterns.length; i < len; i++) { - escapedPatterns.push(_.escapeRegExChars(patterns[i])); + var escapedWord = _.escapeRegExChars(patterns[i]); + if (diacriticInsensitive) { + escapedWord = escapedWord.replace(/\S/g, accent_replacer); + } + escapedPatterns.push(escapedWord); } regexStr = wordsOnly ? "\\b(" + escapedPatterns.join("|") + ")\\b" : "(" + escapedPatterns.join("|") + ")"; return caseSensitive ? new RegExp(regexStr) : new RegExp(regexStr, "i"); @@ -448,6 +489,14 @@ www.mixin(this); this.$hint = $(o.hint); this.$input = $(o.input); + this.$input.attr({ + "aria-activedescendant": "", + "aria-owns": this.$input.attr("id") + "_listbox", + role: "combobox", + "aria-readonly": "true", + "aria-autocomplete": "list" + }); + $(www.menu).attr("id", this.$input.attr("id") + "_listbox"); this.query = this.$input.val(); this.queryWhenFocused = this.hasFocus() ? this.query : null; this.$overflowHelper = buildOverflowHelper(this.$input); @@ -455,6 +504,7 @@ if (this.$hint.length === 0) { this.setHint = this.getHint = this.clearHint = this.clearHintIfInvalid = _.noop; } + this.onSync("cursorchange", this._updateDescendent); } Input.normalizeQuery = function(str) { return _.toStr(str).replace(/^\s*/g, "").replace(/\s{2,}/g, " "); @@ -524,6 +574,9 @@ this.trigger("whitespaceChanged", this.query); } }, + _updateDescendent: function updateDescendent(event, id) { + this.$input.attr("aria-activedescendant", id); + }, bind: function() { var that = this, onBlur, onFocus, onKeydown, onInput; onBlur = _.bind(this._onBlur, this); @@ -647,6 +700,7 @@ "use strict"; var keys, nameGenerator; keys = { + dataset: "tt-selectable-dataset", val: "tt-selectable-display", obj: "tt-selectable-object" }; @@ -666,19 +720,20 @@ } www.mixin(this); this.highlight = !!o.highlight; - this.name = o.name || nameGenerator(); + this.name = _.toStr(o.name || nameGenerator()); this.limit = o.limit || 5; this.displayFn = getDisplayFn(o.display || o.displayKey); this.templates = getTemplates(o.templates, this.displayFn); this.source = o.source.__ttAdapter ? o.source.__ttAdapter() : o.source; this.async = _.isUndefined(o.async) ? this.source.length > 2 : !!o.async; this._resetLastSuggestion(); - this.$el = $(o.node).addClass(this.classes.dataset).addClass(this.classes.dataset + "-" + this.name); + this.$el = $(o.node).attr("role", "presentation").addClass(this.classes.dataset).addClass(this.classes.dataset + "-" + this.name); } Dataset.extractData = function extractData(el) { var $el = $(el); if ($el.data(keys.obj)) { return { + dataset: $el.data(keys.dataset) || "", val: $el.data(keys.val) || "", obj: $el.data(keys.obj) || null }; @@ -697,7 +752,7 @@ } else { this._empty(); } - this.trigger("rendered", this.name, suggestions, false); + this.trigger("rendered", suggestions, false, this.name); }, _append: function append(query, suggestions) { suggestions = suggestions || []; @@ -708,7 +763,7 @@ } else if (!this.$lastSuggestion.length && this.templates.notFound) { this._renderNotFound(query); } - this.trigger("rendered", this.name, suggestions, true); + this.trigger("rendered", suggestions, true, this.name); }, _renderSuggestions: function renderSuggestions(query, suggestions) { var $fragment; @@ -749,7 +804,7 @@ _.each(suggestions, function getSuggestionNode(suggestion) { var $el, context; context = that._injectQuery(query, suggestion); - $el = $(that.templates.suggestion(context)).data(keys.obj, suggestion).data(keys.val, that.displayFn(suggestion)).addClass(that.classes.suggestion + " " + that.classes.selectable); + $el = $(that.templates.suggestion(context)).data(keys.dataset, that.name).data(keys.obj, suggestion).data(keys.val, that.displayFn(suggestion)).addClass(that.classes.suggestion + " " + that.classes.selectable); fragment.appendChild($el[0]); }); this.highlight && highlight({ @@ -787,7 +842,7 @@ this.cancel = function cancel() { canceled = true; that.cancel = $.noop; - that.async && that.trigger("asyncCanceled", query); + that.async && that.trigger("asyncCanceled", query, that.name); }; this.source(query, sync, async); !syncCalled && sync([]); @@ -800,16 +855,17 @@ rendered = suggestions.length; that._overwrite(query, suggestions); if (rendered < that.limit && that.async) { - that.trigger("asyncRequested", query); + that.trigger("asyncRequested", query, that.name); } } function async(suggestions) { suggestions = suggestions || []; if (!canceled && rendered < that.limit) { that.cancel = $.noop; - rendered += suggestions.length; - that._append(query, suggestions.slice(0, that.limit - rendered)); - that.async && that.trigger("asyncReceived", query); + var idx = Math.abs(rendered - that.limit); + rendered += idx; + that._append(query, suggestions.slice(0, idx)); + that.async && that.trigger("asyncReceived", query, that.name); } } }, @@ -843,7 +899,7 @@ suggestion: templates.suggestion || suggestionTemplate }; function suggestionTemplate(context) { - return $("
").text(displayFn(context)); + return $('
').attr("id", _.guid()).text(displayFn(context)); } } function isValidName(str) { @@ -884,10 +940,11 @@ this.trigger.apply(this, arguments); }, _allDatasetsEmpty: function allDatasetsEmpty() { - return _.every(this.datasets, isDatasetEmpty); - function isDatasetEmpty(dataset) { - return dataset.isEmpty(); - } + return _.every(this.datasets, _.bind(function isDatasetEmpty(dataset) { + var isEmpty = dataset.isEmpty(); + this.$node.attr("aria-expanded", !isEmpty); + return isEmpty; + }, this)); }, _getSelectables: function getSelectables() { return this.$node.find(this.selectors.selectable); @@ -912,6 +969,12 @@ var that = this, onSelectableClick; onSelectableClick = _.bind(this._onSelectableClick, this); this.$node.on("click.tt", this.selectors.selectable, onSelectableClick); + this.$node.on("mouseover", this.selectors.selectable, function() { + that.setCursor($(this)); + }); + this.$node.on("mouseleave", function() { + that._removeCursor(); + }); _.each(this.datasets, function(dataset) { dataset.onSync("asyncRequested", that._propagate, that).onSync("asyncCanceled", that._propagate, that).onSync("asyncReceived", that._propagate, that).onSync("rendered", that._onRendered, that).onSync("cleared", that._onCleared, that); }); @@ -921,9 +984,11 @@ return this.$node.hasClass(this.classes.open); }, open: function open() { + this.$node.scrollTop(0); this.$node.addClass(this.classes.open); }, close: function close() { + this.$node.attr("aria-expanded", false); this.$node.removeClass(this.classes.open); this._removeCursor(); }, @@ -988,6 +1053,55 @@ }); return Menu; }(); + var Status = function() { + "use strict"; + function Status(options) { + this.$el = $("", { + role: "status", + "aria-live": "polite" + }).css({ + position: "absolute", + padding: "0", + border: "0", + height: "1px", + width: "1px", + "margin-bottom": "-1px", + "margin-right": "-1px", + overflow: "hidden", + clip: "rect(0 0 0 0)", + "white-space": "nowrap" + }); + options.$input.after(this.$el); + _.each(options.menu.datasets, _.bind(function(dataset) { + if (dataset.onSync) { + dataset.onSync("rendered", _.bind(this.update, this)); + dataset.onSync("cleared", _.bind(this.cleared, this)); + } + }, this)); + } + _.mixin(Status.prototype, { + update: function update(event, suggestions) { + var length = suggestions.length; + var words; + if (length === 1) { + words = { + result: "result", + is: "is" + }; + } else { + words = { + result: "results", + is: "are" + }; + } + this.$el.text(length + " " + words.result + " " + words.is + " available, use up and down arrow keys to navigate."); + }, + cleared: function() { + this.$el.text(""); + } + }); + return Status; + }(); var DefaultMenu = function() { "use strict"; var s = Menu.prototype; @@ -1052,6 +1166,7 @@ this.input = o.input; this.menu = o.menu; this.enabled = true; + this.autoselect = !!o.autoselect; this.active = false; this.input.hasFocus() && this.activate(); this.dir = this.input.getLangDir(); @@ -1098,8 +1213,12 @@ _onDatasetCleared: function onDatasetCleared() { this._updateHint(); }, - _onDatasetRendered: function onDatasetRendered(type, dataset, suggestions, async) { + _onDatasetRendered: function onDatasetRendered(type, suggestions, async, dataset) { this._updateHint(); + if (this.autoselect) { + var cursorClass = this.selectors.cursor.substr(1); + this.menu.$node.find(this.selectors.suggestion).first().addClass(cursorClass); + } this.eventBus.trigger("render", suggestions, async, dataset); }, _onAsyncRequested: function onAsyncRequested(type, dataset, query) { @@ -1122,7 +1241,15 @@ _onEnterKeyed: function onEnterKeyed(type, $e) { var $selectable; if ($selectable = this.menu.getActiveSelectable()) { - this.select($selectable) && $e.preventDefault(); + if (this.select($selectable)) { + $e.preventDefault(); + $e.stopPropagation(); + } + } else if (this.autoselect) { + if (this.select(this.menu.getTopSelectable())) { + $e.preventDefault(); + $e.stopPropagation(); + } } }, _onTabKeyed: function onTabKeyed(type, $e) { @@ -1144,12 +1271,12 @@ }, _onLeftKeyed: function onLeftKeyed() { if (this.dir === "rtl" && this.input.isCursorAtEnd()) { - this.autocomplete(this.menu.getTopSelectable()); + this.autocomplete(this.menu.getActiveSelectable() || this.menu.getTopSelectable()); } }, _onRightKeyed: function onRightKeyed() { if (this.dir === "ltr" && this.input.isCursorAtEnd()) { - this.autocomplete(this.menu.getTopSelectable()); + this.autocomplete(this.menu.getActiveSelectable() || this.menu.getTopSelectable()); } }, _onQueryChanged: function onQueryChanged(e, query) { @@ -1249,9 +1376,9 @@ }, select: function select($selectable) { var data = this.menu.getSelectableData($selectable); - if (data && !this.eventBus.before("select", data.obj)) { + if (data && !this.eventBus.before("select", data.obj, data.dataset)) { this.input.setQuery(data.val, true); - this.eventBus.trigger("select", data.obj); + this.eventBus.trigger("select", data.obj, data.dataset); this.close(); return true; } @@ -1262,21 +1389,24 @@ query = this.input.getQuery(); data = this.menu.getSelectableData($selectable); isValid = data && query !== data.val; - if (isValid && !this.eventBus.before("autocomplete", data.obj)) { + if (isValid && !this.eventBus.before("autocomplete", data.obj, data.dataset)) { this.input.setQuery(data.val); - this.eventBus.trigger("autocomplete", data.obj); + this.eventBus.trigger("autocomplete", data.obj, data.dataset); return true; } return false; }, moveCursor: function moveCursor(delta) { - var query, $candidate, data, payload, cancelMove; + var query, $candidate, data, suggestion, datasetName, cancelMove, id; query = this.input.getQuery(); $candidate = this.menu.selectableRelativeToCursor(delta); data = this.menu.getSelectableData($candidate); - payload = data ? data.obj : null; + suggestion = data ? data.obj : null; + datasetName = data ? data.dataset : null; + id = $candidate ? $candidate.attr("id") : null; + this.input.trigger("cursorchange", id); cancelMove = this._minLengthMet() && this.menu.update(query); - if (!cancelMove && !this.eventBus.before("cursorchange", payload)) { + if (!cancelMove && !this.eventBus.before("cursorchange", suggestion, datasetName)) { this.menu.setCursor($candidate); if (data) { this.input.setInputValue(data.val); @@ -1284,7 +1414,7 @@ this.input.resetInputValue(); this._updateHint(); } - this.eventBus.trigger("cursorchange", payload); + this.eventBus.trigger("cursorchange", suggestion, datasetName); return true; } return false; @@ -1322,7 +1452,7 @@ www = WWW(o.classNames); return this.each(attach); function attach() { - var $input, $wrapper, $hint, $menu, defaultHint, defaultMenu, eventBus, input, menu, typeahead, MenuConstructor; + var $input, $wrapper, $hint, $menu, defaultHint, defaultMenu, eventBus, input, menu, status, typeahead, MenuConstructor; _.each(datasets, function(d) { d.highlight = !!o.highlight; }); @@ -1353,11 +1483,16 @@ node: $menu, datasets: datasets }, www); + status = new Status({ + $input: $input, + menu: menu + }); typeahead = new Typeahead({ input: input, menu: menu, eventBus: eventBus, - minLength: o.minLength + minLength: o.minLength, + autoselect: o.autoselect }, www); $input.data(keys.www, www); $input.data(keys.typeahead, typeahead); @@ -1450,7 +1585,7 @@ return query; } else { ttEach(this, function(t) { - t.setVal(newVal); + t.setVal(_.toStr(newVal)); }); return this; } @@ -1481,8 +1616,10 @@ }); } function buildHintFromInput($input, www) { - return $input.clone().addClass(www.classes.hint).removeData().css(www.css.hint).css(getBackgroundStyles($input)).prop("readonly", true).removeAttr("id name placeholder required").attr({ - autocomplete: "off", + return $input.clone().addClass(www.classes.hint).removeData().css(www.css.hint).css(getBackgroundStyles($input)).prop({ + readonly: true, + required: false + }).removeAttr("id name placeholder").removeClass("required").attr({ spellcheck: "false", tabindex: -1 }); @@ -1495,7 +1632,6 @@ style: $input.attr("style") }); $input.addClass(www.classes.input).attr({ - autocomplete: "off", spellcheck: false }); try { diff --git a/resources/assets/js/timeline.js b/resources/assets/js/timeline.js index 69eec9af0..bae140581 100644 --- a/resources/assets/js/timeline.js +++ b/resources/assets/js/timeline.js @@ -1,13 +1,54 @@ $(document).ready(function() { $('.pagination').hide(); + $('.container.timeline-container').removeClass('d-none'); let elem = document.querySelector('.timeline-feed'); + pixelfed.fetchLikes(); + let infScroll = new InfiniteScroll( elem, { path: '.pagination__next', append: '.timeline-feed', status: '.page-load-status', history: false, }); + infScroll.on( 'append', function( response, path, items ) { pixelfed.hydrateLikes(); + $('.status-card > .card-footer').each(function() { + var el = $(this); + if(!el.hasClass('d-none') && !el.find('input[name="comment"]').val()) { + $(this).addClass('d-none'); + } + }); }); + + +}); + +$(document).on("DOMContentLoaded", function() { + + var active = false; + + var lazyLoad = function() { + if (active === false) { + active = true; + + var lazyImages = [].slice.call(document.querySelectorAll("img.lazy")); + lazyImages.forEach(function(lazyImage) { + if ((lazyImage.getBoundingClientRect().top <= window.innerHeight && lazyImage.getBoundingClientRect().bottom >= 0) && getComputedStyle(lazyImage).display !== "none") { + lazyImage.src = lazyImage.dataset.src; + lazyImage.srcset = lazyImage.dataset.srcset; + lazyImage.classList.remove("lazy"); + + lazyImages = lazyImages.filter(function(image) { + return image !== lazyImage; + }); + } + }); + + active = false; + }; + } + document.addEventListener("scroll", lazyLoad); + window.addEventListener("resize", lazyLoad); + window.addEventListener("orientationchange", lazyLoad); }); diff --git a/resources/assets/sass/custom.scss b/resources/assets/sass/custom.scss index 94c92f960..cb6394607 100644 --- a/resources/assets/sass/custom.scss +++ b/resources/assets/sass/custom.scss @@ -173,11 +173,6 @@ body, button, input, textarea { } } -.fas.fa-heart { - color: #f70ec4!important; -} - - @media (max-width: map-get($grid-breakpoints, "md")) { .border-md-left-0 { border-left:0!important @@ -266,3 +261,36 @@ body, button, input, textarea { animation-name: fadeInDown; animation-duration: 0.5s; } + +.card { + box-shadow: 0 2px 6px 0 hsla(0, 0%, 0%, 0.2); + border: none; +} + +.box-shadow { + box-shadow: 0 2px 6px 0 hsla(0, 0%, 0%, 0.2); +} + +.border-left-primary { + border-left: 3px solid $primary; +} + +.settings-nav .nav-item.active .nav-link { + font-weight: bold !important; +} + +details summary::-webkit-details-marker { + display: none!important; +} + +.details-animated > summary { + display: block; + background-color: #ECF0F1; + padding-top: 50px; + padding-bottom: 50px; + text-align: center; +} + +.details-animated[open] > summary { + display: none!important; +} diff --git a/resources/lang/de/navmenu.php b/resources/lang/de/navmenu.php index bfaf81d23..7b983aa42 100644 --- a/resources/lang/de/navmenu.php +++ b/resources/lang/de/navmenu.php @@ -9,5 +9,6 @@ return [ 'settings' => 'Einstellungen', 'admin' => 'Administration', 'logout' => 'Abmelden', + 'directMessages' => 'Privatnachrichten', ]; \ No newline at end of file diff --git a/resources/lang/de/profile.php b/resources/lang/de/profile.php index 294a90c85..db3a3c26c 100644 --- a/resources/lang/de/profile.php +++ b/resources/lang/de/profile.php @@ -5,4 +5,4 @@ return [ 'emptyFollowers' => 'Diesem Benutzer folgt noch niemand!', 'emptyFollowing' => 'Dieser Benutzer folgt noch niemanden!', 'savedWarning' => 'Nur du kannst sehen was du gespeichert hast', -]; \ No newline at end of file +]; diff --git a/resources/lang/en/navmenu.php b/resources/lang/en/navmenu.php index 427dc9dc8..3c6d2ebbb 100644 --- a/resources/lang/en/navmenu.php +++ b/resources/lang/en/navmenu.php @@ -9,5 +9,6 @@ return [ 'settings' => 'Settings', 'admin' => 'Admin', 'logout' => 'Logout', + 'directMessages' => 'Direct Messages', ]; \ No newline at end of file diff --git a/resources/lang/fr/navmenu.php b/resources/lang/fr/navmenu.php new file mode 100644 index 000000000..b6ab1f608 --- /dev/null +++ b/resources/lang/fr/navmenu.php @@ -0,0 +1,10 @@ + 'Voir mon profil', + 'myTimeline' => 'Ma chronologie', + 'publicTimeline' => 'Chronologie publique', + 'remoteFollow' => 'Suivre à distance', + 'settings' => 'Paramètres', + 'admin' => 'Admin', + 'logout' => ' Se déconnecter', +]; diff --git a/resources/lang/fr/notification.php b/resources/lang/fr/notification.php index 96d3164db..b89456855 100644 --- a/resources/lang/fr/notification.php +++ b/resources/lang/fr/notification.php @@ -2,4 +2,6 @@ return [ 'likedPhoto' => 'a aimé votre photo.', 'startedFollowingYou' => 'a commencé à vous suivre.', + 'commented' => 'commenté sur votre post.', + 'mentionedYou' => 'vous à mentionné.' ]; diff --git a/resources/lang/gl/navmenu.php b/resources/lang/gl/navmenu.php new file mode 100644 index 000000000..1e427d5cc --- /dev/null +++ b/resources/lang/gl/navmenu.php @@ -0,0 +1,13 @@ + 'Ver perfil', + 'myTimeline' => 'A miña liña temporal', + 'publicTimeline' => 'Liña temporal pública', + 'remoteFollow' => 'Seguimento remoto', + 'settings' => 'Axustes', + 'admin' => 'Admin', + 'logout' => 'Saír', + +]; diff --git a/resources/lang/oc/navmenu.php b/resources/lang/oc/navmenu.php new file mode 100644 index 000000000..f40edaab5 --- /dev/null +++ b/resources/lang/oc/navmenu.php @@ -0,0 +1,13 @@ + 'Veire mon perfil', + 'myTimeline' => 'Ma cronologia', + 'publicTimeline' => 'Cronologia publica', + 'remoteFollow' => 'Seguir a distància', + 'settings' => 'Paramètres', + 'admin' => 'Admin', + 'logout' => 'Desconnexion', + +]; diff --git a/resources/lang/oc/notification.php b/resources/lang/oc/notification.php index 38676b253..acffb33e9 100644 --- a/resources/lang/oc/notification.php +++ b/resources/lang/oc/notification.php @@ -4,5 +4,7 @@ return [ 'likedPhoto' => 'a aimat vòstra fòto.', 'startedFollowingYou' => 'a començat de vos seguir.', + 'commented' => 'a comentat vòstra publicacion.', + 'mentionedYou' => 'vos a mencionat.' ]; diff --git a/resources/lang/oc/profile.php b/resources/lang/oc/profile.php index ee70a7b00..473eac3bb 100644 --- a/resources/lang/oc/profile.php +++ b/resources/lang/oc/profile.php @@ -1,5 +1,8 @@ 'Aqueste utilizaire a pas encara de publicacion !', + 'emptyTimeline' => 'Aqueste utilizaire a pas encara de publicacion !', + 'emptyFollowers' => 'Aqueste utilizaire a pas encara pas seguidors !', + 'emptyFollowing' => 'Aqueste utilizaire sèc degun pel moment !', + 'savedWarning' => 'Solament vos vesètz çò que salvagardatz', ]; diff --git a/resources/lang/oc/timeline.php b/resources/lang/oc/timeline.php new file mode 100644 index 000000000..dee3660c3 --- /dev/null +++ b/resources/lang/oc/timeline.php @@ -0,0 +1,7 @@ + 'Vòstre cronologia es voida.' + +]; diff --git a/resources/lang/pl/navmenu.php b/resources/lang/pl/navmenu.php new file mode 100644 index 000000000..07a94950d --- /dev/null +++ b/resources/lang/pl/navmenu.php @@ -0,0 +1,13 @@ + 'Pokaż mój profil', + 'myTimeline' => 'Moja oś czasu', + 'publicTimeline' => 'Publiczna oś czasu', + 'remoteFollow' => 'Zdalne śledzenie', + 'settings' => 'Ustawienia', + 'admin' => 'Administrator', + 'logout' => 'Wyloguj się', + +]; diff --git a/resources/lang/pl/notification.php b/resources/lang/pl/notification.php index d9d2e412f..7e453397f 100644 --- a/resources/lang/pl/notification.php +++ b/resources/lang/pl/notification.php @@ -4,5 +4,7 @@ return [ 'likedPhoto' => 'polubił Twoje zdjęcie.', 'startedFollowingYou' => 'zaczął Cię obserwować.', + 'commented' => 'skomentował Twój wpis', + 'mentionedYou' => 'wspomniał o Tobie.' ]; diff --git a/resources/lang/vendor/backup/gl/notifications.php b/resources/lang/vendor/backup/gl/notifications.php new file mode 100644 index 000000000..8ab623b00 --- /dev/null +++ b/resources/lang/vendor/backup/gl/notifications.php @@ -0,0 +1,35 @@ + 'Mensaxe da exepción: :message', + 'exception_trace' => 'Traza da excepción: :trace', + 'exception_message_title' => 'Mensaxe da excepción', + 'exception_trace_title' => 'Traza da excepción', + + 'backup_failed_subject' => 'Erro no respaldo de :application_name', + 'backup_failed_body' => 'Importante: Algo fallou ao respaldar :application_name', + + 'backup_successful_subject' => 'Respaldo realizado correctamente :application_name', + 'backup_successful_subject_title' => 'Novo respaldo correcto!', + 'backup_successful_body' => 'Parabéns, un novo respaldo de :application_name foi realizado correctamente no disco con nome :disk_name.', + + 'cleanup_failed_subject' => 'Limpando os respaldos de :application_name failed.', + 'cleanup_failed_body' => 'Algo fallou mentras se limpaban os respaldos de :application_name', + + 'cleanup_successful_subject' => 'Limpeza correcta nos respaldos de :application_name', + 'cleanup_successful_subject_title' => 'Limpeza dos respaldos correcta!', + 'cleanup_successful_body' => 'Realizouse correctamente a limpeza dos respaldos de :application_name no disco con nome :disk_name.', + + 'healthy_backup_found_subject' => 'Os respaldos de :application_name no disco :disk_name están en bo estado', + 'healthy_backup_found_subject_title' => 'Os respaldos de :application_name están ben!', + 'healthy_backup_found_body' => 'Os respaldos de :application_name están en bo estado. Bo traballo!', + + 'unhealthy_backup_found_subject' => 'Importante: Os respaldos de :application_name non están en bo estado', + 'unhealthy_backup_found_subject_title' => 'Importante: Os respaldos de :application_name non están ben. :problem', + 'unhealthy_backup_found_body' => 'Os respaldos para :application_name no disco :disk_name non están ben.', + 'unhealthy_backup_found_not_reachable' => 'Non se puido alcanzar o disco de destino. :error', + 'unhealthy_backup_found_empty' => 'Non existen copias de respaldo para esta aplicación.', + 'unhealthy_backup_found_old' => 'O último respaldo realizouse en :date e considerase demasiado antigo.', + 'unhealthy_backup_found_unknown' => 'Lamentámolo, non se puido determinar unha causa concreta.', + 'unhealthy_backup_found_full' => 'Os respaldos están a utilizar demasiado espazo. A utilización actual de :disk_usage é maior que o límite establecido de :disk_limit.', +]; diff --git a/resources/views/account/activity.blade.php b/resources/views/account/activity.blade.php index dec6e16a8..2354b7ef0 100644 --- a/resources/views/account/activity.blade.php +++ b/resources/views/account/activity.blade.php @@ -3,16 +3,53 @@ @section('content')
+
+
+ +
+
+
+ +
    @if($notifications->count() > 0) @foreach($notifications as $notification) -
  • +
  • @switch($notification->action) @case('like') - + {!! $notification->rendered !!} diff --git a/resources/views/account/following.blade.php b/resources/views/account/following.blade.php new file mode 100644 index 000000000..8cb3dbf9e --- /dev/null +++ b/resources/views/account/following.blade.php @@ -0,0 +1,96 @@ +@extends('layouts.app') + +@section('content') +
    +
    +
    +
    + +
    +
    +
    +{{--
    + Notifications + + + + + View All + +
    --}} +
    +
      + + @if($notifications->count() > 0) + @foreach($notifications as $notification) + @php + if(!in_array($notification->action, ['like', 'follow'])) { + continue; + } + @endphp +
    • + @switch($notification->action) + + @case('like') + + + + + {{$notification->actor->username}} + + {{__('liked a post by')}} + + {{$notification->item->profile->username}} + + {{$notification->created_at->diffForHumans(null, true, true, true)}} + + + @if($notification->item_id && $notification->item_type == 'App\Status') + + @endif + + @break + + @case('follow') + + + + + {{$notification->actor->username}} + + {{__('started following')}} + + {{$notification->item->username}} + + {{$notification->created_at->diffForHumans(null, true, true, true)}} + + @break + + @endswitch +
    • + @endforeach +
    + +
    + {{$notifications->links()}} +
    + @else +
    +
    No unread notifications found.
    +
    + @endif +
    +
    +@endsection + +@push('scripts') + +@endpush diff --git a/resources/views/atom/user.blade.php b/resources/views/atom/user.blade.php index dd639dd38..c27ebcd8e 100644 --- a/resources/views/atom/user.blade.php +++ b/resources/views/atom/user.blade.php @@ -26,16 +26,16 @@ @foreach($items as $item) - <![CDATA[{{ $item->caption }}]]> + {{ $item->caption }} {{ url($item->id) }} profile->username }}]]> - caption !!}]]> + {{ $item->caption }} {{ $item->updated_at->toAtomString() }} @endforeach - \ No newline at end of file + diff --git a/resources/views/discover/tags/show.blade.php b/resources/views/discover/tags/show.blade.php index 3099b3e41..49429ebd5 100644 --- a/resources/views/discover/tags/show.blade.php +++ b/resources/views/discover/tags/show.blade.php @@ -7,7 +7,7 @@
    - +
    @@ -16,13 +16,13 @@ {{$tag->name}}

    - {{$count}} posts + {{$tag->posts_count}} posts

- @endsection @@ -39,3 +41,21 @@ @push('meta') @endpush + +@push('scripts') + +@endpush diff --git a/resources/views/errors/404.blade.php b/resources/views/errors/404.blade.php index ff1024304..a4891d46a 100644 --- a/resources/views/errors/404.blade.php +++ b/resources/views/errors/404.blade.php @@ -4,8 +4,9 @@
-
+

404 – Page Not Found

+
diff --git a/resources/views/layouts/app.blade.php b/resources/views/layouts/app.blade.php index b07d4a3f3..c30441fb2 100644 --- a/resources/views/layouts/app.blade.php +++ b/resources/views/layouts/app.blade.php @@ -7,21 +7,20 @@ - - {{ $title or config('app.name', 'Laravel') }} - - + {{ $title ?? config('app.name', 'Laravel') }} + + + - @stack('meta') - + @stack('styles') @@ -34,5 +33,14 @@ @include('layouts.partial.footer') @stack('scripts') + @if(Auth::check()) + + @endif diff --git a/resources/views/layouts/partial/footer.blade.php b/resources/views/layouts/partial/footer.blade.php index 90d9c07ce..d5dd33bcf 100644 --- a/resources/views/layouts/partial/footer.blade.php +++ b/resources/views/layouts/partial/footer.blade.php @@ -1,18 +1,16 @@