ソースを参照

Merge pull request #4847 from pixelfed/staging

Staging
daniel 1 年間 前
コミット
d8a5dc00bb

+ 118 - 0
app/Console/Commands/ImportEmojis.php

@@ -0,0 +1,118 @@
+<?php
+
+namespace App\Console\Commands;
+
+use App\Models\CustomEmoji;
+use Illuminate\Console\Command;
+use Illuminate\Support\Facades\Cache;
+use Illuminate\Support\Facades\Storage;
+
+class ImportEmojis extends Command
+{
+    /**
+     * The name and signature of the console command.
+     *
+     * @var string
+     */
+    protected $signature = 'import:emojis
+                            {path : Path to a tar.gz archive with the emojis}
+                            {--prefix : Define a prefix for the emjoi shortcode}
+                            {--suffix : Define a suffix for the emjoi shortcode}
+                            {--overwrite : Overwrite existing emojis}
+                            {--disabled : Import all emojis as disabled}';
+
+
+    /**
+     * The console command description.
+     *
+     * @var string
+     */
+    protected $description = 'Import emojis to the database';
+
+    /**
+     * Execute the console command.
+     *
+     * @return int
+     */
+    public function handle()
+    {
+        $path = $this->argument('path');
+
+        if (!file_exists($path) || !mime_content_type($path) == 'application/x-tar') {
+            $this->error('Path does not exist or is not a tarfile');
+            return Command::FAILURE;
+        }
+
+        $imported = 0;
+        $skipped = 0;
+        $failed = 0;
+
+        $tar = new \PharData($path);
+        $tar->decompress();
+
+        foreach (new \RecursiveIteratorIterator($tar) as $entry) {
+            $this->line("Processing {$entry->getFilename()}");
+            if (!$entry->isFile() || !$this->isImage($entry) || !$this->isEmoji($entry->getPathname())) {
+                $failed++;
+                continue;
+            }
+
+            $filename = pathinfo($entry->getFilename(), PATHINFO_FILENAME);
+            $extension = pathinfo($entry->getFilename(), PATHINFO_EXTENSION);
+
+            // Skip macOS shadow files
+            if (str_starts_with($filename, '._')) {
+                continue;
+            }
+
+            $shortcode = implode('', [
+                $this->option('prefix'),
+                $filename,
+                $this->option('suffix'),
+            ]);
+
+            $customEmoji = CustomEmoji::whereShortcode($shortcode)->first();
+
+            if ($customEmoji && !$this->option('overwrite')) {
+                $skipped++;
+                continue;
+            }
+
+            $emoji = $customEmoji ?? new CustomEmoji();
+            $emoji->shortcode = $shortcode;
+            $emoji->domain = config('pixelfed.domain.app');
+            $emoji->disabled = $this->option('disabled');
+            $emoji->save();
+
+            $fileName = $emoji->id . '.' . $extension;
+            Storage::putFileAs('public/emoji', $entry->getPathname(), $fileName);
+            $emoji->media_path = 'emoji/' . $fileName;
+            $emoji->save();
+            $imported++;
+            Cache::forget('pf:custom_emoji');
+        }
+
+        $this->line("Imported: {$imported}");
+        $this->line("Skipped: {$skipped}");
+        $this->line("Failed: {$failed}");
+
+        //delete file
+        unlink(str_replace('.tar.gz', '.tar', $path));
+
+        return Command::SUCCESS;
+    }
+
+    private function isImage($file)
+    {
+        $image = getimagesize($file->getPathname());
+        return $image !== false;
+    }
+
+    private function isEmoji($filename)
+    {
+        $allowedMimeTypes = ['image/png', 'image/jpeg', 'image/webp'];
+        $mimeType = mime_content_type($filename);
+
+        return in_array($mimeType, $allowedMimeTypes);
+    }
+}

+ 54 - 37
app/Util/Webfinger/Webfinger.php

