浏览代码

Merge branch 'dev' of https://github.com/dansup/pixelfed into dev

Pierre Jaury 7 年之前
父节点
当前提交
2db1f1174f

+ 1 - 1
README.md

@@ -16,7 +16,7 @@ testing and development.
 
 
 ## Requirements
 ## Requirements
  - PHP >= 7.1.3 (7.2+ recommended for stable version)
  - PHP >= 7.1.3 (7.2+ recommended for stable version)
- - MySQL, Postgres (MariaDB and sqlite are not supported yet)
+ - MySQL >= 5.7, Postgres (MariaDB and sqlite are not supported yet)
  - Redis
  - Redis
  - Composer
  - Composer
  - GD or ImageMagick
  - GD or ImageMagick

+ 14 - 6
app/Http/Controllers/ProfileController.php

@@ -20,14 +20,10 @@ class ProfileController extends Controller
       $user = Profile::whereUsername($username)->firstOrFail();
       $user = Profile::whereUsername($username)->firstOrFail();
       $settings = User::whereUsername($username)->firstOrFail()->settings;
       $settings = User::whereUsername($username)->firstOrFail()->settings;
 
 
-      $mimes = [
-        'application/activity+json', 
-        'application/ld+json; profile="https://www.w3.org/ns/activitystreams"'
-      ];
-
-      if(in_array($request->header('accept'), $mimes) && config('pixelfed.activitypub_enabled')) {
+      if($request->wantsJson() && config('pixelfed.activitypub_enabled')) {
         return $this->showActivityPub($request, $user);
         return $this->showActivityPub($request, $user);
       }
       }
