diff --git a/app/Console/Commands/StoryGC.php b/app/Console/Commands/StoryGC.php new file mode 100644 index 000000000..f6271bb15 --- /dev/null +++ b/app/Console/Commands/StoryGC.php @@ -0,0 +1,64 @@ +take(50)->get(); + + if($stories->count() == 0) { + exit; + } + + foreach($stories as $story) { + if(Storage::exists($story->path) == true) { + Storage::delete($story->path); + } + DB::transaction(function() use($story) { + StoryView::whereStoryId($story->id)->delete(); + $story->delete(); + }); + } + } +} diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php index 18057725e..1e3364afc 100644 --- a/app/Console/Kernel.php +++ b/app/Console/Kernel.php @@ -30,6 +30,7 @@ class Kernel extends ConsoleKernel $schedule->command('media:gc') ->hourly(); $schedule->command('horizon:snapshot')->everyFiveMinutes(); + $schedule->command('story:gc')->everyFiveMinutes(); } /** diff --git a/app/Http/Controllers/FollowerController.php b/app/Http/Controllers/FollowerController.php index f08fd689f..06267dd91 100644 --- a/app/Http/Controllers/FollowerController.php +++ b/app/Http/Controllers/FollowerController.php @@ -111,6 +111,10 @@ class FollowerController extends Controller Cache::forget('api:local:exp:rec:'.$user->id); Cache::forget('user:account:id:'.$target->user_id); Cache::forget('user:account:id:'.$user->user_id); + Cache::forget('px:profile:followers-v1.3:'.$user->id); + Cache::forget('px:profile:followers-v1.3:'.$target->id); + Cache::forget('px:profile:following-v1.3:'.$user->id); + Cache::forget('px:profile:following-v1.3:'.$target->id); return $target->url(); } diff --git a/app/Http/Controllers/InternalApiController.php b/app/Http/Controllers/InternalApiController.php index 9cfff70ce..541988bf5 100644 --- a/app/Http/Controllers/InternalApiController.php +++ b/app/Http/Controllers/InternalApiController.php @@ -244,7 +244,7 @@ class InternalApiController extends Controller 'cw' => 'nullable|boolean', 'visibility' => 'required|string|in:public,private,unlisted|min:2|max:10', 'place' => 'nullable', - 'comments_disabled' => 'nullable|boolean' + 'comments_disabled' => 'nullable' ]); if(config('costar.enabled') == true) { @@ -301,7 +301,7 @@ class InternalApiController extends Controller } if($request->filled('comments_disabled')) { - $status->comments_disabled = $request->input('comments_disabled'); + $status->comments_disabled = (bool) $request->input('comments_disabled'); } $status->caption = strip_tags($request->caption); @@ -314,10 +314,6 @@ class InternalApiController extends Controller $media->save(); } - // $resource = new Fractal\Resource\Collection($status->media()->orderBy('order')->get(), new StatusMediaContainerTransformer()); - // $mediaContainer = $this->fractal->createData($resource)->toArray(); - // $status->media_container = json_encode($mediaContainer); - $visibility = $profile->unlisted == true && $visibility == 'public' ? 'unlisted' : $visibility; $cw = $profile->cw == true ? true : $cw; $status->is_nsfw = $cw; diff --git a/app/Http/Controllers/ProfileController.php b/app/Http/Controllers/ProfileController.php index 7e66211d2..20e4d08ec 100644 --- a/app/Http/Controllers/ProfileController.php +++ b/app/Http/Controllers/ProfileController.php @@ -9,6 +9,7 @@ use View; use App\Follower; use App\FollowRequest; use App\Profile; +use App\Story; use App\User; use App\UserFilter; use League\Fractal; @@ -135,6 +136,21 @@ class ProfileController extends Controller return false; } + public static function accountCheck(Profile $profile) + { + switch ($profile->status) { + case 'disabled': + case 'suspended': + case 'delete': + return view('profile.disabled'); + break; + + default: + break; + } + return abort(404); + } + protected function blockedProfileCheck(Profile $profile) { $pid = Auth::user()->profile->id; @@ -215,4 +231,18 @@ class ProfileController extends Controller return response($content)->withHeaders(['X-Frame-Options' => 'ALLOWALL']); } + + public function stories(Request $request, $username) + { + abort_if(!config('instance.stories.enabled') || !$request->user(), 404); + $profile = Profile::whereNull('domain')->whereUsername($username)->firstOrFail(); + $pid = $profile->id; + $authed = Auth::user()->profile; + abort_if($pid != $authed->id && $profile->followedBy($authed) == false, 404); + $exists = Story::whereProfileId($pid) + ->where('expires_at', '>', now()) + ->count(); + abort_unless($exists > 0, 404); + return view('profile.story', compact('pid')); + } } diff --git a/app/Http/Controllers/StoryController.php b/app/Http/Controllers/StoryController.php index f2fc82b39..d5cc33bbe 100644 --- a/app/Http/Controllers/StoryController.php +++ b/app/Http/Controllers/StoryController.php @@ -3,6 +3,15 @@ namespace App\Http\Controllers; use Illuminate\Http\Request; +use Illuminate\Support\Str; +use App\Media; +use App\Profile; +use App\Story; +use App\StoryView; +use App\Services\StoryService; +use Cache, Storage; +use App\Services\FollowerService; + class StoryController extends Controller { @@ -12,8 +21,235 @@ class StoryController extends Controller $this->middleware('auth'); } - public function home(Request $request) + public function apiV1Add(Request $request) { - return view('stories.home'); + abort_if(!config('instance.stories.enabled') || !$request->user(), 404); + + $this->validate($request, [ + 'file.*' => function() { + return [ + 'required', + 'mimes:image/jpeg,image/png', + 'max:' . config('pixelfed.max_photo_size'), + ]; + }, + ]); + + $user = $request->user(); + + if(Story::whereProfileId($user->profile_id)->where('expires_at', '>', now())->count() >= Story::MAX_PER_DAY) { + abort(400, 'You have reached your limit for new Stories today.'); + } + + $story = new Story(); + $story->profile_id = $user->profile_id; + $story->save(); + + $monthHash = substr(hash('sha1', date('Y').date('m')), 0, 12); + $rid = Str::random(6).'.'.Str::random(9); + + $photo = $request->file('file'); + + $mimes = explode(',', config('pixelfed.media_types')); + if(in_array($photo->getMimeType(), [ + 'image/jpeg', + 'image/png' + ]) == false) { + abort(400, 'Invalid media type'); + return; + } + + $storagePath = "public/_esm.t1/{$monthHash}/{$story->id}/{$rid}"; + $path = $photo->store($storagePath); + + $story->path = $path; + $story->local = true; + $story->expires_at = now()->addHours(24); + $story->save(); + + return [ + 'code' => 200, + 'msg' => 'Successfully added', + 'media_url' => url(Storage::url($story->path)) + ]; + } + + public function apiV1Delete(Request $request, $id) + { + abort_if(!config('instance.stories.enabled') || !$request->user(), 404); + + $user = $request->user(); + + $story = Story::whereProfileId($user->profile_id) + ->findOrFail($id); + + if(Storage::exists($story->path) == true) { + Storage::delete($story->path); + } + + $story->delete(); + + return [ + 'code' => 200, + 'msg' => 'Successfully deleted' + ]; + } + + public function apiV1Recent(Request $request) + { + abort_if(!config('instance.stories.enabled') || !$request->user(), 404); + + $profile = $request->user()->profile; + $following = FollowerService::build()->profile($profile)->following(); + + $stories = Story::with('profile') + ->whereIn('profile_id', $following) + ->groupBy('profile_id') + ->where('expires_at', '>', now()) + ->orderByDesc('expires_at') + ->take(9) + ->get() + ->map(function($s, $k) { + return [ + 'id' => (string) $s->id, + 'photo' => $s->profile->avatarUrl(), + 'name' => $s->profile->username, + 'link' => $s->profile->url(), + 'lastUpdated' => (int) $s->created_at->format('U'), + 'seen' => $s->seen(), + 'items' => [], + 'pid' => (string) $s->profile->id + ]; + }); + + return response()->json($stories, 200, [], JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES); + } + + public function apiV1Fetch(Request $request, $id) + { + abort_if(!config('instance.stories.enabled') || !$request->user(), 404); + + $profile = $request->user()->profile; + if($id == $profile->id) { + $publicOnly = true; + } else { + $following = FollowerService::build()->profile($profile)->following(); + $publicOnly = in_array($id, $following); + } + + $stories = Story::whereProfileId($id) + ->orderBy('expires_at', 'desc') + ->where('expires_at', '>', now()) + ->when(!$publicOnly, function($query, $publicOnly) { + return $query->wherePublic(true); + }) + ->get() + ->map(function($s, $k) { + return [ + 'id' => (string) $s->id, + 'type' => 'photo', + 'length' => 3, + 'src' => url(Storage::url($s->path)), + 'preview' => null, + 'link' => null, + 'linkText' => null, + 'time' => $s->created_at->format('U'), + 'expires_at' => (int) $s->expires_at->format('U'), + 'seen' => $s->seen() + ]; + })->toArray(); + return response()->json($stories, 200, [], JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES); + } + + public function apiV1Profile(Request $request, $id) + { + abort_if(!config('instance.stories.enabled') || !$request->user(), 404); + + $authed = $request->user()->profile; + $profile = Profile::findOrFail($id); + if($id == $authed->id) { + $publicOnly = true; + } else { + $following = FollowerService::build()->profile($authed)->following(); + $publicOnly = in_array($id, $following); + } + + $stories = Story::whereProfileId($profile->id) + ->orderBy('expires_at') + ->where('expires_at', '>', now()) + ->when(!$publicOnly, function($query, $publicOnly) { + return $query->wherePublic(true); + }) + ->get() + ->map(function($s, $k) { + return [ + 'id' => $s->id, + 'type' => 'photo', + 'length' => 3, + 'src' => url(Storage::url($s->path)), + 'preview' => null, + 'link' => null, + 'linkText' => null, + 'time' => $s->created_at->format('U'), + 'expires_at' => (int) $s->expires_at->format('U'), + 'seen' => $s->seen() + ]; + })->toArray(); + if(count($stories) == 0) { + return []; + } + $cursor = count($stories) - 1; + $stories = [[ + 'id' => (string) $stories[$cursor]['id'], + 'photo' => $profile->avatarUrl(), + 'name' => $profile->username, + 'link' => $profile->url(), + 'lastUpdated' => (int) now()->format('U'), + 'seen' => null, + 'items' => $stories, + 'pid' => (string) $profile->id + ]]; + return response()->json($stories, 200, [], JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES); + } + + public function apiV1Viewed(Request $request) + { + abort_if(!config('instance.stories.enabled') || !$request->user(), 404); + + $this->validate($request, [ + 'id' => 'required|integer|min:1|exists:stories', + ]); + + StoryView::firstOrCreate([ + 'story_id' => $request->input('id'), + 'profile_id' => $request->user()->profile_id + ]); + + return ['code' => 200]; + } + + public function compose(Request $request) + { + abort_if(!config('instance.stories.enabled') || !$request->user(), 404); + return view('stories.compose'); + } + + public function apiV1Exists(Request $request, $id) + { + abort_if(!config('instance.stories.enabled'), 404); + + $res = (bool) Story::whereProfileId($id) + ->where('expires_at', '>', now()) + ->count(); + + return response()->json($res); + } + + public function iRedirect(Request $request) + { + $user = $request->user(); + abort_if(!$user, 404); + $username = $user->username; + return redirect("/stories/{$username}"); } } diff --git a/app/Profile.php b/app/Profile.php index e01ad368b..b386ffda6 100644 --- a/app/Profile.php +++ b/app/Profile.php @@ -303,4 +303,9 @@ class Profile extends Model ->whereFollowingId($this->id) ->exists(); } + + public function stories() + { + return $this->hasMany(Story::class); + } } diff --git a/app/Providers/AuthServiceProvider.php b/app/Providers/AuthServiceProvider.php index 3448b3005..7e3b460f8 100644 --- a/app/Providers/AuthServiceProvider.php +++ b/app/Providers/AuthServiceProvider.php @@ -36,7 +36,6 @@ class AuthServiceProvider extends ServiceProvider 'read', 'write', 'follow', - 'push' ]); Passport::tokensCan([ diff --git a/app/Status.php b/app/Status.php index 5819d0961..ac04b31a4 100644 --- a/app/Status.php +++ b/app/Status.php @@ -131,13 +131,9 @@ class Status extends Model $media = $this->firstMedia(); $path = $media->media_path; $hash = is_null($media->processed_at) ? md5('unprocessed') : md5($media->created_at); - if(config('pixelfed.cloud_storage') == true) { - $url = Storage::disk(config('filesystems.cloud'))->url($path)."?v={$hash}"; - } else { - $url = Storage::url($path)."?v={$hash}"; - } + $url = $media->cdn_url ? $media->cdn_url . "?v={$hash}" : url(Storage::url($path)."?v={$hash}"); - return url($url); + return $url; } public function likes() diff --git a/app/Story.php b/app/Story.php index 104f0e58f..b80101aec 100644 --- a/app/Story.php +++ b/app/Story.php @@ -10,6 +10,8 @@ class Story extends Model { use HasSnowflakePrimary; + public const MAX_PER_DAY = 10; + /** * Indicates if the IDs are auto-incrementing. * @@ -24,6 +26,8 @@ class Story extends Model */ protected $dates = ['published_at', 'expires_at']; + protected $fillable = ['profile_id']; + protected $visible = ['id']; public function profile() @@ -31,16 +35,6 @@ class Story extends Model return $this->belongsTo(Profile::class); } - public function items() - { - return $this->hasMany(StoryItem::class); - } - - public function reactions() - { - return $this->hasMany(StoryReaction::class); - } - public function views() { return $this->hasMany(StoryView::class); @@ -48,7 +42,13 @@ class Story extends Model public function seen($pid = false) { - $id = $pid ?? Auth::user()->profile->id; - return $this->views()->whereProfileId($id)->exists(); + return StoryView::whereStoryId($this->id) + ->whereProfileId(Auth::user()->profile->id) + ->exists(); + } + + public function permalink() + { + return url("/story/$this->id"); } } diff --git a/app/Transformer/Api/StatusTransformer.php b/app/Transformer/Api/StatusTransformer.php index 5a8fb0ba4..80074e7b8 100644 --- a/app/Transformer/Api/StatusTransformer.php +++ b/app/Transformer/Api/StatusTransformer.php @@ -62,7 +62,7 @@ class StatusTransformer extends Fractal\TransformerAbstract public function includeMediaAttachments(Status $status) { - return Cache::remember('status:transformer:media:attachments:'.$status->id, now()->addDays(14), function() use($status) { + return Cache::remember('status:transformer:media:attachments:'.$status->id, now()->addMinutes(14), function() use($status) { if(in_array($status->type, ['photo', 'video', 'video:album', 'photo:album', 'loop', 'photo:video:album'])) { $media = $status->media()->orderBy('order')->get(); return $this->collection($media, new MediaTransformer()); diff --git a/app/Util/ActivityPub/Helpers.php b/app/Util/ActivityPub/Helpers.php index a70d828de..e6d85baec 100644 --- a/app/Util/ActivityPub/Helpers.php +++ b/app/Util/ActivityPub/Helpers.php @@ -406,7 +406,6 @@ class Helpers { $remoteUsername = "@{$username}@{$domain}"; abort_if(!self::validateUrl($res['inbox']), 400); - abort_if(!self::validateUrl($res['outbox']), 400); abort_if(!self::validateUrl($res['id']), 400); $profile = Profile::whereRemoteUrl($res['id'])->first(); @@ -451,4 +450,20 @@ class Helpers { $response = curl_exec($ch); return; } + + public static function apSignedPostRequest($senderProfile, $url, $body) + { + abort_if(!self::validateUrl($url), 400); + + $payload = json_encode($body); + $headers = HttpSignature::sign($senderProfile, $url, $body); + + $ch = curl_init($url); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); + curl_setopt($ch, CURLOPT_POSTFIELDS, $payload); + curl_setopt($ch, CURLOPT_HEADER, true); + $response = curl_exec($ch); + return; + } } diff --git a/app/Util/Lexer/RestrictedNames.php b/app/Util/Lexer/RestrictedNames.php index 7aa2fd8ee..a7c32e42c 100644 --- a/app/Util/Lexer/RestrictedNames.php +++ b/app/Util/Lexer/RestrictedNames.php @@ -12,7 +12,6 @@ class RestrictedNames 'download', 'domainadmin', 'domainadministrator', - 'email', 'errors', 'events', 'example', @@ -26,7 +25,7 @@ class RestrictedNames 'hostmaster', 'imap', 'info', - 'info', + 'information', 'is', 'isatap', 'it', @@ -142,6 +141,8 @@ class RestrictedNames 'drives', 'driver', 'e', + 'email', + 'emails', 'error', 'explore', 'export', @@ -206,6 +207,10 @@ class RestrictedNames 'news', 'news', 'newsfeed', + 'newsroom', + 'newsrooms', + 'news-room', + 'news-rooms', 'o', 'oauth', 'official', diff --git a/app/Util/RateLimit/User.php b/app/Util/RateLimit/User.php index ddcfc2212..c6cbf85ca 100644 --- a/app/Util/RateLimit/User.php +++ b/app/Util/RateLimit/User.php @@ -6,7 +6,7 @@ trait User { public function isTrustedAccount() { - return $this->created_at->lt(now()->subDays(20)); + return $this->created_at->lt(now()->subDays(60)); } public function getMaxPostsPerHourAttribute() @@ -98,4 +98,19 @@ trait User { { return 5000; } + + public function getMaxStoriesPerHourAttribute() + { + return 20; + } + + public function getMaxStoriesPerDayAttribute() + { + return 30; + } + + public function getMaxStoryDeletePerDayAttribute() + { + return 35; + } } \ No newline at end of file diff --git a/app/Util/Site/Config.php b/app/Util/Site/Config.php index ffa80a522..096625087 100644 --- a/app/Util/Site/Config.php +++ b/app/Util/Site/Config.php @@ -51,7 +51,7 @@ class Config { 'features' => [ 'mobile_apis' => config('pixelfed.oauth_enabled'), 'circles' => false, - 'stories' => false, + 'stories' => config('instance.stories.enabled'), 'video' => Str::contains(config('pixelfed.media_types'), 'video/mp4'), 'import' => [ 'instagram' => config('pixelfed.import.instagram.enabled'), diff --git a/composer.lock b/composer.lock index d4c9da6aa..c6a6bfff7 100644 --- a/composer.lock +++ b/composer.lock @@ -60,16 +60,16 @@ }, { "name": "aws/aws-sdk-php", - "version": "3.125.0", + "version": "3.128.2", "source": { "type": "git", "url": "https://github.com/aws/aws-sdk-php.git", - "reference": "d9ffe7cf9cc93d3c49f4f6d2db6cf0c469686f9c" + "reference": "a81485e12b2545aff17134bbf29442037f3fcadb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/d9ffe7cf9cc93d3c49f4f6d2db6cf0c469686f9c", - "reference": "d9ffe7cf9cc93d3c49f4f6d2db6cf0c469686f9c", + "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/a81485e12b2545aff17134bbf29442037f3fcadb", + "reference": "a81485e12b2545aff17134bbf29442037f3fcadb", "shasum": "" }, "require": { @@ -94,7 +94,8 @@ "nette/neon": "^2.3", "phpunit/phpunit": "^4.8.35|^5.4.3", "psr/cache": "^1.0", - "psr/simple-cache": "^1.0" + "psr/simple-cache": "^1.0", + "sebastian/comparator": "^1.2.3" }, "suggest": { "aws/aws-php-sns-message-validator": "To validate incoming SNS notifications", @@ -139,7 +140,7 @@ "s3", "sdk" ], - "time": "2019-12-02T23:15:42+00:00" + "time": "2019-12-10T19:12:09+00:00" }, { "name": "barryvdh/laravel-cors", @@ -448,25 +449,25 @@ }, { "name": "dnoegel/php-xdg-base-dir", - "version": "0.1", + "version": "v0.1.1", "source": { "type": "git", "url": "https://github.com/dnoegel/php-xdg-base-dir.git", - "reference": "265b8593498b997dc2d31e75b89f053b5cc9621a" + "reference": "8f8a6e48c5ecb0f991c2fdcf5f154a47d85f9ffd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/dnoegel/php-xdg-base-dir/zipball/265b8593498b997dc2d31e75b89f053b5cc9621a", - "reference": "265b8593498b997dc2d31e75b89f053b5cc9621a", + "url": "https://api.github.com/repos/dnoegel/php-xdg-base-dir/zipball/8f8a6e48c5ecb0f991c2fdcf5f154a47d85f9ffd", + "reference": "8f8a6e48c5ecb0f991c2fdcf5f154a47d85f9ffd", "shasum": "" }, "require": { "php": ">=5.3.2" }, "require-dev": { - "phpunit/phpunit": "@stable" + "phpunit/phpunit": "~7.0|~6.0|~5.0|~4.8.35" }, - "type": "project", + "type": "library", "autoload": { "psr-4": { "XdgBaseDir\\": "src/" @@ -477,7 +478,7 @@ "MIT" ], "description": "implementation of xdg base directory specification for php", - "time": "2014-10-24T07:27:01+00:00" + "time": "2019-12-04T15:06:13+00:00" }, { "name": "doctrine/cache", @@ -1246,16 +1247,16 @@ }, { "name": "guzzlehttp/guzzle", - "version": "6.4.1", + "version": "6.5.0", "source": { "type": "git", "url": "https://github.com/guzzle/guzzle.git", - "reference": "0895c932405407fd3a7368b6910c09a24d26db11" + "reference": "dbc2bc3a293ed6b1ae08a3651e2bfd213d19b6a5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/0895c932405407fd3a7368b6910c09a24d26db11", - "reference": "0895c932405407fd3a7368b6910c09a24d26db11", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/dbc2bc3a293ed6b1ae08a3651e2bfd213d19b6a5", + "reference": "dbc2bc3a293ed6b1ae08a3651e2bfd213d19b6a5", "shasum": "" }, "require": { @@ -1270,12 +1271,13 @@ "psr/log": "^1.1" }, "suggest": { + "ext-intl": "Required for Internationalized Domain Name (IDN) support", "psr/log": "Required for using the Log middleware" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "6.3-dev" + "dev-master": "6.5-dev" } }, "autoload": { @@ -1308,7 +1310,7 @@ "rest", "web service" ], - "time": "2019-10-23T15:58:00+00:00" + "time": "2019-12-07T18:20:45+00:00" }, { "name": "guzzlehttp/promises", @@ -1592,16 +1594,16 @@ }, { "name": "jaybizzle/crawler-detect", - "version": "v1.2.89", + "version": "v1.2.90", "source": { "type": "git", "url": "https://github.com/JayBizzle/Crawler-Detect.git", - "reference": "374d699ce4944107015eee0798eab072e3c47df9" + "reference": "35f963386e6a189697fe4b14dc91fb42b17fda4b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/JayBizzle/Crawler-Detect/zipball/374d699ce4944107015eee0798eab072e3c47df9", - "reference": "374d699ce4944107015eee0798eab072e3c47df9", + "url": "https://api.github.com/repos/JayBizzle/Crawler-Detect/zipball/35f963386e6a189697fe4b14dc91fb42b17fda4b", + "reference": "35f963386e6a189697fe4b14dc91fb42b17fda4b", "shasum": "" }, "require": { @@ -1637,7 +1639,7 @@ "crawlerdetect", "php crawler detect" ], - "time": "2019-11-16T13:47:52+00:00" + "time": "2019-12-08T20:03:27+00:00" }, { "name": "jenssegers/agent", @@ -1710,16 +1712,16 @@ }, { "name": "laravel/framework", - "version": "v6.6.0", + "version": "v6.7.0", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "b48528ba5422ac909dbabf0b1cc34534928e7bce" + "reference": "ba4204f3a8b9672b6116398c165bd9c0c6eac077" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/b48528ba5422ac909dbabf0b1cc34534928e7bce", - "reference": "b48528ba5422ac909dbabf0b1cc34534928e7bce", + "url": "https://api.github.com/repos/laravel/framework/zipball/ba4204f3a8b9672b6116398c165bd9c0c6eac077", + "reference": "ba4204f3a8b9672b6116398c165bd9c0c6eac077", "shasum": "" }, "require": { @@ -1815,7 +1817,7 @@ "league/flysystem-sftp": "Required to use the Flysystem SFTP driver (^1.0).", "moontoast/math": "Required to use ordered UUIDs (^1.1).", "pda/pheanstalk": "Required to use the beanstalk queue driver (^4.0).", - "psr/http-message": "Required to allow Storage::put to accept a StreamInterface (^1.0)", + "psr/http-message": "Required to allow Storage::put to accept a StreamInterface (^1.0).", "pusher/pusher-php-server": "Required to use the Pusher broadcast driver (^4.0).", "symfony/cache": "Required to PSR-6 cache bridge (^4.3.4).", "symfony/psr-http-message-bridge": "Required to use PSR-7 bridging features (^1.2).", @@ -1852,7 +1854,7 @@ "framework", "laravel" ], - "time": "2019-11-26T15:33:08+00:00" + "time": "2019-12-10T16:01:57+00:00" }, { "name": "laravel/helpers", @@ -1909,16 +1911,16 @@ }, { "name": "laravel/horizon", - "version": "v3.4.3", + "version": "v3.4.4", "source": { "type": "git", "url": "https://github.com/laravel/horizon.git", - "reference": "37226dd66318014fac20351b4cc7ca209dd4ccb6" + "reference": "7c36d24b200b60a059ab20f5b53f5bb6f4d2da40" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/horizon/zipball/37226dd66318014fac20351b4cc7ca209dd4ccb6", - "reference": "37226dd66318014fac20351b4cc7ca209dd4ccb6", + "url": "https://api.github.com/repos/laravel/horizon/zipball/7c36d24b200b60a059ab20f5b53f5bb6f4d2da40", + "reference": "7c36d24b200b60a059ab20f5b53f5bb6f4d2da40", "shasum": "" }, "require": { @@ -1926,9 +1928,9 @@ "ext-json": "*", "ext-pcntl": "*", "ext-posix": "*", - "illuminate/contracts": "~5.7.0|~5.8.0|^6.0|^7.0", - "illuminate/queue": "~5.7.0|~5.8.0|^6.0|^7.0", - "illuminate/support": "~5.7.0|~5.8.0|^6.0|^7.0", + "illuminate/contracts": "~5.7.0|~5.8.0|^6.0", + "illuminate/queue": "~5.7.0|~5.8.0|^6.0", + "illuminate/support": "~5.7.0|~5.8.0|^6.0", "php": ">=7.1.0", "predis/predis": "^1.1", "ramsey/uuid": "^3.5", @@ -1937,7 +1939,7 @@ }, "require-dev": { "mockery/mockery": "^1.0", - "orchestra/testbench": "^3.7|^4.0|^5.0", + "orchestra/testbench": "^3.7|^4.0", "phpunit/phpunit": "^7.0|^8.0" }, "type": "library", @@ -1974,7 +1976,7 @@ "laravel", "queue" ], - "time": "2019-11-19T16:23:21+00:00" + "time": "2019-12-10T16:50:59+00:00" }, { "name": "laravel/passport", @@ -2217,16 +2219,16 @@ }, { "name": "league/flysystem", - "version": "1.0.57", + "version": "1.0.61", "source": { "type": "git", "url": "https://github.com/thephpleague/flysystem.git", - "reference": "0e9db7f0b96b9f12dcf6f65bc34b72b1a30ea55a" + "reference": "4fb13c01784a6c9f165a351e996871488ca2d8c9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/0e9db7f0b96b9f12dcf6f65bc34b72b1a30ea55a", - "reference": "0e9db7f0b96b9f12dcf6f65bc34b72b1a30ea55a", + "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/4fb13c01784a6c9f165a351e996871488ca2d8c9", + "reference": "4fb13c01784a6c9f165a351e996871488ca2d8c9", "shasum": "" }, "require": { @@ -2297,7 +2299,7 @@ "sftp", "storage" ], - "time": "2019-10-16T21:01:05+00:00" + "time": "2019-12-08T21:46:50+00:00" }, { "name": "league/flysystem-aws-s3-v3", @@ -4061,20 +4063,20 @@ }, { "name": "psy/psysh", - "version": "v0.9.11", + "version": "v0.9.12", "source": { "type": "git", "url": "https://github.com/bobthecow/psysh.git", - "reference": "75d9ac1c16db676de27ab554a4152b594be4748e" + "reference": "90da7f37568aee36b116a030c5f99c915267edd4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/bobthecow/psysh/zipball/75d9ac1c16db676de27ab554a4152b594be4748e", - "reference": "75d9ac1c16db676de27ab554a4152b594be4748e", + "url": "https://api.github.com/repos/bobthecow/psysh/zipball/90da7f37568aee36b116a030c5f99c915267edd4", + "reference": "90da7f37568aee36b116a030c5f99c915267edd4", "shasum": "" }, "require": { - "dnoegel/php-xdg-base-dir": "0.1", + "dnoegel/php-xdg-base-dir": "0.1.*", "ext-json": "*", "ext-tokenizer": "*", "jakub-onderka/php-console-highlighter": "0.3.*|0.4.*", @@ -4131,7 +4133,7 @@ "interactive", "shell" ], - "time": "2019-11-27T22:44:29+00:00" + "time": "2019-12-06T14:19:43+00:00" }, { "name": "ralouphie/getallheaders", @@ -6485,19 +6487,19 @@ "source": { "type": "git", "url": "https://github.com/barryvdh/laravel-debugbar.git", - "reference": "55cd3f5e892eee6f5aca414d465cc224b062bea6" + "reference": "35638e4f5e714a12dec5ca062e68c625c1309c1c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/barryvdh/laravel-debugbar/zipball/55cd3f5e892eee6f5aca414d465cc224b062bea6", - "reference": "55cd3f5e892eee6f5aca414d465cc224b062bea6", + "url": "https://api.github.com/repos/barryvdh/laravel-debugbar/zipball/35638e4f5e714a12dec5ca062e68c625c1309c1c", + "reference": "35638e4f5e714a12dec5ca062e68c625c1309c1c", "shasum": "" }, "require": { "illuminate/routing": "^5.5|^6", "illuminate/session": "^5.5|^6", "illuminate/support": "^5.5|^6", - "maximebf/debugbar": "~1.15.0", + "maximebf/debugbar": "^1.15", "php": ">=7.0", "symfony/debug": "^3|^4|^5", "symfony/finder": "^3|^4|^5" @@ -6545,7 +6547,7 @@ "profiler", "webprofiler" ], - "time": "2019-11-24T09:49:45+00:00" + "time": "2019-12-07T09:33:13+00:00" }, { "name": "composer/ca-bundle", @@ -7460,20 +7462,20 @@ }, { "name": "maximebf/debugbar", - "version": "v1.15.1", + "version": "v1.16.0", "source": { "type": "git", "url": "https://github.com/maximebf/php-debugbar.git", - "reference": "6c4277f6117e4864966c9cb58fb835cee8c74a1e" + "reference": "6ca3502de5e5889dc21311d2461f8cc3b6a094b1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/maximebf/php-debugbar/zipball/6c4277f6117e4864966c9cb58fb835cee8c74a1e", - "reference": "6c4277f6117e4864966c9cb58fb835cee8c74a1e", + "url": "https://api.github.com/repos/maximebf/php-debugbar/zipball/6ca3502de5e5889dc21311d2461f8cc3b6a094b1", + "reference": "6ca3502de5e5889dc21311d2461f8cc3b6a094b1", "shasum": "" }, "require": { - "php": ">=5.6", + "php": "^7.1", "psr/log": "^1.0", "symfony/var-dumper": "^2.6|^3|^4" }, @@ -7488,7 +7490,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.15-dev" + "dev-master": "1.16-dev" } }, "autoload": { @@ -7517,7 +7519,7 @@ "debug", "debugbar" ], - "time": "2019-09-24T14:55:42+00:00" + "time": "2019-10-18T14:34:16+00:00" }, { "name": "mockery/mockery", @@ -8617,16 +8619,16 @@ }, { "name": "phpunit/phpunit", - "version": "8.4.3", + "version": "8.5.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "67f9e35bffc0dd52d55d565ddbe4230454fd6a4e" + "reference": "3ee1c1fd6fc264480c25b6fb8285edefe1702dab" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/67f9e35bffc0dd52d55d565ddbe4230454fd6a4e", - "reference": "67f9e35bffc0dd52d55d565ddbe4230454fd6a4e", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/3ee1c1fd6fc264480c25b6fb8285edefe1702dab", + "reference": "3ee1c1fd6fc264480c25b6fb8285edefe1702dab", "shasum": "" }, "require": { @@ -8670,7 +8672,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "8.4-dev" + "dev-master": "8.5-dev" } }, "autoload": { @@ -8696,7 +8698,7 @@ "testing", "xunit" ], - "time": "2019-11-06T09:42:23+00:00" + "time": "2019-12-06T05:41:38+00:00" }, { "name": "scrivo/highlight.php", @@ -9602,16 +9604,16 @@ }, { "name": "squizlabs/php_codesniffer", - "version": "3.5.2", + "version": "3.5.3", "source": { "type": "git", "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", - "reference": "65b12cdeaaa6cd276d4c3033a95b9b88b12701e7" + "reference": "557a1fc7ac702c66b0bbfe16ab3d55839ef724cb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/65b12cdeaaa6cd276d4c3033a95b9b88b12701e7", - "reference": "65b12cdeaaa6cd276d4c3033a95b9b88b12701e7", + "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/557a1fc7ac702c66b0bbfe16ab3d55839ef724cb", + "reference": "557a1fc7ac702c66b0bbfe16ab3d55839ef724cb", "shasum": "" }, "require": { @@ -9649,7 +9651,7 @@ "phpcs", "standards" ], - "time": "2019-10-28T04:36:32+00:00" + "time": "2019-12-04T04:46:47+00:00" }, { "name": "symfony/http-client", diff --git a/config/instance.php b/config/instance.php index ea37a5b30..3f4762fab 100644 --- a/config/instance.php +++ b/config/instance.php @@ -47,4 +47,8 @@ return [ 'custom' => env('USERNAME_REMOTE_CUSTOM_TEXT', null) ] ], + + 'stories' => [ + 'enabled' => env('STORIES_ENABLED', false), + ] ]; \ No newline at end of file diff --git a/config/passport.php b/config/passport.php new file mode 100644 index 000000000..a4565f372 --- /dev/null +++ b/config/passport.php @@ -0,0 +1,20 @@ + env('PASSPORT_PRIVATE_KEY'), + + 'public_key' => env('PASSPORT_PUBLIC_KEY'), + +]; diff --git a/config/pixelfed.php b/config/pixelfed.php index a9710a2aa..34434f5c8 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.10.6', + 'version' => '0.10.7', /* |-------------------------------------------------------------------------- diff --git a/database/migrations/2019_12_25_042317_update_stories_table.php b/database/migrations/2019_12_25_042317_update_stories_table.php new file mode 100644 index 000000000..da778225e --- /dev/null +++ b/database/migrations/2019_12_25_042317_update_stories_table.php @@ -0,0 +1,63 @@ +getDatabasePlatform()->registerDoctrineTypeMapping('enum', 'string'); + } + /** + * Run the migrations. + * + * @return void + */ + public function up() + { + Schema::dropIfExists('stories'); + Schema::dropIfExists('story_items'); + Schema::dropIfExists('story_reactions'); + Schema::dropIfExists('story_views'); + + Schema::create('stories', function (Blueprint $table) { + $table->bigIncrements('id'); + $table->bigInteger('profile_id')->unsigned()->index(); + $table->string('type')->nullable(); + $table->unsignedInteger('size')->nullable(); + $table->string('mime')->nullable(); + $table->smallInteger('duration')->unsigned(); + $table->string('path')->nullable(); + $table->string('cdn_url')->nullable(); + $table->boolean('public')->default(false)->index(); + $table->boolean('local')->default(false)->index(); + $table->unsignedInteger('view_count')->nullable(); + $table->unsignedInteger('comment_count')->nullable(); + $table->json('story')->nullable(); + $table->unique(['profile_id', 'path']); + $table->timestamp('expires_at')->index(); + $table->timestamps(); + }); + + Schema::create('story_views', function (Blueprint $table) { + $table->bigIncrements('id'); + $table->bigInteger('story_id')->unsigned()->index(); + $table->bigInteger('profile_id')->unsigned()->index(); + $table->unique(['profile_id', 'story_id']); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('stories'); + Schema::dropIfExists('story_views'); + } +} diff --git a/public/js/components.js b/public/js/components.js index 4f3858e2b..fa7b5636e 100644 Binary files a/public/js/components.js and b/public/js/components.js differ diff --git a/public/js/compose.js b/public/js/compose.js index 9e69d9c66..30d7f8f48 100644 Binary files a/public/js/compose.js and b/public/js/compose.js differ diff --git a/public/js/profile.js b/public/js/profile.js index cc260afe2..51cb8b578 100644 Binary files a/public/js/profile.js and b/public/js/profile.js differ diff --git a/public/js/story-compose.js b/public/js/story-compose.js new file mode 100644 index 000000000..88ee9ada1 Binary files /dev/null and b/public/js/story-compose.js differ diff --git a/public/js/theme-monokai.js b/public/js/theme-monokai.js index 46c07b07f..842203af1 100644 Binary files a/public/js/theme-monokai.js and b/public/js/theme-monokai.js differ diff --git a/public/js/timeline.js b/public/js/timeline.js index c3e02488f..879dc57a2 100644 Binary files a/public/js/timeline.js and b/public/js/timeline.js differ diff --git a/public/js/vendor.js b/public/js/vendor.js index a6cb1ab79..5e5215e2a 100644 Binary files a/public/js/vendor.js and b/public/js/vendor.js differ diff --git a/public/mix-manifest.json b/public/mix-manifest.json index a5fc352f1..d13dff484 100644 Binary files a/public/mix-manifest.json and b/public/mix-manifest.json differ diff --git a/resources/assets/js/components/ComposeModal.vue b/resources/assets/js/components/ComposeModal.vue index daced1ef2..37a633045 100644 --- a/resources/assets/js/components/ComposeModal.vue +++ b/resources/assets/js/components/ComposeModal.vue @@ -84,52 +84,6 @@
Share up to {{config.uploader.album_limit}} photos or videos.
-- Add to Story -
-Add a photo or video to your story.
-- New Collection -
-Create a curated collection of photos.
-- Try ComposeUI v4 - - BETA - + New Post
-The next generation compose experience.
+Share up to {{config.uploader.album_limit}} photos or videos
+
+
+ New Story
+
+ BETA
+
+ Add Photo to Story
+ New Collection
+
+ BETA
+
+ New collection of posts