@@ -4,43 +4,60 @@ namespace App\Util\Webfinger;
 
 class Webfinger
 {
-	protected $user;
-	protected $subject;
-	protected $aliases;
-	protected $links;
+    protected $user;
+    protected $subject;
+    protected $aliases;
+    protected $links;
 
-	public function __construct($user)
-	{
-		$this->subject = 'acct:'.$user->username.'@'.parse_url(config('app.url'), PHP_URL_HOST);
-		$this->aliases = [
-			$user->url(),
-			$user->permalink(),
-		];
-		$this->links = [
-			[
-				'rel'  => 'http://webfinger.net/rel/profile-page',
-				'type' => 'text/html',
-				'href' => $user->url(),
-			],
-			[
-				'rel'  => 'http://schemas.google.com/g/2010#updates-from',
-				'type' => 'application/atom+xml',
-				'href' => $user->permalink('.atom'),
-			],
-			[
-				'rel'  => 'self',
-				'type' => 'application/activity+json',
-				'href' => $user->permalink(),
-			],
-		];
-	}
+    public function __construct($user)
+    {
+        $avatar = $user ? $user->avatarUrl() : url('/storage/avatars/default.jpg');
+        $avatarPath = parse_url($avatar, PHP_URL_PATH);
+        $extension = pathinfo($avatarPath, PATHINFO_EXTENSION);
+        $mimeTypes = [
+            'jpg' => 'image/jpeg',
+            'jpeg' => 'image/jpeg',
+            'png' => 'image/png',
+            'gif' => 'image/gif',
+            'svg' => 'image/svg',
+        ];
+        $avatarType = $mimeTypes[$extension] ?? 'application/octet-stream';
 
-	public function generate()
-	{
-		return [
-			'subject' => $this->subject,
-			'aliases' => $this->aliases,
-			'links'   => $this->links,
-		];
-	}
+        $this->subject = 'acct:'.$user->username.'@'.parse_url(config('app.url'), PHP_URL_HOST);
+        $this->aliases = [
+            $user->url(),
+            $user->permalink(),
+        ];
+        $this->links = [
+            [
+                'rel'  => 'http://webfinger.net/rel/profile-page',
+                'type' => 'text/html',
+                'href' => $user->url(),
+            ],
+            [
+                'rel'  => 'http://schemas.google.com/g/2010#updates-from',
+                'type' => 'application/atom+xml',
+                'href' => $user->permalink('.atom'),
+            ],
+            [
+                'rel'  => 'self',
+                'type' => 'application/activity+json',
+                'href' => $user->permalink(),
+            ],
+            [
+                'rel' => 'http://webfinger.net/rel/avatar',
+                'type' => $avatarType,
+                'href' => $avatar,
+            ],
+        ];
+    }
+
+    public function generate()
+    {
+        return [
+            'subject' => $this->subject,
+            'aliases' => $this->aliases,
+            'links'   => $this->links,
+        ];
+    }
 }

+ 20 - 2
resources/assets/js/components/NotificationCard.vue

@@ -34,7 +34,16 @@
 						</div>
 						<div v-else-if="n.type == 'comment'">
 							<p class="my-0">
-								<a :href="getProfileUrl(n.account)" class="font-weight-bold text-dark word-break" :title="n.account.username">{{n.account.local == false ? '@':''}}{{truncate(n.account.username)}}</a> commented on your <a class="font-weight-bold" v-bind:href="getPostUrl(n.status)">post</a>.
+								<a :href="getProfileUrl(n.account)" class="font-weight-bold text-dark word-break" :title="n.account.username">{{n.account.local == false ? '@':''}}{{truncate(n.account.username)}}</a> commented on your
+								<span v-if="n.status && n.status.hasOwnProperty('media_attachments')">
+									<a class="font-weight-bold" v-bind:href="getPostUrl(n.status)" :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" style="object-fit: cover;">
+									</b-popover>
+								</span>
+								<span v-else>
+									<a class="font-weight-bold" v-bind:href="getPostUrl(n.status)">post</a>.
+								</span>
 							</p>
 						</div>
 						<div v-else-if="n.type == 'group:comment'">
@@ -64,7 +73,16 @@
 						</div>
 						<div v-else-if="n.type == 'share'">
 							<p class="my-0">
-								<a :href="getProfileUrl(n.account)" class="font-weight-bold text-dark word-break" :title="n.account.username">{{n.account.local == false ? '@':''}}{{truncate(n.account.username)}}</a> shared your <a class="font-weight-bold" v-bind:href="getPostUrl(n.status)">post</a>.
+								<a :href="getProfileUrl(n.account)" class="font-weight-bold text-dark word-break" :title="n.account.username">{{n.account.local == false ? '@':''}}{{truncate(n.account.username)}}</a> shared your
+								<span v-if="n.status && n.status.hasOwnProperty('media_attachments')">
+									<a class="font-weight-bold" v-bind:href="getPostUrl(n.status)" :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" style="object-fit: cover;">
+									</b-popover>
+								</span>
+								<span v-else>
+									<a class="font-weight-bold" v-bind:href="getPostUrl(n.status)">post</a>.
+								</span>
 							</p>
 						</div>
 						<div v-else-if="n.type == 'modlog'">