+
       if($user->is_private == true) {
       if($user->is_private == true) {
         $can_access = $this->privateProfileCheck($user);
         $can_access = $this->privateProfileCheck($user);
         if($can_access !== true) {
         if($can_access !== true) {
@@ -48,6 +44,18 @@ class ProfileController extends Controller
       return view('profile.show', compact('user', 'settings', 'owner', 'is_following', 'is_admin', 'timeline'));
       return view('profile.show', compact('user', 'settings', 'owner', 'is_following', 'is_admin', 'timeline'));
     }
     }
 
 
+    public function permalinkRedirect(Request $request, $username)
+    {
+      $user = Profile::whereUsername($username)->firstOrFail();
+      $settings = User::whereUsername($username)->firstOrFail()->settings;
+
+      if($request->wantsJson() && config('pixelfed.activitypub_enabled')) {
+        return $this->showActivityPub($request, $user);
+      }
+
+      return redirect($user->url());
+    }
+
     protected function privateProfileCheck(Profile $profile)
     protected function privateProfileCheck(Profile $profile)
     {
     {
       if(Auth::check() === false) {
       if(Auth::check() === false) {

+ 30 - 7
app/Http/Controllers/StatusController.php

@@ -3,33 +3,40 @@
 namespace App\Http\Controllers;
 namespace App\Http\Controllers;
 
 
 use Auth, Cache;
 use Auth, Cache;
-use App\Jobs\StatusPipeline\{NewStatusPipeline, StatusDelete};
-use App\Jobs\ImageOptimizePipeline\ImageOptimize;
+use League\Fractal;
 use Illuminate\Http\Request;
 use Illuminate\Http\Request;
-use App\{Media, Profile, Status, User};
 use Vinkla\Hashids\Facades\Hashids;
 use Vinkla\Hashids\Facades\Hashids;
+use App\{Media, Profile, Status, User};
+use App\Jobs\ImageOptimizePipeline\ImageOptimize;
+use App\Transformer\ActivityPub\StatusTransformer;
+use App\Jobs\StatusPipeline\{NewStatusPipeline, StatusDelete};
 
 
 class StatusController extends Controller
 class StatusController extends Controller
 {
 {
     public function show(Request $request, $username, int $id)
     public function show(Request $request, $username, int $id)
     {
     {
         $user = Profile::whereUsername($username)->firstOrFail();
         $user = Profile::whereUsername($username)->firstOrFail();
+
         $status = Status::whereProfileId($user->id)
         $status = Status::whereProfileId($user->id)
                 ->withCount(['likes', 'comments', 'media'])
                 ->withCount(['likes', 'comments', 'media'])
                 ->findOrFail($id);
                 ->findOrFail($id);
+
         if(!$status->media_path && $status->in_reply_to_id) {
         if(!$status->media_path && $status->in_reply_to_id) {
           return redirect($status->url());
           return redirect($status->url());
         }
         }
+
+        if($request->wantsJson() && config('pixelfed.activitypub_enabled')) {
+          return $this->showActivityPub($request, $status);
+        }
+
         $replies = Status::whereInReplyToId($status->id)->simplePaginate(30);
         $replies = Status::whereInReplyToId($status->id)->simplePaginate(30);
+
         return view('status.show', compact('user', 'status', 'replies'));
         return view('status.show', compact('user', 'status', 'replies'));
     }
     }
 
 
     public function compose()
     public function compose()
     {
     {
-        if(Auth::check() == false)
-        { 
-          abort(403); 
-        }
+        $this->authCheck();
         return view('status.compose');
         return view('status.compose');
     }
     }
 
 
@@ -156,4 +163,20 @@ class StatusController extends Controller
 
 
         return $response;
         return $response;
     }
     }
+
+    public function showActivityPub(Request $request, $status)
+    {
+      $fractal = new Fractal\Manager();
+      $resource = new Fractal\Resource\Item($status, new StatusTransformer);
+      $res = $fractal->createData($resource)->toArray();
+      return response(json_encode($res['data']))->header('Content-Type', 'application/activity+json');
+    }
+
+    protected function authCheck()
+    {
+        if(Auth::check() == false)
+        { 
+          abort(403); 
+        }
+    }
 }
 }

+ 2 - 2
app/Transformer/ActivityPub/ProfileOutbox.php

@@ -13,7 +13,7 @@ class ProfileOutbox extends Fractal\TransformerAbstract
       $count = $profile->statuses()->count();
       $count = $profile->statuses()->count();
       $statuses = $profile->statuses()->has('media')->orderBy('id','desc')->take(20)->get()->map(function($i, $k) {
       $statuses = $profile->statuses()->has('media')->orderBy('id','desc')->take(20)->get()->map(function($i, $k) {
         $item = [
         $item = [
-          'id'  => $i->url(),
+          'id'  => $i->permalink(),
           // TODO: handle other types
           // TODO: handle other types
           'type' => 'Create',
           'type' => 'Create',
           'actor' => $i->profile->url(),
           'actor' => $i->profile->url(),
@@ -47,7 +47,7 @@ class ProfileOutbox extends Fractal\TransformerAbstract
               // TODO: add cc's
               // TODO: add cc's
               //"{$notice->getProfile()->getUrl()}/subscribers",
               //"{$notice->getProfile()->getUrl()}/subscribers",
             ],
             ],
-            'sensitive' => null,
+            'sensitive' => (bool) $i->is_nsfw,
             'atomUri' => $i->url(),
             'atomUri' => $i->url(),
             'inReplyToAtomUri' => null,
             'inReplyToAtomUri' => null,
             'conversation' => $i->url(),
             'conversation' => $i->url(),

+ 18 - 5
app/Transformer/ActivityPub/ProfileTransformer.php

@@ -11,7 +11,16 @@ class ProfileTransformer extends Fractal\TransformerAbstract
   public function transform(Profile $profile)
   public function transform(Profile $profile)
   {
   {
       return [
       return [
-          '@context' => 'https://www.w3.org/ns/activitystreams',
+          '@context' => [
+            'https://www.w3.org/ns/activitystreams',
+            'https://w3id.org/security/v1',
+            [
+              "manuallyApprovesFollowers" => "as:manuallyApprovesFollowers",
+              "featured" => [
+                "https://pixelfed.org/ns#featured" => ["@type" => "@id"],
+              ]
+            ]
+          ],
           'id' => $profile->permalink(),
           'id' => $profile->permalink(),
           'type' => 'Person',
           'type' => 'Person',
           'following' => $profile->permalink('/following'),
           'following' => $profile->permalink('/following'),
@@ -23,9 +32,9 @@ class ProfileTransformer extends Fractal\TransformerAbstract
           'name'  => $profile->name,
           'name'  => $profile->name,
           'summary' => $profile->bio,
           'summary' => $profile->bio,
           'url' => $profile->url(),
           'url' => $profile->url(),
-          'manuallyApprovesFollowers' => $profile->is_private,
-          'follower_count' => $profile->followers()->count(),
-          'following_count' => $profile->following()->count(),
+          'manuallyApprovesFollowers' => (bool) $profile->is_private,
+          // 'follower_count' => $profile->followers()->count(),
+          // 'following_count' => $profile->following()->count(),
           'publicKey' => [
           'publicKey' => [
             'id' => $profile->permalink() . '#main-key',
             'id' => $profile->permalink() . '#main-key',
             'owner' => $profile->permalink(),
             'owner' => $profile->permalink(),
@@ -34,7 +43,11 @@ class ProfileTransformer extends Fractal\TransformerAbstract
           'endpoints' => [
           'endpoints' => [
             'sharedInbox' => config('routes.api.sharedInbox')
             'sharedInbox' => config('routes.api.sharedInbox')
           ],
           ],
-              
+          'icon' => [
+            'type' => 'Image',
+            'mediaType' => 'image/jpeg',
+            'url' => $profile->avatarUrl()
+          ]
       ];
       ];
   }
   }
 
 

+ 62 - 0
app/Transformer/ActivityPub/StatusTransformer.php

@@ -0,0 +1,62 @@
+<?php
+
+namespace App\Transformer\ActivityPub;
+
+use App\{Profile, Status};
+use League\Fractal;
+
+class StatusTransformer extends Fractal\TransformerAbstract
+{
+
+  public function transform(Status $status)
+  {
+      return [
+          '@context' => [
+            'https://www.w3.org/ns/activitystreams',
+            'https://w3id.org/security/v1',
+            [
+              "manuallyApprovesFollowers" => "as:manuallyApprovesFollowers",
+              "featured" => [
+                "https://pixelfed.org/ns#featured" => ["@type" => "@id"],
+              ]
+            ]
+          ],
+          'id' => $status->url(),
+
+          // TODO: handle other types
+          'type' => 'Note',
+
+          // XXX: CW Title
+          'summary' => null,
+          'content' => $status->rendered ?? $status->caption,
+          'inReplyTo' => null,
+
+          // TODO: fix date format
+          'published' => $status->created_at->toAtomString(),
+          'url' => $status->url(),
+          'attributedTo' => $status->profile->permalink(),
+          'to' => [
+            // TODO: handle proper scope
+            'https://www.w3.org/ns/activitystreams#Public'
+          ],
+          'cc' => [
+            // TODO: add cc's
+            $status->profile->permalink('/followers'),
+          ],
+          'sensitive' => (bool) $status->is_nsfw,
+          'atomUri' => $status->url(),
+          'inReplyToAtomUri' => null,
+          'conversation' => $status->url(),
+          'attachment' => $status->media->map(function($media) {
+            return [
+              'type' => 'Document',
+              'mediaType' => $media->mime,
+              'url' => $media->url(),
+              'name' => null
+            ];
+          }),
+          'tag' => []
+      ];
+  }
+
+}

+ 1 - 1
config/pixelfed.php

@@ -23,7 +23,7 @@ return [
     | This value is the version of your PixelFed instance.
     | This value is the version of your PixelFed instance.
     |
     |
     */
     */
-    'version' => '0.1.5',
+    'version' => '0.1.6',
 
 
     /*
     /*
     |--------------------------------------------------------------------------
     |--------------------------------------------------------------------------

+ 1 - 1
contrib/nginx.conf

@@ -7,7 +7,7 @@ server {
 	root /var/www/html/public;
 	root /var/www/html/public;
 
 
 	location / {
 	location / {
-		try_files $uri $uri/ /index.php;
+		try_files $uri $uri/ /$is_args$args;
 	}
 	}
 
 
 	location ~ \.php$ {
 	location ~ \.php$ {

+ 4 - 0
resources/assets/sass/custom.scss

@@ -294,3 +294,7 @@ details summary::-webkit-details-marker {
 .details-animated[open] > summary {
 .details-animated[open] > summary {
   display: none!important;
   display: none!important;
 }
 }
+
+.profile-avatar img {
+  object-fit: cover;
+}

+ 1 - 0
resources/lang/en/profile.php

@@ -4,5 +4,6 @@ return [
   'emptyTimeline' => 'This user has no posts yet!',
   'emptyTimeline' => 'This user has no posts yet!',
   'emptyFollowers' => 'This user has no followers yet!',
   'emptyFollowers' => 'This user has no followers yet!',
   'emptyFollowing' => 'This user is not following anyone yet!',
   'emptyFollowing' => 'This user is not following anyone yet!',
+  'emptySaved' => 'You haven’t saved any post yet!',
   'savedWarning'  => 'Only you can see what you’ve saved',
   'savedWarning'  => 'Only you can see what you’ve saved',
 ];
 ];

+ 1 - 0
resources/lang/pl/navmenu.php

@@ -9,5 +9,6 @@ return [
     'settings' => 'Ustawienia',
     'settings' => 'Ustawienia',
     'admin' => 'Administrator',
     'admin' => 'Administrator',
     'logout' => 'Wyloguj się',
     'logout' => 'Wyloguj się',
+    'directMessages' => 'Wiadomości bezpośrednie',
 
 
 ];
 ];

+ 4 - 4
resources/lang/pl/notification.php

@@ -2,9 +2,9 @@
 
 
 return [
 return [
 
 
-  'likedPhoto' => 'polubił Twoje zdjęcie.',
-  'startedFollowingYou' => 'zaczął Cię obserwować.',
-  'commented' => 'skomentował Twój wpis',
-  'mentionedYou' => 'wspomniał o Tobie.'
+  'likedPhoto' => 'polubił(a) Twoje zdjęcie.',
+  'startedFollowingYou' => 'zaczął(-ęła) Cię obserwować.',
+  'commented' => 'skomentował(a) Twój wpis',
+  'mentionedYou' => 'wspomniał(a) o Tobie.'
 
 
 ];
 ];

+ 2 - 1
resources/lang/pl/profile.php

@@ -4,5 +4,6 @@ return [
   'emptyTimeline' => 'Ten użytkownik nie opublikował jeszcze niczego!',
   'emptyTimeline' => 'Ten użytkownik nie opublikował jeszcze niczego!',
   'emptyFollowers' => 'Nikt nie obserwuje tego użytkownika!',
   'emptyFollowers' => 'Nikt nie obserwuje tego użytkownika!',
   'emptyFollowing' => 'Ten użytkownik nie obserwuje nikogo!',
   'emptyFollowing' => 'Ten użytkownik nie obserwuje nikogo!',
-  'savedWarning'  => 'Tylko Ty widzisz to, co zapisałeś',
+  'emptySaved' => 'Nie zapisałeś(-aś) jeszcze niczego!',
+  'savedWarning'  => 'Tylko Ty widzisz to, co zapisałeś(-aś)',
 ];
 ];

+ 3 - 3
resources/views/profile/followers.blade.php

@@ -5,11 +5,11 @@
 @include('profile.partial.user-info')
 @include('profile.partial.user-info')
 
 
 <div class="container following-page" style="min-height: 60vh;">
 <div class="container following-page" style="min-height: 60vh;">
-  <div class="col-12 col-md-8 offset-md-2">
+  <div class="col-12 col-md-8 offset-md-2 px-0">
     @if($followers->count() !== 0)
     @if($followers->count() !== 0)
-    <ul class="list-group mt-4">
+    <ul class="list-group mt-4 px-0">
       @foreach($followers as $user)
       @foreach($followers as $user)
-      <li class="list-group-item following">
+      <li class="list-group-item following card-md-rounded-0">
           <span class="following-icon pr-3">
           <span class="following-icon pr-3">
             <img src="{{$user->avatarUrl()}}" width="32px" class="rounded-circle">
             <img src="{{$user->avatarUrl()}}" width="32px" class="rounded-circle">
           </span>
           </span>

+ 3 - 3
resources/views/profile/following.blade.php

@@ -5,11 +5,11 @@
 @include('profile.partial.user-info')
 @include('profile.partial.user-info')
 
 
 <div class="container following-page" style="min-height: 60vh;">
 <div class="container following-page" style="min-height: 60vh;">
-  <div class="col-12 col-md-8 offset-md-2">
+  <div class="col-12 col-md-8 offset-md-2 px-0">
     @if($following->count() !== 0)
     @if($following->count() !== 0)
-    <ul class="list-group mt-4">
+    <ul class="list-group mt-4 px-0">
       @foreach($following as $user)
       @foreach($following as $user)
-      <li class="list-group-item following">
+      <li class="list-group-item following card-md-rounded-0">
           <span class="following-icon pr-3">
           <span class="following-icon pr-3">
             <img src="{{$user->avatarUrl()}}" width="32px" class="rounded-circle">
             <img src="{{$user->avatarUrl()}}" width="32px" class="rounded-circle">
           </span>
           </span>

+ 5 - 1
resources/views/profile/show.blade.php

@@ -56,7 +56,11 @@
         <div class="card">
         <div class="card">
           <div class="card-body py-5 my-5">
           <div class="card-body py-5 my-5">
             <div class="d-flex my-5 py-5 justify-content-center align-items-center">
             <div class="d-flex my-5 py-5 justify-content-center align-items-center">
-              <p class="lead font-weight-bold">{{ __('profile.emptyTimeline') }}</p>
+              @if($owner && request()->is('*/saved'))
+                <p class="lead font-weight-bold">{{ __('profile.emptySaved') }}</p>
+              @else
+                <p class="lead font-weight-bold">{{ __('profile.emptyTimeline') }}</p>
+              @endif
             </div>
             </div>
           </div>
           </div>
       </div>
       </div>

+ 9 - 9
resources/views/status/show.blade.php

@@ -6,14 +6,14 @@
   <div class="card card-md-rounded-0 status-container orientation-{{$status->firstMedia()->orientation ?? 'unknown'}}">
   <div class="card card-md-rounded-0 status-container orientation-{{$status->firstMedia()->orientation ?? 'unknown'}}">
     <div class="row mx-0">
     <div class="row mx-0">
     <div class="d-flex d-md-none align-items-center justify-content-between card-header bg-white w-100">
     <div class="d-flex d-md-none align-items-center justify-content-between card-header bg-white w-100">
-      <div class="d-flex align-items-center status-username">
+      <a href="{{$user->url()}}" class="d-flex align-items-center status-username text-truncate" data-toggle="tooltip" data-placement="bottom" title="{{$user->username}}">
         <div class="status-avatar mr-2">
         <div class="status-avatar mr-2">
-          <img class="img-thumbnail" src="{{$user->avatarUrl()}}" width="24px" height="24px" style="border-radius:12px;">
+          <img src="{{$user->avatarUrl()}}" width="24px" height="24px" style="border-radius:12px;">
         </div>
         </div>
         <div class="username">
         <div class="username">
-          <a href="{{$user->url()}}" class="username-link font-weight-bold text-dark">{{$user->username}}</a>
+          <span class="username-link font-weight-bold text-dark">{{$user->username}}</span>
         </div>
         </div>
-      </div>
+      </a>
      </div>
      </div>
       <div class="col-12 col-md-8 status-photo px-0">
       <div class="col-12 col-md-8 status-photo px-0">
         @if($status->is_nsfw && $status->media_count == 1)
         @if($status->is_nsfw && $status->media_count == 1)
@@ -61,14 +61,14 @@
       </div>
       </div>
       <div class="col-12 col-md-4 px-0 d-flex flex-column border-left border-md-left-0">
       <div class="col-12 col-md-4 px-0 d-flex flex-column border-left border-md-left-0">
         <div class="d-md-flex d-none align-items-center justify-content-between card-header py-3 bg-white">
         <div class="d-md-flex d-none align-items-center justify-content-between card-header py-3 bg-white">
-          <div class="d-flex align-items-center status-username">
+          <a href="{{$user->url()}}" class="d-flex align-items-center status-username text-truncate" data-toggle="tooltip" data-placement="bottom" title="{{$user->username}}">
             <div class="status-avatar mr-2">
             <div class="status-avatar mr-2">
-              <img class="img-thumbnail" src="{{$user->avatarUrl()}}" width="24px" height="24px" style="border-radius:12px;">
+              <img src="{{$user->avatarUrl()}}" width="24px" height="24px" style="border-radius:12px;">
             </div>
             </div>
             <div class="username">
             <div class="username">
-              <a href="{{$user->url()}}" class="username-link font-weight-bold text-dark">{{$user->username}}</a>
+              <span class="username-link font-weight-bold text-dark">{{$user->username}}</span>
             </div>
             </div>
-          </div>
+          </a>
             <div class="float-right">
             <div class="float-right">
               <div class="dropdown">
               <div class="dropdown">
                 <button class="btn btn-link text-dark no-caret dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" title="Post options">
                 <button class="btn btn-link text-dark no-caret dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" title="Post options">
@@ -169,4 +169,4 @@
 @push('meta')
 @push('meta')
 <meta property="og:description" content="{{ $status->caption }}">
 <meta property="og:description" content="{{ $status->caption }}">
     <meta property="og:image" content="{{$status->mediaUrl()}}">
     <meta property="og:image" content="{{$status->mediaUrl()}}">
-@endpush
+    <link href='{{$status->url()}}' rel='alternate' type='application/activity+json'>@endpush

+ 1 - 3
routes/web.php

@@ -123,9 +123,7 @@ Route::domain(config('pixelfed.domain.app'))->middleware('validemail')->group(fu
     Route::redirect('/', '/');
     Route::redirect('/', '/');
     Route::get('{user}.atom', 'ProfileController@showAtomFeed');
     Route::get('{user}.atom', 'ProfileController@showAtomFeed');
     Route::get('{username}/outbox', 'FederationController@userOutbox');
     Route::get('{username}/outbox', 'FederationController@userOutbox');
-    Route::get('{user}', function($user) {
-      return redirect('/'.$user);
-    });
+    Route::get('{username}', 'ProfileController@permalinkRedirect');
   });
   });
 
 
   Route::get('p/{username}/{id}/c/{cid}', 'CommentController@show');
   Route::get('p/{username}/{id}/c/{cid}', 'CommentController@show');