浏览代码

Merge pull request #2220 from pixelfed/staging

Update to Laravel 7.0
daniel 5 年之前
父节点
当前提交
e6984451dd

+ 6 - 0
CHANGELOG.md

@@ -8,6 +8,7 @@
 - Added Bookmarks to v1 api ([99cb48c5](https://github.com/pixelfed/pixelfed/commit/99cb48c5))
 - Added New Post notification to Timeline ([a0e7c4d5](https://github.com/pixelfed/pixelfed/commit/a0e7c4d5))
 - Add Instagram Import ([e2a6bdd0](https://github.com/pixelfed/pixelfed/commit/e2a6bdd0))
+- Add notification preview to NotificationCard ([28445e27](https://github.com/pixelfed/pixelfed/commit/28445e27))
 
 ### Updated
 - Updated PostComponent, fix remote urls ([42716ccc](https://github.com/pixelfed/pixelfed/commit/42716ccc))
@@ -43,6 +44,11 @@
 - Updated PostComponent, improve embed model. Fixes ([#2189](https://github.com/pixelfed/pixelfed/issues/2189)) ([b12e504e](https://github.com/pixelfed/pixelfed/commit/b12e504e))
 - Updated PostComponent, hide edit button after 24 hours. Fixes ([#2188](https://github.com/pixelfed/pixelfed/issues/2188)) ([a1fee6a2](https://github.com/pixelfed/pixelfed/commit/a1fee6a2))
 - Updated AP Inbox, add follow notifications ([b8819fbb](https://github.com/pixelfed/pixelfed/commit/b8819fbb))
+- Updated Api Transformers, fixes ([#2234](https://github.com/pixelfed/pixelfed/issues/2234)) ([63007891](https://github.com/pixelfed/pixelfed/commit/63007891))
+- Updated ApiV1Controller, fix instance endpoint ([#2233](https://github.com/pixelfed/pixelfed/issues/2233)) ([b7ee9981](https://github.com/pixelfed/pixelfed/commit/b7ee9981))
+- Updated AP Inbox, remove trailing comma ([5c443548](https://github.com/pixelfed/pixelfed/commit/5c443548))
+- Updated AP Helpers, update bio + name ([4bee8397](https://github.com/pixelfed/pixelfed/commit/4bee8397))
+- Updated Profile component, add bookmark loader ([c8d5edc9](https://github.com/pixelfed/pixelfed/commit/c8d5edc9))
 
 
 ## [v0.10.9 (2020-04-17)](https://github.com/pixelfed/pixelfed/compare/v0.10.8...v0.10.9)

+ 3 - 3
app/Exceptions/Handler.php

@@ -2,8 +2,8 @@
 
 namespace App\Exceptions;
 
-use Exception;
 use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
+use Throwable;
 
 class Handler extends ExceptionHandler
 {
@@ -33,7 +33,7 @@ class Handler extends ExceptionHandler
      *
      * @return void
      */
-    public function report(Exception $exception)
+    public function report(Throwable $exception)
     {
         parent::report($exception);
     }
@@ -46,7 +46,7 @@ class Handler extends ExceptionHandler
      *
      * @return \Illuminate\Http\Response
      */
-    public function render($request, Exception $exception)
+    public function render($request, Throwable $exception)
     {
         return parent::render($request, $exception);
     }

+ 6 - 6
app/Http/Controllers/Api/ApiV1Controller.php

@@ -938,7 +938,7 @@ class ApiV1Controller extends Controller
             'description' => 'Pixelfed - Photo sharing for everyone',
             'email' => config('instance.email'),
             'languages' => ['en'],
-            'max_toot_chars' => config('pixelfed.max_caption_length'),
+            'max_toot_chars' => (int) config('pixelfed.max_caption_length'),
             'registrations' => config('pixelfed.open_registration'),
             'stats' => [
                 'user_count' => 0,
@@ -951,11 +951,11 @@ class ApiV1Controller extends Controller
             'urls' => [],
             'version' => '2.7.2 (compatible; Pixelfed ' . config('pixelfed.version') . ')',
             'environment' => [
-                'max_photo_size' => config('pixelfed.max_photo_size'),
-                'max_avatar_size' => config('pixelfed.max_avatar_size'),
-                'max_caption_length' => config('pixelfed.max_caption_length'),
-                'max_bio_length' => config('pixelfed.max_bio_length'),
-                'max_album_length' => config('pixelfed.max_album_length'),
+                'max_photo_size' => (int) config('pixelfed.max_photo_size'),
+                'max_avatar_size' => (int) config('pixelfed.max_avatar_size'),
+                'max_caption_length' => (int) config('pixelfed.max_caption_length'),
+                'max_bio_length' => (int) config('pixelfed.max_bio_length'),
+                'max_album_length' => (int) config('pixelfed.max_album_length'),
                 'mobile_apis' => config('pixelfed.oauth_enabled')
 
             ]

+ 3 - 1
app/Http/Kernel.php

@@ -36,11 +36,12 @@ class Kernel extends HttpKernel
             \Illuminate\View\Middleware\ShareErrorsFromSession::class,
             \App\Http\Middleware\VerifyCsrfToken::class,
             \Illuminate\Routing\Middleware\SubstituteBindings::class,
+            // 'restricted',
         ],
 
         'api' => [
             'bindings',
-            \Barryvdh\Cors\HandleCors::class,
+            \Fruitcake\Cors\HandleCors::class,
         ],
     ];
 
@@ -65,5 +66,6 @@ class Kernel extends HttpKernel
         'throttle'      => \Illuminate\Routing\Middleware\ThrottleRequests::class,
         'twofactor'     => \App\Http\Middleware\TwoFactorAuth::class,
         'validemail'    => \App\Http\Middleware\EmailVerificationCheck::class,
+        // 'restricted'    => \App\Http\Middleware\RestrictedAccess::class,
     ];
 }

+ 4 - 2
app/Providers/AuthServiceProvider.php

@@ -27,11 +27,13 @@ class AuthServiceProvider extends ServiceProvider
         $this->registerPolicies();
 
         if(config('pixelfed.oauth_enabled')) {
-            Passport::routes(null, ['middleware' => ['twofactor', \Barryvdh\Cors\HandleCors::class]]);
+            Passport::routes(null, ['middleware' => ['twofactor', \Fruitcake\Cors\HandleCors::class]]);
             Passport::tokensExpireIn(now()->addDays(15));
             Passport::refreshTokensExpireIn(now()->addDays(30));
             Passport::enableImplicitGrant();
-            
+            if(config('instance.oauth.pat.enabled')) {
+                Passport::personalAccessClientId(config('instance.oauth.pat.id'));
+            }
             Passport::setDefaultScope([
                 'read',
                 'write',

+ 2 - 2
app/Transformer/Api/Mastodon/v1/NotificationTransformer.php

@@ -37,10 +37,10 @@ class NotificationTransformer extends Fractal\TransformerAbstract
 			if($status) {
 				return $this->item($status, new StatusTransformer());
 			} else {
-				return null;
+				return $this->collection([], new StatusTransformer());
 			}
 		} else {
-			return null;
+			return $this->collection([], new StatusTransformer());
 		}
 	}
 

+ 2 - 0
app/Transformer/Api/Mastodon/v1/StatusTransformer.php

@@ -63,6 +63,8 @@ class StatusTransformer extends Fractal\TransformerAbstract
             if(in_array($status->type, ['photo', 'video', 'photo:album', 'loop', 'photo:video:album'])) {
                 $media = $status->media()->orderBy('order')->get();
                 return $this->collection($media, new MediaTransformer());
+            } else {
+                return $this->collection([], new MediaTransformer());
             }
         });
     }

+ 14 - 0
app/Util/ActivityPub/Helpers.php

@@ -131,6 +131,10 @@ class Helpers {
 
 	public static function validateUrl($url)
 	{
+		if(is_array($url)) {
+			$url = $url[0];
+		}
+		
 		$localhosts = [
 			'127.0.0.1', 'localhost', '::1'
 		];
@@ -433,6 +437,16 @@ class Helpers {
 				// RemoteFollowImportRecent::dispatch($res, $profile);
 				CreateAvatar::dispatch($profile);
 			}
+		} else {
+			// Update info after 24 hours
+			if($profile->last_fetched_at == null || 
+			   $profile->last_fetched_at->lt(now()->subHours(24)) == true
+			) {
+				$profile->name = isset($res['name']) ? Purify::clean($res['name']) : 'user';
+				$profile->bio = isset($res['summary']) ? Purify::clean($res['summary']) : null;
+				$profile->last_fetched_at = now();
+				$profile->save();
+			}
 		}
 		return $profile;
 	}

+ 1 - 1
app/Util/ActivityPub/Inbox.php

@@ -299,7 +299,7 @@ class Inbox
     {
         if(!isset(
             $this->payload['actor'], 
-            $this->payload['object'], 
+            $this->payload['object'] 
         )) {
             return;
         }

+ 3 - 0
app/Util/Lexer/Extractor.php

@@ -452,6 +452,9 @@ class Extractor extends Regex
             $start_position = $at[1] > 0 ? StringUtils::strlen(substr($tweet, 0, $at[1])) : $at[1];
             $end_position = $start_position + StringUtils::strlen($at[0]) + StringUtils::strlen($username[0]);
             $screenname = trim($all[0]) == '@'.$username[0] ? $username[0] : trim($all[0]);
+            if(config('app.env') == 'production' && \App\Profile::whereUsername($screenname)->exists() == false) {
+                continue;
+            }
             $entity = [
                 'screen_name' => $screenname,
                 'list_slug'   => $list_slug[0],

+ 10 - 11
composer.json

@@ -13,22 +13,23 @@
         "ext-json": "*",
         "ext-mbstring": "*",
         "ext-openssl": "*",
-        "barryvdh/laravel-cors": "^0.11.4",
         "beyondcode/laravel-self-diagnosis": "^1.0.2",
+        "brick/math": "^0.8",
         "doctrine/dbal": "^2.7",
         "fideloper/proxy": "^4.0",
+        "fruitcake/laravel-cors": "^2.0",
         "intervention/image": "^2.4",
         "jenssegers/agent": "^2.6",
-        "laravel/framework": "^6.0",
+        "laravel/framework": "^7.0",
         "laravel/helpers": "^1.1",
-        "laravel/horizon": "^3.3",
+        "laravel/horizon": "^4.0",
         "laravel/passport": "^7.0",
-        "laravel/tinker": "^1.0",
+        "laravel/tinker": "^2.0",
+        "laravel/ui": "^2.0",
         "league/flysystem-aws-s3-v3": "~1.0",
         "league/flysystem-cached-adapter": "~1.0",
         "league/iso3166": "^2.1",
-        "brick/math": "^0.8",
-        "pbmedia/laravel-ffmpeg": "5.0.*",
+        "pbmedia/laravel-ffmpeg": "^7.0",
         "phpseclib/phpseclib": "~2.0",
         "pixelfed/bacon-qr-code": "^3.0",
         "pixelfed/fractal": "^0.18.0",
@@ -41,13 +42,11 @@
         "stevebauman/purify": "3.0.*"
     },
     "require-dev": {
-        "barryvdh/laravel-debugbar": "dev-master",
-        "facade/ignition": "^1.4",
+        "facade/ignition": "^2.0",
         "fzaninotto/faker": "^1.4",
         "mockery/mockery": "^1.0",
-        "nunomaduro/collision": "^3.0",
-        "nunomaduro/phpinsights": "^1.9",
-        "phpunit/phpunit": "^8.0"
+        "nunomaduro/collision": "^4.1",
+        "phpunit/phpunit": "^8.5"
     },
     "autoload": {
         "classmap": [

文件差异内容过多而无法显示
+ 354 - 293
composer.lock


+ 60 - 0
config/cors.php

@@ -0,0 +1,60 @@
+<?php
+
+return [
+
+    /*
+    |--------------------------------------------------------------------------
+    | Laravel CORS Options
+    |--------------------------------------------------------------------------
+    |
+    | The allowed_methods and allowed_headers options are case-insensitive.
+    |
+    | You don't need to provide both allowed_origins and allowed_origins_patterns.
+    | If one of the strings passed matches, it is considered a valid origin.
+    |
+    | If array('*') is provided to allowed_methods, allowed_origins or allowed_headers
+    | all methods / origins / headers are allowed.
+    |
+    */
+
+    /*
+     * You can enable CORS for 1 or multiple paths.
+     * Example: ['api/*']
+     */
+    'paths' => [],
+
+    /*
+    * Matches the request method. `[*]` allows all methods.
+    */
+    'allowed_methods' => ['*'],
+
+    /*
+     * Matches the request origin. `[*]` allows all origins. Wildcards can be used, eg `*.mydomain.com`
+     */
+    'allowed_origins' => ['*'],
+
+    /*
+     * Patterns that can be used with `preg_match` to match the origin.
+     */
+    'allowed_origins_patterns' => [],
+
+    /*
+     * Sets the Access-Control-Allow-Headers response header. `[*]` allows all headers.
+     */
+    'allowed_headers' => ['*'],
+
+    /*
+     * Sets the Access-Control-Expose-Headers response header with these headers.
+     */
+    'exposed_headers' => [],
+
+    /*
+     * Sets the Access-Control-Max-Age response header when > 0.
+     */
+    'max_age' => 0,
+
+    /*
+     * Sets the Access-Control-Allow-Credentials header.
+     */
+    'supports_credentials' => false,
+];

二进制
public/js/profile.js


二进制
public/js/timeline.js


二进制
public/mix-manifest.json


+ 17 - 1
resources/assets/js/components/NotificationCard.vue

@@ -20,7 +20,16 @@
 					<div class="media-body font-weight-light small">
 						<div v-if="n.type == 'favourite'">
 							<p class="my-0">
-								<a :href="n.account.url" class="font-weight-bold text-dark word-break" :title="n.account.username">{{truncate(n.account.username)}}</a> liked your <a class="font-weight-bold" v-bind:href="n.status.url">post</a>.
+								<a :href="n.account.url" class="font-weight-bold text-dark word-break" :title="n.account.username">{{truncate(n.account.username)}}</a> liked your 
+								<span v-if="n.status.hasOwnProperty('media_attachments')">
+									<a class="font-weight-bold" v-bind:href="n.status.url" :id="'fvn-' + n.id">post</a>.
+									<b-popover :target="'fvn-' + n.id" title="" triggers="hover" placement="top" boundary="window">
+										<img :src="notificationPreview(n)" width="100px" height="100px">
+									</b-popover>
+								</span>
+								<span v-else>
+									<a class="font-weight-bold" v-bind:href="n.status.url">post</a>.
+								</span>
 							</p>
 						</div>
 						<div v-else-if="n.type == 'comment'">
@@ -219,6 +228,13 @@
 
 			redirect(url) {
 				window.location.href = url;
+			},
+
+			notificationPreview(n) {
+				if(!n.status.hasOwnProperty('media_attachments') || !n.status.media_attachments.length) {
+					return '/storage/no-preview.png';
+				}
+				return n.status.media_attachments[0].preview_url;
 			}
 		}
 	}

+ 40 - 27
resources/assets/js/components/Profile.vue

@@ -210,35 +210,46 @@
 						</infinite-loading>
 					</div>
 					<div v-if="mode == 'bookmarks'">
-						<div v-if="bookmarks.length" class="row">
-							<div class="col-4 p-1 p-sm-2 p-md-3" v-for="(s, index) in bookmarks">
-								<a class="card info-overlay card-md-border-0" :href="s.url">
-									<div class="square">
-										<span v-if="s.pf_type == 'photo:album'" class="float-right mr-3 post-icon"><i class="fas fa-images fa-2x"></i></span>
-										<span v-if="s.pf_type == 'video'" class="float-right mr-3 post-icon"><i class="fas fa-video fa-2x"></i></span>
-										<span v-if="s.pf_type == 'video:album'" class="float-right mr-3 post-icon"><i class="fas fa-film fa-2x"></i></span>
-										<div class="square-content" v-bind:style="previewBackground(s)">
-										</div>
-										<div class="info-overlay-text">
-											<h5 class="text-white m-auto font-weight-bold">
-												<span>
-													<span class="far fa-heart fa-lg p-2 d-flex-inline"></span>
-													<span class="d-flex-inline">{{s.favourites_count}}</span>
-												</span>
-												<span>
-													<span class="fas fa-retweet fa-lg p-2 d-flex-inline"></span>
-													<span class="d-flex-inline">{{s.reblogs_count}}</span>
-												</span>
-											</h5>
-										</div>
+						<div v-if="bookmarksLoading">
+							<div class="row">
+								<div class="col-12">
+									<div class="p-1 p-sm-2 p-md-3 d-flex justify-content-center align-items-center" style="height: 30vh;">
+										<img src="/img/pixelfed-icon-grey.svg" class="">
 									</div>
-								</a>
+								</div>
 							</div>
 						</div>
-						<div v-else class="col-12">
-							<div class="py-5 text-center text-muted">
-								<p><i class="fas fa-bookmark fa-2x"></i></p>
-								<p class="h2 font-weight-light pt-3">No saved bookmarks</p>
+						<div v-else>
+							<div v-if="bookmarks.length" class="row">
+								<div class="col-4 p-1 p-sm-2 p-md-3" v-for="(s, index) in bookmarks">
+									<a class="card info-overlay card-md-border-0" :href="s.url">
+										<div class="square">
+											<span v-if="s.pf_type == 'photo:album'" class="float-right mr-3 post-icon"><i class="fas fa-images fa-2x"></i></span>
+											<span v-if="s.pf_type == 'video'" class="float-right mr-3 post-icon"><i class="fas fa-video fa-2x"></i></span>
+											<span v-if="s.pf_type == 'video:album'" class="float-right mr-3 post-icon"><i class="fas fa-film fa-2x"></i></span>
+											<div class="square-content" v-bind:style="previewBackground(s)">
+											</div>
+											<div class="info-overlay-text">
+												<h5 class="text-white m-auto font-weight-bold">
+													<span>
+														<span class="far fa-heart fa-lg p-2 d-flex-inline"></span>
+														<span class="d-flex-inline">{{s.favourites_count}}</span>
+													</span>
+													<span>
+														<span class="fas fa-retweet fa-lg p-2 d-flex-inline"></span>
+														<span class="d-flex-inline">{{s.reblogs_count}}</span>
+													</span>
+												</h5>
+											</div>
+										</div>
+									</a>
+								</div>
+							</div>
+							<div v-else class="col-12">
+								<div class="py-5 text-center text-muted">
+									<p><i class="fas fa-bookmark fa-2x"></i></p>
+									<p class="h2 font-weight-light pt-3">No saved bookmarks</p>
+								</div>
 							</div>
 						</div>
 					</div>
@@ -641,6 +652,7 @@
 				followingModalSearch: null,
 				followingModalSearchCache: null,
 				followingModalTab: 'following',
+				bookmarksLoading: true,
 			}
 		},
 		beforeMount() {
@@ -789,7 +801,8 @@
 				if(this.mode == 'bookmarks' && this.bookmarks.length == 0) {
 					axios.get('/api/local/bookmarks')
 					.then(res => {
-						this.bookmarks = res.data
+						this.bookmarks = res.data;
+						this.bookmarksLoading = false;
 					});
 				}
 				if(this.mode == 'collections' && this.collections.length == 0) {

+ 12 - 6
resources/assets/js/components/StoryTimelineComponent.vue

@@ -1,7 +1,7 @@
 <template>
 	<div>
-		<div v-if="stories.length != 0">
-			<div id="storyContainer" :class="[list == true ? 'mt-1 mr-3 mb-0 ml-1':'m-3']"></div>
+		<div v-if="show" class="card card-body p-0 border mt-4 mb-3 shadow-none">
+			<div id="storyContainer" :class="[list == true ? 'mt-1 mr-3 mb-0 ml-1':'mx-3 mt-3 mb-0 pb-0']"></div>
 		</div>
 	</div>
 </template>
@@ -21,6 +21,7 @@
 		props: ['list'],
 		data() {
 			return {
+				show: false,
 				stories: {},
 			}
 		},
@@ -34,6 +35,10 @@
 				axios.get('/api/stories/v0/recent')
 				.then(res => {
 					let data = res.data;
+					if(!res.data.length) {
+						this.show = false;
+						return;
+					}
 					let stories = new Zuck('storyContainer', {
 						list: this.list == true ? true : false,
 						stories: data,
@@ -70,6 +75,7 @@
 						});
 					});
 				});
+				this.show = true;
 			}
 		}
 	}
@@ -79,17 +85,17 @@
 	#storyContainer .story {
 		margin-right: 2rem;
 		width: 100%;
-		max-width: 64px;
+		max-width: 60px;
 	}
 	.stories.carousel .story > .item-link > .item-preview {
-		height: 64px;
+		height: 60px;
 	}
 	#zuck-modal.with-effects {
 		width: 100%;
 	}
 	.stories.carousel .story > .item-link > .info .name {
-		font-weight: 600;
-		font-size: 12px;
+		font-weight: 500;
+		font-size: 11px;
 	}
 	.stories.carousel .story > .item-link > .info {
 	}

+ 3 - 2
resources/assets/js/components/Timeline.vue

@@ -7,7 +7,7 @@
 			</p>
 		</div>
 		<div :class="[modes.distractionFree ? 'col-md-8 col-lg-8 offset-md-2 px-0 mb-sm-3 timeline order-2 order-md-1':'col-md-8 col-lg-8 px-0 mb-sm-3 timeline order-2 order-md-1']">
-			<div v-if="config.features.stories">
+			<div style="margin-top:32px;">
 				<story-component v-if="config.features.stories"></story-component>
 			</div>
 			<div>
@@ -1533,8 +1533,9 @@
 						}
 					}).then(res => {
 						let self = this;
+						let tids = this.feed.map(status => status.id);
 						let data = res.data.filter(d => {
-							return d.id > self.min_id
+							return d.id > self.min_id && tids.indexOf(d.id) == -1;
 						});
 						let ids = data.map(status => status.id);
 						let max = Math.max(...ids).toString();

部分文件因为文件数量过多而无法显示