diff --git a/CHANGELOG.md b/CHANGELOG.md index 530daff9d..b63bda23c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,9 @@ ## [Unreleased](https://github.com/pixelfed/pixelfed/compare/v0.10.9...dev) ### Added +- ActivityPubFetchService for signed GET requests ([8763bfc5](https://github.com/pixelfed/pixelfed/commit/8763bfc5)) - Custom content warnings for remote posts ([6afc61a4](https://github.com/pixelfed/pixelfed/commit/6afc61a4)) +- Thai translations ([https://github.com/pixelfed/pixelfed/commit/74cd536](https://github.com/pixelfed/pixelfed/commit/74cd536)) ### Updated - Updated PostComponent, fix remote urls ([42716ccc](https://github.com/pixelfed/pixelfed/commit/42716ccc)) @@ -14,6 +16,17 @@ - Updated PublicApiControllers, fix block/mutes filtering on public timeline ([08383dd4](https://github.com/pixelfed/pixelfed/commit/08383dd4)) - Updated FixUsernames command, fixes remote username search ([0f943f67](https://github.com/pixelfed/pixelfed/commit/0f943f67)) - Updated Timeline component, fix mod tools ([b1d5eb05](https://github.com/pixelfed/pixelfed/commit/b1d5eb05)) +- Updated Profile.vue component, fix pagination bug ([46767810](https://github.com/pixelfed/pixelfed/commit/46767810)) +- Updated purify config, fix microformats support ([877023fb](https://github.com/pixelfed/pixelfed/commit/877023fb)) +- Updated LikeController, fix likes_count bug ([996866cb](https://github.com/pixelfed/pixelfed/commit/996866cb)) +- Updated AccountController, added followRequestJson method ([483548e2](https://github.com/pixelfed/pixelfed/commit/483548e2)) +- Updated UserInvite model, added sender relation ([591a1929](https://github.com/pixelfed/pixelfed/commit/591a1929)) +- Updated migrations, added UIKit ([fcab5010](https://github.com/pixelfed/pixelfed/commit/fcab5010)) +- Updated AccountTransformer, added last_fetched_at attribute ([38b0233e](https://github.com/pixelfed/pixelfed/commit/38b0233e)) +- Updated StoryItemTransformer, increase story length to 5 seconds ([924e424c](https://github.com/pixelfed/pixelfed/commit/924e424c)) +- Updated StatusController, fix reblog_count bug ([1dc65e93](https://github.com/pixelfed/pixelfed/commit/1dc65e93)) +- Updated NotificationCard.vue component, add follow requests at top of card, remove card-header ([5e48ffca](https://github.com/pixelfed/pixelfed/commit/5e48ffca)) +- Updated RemoteProfile.vue component, add warning for empty profiles and last_fetched_at ([66f44a9d](https://github.com/pixelfed/pixelfed/commit/66f44a9d)) ## [v0.10.9 (2020-04-17)](https://github.com/pixelfed/pixelfed/compare/v0.10.8...v0.10.9) diff --git a/app/Http/Controllers/AccountController.php b/app/Http/Controllers/AccountController.php index 6f136c3ad..a554f17d8 100644 --- a/app/Http/Controllers/AccountController.php +++ b/app/Http/Controllers/AccountController.php @@ -327,6 +327,27 @@ class AccountController extends Controller return view('account.follow-requests', compact('followers')); } + public function followRequestsJson(Request $request) + { + $pid = Auth::user()->profile_id; + $followers = FollowRequest::whereFollowingId($pid)->orderBy('id','desc')->whereIsRejected(0)->get(); + $res = [ + 'count' => $followers->count(), + 'accounts' => $followers->take(10)->map(function($a) { + $actor = $a->actor; + return [ + 'id' => $actor->id, + 'username' => $actor->username, + 'avatar' => $actor->avatarUrl(), + 'url' => $actor->url(), + 'local' => $actor->domain == null, + 'following' => $actor->followedBy(Auth::user()->profile) + ]; + }) + ]; + return response()->json($res, 200, [], JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES); + } + public function followRequestHandle(Request $request) { $this->validate($request, [ diff --git a/app/Http/Controllers/LikeController.php b/app/Http/Controllers/LikeController.php index fa3caaf7c..601286179 100644 --- a/app/Http/Controllers/LikeController.php +++ b/app/Http/Controllers/LikeController.php @@ -27,7 +27,7 @@ class LikeController extends Controller $profile = $user->profile; $status = Status::findOrFail($request->input('item')); - $count = $status->likes_count; + $count = $status->likes()->count(); if ($status->likes()->whereProfileId($profile->id)->count() !== 0) { $like = Like::whereProfileId($profile->id)->whereStatusId($status->id)->firstOrFail(); diff --git a/app/Http/Controllers/StatusController.php b/app/Http/Controllers/StatusController.php index ef1d59395..cf209bf15 100644 --- a/app/Http/Controllers/StatusController.php +++ b/app/Http/Controllers/StatusController.php @@ -175,7 +175,7 @@ class StatusController extends Controller ->whereIn('scope', ['public', 'unlisted']) ->findOrFail($request->input('item')); - $count = $status->shares_count; + $count = $status->shares()->count(); $exists = Status::whereProfileId(Auth::user()->profile->id) ->whereReblogOfId($status->id) diff --git a/app/Http/Controllers/UIKitController.php b/app/Http/Controllers/UIKitController.php new file mode 100644 index 000000000..bfa720dca --- /dev/null +++ b/app/Http/Controllers/UIKitController.php @@ -0,0 +1,10 @@ +belongsTo(User::class, 'user_id'); diff --git a/app/Profile.php b/app/Profile.php index e9907f710..7c8325ad6 100644 --- a/app/Profile.php +++ b/app/Profile.php @@ -18,7 +18,10 @@ class Profile extends Model */ public $incrementing = false; - protected $dates = ['deleted_at']; + protected $dates = [ + 'deleted_at', + 'last_fetched_at' + ]; protected $hidden = ['private_key']; protected $visible = ['id', 'user_id', 'username', 'name']; protected $fillable = ['user_id']; diff --git a/app/Services/ActivityPubFetchService.php b/app/Services/ActivityPubFetchService.php new file mode 100644 index 000000000..765c62765 --- /dev/null +++ b/app/Services/ActivityPubFetchService.php @@ -0,0 +1,59 @@ + 'application/activity+json, application/json', + 'User-Agent' => 'PixelfedBot - https://pixelfed.org' + ]; + + public static function queue() + { + return new self; + } + + public function signed($signed = true) + { + $this->signed = $signed; + return $this; + } + + public function actor($profile) + { + $this->actor = $profile; + return $this; + } + + public function url($url) + { + if(!Helpers::validateUrl($url)) { + throw new \Exception('Invalid URL'); + } + $this->url = $url; + return $this; + } + + public function get() + { + if($this->signed == true && $this->actor == null) { + throw new \Exception('Cannot sign request without actor'); + } + return $this->signedRequest(); + } + + protected function signedRequest() + { + $this->headers = HttpSignature::sign($this->actor, $this->url, false, $this->headers); + return Zttp::withHeaders($this->headers)->get($this->url)->body(); + } +} \ No newline at end of file diff --git a/app/Transformer/Api/AccountTransformer.php b/app/Transformer/Api/AccountTransformer.php index 0486495c8..16a45b97c 100644 --- a/app/Transformer/Api/AccountTransformer.php +++ b/app/Transformer/Api/AccountTransformer.php @@ -34,7 +34,8 @@ class AccountTransformer extends Fractal\TransformerAbstract 'local' => (bool) $local, 'is_admin' => (bool) $is_admin, 'created_at' => $profile->created_at->toJSON(), - 'header_bg' => $profile->header_bg + 'header_bg' => $profile->header_bg, + 'last_fetched_at' => optional($profile->last_fetched_at)->toJSON() ]; } diff --git a/app/Transformer/Api/StoryItemTransformer.php b/app/Transformer/Api/StoryItemTransformer.php index 1fbd9c37d..7c59ef22e 100644 --- a/app/Transformer/Api/StoryItemTransformer.php +++ b/app/Transformer/Api/StoryItemTransformer.php @@ -14,7 +14,7 @@ class StoryItemTransformer extends Fractal\TransformerAbstract return [ 'id' => (string) $item->id, 'type' => $item->type, - 'length' => $item->duration != 0 ? $item->duration : 3, + 'length' => 5, 'src' => $item->url(), 'preview' => null, 'link' => null, diff --git a/app/UIKit.php b/app/UIKit.php new file mode 100644 index 000000000..c47e2249c --- /dev/null +++ b/app/UIKit.php @@ -0,0 +1,21 @@ +where('k', $k)->first()->v; + } +} diff --git a/app/UserInvite.php b/app/UserInvite.php index c8761a775..6b9a839f9 100644 --- a/app/UserInvite.php +++ b/app/UserInvite.php @@ -6,10 +6,13 @@ use Illuminate\Database\Eloquent\Model; class UserInvite extends Model { + public function sender() + { + return $this->belongsTo(Profile::class, 'profile_id'); + } + public function url() { - $path = '/i/invite/code'; - $url = url($path, [$this->key, $this->token]); - return $url; + return url("/i/invite/code/{$this->key}/{$this->token}"); } } diff --git a/config/purify.php b/config/purify.php index da156c5f7..681f55dd6 100644 --- a/config/purify.php +++ b/config/purify.php @@ -68,8 +68,8 @@ return [ */ 'HTML.Allowed' => env('RESTRICT_HTML_TYPES', true) ? - 'a[href|title|rel],p,span,br' : - 'a[href|title|rel],p,span,strong,em,del,b,i,s,strike,h1,h2,h3,h4,h5,h6,ul,ol,li,br', + 'a[href|title|rel|class],p[class],span[class],br' : + 'a[href|title|rel|class],p[class],span[class],strong,em,del,b,i,s,strike,h1,h2,h3,h4,h5,h6,ul,ol,li,br', /* @@ -133,6 +133,27 @@ return [ 'AutoFormat.RemoveEmpty' => false, + 'Attr.AllowedClasses' => [ + 'h-feed', + 'h-entry', + 'h-cite', + 'h-card', + 'p-author', + 'p-name', + 'p-in-reply-to', + 'p-repost-of', + 'p-comment', + 'u-photo', + 'u-uid', + 'u-url', + 'dt-published', + 'e-content', + 'mention', + 'hashtag', + 'ellipsis', + 'invisible' + ], + 'Attr.AllowedRel' => [ 'noreferrer', 'noopener', diff --git a/database/migrations/2020_04_13_045435_create_uikit_table.php b/database/migrations/2020_04_13_045435_create_uikit_table.php new file mode 100644 index 000000000..3fa856ff7 --- /dev/null +++ b/database/migrations/2020_04_13_045435_create_uikit_table.php @@ -0,0 +1,39 @@ +bigIncrements('id'); + $table->string('k')->unique()->index(); + $table->text('v')->nullable(); + $table->json('meta')->nullable(); + // default value for rollbacks + $table->text('defv')->nullable(); + // delta history + $table->text('dhis')->nullable(); + $table->unsignedInteger('edit_count')->default(0)->nullable(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('uikit'); + } +} diff --git a/public/js/app.js b/public/js/app.js index b35bd672f..3a52f6073 100644 Binary files a/public/js/app.js and b/public/js/app.js differ diff --git a/public/js/profile.js b/public/js/profile.js index eff773157..83f4e338c 100644 Binary files a/public/js/profile.js and b/public/js/profile.js differ diff --git a/public/js/rempro.js b/public/js/rempro.js index 811a6e606..38f430809 100644 Binary files a/public/js/rempro.js and b/public/js/rempro.js differ diff --git a/public/js/status.js b/public/js/status.js index fe31e0097..6635bae54 100644 Binary files a/public/js/status.js and b/public/js/status.js differ diff --git a/public/js/timeline.js b/public/js/timeline.js index 82f897ce6..2ead8ca83 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 966872bdd..61b9e62c0 100644 Binary files a/public/mix-manifest.json and b/public/mix-manifest.json differ diff --git a/resources/assets/js/app.js b/resources/assets/js/app.js index 97b33d9bb..fcfa03e21 100644 --- a/resources/assets/js/app.js +++ b/resources/assets/js/app.js @@ -69,6 +69,31 @@ window.App.util = { return 0; } return new Intl.NumberFormat(locale, { notation: notation , compactDisplay: "short" }).format(count); + }), + timeAgo: (function(ts) { + let date = Date.parse(ts); + let seconds = Math.floor((new Date() - date) / 1000); + let interval = Math.floor(seconds / 31536000); + if (interval >= 1) { + return interval + "y"; + } + interval = Math.floor(seconds / 604800); + if (interval >= 1) { + return interval + "w"; + } + interval = Math.floor(seconds / 86400); + if (interval >= 1) { + return interval + "d"; + } + interval = Math.floor(seconds / 3600); + if (interval >= 1) { + return interval + "h"; + } + interval = Math.floor(seconds / 60); + if (interval >= 1) { + return interval + "m"; + } + return Math.floor(seconds) + "s"; }) }, filters: [ diff --git a/resources/assets/js/components/NotificationCard.vue b/resources/assets/js/components/NotificationCard.vue index b82d98770..b5a03bc97 100644 --- a/resources/assets/js/components/NotificationCard.vue +++ b/resources/assets/js/components/NotificationCard.vue @@ -2,19 +2,19 @@
+
{{followRequests.count}} Follow Requests
++
{{profile.display_name}}
-{{profile.acct}}
- +{{profile.acct}}
{{profile.statuses_count}} @@ -49,8 +48,10 @@ Followers
+Last updated: