Add `wcf1_user.avatarFileID` column
authorCyperghost <olaf_schmitz_1@t-online.de>
Wed, 6 Nov 2024 10:41:11 +0000 (11:41 +0100)
committerCyperghost <olaf_schmitz_1@t-online.de>
Wed, 6 Nov 2024 10:41:11 +0000 (11:41 +0100)
wcfsetup/install/files/acp/database/update_com.woltlab.wcf_6.2.php [new file with mode: 0644]
wcfsetup/install/files/lib/data/user/User.class.php
wcfsetup/install/files/lib/system/file/processor/UserAvatarFileProcessor.class.php
wcfsetup/setup/db/install.sql

diff --git a/wcfsetup/install/files/acp/database/update_com.woltlab.wcf_6.2.php b/wcfsetup/install/files/acp/database/update_com.woltlab.wcf_6.2.php
new file mode 100644 (file)
index 0000000..c275d35
--- /dev/null
@@ -0,0 +1,29 @@
+<?php
+
+/**
+ * Updates the database layout during the update from 6.1 to 6.2.
+ *
+ * @author    Olaf Braun
+ * @copyright 2001-2024 WoltLab GmbH
+ * @license   GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ */
+
+use wcf\system\database\table\column\IntDatabaseTableColumn;
+use wcf\system\database\table\index\DatabaseTableForeignKey;
+use wcf\system\database\table\PartialDatabaseTable;
+
+return [
+    PartialDatabaseTable::create('wcf1_user')
+        ->columns([
+            IntDatabaseTableColumn::create('avatarFileID')
+                ->length(10)
+                ->defaultValue(null),
+        ])
+        ->foreignKeys([
+            DatabaseTableForeignKey::create()
+                ->columns(['avatarFileID'])
+                ->referencedTable('wcf1_file')
+                ->referencedColumns(['fileID'])
+                ->onDelete('SET NULL'),
+        ]),
+];
index f3eecdfa3d6448eea136f789996afdfef285364e..dc96fb7086ccca8f197012f1d5c67f77133f8611 100644 (file)
@@ -48,6 +48,7 @@ use wcf\util\UserUtil;
  * @property-read   int $reactivationCode       code used for authenticating setting new email address or empty if no new email address has been set
  * @property-read   string $registrationIpAddress      ip address of the user at the time of registration or empty if user has been created manually or if no ip address are logged
  * @property-read   int|null $avatarID           id of the user's avatar or null if they have no avatar
+ * @property-read   int|null $avatarFileID           id of the user's avatar core file or null if they have no avatar
  * @property-read   int $disableAvatar          is `1` if the user's avatar has been disabled, otherwise `0`
  * @property-read   string $disableAvatarReason        reason why the user's avatar is disabled
  * @property-read   int $disableAvatarExpires       timestamp at which the user's avatar will automatically be enabled again
index 0421ca20cf47bdedc6c0d1b878c4bdaf85cee994..2f2a6c25237568922bcc6faad7d797d2c11f2fce 100644 (file)
@@ -3,11 +3,15 @@
 namespace wcf\system\file\processor;
 
 use wcf\data\file\File;
-use wcf\data\file\thumbnail\FileThumbnail;
 use wcf\data\user\avatar\UserAvatar;
 use wcf\data\user\User;
+use wcf\data\user\UserEditor;
 use wcf\system\cache\runtime\UserRuntimeCache;
+use wcf\system\database\util\PreparedStatementConditionBuilder;
+use wcf\system\exception\UserInputException;
+use wcf\system\user\storage\UserStorageHandler;
 use wcf\system\WCF;
