<?php
namespace wcf\data\user;
use wcf\data\object\type\ObjectTypeCache;
-use wcf\data\user\cover\photo\UserCoverPhoto;
use wcf\data\user\group\UserGroup;
use wcf\system\bbcode\BBCodeHandler;
use wcf\system\cache\runtime\UserProfileRuntimeCache;
use wcf\system\database\util\PreparedStatementConditionBuilder;
use wcf\system\exception\PermissionDeniedException;
-use wcf\system\exception\SystemException;
use wcf\system\exception\UserInputException;
use wcf\system\html\input\HtmlInputProcessor;
use wcf\system\html\output\HtmlOutputProcessor;
-use wcf\system\image\ImageHandler;
use wcf\system\message\embedded\object\MessageEmbeddedObjectManager;
use wcf\system\option\user\UserOptionHandler;
use wcf\system\upload\UploadFile;
use wcf\system\upload\UploadHandler;
+use wcf\system\upload\UserCoverPhotoUploadFileSaveStrategy;
use wcf\system\upload\UserCoverPhotoUploadFileValidationStrategy;
use wcf\system\user\group\assignment\UserGroupAssignmentHandler;
use wcf\system\user\storage\UserStorageHandler;
use wcf\system\WCF;
use wcf\util\ArrayUtil;
-use wcf\util\FileUtil;
use wcf\util\MessageUtil;
use wcf\util\StringUtil;
* @since 3.1
*/
public function uploadCoverPhoto() {
+ $saveStrategy = new UserCoverPhotoUploadFileSaveStrategy((!empty($this->parameters['userID']) ? intval($this->parameters['userID']) : WCF::getUser()->userID));
+ /** @noinspection PhpUndefinedMethodInspection */
+ $this->parameters['__files']->saveFiles($saveStrategy);
+
if ($this->uploadFile->getValidationErrorType()) {
return [
'filesize' => $this->uploadFile->getFilesize(),
'errorType' => $this->uploadFile->getValidationErrorType()
];
}
-
- try {
- // shrink cover photo if necessary
- $fileLocation = $this->enforceCoverPhotoDimensions($this->uploadFile->getLocation());
- }
- catch (UserInputException $e) {
- return [
- 'filesize' => $this->uploadFile->getFilesize(),
- 'errorMessage' => WCF::getLanguage()->getDynamicVariable('wcf.user.coverPhoto.upload.error.' . $e->getType(), [
- 'file' => $this->uploadFile
- ]),
- 'errorType' => $e->getType()
- ];
- }
-
- // delete old cover photo
- if ($this->user->coverPhotoHash) {
- UserProfileRuntimeCache::getInstance()->getObject($this->user->userID)->getCoverPhoto()->delete();
- }
-
- // update user
- (new UserEditor($this->user))->update([
- // always generate a new hash to invalidate the browser cache and to avoid filename guessing
- 'coverPhotoHash' => StringUtil::getRandomID(),
- 'coverPhotoExtension' => $this->uploadFile->getFileExtension()
- ]);
-
- // force-reload the user profile to use a predictable code-path to fetch the cover photo
- UserProfileRuntimeCache::getInstance()->removeObject($this->user->userID);
- $userProfile = UserProfileRuntimeCache::getInstance()->getObject($this->user->userID);
- $coverPhoto = $userProfile->getCoverPhoto();
-
- // check images directory and create subdirectory if necessary
- $dir = dirname($coverPhoto->getLocation());
- if (!@file_exists($dir)) {
- FileUtil::makePath($dir);
- }
-
- if (@copy($fileLocation, $coverPhoto->getLocation())) {
- @unlink($fileLocation);
-
- return [
- 'url' => $coverPhoto->getURL()
- ];
- }
else {
return [
- 'filesize' => $this->uploadFile->getFilesize(),
- 'errorMessage' => WCF::getLanguage()->getDynamicVariable('wcf.user.coverPhoto.upload.error.uploadFailed', [
- 'file' => $this->uploadFile
- ]),
- 'errorType' => 'uploadFailed'
+ 'url' => $saveStrategy->getCoverPhoto()->getURL()
];
}
}
return $optionHandler;
}
-
- /**
- * Enforces dimensions for given cover photo.
- *
- * @param string $filename
- * @return string
- * @throws UserInputException
- */
- protected function enforceCoverPhotoDimensions($filename) {
- $imageData = getimagesize($filename);
- if ($imageData[0] > UserCoverPhoto::MAX_WIDTH || $imageData[1] > UserCoverPhoto::MAX_HEIGHT) {
- try {
- $adapter = ImageHandler::getInstance()->getAdapter();
- $adapter->loadFile($filename);
- $filename = FileUtil::getTemporaryFilename();
- $thumbnail = $adapter->createThumbnail(UserCoverPhoto::MAX_WIDTH, UserCoverPhoto::MAX_HEIGHT);
- $adapter->writeImage($thumbnail, $filename);
- }
- catch (SystemException $e) {
- throw new UserInputException('coverPhoto', 'maxSize');
- }
-
- // check dimensions (after shrink)
- $imageData = getimagesize($filename);
- if ($imageData[0] < UserCoverPhoto::MIN_WIDTH || $imageData[1] < UserCoverPhoto::MIN_HEIGHT) {
- throw new UserInputException('coverPhoto', 'maxSize');
- }
-
- // check filesize (after shrink)
- if (@filesize($filename) > WCF::getSession()->getPermission('user.profile.coverPhoto.maxSize')) {
- throw new UserInputException('coverPhoto', 'maxSize');
- }
- }
-
- return $filename;
- }
}
use wcf\system\exception\PermissionDeniedException;
use wcf\system\exception\SystemException;
use wcf\system\exception\UserInputException;
-use wcf\system\image\ImageHandler;
+use wcf\system\upload\AvatarUploadFileSaveStrategy;
use wcf\system\upload\AvatarUploadFileValidationStrategy;
use wcf\system\upload\UploadFile;
use wcf\system\user\storage\UserStorageHandler;
* Handles uploaded attachments.
*/
public function upload() {
- /** @var UploadFile[] $files */
+ /** @var UploadFile $file */
+ $file = $this->parameters['__files']->getFiles()[0];
+ $saveStrategy = new AvatarUploadFileSaveStrategy((!empty($this->parameters['userID']) ? intval($this->parameters['userID']) : WCF::getUser()->userID));
/** @noinspection PhpUndefinedMethodInspection */
- $files = $this->parameters['__files']->getFiles();
- $userID = (!empty($this->parameters['userID']) ? intval($this->parameters['userID']) : WCF::getUser()->userID);
- $user = ($userID != WCF::getUser()->userID ? new User($userID) : WCF::getUser());
- $file = $files[0];
+ $this->parameters['__files']->saveFiles($saveStrategy);
- try {
- if (!$file->getValidationErrorType()) {
- // shrink avatar if necessary
- $fileLocation = $this->enforceDimensions($file->getLocation());
- $imageData = getimagesize($fileLocation);
-
- $data = [
- 'avatarName' => $file->getFilename(),
- 'avatarExtension' => $file->getFileExtension(),
- 'width' => $imageData[0],
- 'height' => $imageData[1],
- 'userID' => $userID,
- 'fileHash' => sha1_file($fileLocation)
- ];
-
- // create avatar
- $avatar = UserAvatarEditor::create($data);
-
- // check avatar directory
- // and create subdirectory if necessary
- $dir = dirname($avatar->getLocation());
- if (!@file_exists($dir)) {
- FileUtil::makePath($dir);
- }
-
- // move uploaded file
- if (@copy($fileLocation, $avatar->getLocation())) {
- @unlink($fileLocation);
-
- // delete old avatar
- if ($user->avatarID) {
- $action = new UserAvatarAction([$user->avatarID], 'delete');
- $action->executeAction();
- }
-
- // update user
- $userEditor = new UserEditor($user);
- $userEditor->update([
- 'avatarID' => $avatar->avatarID,
- 'enableGravatar' => 0
- ]);
-
- // reset user storage
- UserStorageHandler::getInstance()->reset([$userID], 'avatar');
-
- // return result
- return [
- 'avatarID' => $avatar->avatarID,
- 'url' => $avatar->getURL(96)
- ];
- }
- else {
- // moving failed; delete avatar
- $editor = new UserAvatarEditor($avatar);
- $editor->delete();
- throw new UserInputException('avatar', 'uploadFailed');
- }
- }
+ if ($file->getValidationErrorType()) {
+ return ['errorType' => $file->getValidationErrorType()];
}
- catch (UserInputException $e) {
- $file->setValidationErrorType($e->getType());
+ else {
+ return [
+ 'avatarID' => $saveStrategy->getAvatar()->avatarID,
+ 'url' => $saveStrategy->getAvatar()->getURL(96)
+ ];
}
-
- return ['errorType' => $file->getValidationErrorType()];
}
/**
* @throws UserInputException
*/
protected function enforceDimensions($filename) {
- $imageData = getimagesize($filename);
- if ($imageData[0] > UserAvatar::AVATAR_SIZE || $imageData[1] > UserAvatar::AVATAR_SIZE) {
- try {
- $adapter = ImageHandler::getInstance()->getAdapter();
- $adapter->loadFile($filename);
- $filename = FileUtil::getTemporaryFilename();
- $thumbnail = $adapter->createThumbnail(UserAvatar::AVATAR_SIZE, UserAvatar::AVATAR_SIZE, false);
- $adapter->writeImage($thumbnail, $filename);
- }
- catch (SystemException $e) {
- throw new UserInputException('avatar', 'tooLarge');
- }
-
- // check filesize (after shrink)
- if (@filesize($filename) > WCF::getSession()->getPermission('user.profile.avatar.maxSize')) {
- throw new UserInputException('avatar', 'tooLarge');
- }
+ try {
+ $filename = ImageUtil::enforceDimensions($filename, UserAvatar::AVATAR_SIZE, UserAvatar::AVATAR_SIZE);
+ }
+ /** @noinspection PhpRedundantCatchClauseInspection */
+ catch (SystemException $e) {
+ throw new UserInputException('avatar', 'tooLarge');
+ }
+
+ // check filesize (after shrink)
+ if (@filesize($filename) > WCF::getSession()->getPermission('user.profile.avatar.maxSize')) {
+ throw new UserInputException('avatar', 'tooLarge');
}
return $filename;
--- /dev/null
+<?php
+namespace wcf\system\upload;
+use wcf\data\user\avatar\UserAvatar;
+use wcf\data\user\avatar\UserAvatarAction;
+use wcf\data\user\avatar\UserAvatarEditor;
+use wcf\data\user\User;
+use wcf\data\user\UserEditor;
+use wcf\system\exception\SystemException;
+use wcf\system\user\storage\UserStorageHandler;
+use wcf\system\WCF;
+use wcf\util\FileUtil;
+use wcf\util\ImageUtil;
+
+/**
+ * Save strategy for avatar uploads.
+ *
+ * @author Marcel Werk
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\System\Upload
+ * @since 5.2
+ */
+class AvatarUploadFileSaveStrategy implements IUploadFileSaveStrategy {
+ /**
+ * @var integer
+ */
+ protected $userID = 0;
+
+ /**
+ * @var User
+ */
+ protected $user;
+
+ /**
+ * @var UserAvatar
+ */
+ protected $avatar;
+
+ /**
+ * Creates a new instance of AvatarUploadFileSaveStrategy.
+ *
+ * @param integer $userID
+ */
+ public function __construct($userID = null) {
+ $this->userID = ($userID ?: WCF::getUser()->userID);
+ $this->user = ($this->userID != WCF::getUser()->userID ? new User($userID) : WCF::getUser());
+ }
+
+ /**
+ * @return UserAvatar
+ */
+ public function getAvatar() {
+ return $this->avatar;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function save(UploadFile $uploadFile) {
+ if (!$uploadFile->getValidationErrorType()) {
+ // rotate avatar if necessary
+ /** @noinspection PhpUnusedLocalVariableInspection */
+ $fileLocation = ImageUtil::fixOrientation($uploadFile->getLocation());
+
+ // shrink avatar if necessary
+ try {
+ $fileLocation = ImageUtil::enforceDimensions($fileLocation, UserAvatar::AVATAR_SIZE, UserAvatar::AVATAR_SIZE, false);
+ }
+ /** @noinspection PhpRedundantCatchClauseInspection */
+ catch (SystemException $e) {
+ $uploadFile->setValidationErrorType('tooLarge');
+ return;
+ }
+
+ // check filesize (after shrink)
+ if (@filesize($fileLocation) > WCF::getSession()->getPermission('user.profile.avatar.maxSize')) {
+ $uploadFile->setValidationErrorType('tooLarge');
+ return;
+ }
+
+ $imageData = getimagesize($fileLocation);
+ $data = [
+ 'avatarName' => $uploadFile->getFilename(),
+ 'avatarExtension' => $uploadFile->getFileExtension(),
+ 'width' => $imageData[0],
+ 'height' => $imageData[1],
+ 'userID' => $this->userID,
+ 'fileHash' => sha1_file($fileLocation)
+ ];
+
+ // create avatar
+ $this->avatar = UserAvatarEditor::create($data);
+
+ // check avatar directory
+ // and create subdirectory if necessary
+ $dir = dirname($this->avatar->getLocation());
+ if (!@file_exists($dir)) {
+ FileUtil::makePath($dir);
+ }
+
+ // move uploaded file
+ if (@copy($fileLocation, $this->avatar->getLocation())) {
+ @unlink($fileLocation);
+
+ // delete old avatar
+ if ($this->user->avatarID) {
+ $action = new UserAvatarAction([$this->user->avatarID], 'delete');
+ $action->executeAction();
+ }
+
+ // update user
+ $userEditor = new UserEditor($this->user);
+ $userEditor->update([
+ 'avatarID' => $this->avatar->avatarID,
+ 'enableGravatar' => 0
+ ]);
+
+ // reset user storage
+ UserStorageHandler::getInstance()->reset([$this->userID], 'avatar');
+ }
+ else {
+ // moving failed; delete avatar
+ $editor = new UserAvatarEditor($this->avatar);
+ $editor->delete();
+
+ $uploadFile->setValidationErrorType('uploadFailed');
+ }
+ }
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\upload;
+use wcf\data\user\cover\photo\IUserCoverPhoto;
+use wcf\data\user\cover\photo\UserCoverPhoto;
+use wcf\data\user\User;
+use wcf\data\user\UserEditor;
+use wcf\system\cache\runtime\UserProfileRuntimeCache;
+use wcf\system\exception\SystemException;
+use wcf\system\WCF;
+use wcf\util\FileUtil;
+use wcf\util\ImageUtil;
+use wcf\util\StringUtil;
+
+/**
+ * Save strategy for user cover photos.
+ *
+ * @author Marcel Werk
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\System\Upload
+ * @since 5.2
+ */
+class UserCoverPhotoUploadFileSaveStrategy implements IUploadFileSaveStrategy {
+ /**
+ * @var integer
+ */
+ protected $userID = 0;
+
+ /**
+ * @var User
+ */
+ protected $user;
+
+ /**
+ * @var IUserCoverPhoto
+ */
+ protected $coverPhoto;
+
+ /**
+ * Creates a new instance of UserCoverPhotoUploadFileSaveStrategy.
+ *
+ * @param integer $userID
+ */
+ public function __construct($userID = null) {
+ $this->userID = ($userID ?: WCF::getUser()->userID);
+ $this->user = ($this->userID != WCF::getUser()->userID ? new User($userID) : WCF::getUser());
+ }
+
+ /**
+ * @return IUserCoverPhoto
+ */
+ public function getCoverPhoto() {
+ return $this->coverPhoto;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function save(UploadFile $uploadFile) {
+ if (!$uploadFile->getValidationErrorType()) {
+ // rotate image if necessary
+ /** @noinspection PhpUnusedLocalVariableInspection */
+ $fileLocation = ImageUtil::fixOrientation($uploadFile->getLocation());
+
+ // shrink cover photo if necessary
+ try {
+ $newFileLocation = ImageUtil::enforceDimensions($fileLocation, UserCoverPhoto::MAX_WIDTH, UserCoverPhoto::MAX_HEIGHT);
+ }
+ /** @noinspection PhpRedundantCatchClauseInspection */
+ catch (SystemException $e) {
+ $uploadFile->setValidationErrorType('maxSize');
+ return;
+ }
+
+ if ($newFileLocation != $fileLocation) {
+ // check dimensions (after shrink)
+ $imageData = getimagesize($newFileLocation);
+ if ($imageData[0] < UserCoverPhoto::MIN_WIDTH || $imageData[1] < UserCoverPhoto::MIN_HEIGHT) {
+ $uploadFile->setValidationErrorType('tooLarge');
+ return;
+ }
+
+ // check filesize (after shrink)
+ if (@filesize($newFileLocation) > WCF::getSession()->getPermission('user.profile.coverPhoto.maxSize')) {
+ $uploadFile->setValidationErrorType('tooLarge');
+ return;
+ }
+ }
+ $fileLocation = $newFileLocation;
+
+ // delete old cover photo
+ if ($this->user->coverPhotoHash) {
+ UserProfileRuntimeCache::getInstance()->getObject($this->user->userID)->getCoverPhoto()->delete();
+ }
+
+ // update user
+ (new UserEditor($this->user))->update([
+ // always generate a new hash to invalidate the browser cache and to avoid filename guessing
+ 'coverPhotoHash' => StringUtil::getRandomID(),
+ 'coverPhotoExtension' => $uploadFile->getFileExtension()
+ ]);
+
+ // force-reload the user profile to use a predictable code-path to fetch the cover photo
+ UserProfileRuntimeCache::getInstance()->removeObject($this->user->userID);
+ $userProfile = UserProfileRuntimeCache::getInstance()->getObject($this->user->userID);
+ $this->coverPhoto = $userProfile->getCoverPhoto();
+
+ // check images directory and create subdirectory if necessary
+ $dir = dirname($this->coverPhoto->getLocation());
+ if (!@file_exists($dir)) {
+ FileUtil::makePath($dir);
+ }
+
+ // move uploaded file
+ if (!@copy($fileLocation, $this->coverPhoto->getLocation())) {
+ // copy failed
+ @unlink($fileLocation);
+ (new UserEditor($this->user))->update([
+ 'coverPhotoHash' => '',
+ 'coverPhotoExtension' => ''
+ ]);
+ $uploadFile->setValidationErrorType('uploadFailed');
+ }
+
+ @unlink($fileLocation);
+ }
+ }
+}
<?php
namespace wcf\util;
+use wcf\system\exception\SystemException;
+use wcf\system\image\ImageHandler;
/**
* Contains image-related functions.
}
}
+ /**
+ * Enforces dimensions for given image.
+ *
+ * @param string $filename
+ * @param integer $maxWidth
+ * @param integer $maxHeight
+ * @param boolean $obtainDimensions
+ * @return string new filename if file was changed, otherwise old filename
+ * @since 5.2
+ */
+ public static function enforceDimensions($filename, $maxWidth, $maxHeight, $obtainDimensions = true) {
+ $imageData = getimagesize($filename);
+ if ($imageData[0] > $maxWidth || $imageData[1] > $maxHeight) {
+ $adapter = ImageHandler::getInstance()->getAdapter();
+ $adapter->loadFile($filename);
+ $filename = FileUtil::getTemporaryFilename();
+ $thumbnail = $adapter->createThumbnail($maxWidth, $maxHeight, $obtainDimensions);
+ $adapter->writeImage($thumbnail, $filename);
+ }
+
+ return $filename;
+ }
+
+ /**
+ * Rotates the given image based on the orientation stored in the exif data.
+ *
+ * @param string $filename
+ * @return string new filename if file was changed, otherwise old filename
+ * @since 5.2
+ */
+ public static function fixOrientation($filename) {
+ try {
+ $exifData = ExifUtil::getExifData($filename);
+ if (!empty($exifData)) {
+ $orientation = ExifUtil::getOrientation($exifData);
+ if ($orientation != ExifUtil::ORIENTATION_ORIGINAL) {
+ $adapter = ImageHandler::getInstance()->getAdapter();
+ $adapter->loadFile($filename);
+
+ $newImage = null;
+ switch ($orientation) {
+ case ExifUtil::ORIENTATION_180_ROTATE:
+ $newImage = $adapter->rotate(180);
+ break;
+
+ case ExifUtil::ORIENTATION_90_ROTATE:
+ $newImage = $adapter->rotate(90);
+ break;
+
+ case ExifUtil::ORIENTATION_270_ROTATE:
+ $newImage = $adapter->rotate(270);
+ break;
+
+ case ExifUtil::ORIENTATION_HORIZONTAL_FLIP:
+ case ExifUtil::ORIENTATION_VERTICAL_FLIP:
+ case ExifUtil::ORIENTATION_VERTICAL_FLIP_270_ROTATE:
+ case ExifUtil::ORIENTATION_HORIZONTAL_FLIP_270_ROTATE:
+ // unsupported
+ break;
+ }
+
+ if ($newImage !== null) {
+ $adapter->load($newImage, $adapter->getType());
+ }
+
+ $newFilename = FileUtil::getTemporaryFilename();
+ $adapter->writeImage($newFilename);
+ $filename = $newFilename;
+ }
+ }
+ }
+ catch (SystemException $e) {}
+
+ return $filename;
+ }
+
/**
* Forbid creation of ImageUtil objects.
*/