+use wcf\util\FileUtil;
 
 /**
  * @author      Olaf Braun
@@ -32,42 +36,91 @@ final class UserAvatarFileProcessor extends AbstractFileProcessor
     #[\Override]
     public function canAdopt(File $file, array $context): bool
     {
-        // TODO
-        return true;
+        $userFromContext = $this->getUser($context);
+        $userFromCoreFile = $this->getUserByFile($file);
+
+        if ($userFromContext === null) {
+            return true;
+        }
+
+        if ($userFromContext->userID === $userFromCoreFile->userID) {
+            return true;
+        }
+
+        return false;
     }
 
     #[\Override]
     public function adopt(File $file, array $context): void
     {
-        // TODO
+        $user = $this->getUser($context);
+        if ($user === null) {
+            return;
+        }
+
+        (new UserEditor($user))->update([
+            'avatarFileID' => $file->fileID,
+        ]);
+        // reset user storage
+        UserStorageHandler::getInstance()->reset([$user->userID], 'avatar');
     }
 
     #[\Override]
     public function acceptUpload(string $filename, int $fileSize, array $context): FileProcessorPreflightResult
     {
-        // TODO
+        $user = $this->getUser($context);
+
+        if ($user === null) {
+            return FileProcessorPreflightResult::InvalidContext;
+        }
+
+        if (!$this->canEditAvatar($user)) {
+            return FileProcessorPreflightResult::InsufficientPermissions;
+        }
+
+        if ($fileSize > $this->getMaximumSize($context)) {
+            return FileProcessorPreflightResult::FileSizeTooLarge;
+        }
+
+        if (!FileUtil::endsWithAllowedExtension($filename, $this->getAllowedFileExtensions($context))) {
+            return FileProcessorPreflightResult::FileExtensionNotPermitted;
+        }
+
         return FileProcessorPreflightResult::Passed;
     }
 
     #[\Override]
-    public function canDelete(File $file): bool
+    public function validateUpload(File $file): void
     {
-        // TODO
-        return true;
+        $imageData = @\getimagesize($file->getPathname());
+        if ($imageData === false) {
+            throw new UserInputException('file', 'noImage');
+        }
+
+        if ($imageData[0] !== $imageData[1]) {
+            throw new UserInputException('file', 'notSquare');
+        }
+
+        if ($imageData[0] != UserAvatar::AVATAR_SIZE && $imageData[0] != UserAvatar::AVATAR_SIZE_2X) {
+            throw new UserInputException('file', 'wrongSize');
+        }
     }
 
     #[\Override]
-    public function canDownload(File $file): bool
+    public function canDelete(File $file): bool
     {
-        // TODO
-        return true;
+        $user = $this->getUserByFile($file);
+        if ($user === null) {
+            return false;
+        }
+
+        return $this->canEditAvatar($user);
     }
 
     #[\Override]
-    public function getMaximumCount(array $context): ?int
+    public function canDownload(File $file): bool
     {
-        // TODO
-        return null;
+        return true;
     }
 
     #[\Override]
@@ -84,22 +137,28 @@ final class UserAvatarFileProcessor extends AbstractFileProcessor
         ];
     }
 
-    #[\Override]
-    public function adoptThumbnail(FileThumbnail $thumbnail): void
-    {
-        // TODO
-    }
-
     #[\Override]
     public function delete(array $fileIDs, array $thumbnailIDs): void
     {
-        // TODO
+        $conditionBuilder = new PreparedStatementConditionBuilder();
+        $conditionBuilder->add('avatarFileID IN (?)', [$fileIDs]);
+
+        $sql = "UPDATE wcf1_user
+                SET    avatarFileID = ?
+                " . $conditionBuilder;
+        $statement = WCF::getDB()->prepare($sql);
+        $statement->execute([null, ...$conditionBuilder->getParameters()]);
     }
 
     #[\Override]
     public function countExistingFiles(array $context): ?int
     {
-        // TODO
+        $user = $this->getUser($context);
+        if ($user === null) {
+            return null;
+        }
+
+        return $user->avatarFileID === null ? 0 : 1;
     }
 
     #[\Override]
@@ -135,4 +194,32 @@ final class UserAvatarFileProcessor extends AbstractFileProcessor
 
         return UserRuntimeCache::getInstance()->getObject($userID);
     }
+
+    private function getUserByFile(File $file): ?User
+    {
+        $sql = "SELECT *
+                FROM   wcf1_user
+                WHERE  avatarFileID = ?";
+        $statement = WCF::getDB()->prepare($sql);
+        $statement->execute([$file->fileID]);
+
+        return $statement->fetchObject(User::class);
+    }
+
+    private function canEditAvatar(User $user): bool
+    {
+        if (WCF::getSession()->getPermission('admin.user.canEditUser')) {
+            return true;
+        }
+
+        if ($user->userID !== WCF::getUser()->userID) {
+            return false;
+        }
+
+        if (WCF::getUser()->disableAvatar) {
+            return false;
+        }
+
+        return WCF::getSession()->getPermission('user.profile.avatar.canUploadAvatar');
+    }
 }
index bcacca9395884baa9e9ce640a034a22d76d87034..eda9b5ca3fd321354db588871ed194f6fe309d85 100644 (file)
@@ -1558,6 +1558,7 @@ CREATE TABLE wcf1_user (
        reactivationCode INT(10) NOT NULL DEFAULT 0,
        registrationIpAddress VARCHAR(39) NOT NULL DEFAULT '',
        avatarID INT(10),
+       avatarFileID INT(10) DEFAULT NULL,
        disableAvatar TINYINT(1) NOT NULL DEFAULT 0,
        disableAvatarReason TEXT,
        disableAvatarExpires INT(10) NOT NULL DEFAULT 0,