From: Marcel Werk Date: Sun, 24 Jul 2016 21:04:07 +0000 (+0200) Subject: Overhauled user profile header and avatar sizes X-Git-Tag: 3.0.0_Beta_1~975 X-Git-Url: https://git.stricted.de/?a=commitdiff_plain;h=2772d4eb46511bc1cdc431bb80c184f8172dd500;p=GitHub%2FWoltLab%2FWCF.git Overhauled user profile header and avatar sizes --- diff --git a/CHANGELOG.md b/CHANGELOG.md index 771e5bae34..9406d35d6d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -154,6 +154,8 @@ * Obsolete interface `wcf\page\ITrackablePage` deprecated. * PIP `wcf\system\package\plugin\SitemapPackageInstallationPlugin` removed. * Option `share_buttons_show_count` removed. +* Option `max_avatar_width` removed. +* Option `max_avatar_height` removed. #### Documentation diff --git a/com.woltlab.wcf/option.xml b/com.woltlab.wcf/option.xml index 8e84ee19c4..f1d6162ddf 100644 --- a/com.woltlab.wcf/option.xml +++ b/com.woltlab.wcf/option.xml @@ -1186,20 +1186,6 @@ monsterid:wcf.acp.option.gravatar_default_type.monsterid retro:wcf.acp.option.gravatar_default_type.retro]]> module_gravatar - - diff --git a/com.woltlab.wcf/templates/avatarCropDialog.tpl b/com.woltlab.wcf/templates/avatarCropDialog.tpl deleted file mode 100644 index 8dca628c77..0000000000 --- a/com.woltlab.wcf/templates/avatarCropDialog.tpl +++ /dev/null @@ -1,16 +0,0 @@ -
-
{lang}wcf.user.avatar.type.custom.crop{/lang}
-
-
- {@$avatar->getImageTag()} -
-
-
- - {lang}wcf.user.avatar.type.custom.crop.description{/lang} -
-
- -
- -
diff --git a/com.woltlab.wcf/templates/avatarEdit.tpl b/com.woltlab.wcf/templates/avatarEdit.tpl index 3706998700..cd290589ed 100644 --- a/com.woltlab.wcf/templates/avatarEdit.tpl +++ b/com.woltlab.wcf/templates/avatarEdit.tpl @@ -26,11 +26,7 @@
{if $avatarType == 'custom'} - {if $__wcf->getUserProfileHandler()->getAvatar()->canCrop()} - {@$__wcf->getUserProfileHandler()->getAvatar()->getCropImageTag(96)} - {else} - {@$__wcf->getUserProfileHandler()->getAvatar()->getImageTag(96)} - {/if} + {@$__wcf->getUserProfileHandler()->getAvatar()->getImageTag(96)} {else} {/if} @@ -84,7 +80,6 @@ //user->disableAvatar} - {if $__wcf->getUserProfileHandler()->getAvatar()->canCrop()} - new WCF.User.Avatar.Upload(0, new WCF.User.Avatar.Crop({@$__wcf->getUserProfileHandler()->getAvatar()->avatarID})); - {else} - new WCF.User.Avatar.Upload(); - {/if} + new WCF.User.Avatar.Upload(); {/if} }); //]]> diff --git a/com.woltlab.wcf/templates/user.tpl b/com.woltlab.wcf/templates/user.tpl index 57eeddd76d..3ba1c173a1 100644 --- a/com.woltlab.wcf/templates/user.tpl +++ b/com.woltlab.wcf/templates/user.tpl @@ -1,7 +1,5 @@ {capture assign='pageTitle'}{$user->username} - {lang}wcf.user.members{/lang}{/capture} -{assign var='contentHeader' value=' '}{* necessary to hide default content header in heade.tpl *} - {capture assign='headContent'} {event name='javascriptInclude'} diff --git a/wcfsetup/install/files/js/WCF.User.js b/wcfsetup/install/files/js/WCF.User.js index 96071f286a..2619609796 100644 --- a/wcfsetup/install/files/js/WCF.User.js +++ b/wcfsetup/install/files/js/WCF.User.js @@ -2286,202 +2286,12 @@ WCF.User.Action.Ignore = Class.extend({ */ WCF.User.Avatar = {}; -/** - * Handles cropping an avatar. - */ -WCF.User.Avatar.Crop = Class.extend({ - /** - * current crop setting in x-direction - * @var integer - */ - _cropX: 0, - - /** - * current crop setting in y-direction - * @var integer - */ - _cropY: 0, - - /** - * avatar crop dialog - * @var jQuery - */ - _dialog: null, - - /** - * action proxy to send the crop AJAX requests - * @var WCF.Action.Proxy - */ - _proxy: null, - - /** - * maximum size of thumbnails - * @var integer - */ - MAX_THUMBNAIL_SIZE: 128, - - /** - * Creates a new instance of WCF.User.Avatar.Crop. - * - * @param integer avatarID - */ - init: function(avatarID) { - this._avatarID = avatarID; - - if (this._dialog) { - this.destroy(); - } - this._dialog = null; - - // check if object already had been initialized - if (!this._proxy) { - this._proxy = new WCF.Action.Proxy({ - success: $.proxy(this._success, this) - }); - } - - $('.userAvatarCrop').click($.proxy(this._showCropDialog, this)); - }, - - /** - * Destroys the avatar crop interface. - */ - destroy: function() { - this._dialog.remove(); - }, - - /** - * Sends AJAX request to crop avatar. - * - * @param object event - */ - _crop: function(event) { - this._proxy.setOption('data', { - actionName: 'cropAvatar', - className: 'wcf\\data\\user\\avatar\\UserAvatarAction', - objectIDs: [ this._avatarID ], - parameters: { - cropX: this._cropX, - cropY: this._cropY - } - }); - this._proxy.sendRequest(); - }, - - /** - * Initializes the dialog after a successful 'getCropDialog' request. - * - * @param object data - */ - _getCropDialog: function(data) { - if (!this._dialog) { - this._dialog = $('
').hide().appendTo(document.body); - this._dialog.wcfDialog({ - title: WCF.Language.get('wcf.user.avatar.type.custom.crop') - }); - } - - this._dialog.html(data.returnValues.template); - this._dialog.find('button[data-type="save"]').click($.proxy(this._crop, this)); - - this._cropX = data.returnValues.cropX; - this._cropY = data.returnValues.cropY; - - var $image = $('#userAvatarCropSelection > img'); - $('#userAvatarCropSelection').css({ - height: $image.height() + 'px', - width: $image.width() + 'px' - }); - $('#userAvatarCropOverlaySelection').css({ - 'background-image': 'url(' + $image.attr('src') + ')', - 'background-position': -this._cropX + 'px ' + -this._cropY + 'px', - 'left': this._cropX + 'px', - 'top': this._cropY + 'px' - }).draggable({ - containment: 'parent', - drag : $.proxy(this._updateSelection, this), - stop : $.proxy(this._updateSelection, this) - }); - - this._dialog.find('button[data-type="save"]').click($.proxy(this._save, this)); - - this._dialog.wcfDialog('render'); - }, - - /** - * Shows the cropping dialog. - */ - _showCropDialog: function() { - if (!this._dialog) { - this._proxy.setOption('data', { - actionName: 'getCropDialog', - className: 'wcf\\data\\user\\avatar\\UserAvatarAction', - objectIDs: [ this._avatarID ] - }); - this._proxy.sendRequest(); - } - else { - this._dialog.wcfDialog('open'); - } - }, - - /** - * Handles successful AJAX request. - * - * @param object data - * @param string textStatus - * @param jQuery jqXHR - */ - _success: function(data, textStatus, jqXHR) { - switch (data.actionName) { - case 'getCropDialog': - this._getCropDialog(data); - break; - - case 'cropAvatar': - $('#avatarUpload > dt > img').replaceWith($('').css({ - width: '96px', - height: '96px' - }).click($.proxy(this._showCropDialog, this))); - - WCF.DOMNodeInsertedHandler.execute(); - - this._dialog.wcfDialog('close'); - - var $notification = new WCF.System.Notification(); - $notification.show(); - break; - } - }, - - /** - * Updates the current crop selection if the selection overlay is dragged. - * - * @param object event - * @param object ui - */ - _updateSelection: function(event, ui) { - this._cropX = ui.position.left; - this._cropY = ui.position.top; - - $('#userAvatarCropOverlaySelection').css({ - 'background-position': -ui.position.left + 'px ' + -ui.position.top + 'px' - }); - } -}); - /** * Avatar upload function * * @see WCF.Upload */ WCF.User.Avatar.Upload = WCF.Upload.extend({ - /** - * handles cropping the avatar - * @var WCF.User.Avatar.Crop - */ - _avatarCrop: null, - /** * user id of avatar owner * @var integer @@ -2492,12 +2302,10 @@ WCF.User.Avatar.Upload = WCF.Upload.extend({ * Initalizes a new WCF.User.Avatar.Upload object. * * @param integer userID - * @param WCF.User.Avatar.Crop avatarCrop */ - init: function(userID, avatarCrop) { + init: function(userID) { this._super($('#avatarUpload > dd > div'), undefined, 'wcf\\data\\user\\avatar\\UserAvatarAction'); this._userID = userID || 0; - this._avatarCrop = avatarCrop; $('#avatarForm input[type=radio]').change(function() { if ($(this).val() == 'custom') { @@ -2524,20 +2332,7 @@ WCF.User.Avatar.Upload = WCF.Upload.extend({ */ _success: function(uploadID, data) { if (data.returnValues.url) { - this._updateImage(data.returnValues.url, data.returnValues.canCrop); - - if (data.returnValues.canCrop) { - if (!this._avatarCrop) { - this._avatarCrop = new WCF.User.Avatar.Crop(data.returnValues.avatarID); - } - else { - this._avatarCrop.init(data.returnValues.avatarID); - } - } - else if (this._avatarCrop) { - this._avatarCrop.destroy(); - this._avatarCrop = null; - } + this._updateImage(data.returnValues.url); // hide error $('#avatarUpload > dd > .innerError').remove(); @@ -2556,9 +2351,8 @@ WCF.User.Avatar.Upload = WCF.Upload.extend({ * Updates the displayed avatar image. * * @param string url - * @param boolean canCrop */ - _updateImage: function(url, canCrop) { + _updateImage: function(url) { $('#avatarUpload > dt > img').remove(); var $image = $('').css({ 'height': 'auto', @@ -2566,10 +2360,6 @@ WCF.User.Avatar.Upload = WCF.Upload.extend({ 'max-width': '96px', 'width': 'auto' }); - if (canCrop) { - $image.addClass('userAvatarCrop').addClass('jsTooltip'); - $image.attr('title', WCF.Language.get('wcf.user.avatar.type.custom.crop')); - } $('#avatarUpload > dt').prepend($image); diff --git a/wcfsetup/install/files/js/WoltLab/WCF/Ui/User/Editor.js b/wcfsetup/install/files/js/WoltLab/WCF/Ui/User/Editor.js index c7cd50e922..05c6d7048b 100644 --- a/wcfsetup/install/files/js/WoltLab/WCF/Ui/User/Editor.js +++ b/wcfsetup/install/files/js/WoltLab/WCF/Ui/User/Editor.js @@ -24,7 +24,7 @@ define(['Ajax', 'Language', 'StringUtil', 'Dom/Util', 'Ui/Dialog', 'Ui/Notificat // init buttons ['ban', 'disableAvatar', 'disableSignature', 'enable'].forEach((function(action) { - var button = elBySel('.jsButtonUser' + StringUtil.ucfirst(action), _userHeader); + var button = elBySel('.userProfileButtonMenu .jsButtonUser' + StringUtil.ucfirst(action)); // button is missing if users lacks the permission if (button) { @@ -121,7 +121,7 @@ define(['Ajax', 'Language', 'StringUtil', 'Dom/Util', 'Ui/Dialog', 'Ui/Notificat case 'ban': case 'unban': elData(_userHeader, 'banned', (data.actionName === 'ban')); - elBySel('.jsButtonUserBan', _userHeader).textContent = Language.get('wcf.user.' + (data.actionName === 'ban' ? 'unban' : 'ban')); + elBySel('.userProfileButtonMenu .jsButtonUserBan').textContent = Language.get('wcf.user.' + (data.actionName === 'ban' ? 'unban' : 'ban')); var contentTitle = elBySel('.contentTitle', _userHeader); var banIcon = elBySel('.jsUserBanned', contentTitle); @@ -140,21 +140,21 @@ define(['Ajax', 'Language', 'StringUtil', 'Dom/Util', 'Ui/Dialog', 'Ui/Notificat case 'disableAvatar': case 'enableAvatar': elData(_userHeader, 'disable-avatar', (data.actionName === 'disableAvatar')); - elBySel('.jsButtonUserDisableAvatar', _userHeader).textContent = Language.get('wcf.user.' + (data.actionName === 'disableAvatar' ? 'enable' : 'disable') + 'Avatar'); + elBySel('.userProfileButtonMenu .jsButtonUserDisableAvatar').textContent = Language.get('wcf.user.' + (data.actionName === 'disableAvatar' ? 'enable' : 'disable') + 'Avatar'); break; case 'disableSignature': case 'enableSignature': elData(_userHeader, 'disable-signature', (data.actionName === 'disableSignature')); - elBySel('.jsButtonUserDisableSignature', _userHeader).textContent = Language.get('wcf.user.' + (data.actionName === 'disableSignature' ? 'enable' : 'disable') + 'Signature'); + elBySel('.userProfileButtonMenu .jsButtonUserDisableSignature').textContent = Language.get('wcf.user.' + (data.actionName === 'disableSignature' ? 'enable' : 'disable') + 'Signature'); break; case 'enable': case 'disable': elData(_userHeader, 'is-disabled', (data.actionName === 'disable')); - elBySel('.jsButtonUserEnable', _userHeader).textContent = Language.get('wcf.acp.user.' + (data.actionName === 'enable' ? 'disable' : 'enable')); + elBySel('.userProfileButtonMenu .jsButtonUserEnable').textContent = Language.get('wcf.acp.user.' + (data.actionName === 'enable' ? 'disable' : 'enable')); break; } diff --git a/wcfsetup/install/files/lib/action/GravatarDownloadAction.class.php b/wcfsetup/install/files/lib/action/GravatarDownloadAction.class.php index 046c61feaf..e6916ae784 100644 --- a/wcfsetup/install/files/lib/action/GravatarDownloadAction.class.php +++ b/wcfsetup/install/files/lib/action/GravatarDownloadAction.class.php @@ -34,7 +34,7 @@ class GravatarDownloadAction extends AbstractAction { * avatar size * @var integer */ - public $size = 150; + public $size = UserAvatar::AVATAR_SIZE; /** * @inheritDoc @@ -47,13 +47,6 @@ class GravatarDownloadAction extends AbstractAction { if (!$this->user->userID) { throw new IllegalLinkException(); } - - if (!empty($_REQUEST['size'])) { - $this->size = intval($_REQUEST['size']); - if (!in_array($this->size, UserAvatar::$avatarThumbnailSizes)) { - $this->size = 150; - } - } } /** diff --git a/wcfsetup/install/files/lib/data/user/avatar/DefaultAvatar.class.php b/wcfsetup/install/files/lib/data/user/avatar/DefaultAvatar.class.php index 0a61b1db37..bcf78dddef 100644 --- a/wcfsetup/install/files/lib/data/user/avatar/DefaultAvatar.class.php +++ b/wcfsetup/install/files/lib/data/user/avatar/DefaultAvatar.class.php @@ -16,7 +16,7 @@ class DefaultAvatar implements IUserAvatar { * image size * @var integer */ - public $size = 150; + public $size = UserAvatar::AVATAR_SIZE; /** * @inheritDoc diff --git a/wcfsetup/install/files/lib/data/user/avatar/Gravatar.class.php b/wcfsetup/install/files/lib/data/user/avatar/Gravatar.class.php index a7923862bd..b220e7bcbd 100644 --- a/wcfsetup/install/files/lib/data/user/avatar/Gravatar.class.php +++ b/wcfsetup/install/files/lib/data/user/avatar/Gravatar.class.php @@ -54,9 +54,9 @@ class Gravatar extends DefaultAvatar { /** * urls of this gravatar - * @var string[] + * @var string */ - protected $url = []; + protected $url = ''; /** * Creates a new Gravatar object. @@ -75,34 +75,20 @@ class Gravatar extends DefaultAvatar { * @inheritDoc */ public function getURL($size = null) { - if ($size === null) $size = $this->size; - else { - switch ($size) { - case 16: - case 24: - $size = 32; - break; - case 48: - case 64: - $size = 96; - break; - } - } - - if (!isset($this->url[$size])) { + if (empty($this->url)) { // try to use cached gravatar - $cachedFilename = sprintf(self::GRAVATAR_CACHE_LOCATION, md5(mb_strtolower($this->gravatar)), $size, $this->fileExtension); + $cachedFilename = sprintf(self::GRAVATAR_CACHE_LOCATION, md5(mb_strtolower($this->gravatar)), $this->size, $this->fileExtension); if (file_exists(WCF_DIR.$cachedFilename) && filemtime(WCF_DIR.$cachedFilename) > (TIME_NOW - (self::GRAVATAR_CACHE_EXPIRE * 86400))) { - $this->url[$size] = WCF::getPath().$cachedFilename; + $this->url = WCF::getPath().$cachedFilename; } else { - $this->url[$size] = LinkHandler::getInstance()->getLink('GravatarDownload', [ + $this->url = LinkHandler::getInstance()->getLink('GravatarDownload', [ 'forceFrontend' => true - ], 'userID='.$this->userID.'&size='.$size); + ], 'userID='.$this->userID); } } - return $this->url[$size]; + return $this->url; } /** @@ -122,35 +108,4 @@ class Gravatar extends DefaultAvatar { return false; } } - - /** - * @inheritDoc - */ - public function getImageTag($size = null) { - if ($size === null) $size = $this->size; - - $retinaSize = null; - switch ($size) { - case 16: - $retinaSize = 32; - break; - case 24: - case 32: - case 48: - $retinaSize = 96; - break; - case 96: - $retinaSize = 128; - break; - } - - return ''; - } - - /** - * @inheritDoc - */ - public function canCrop() { - return false; - } } diff --git a/wcfsetup/install/files/lib/data/user/avatar/IUserAvatar.class.php b/wcfsetup/install/files/lib/data/user/avatar/IUserAvatar.class.php index 03ca3289ea..80ea37c123 100644 --- a/wcfsetup/install/files/lib/data/user/avatar/IUserAvatar.class.php +++ b/wcfsetup/install/files/lib/data/user/avatar/IUserAvatar.class.php @@ -14,6 +14,7 @@ interface IUserAvatar { * Returns true if this avatar can be cropped. * * @return boolean + * @deprecated 3.0 */ public function canCrop(); @@ -38,6 +39,7 @@ interface IUserAvatar { * * @param integer $size * @return string + * @deprecated 3.0 */ public function getCropImageTag($size = null); diff --git a/wcfsetup/install/files/lib/data/user/avatar/UserAvatar.class.php b/wcfsetup/install/files/lib/data/user/avatar/UserAvatar.class.php index f6a877a0d0..fd6811949e 100644 --- a/wcfsetup/install/files/lib/data/user/avatar/UserAvatar.class.php +++ b/wcfsetup/install/files/lib/data/user/avatar/UserAvatar.class.php @@ -19,13 +19,12 @@ use wcf\system\WCF; * @property-read integer $height * @property-read integer|null $userID * @property-read string $fileHash - * @property-read integer $cropX - * @property-read integer $cropY */ class UserAvatar extends DatabaseObject implements IUserAvatar { /** * needed avatar thumbnail sizes * @var integer[] + * @deprecated 3.0 */ public static $avatarThumbnailSizes = [32, 96, 128, 256]; @@ -42,15 +41,23 @@ class UserAvatar extends DatabaseObject implements IUserAvatar { /** * maximum thumbnail size * @var integer + * @deprecated 3.0 */ public static $maxThumbnailSize = 128; /** * minimum height and width of an uploaded avatar * @var integer + * @deprecated 3.0 */ const MIN_AVATAR_SIZE = 96; + /** + * minimum height and width of an uploaded avatar + * @var integer + */ + const AVATAR_SIZE = 128; + /** * Returns the physical location of this avatar. * @@ -68,32 +75,6 @@ class UserAvatar extends DatabaseObject implements IUserAvatar { * @return string */ public function getFilename($size = null) { - switch ($size) { - case 16: - case 24: - $size = 32; - break; - - case 48: - case 64: - if ($this->width > 96 || $this->height > 96) { - $size = 96; - } - else { - $size = null; - } - break; - - case 160: - if ($this->width > 256 || $this->height > 256) { - $size = 256; - } - else { - $size = null; - } - break; - } - return substr($this->fileHash, 0, 2) . '/' . ($this->avatarID) . '-' . $this->fileHash . ($size !== null ? ('-' . $size) : '') . '.' . $this->avatarExtension; } @@ -101,77 +82,21 @@ class UserAvatar extends DatabaseObject implements IUserAvatar { * @inheritDoc */ public function getURL($size = null) { - if ($size !== null && $size !== 'resized') { - if ($size >= $this->width || $size >= $this->height) $size = null; - } - - return WCF::getPath() . 'images/avatars/' . $this->getFilename($size); + return WCF::getPath() . 'images/avatars/' . $this->getFilename(); } /** * @inheritDoc */ public function getImageTag($size = null) { - $width = $this->width; - $height = $this->height; - if ($size !== null) { - if ($this->width > $size && $this->height > $size) { - $width = $height = $size; - } - else if ($this->width > $size || $this->height > $size) { - $widthFactor = $size / $this->width; - $heightFactor = $size / $this->height; - - if ($widthFactor < $heightFactor) { - $width = $size; - $height = round($this->height * $widthFactor, 0); - } - else { - $width = round($this->width * $heightFactor, 0); - $height = $size; - } - } - } - - $retinaSize = null; - switch ($size) { - case 16: - $retinaSize = 32; - break; - - case 24: - case 32: - case 48: - $retinaSize = 96; - break; - - case 64: - case 96: - if ($this->width >= 128 && $this->height >= 128) { - $retinaSize = 128; - } - break; - - case 128: - if ($this->width >= 128 && $this->height >= 128) { - $retinaSize = 256; - } - break; - } - - return ''; + return ''; } /** * @inheritDoc */ public function getCropImageTag($size = null) { - $imageTag = $this->getImageTag($size); - - // append CSS classes and append title - $title = StringUtil::encodeHTML(WCF::getLanguage()->get('wcf.user.avatar.type.custom.crop')); - - return str_replace('class="userAvatarImage"', 'class="userAvatarImage userAvatarCrop jsTooltip" title="'.$title.'"', $imageTag); + return ''; } /** @@ -192,6 +117,6 @@ class UserAvatar extends DatabaseObject implements IUserAvatar { * @inheritDoc */ public function canCrop() { - return $this->width != $this->height && $this->width > self::$maxThumbnailSize && $this->height > self::$maxThumbnailSize; + return false; } } diff --git a/wcfsetup/install/files/lib/data/user/avatar/UserAvatarAction.class.php b/wcfsetup/install/files/lib/data/user/avatar/UserAvatarAction.class.php index fd61217f3b..58d91444e8 100644 --- a/wcfsetup/install/files/lib/data/user/avatar/UserAvatarAction.class.php +++ b/wcfsetup/install/files/lib/data/user/avatar/UserAvatarAction.class.php @@ -3,7 +3,6 @@ namespace wcf\data\user\avatar; use wcf\data\user\User; use wcf\data\user\UserEditor; use wcf\data\AbstractDatabaseObjectAction; -use wcf\system\cache\runtime\UserProfileRuntimeCache; use wcf\system\exception\IllegalLinkException; use wcf\system\exception\PermissionDeniedException; use wcf\system\exception\SystemException; @@ -107,10 +106,6 @@ class UserAvatarAction extends AbstractDatabaseObjectAction { if (@copy($fileLocation, $avatar->getLocation())) { @unlink($fileLocation); - // create thumbnails - $action = new UserAvatarAction([$avatar], 'generateThumbnails'); - $action->executeAction(); - // delete old avatar if ($user->avatarID) { $action = new UserAvatarAction([$user->avatarID], 'delete'); @@ -130,7 +125,6 @@ class UserAvatarAction extends AbstractDatabaseObjectAction { // return result return [ 'avatarID' => $avatar->avatarID, - 'canCrop' => $avatar->canCrop(), 'url' => $avatar->getURL(96) ]; } @@ -149,27 +143,6 @@ class UserAvatarAction extends AbstractDatabaseObjectAction { return ['errorType' => $file->getValidationErrorType()]; } - /** - * Generates the thumbnails of the avatars in all needed sizes. - */ - public function generateThumbnails() { - if (empty($this->objects)) { - $this->readObjects(); - } - - foreach ($this->getObjects() as $avatar) { - $adapter = ImageHandler::getInstance()->getAdapter(); - $adapter->loadFile($avatar->getLocation()); - - foreach (UserAvatar::$avatarThumbnailSizes as $size) { - if ($avatar->width <= $size && $avatar->height <= $size) break 2; - - $thumbnail = $adapter->createThumbnail($size, $size, false); - $adapter->writeImage($thumbnail, $avatar->getLocation($size)); - } - } - } - /** * Fetches an avatar from a remote server and sets it for given user. */ @@ -243,10 +216,6 @@ class UserAvatarAction extends AbstractDatabaseObjectAction { if (@copy($filename, $avatar->getLocation())) { @unlink($filename); - // create thumbnails - $action = new UserAvatarAction([$avatar], 'generateThumbnails'); - $action->executeAction(); - $avatarID = $avatar->avatarID; } else { @@ -287,20 +256,12 @@ class UserAvatarAction extends AbstractDatabaseObjectAction { */ protected function enforceDimensions($filename) { $imageData = getimagesize($filename); - if ($imageData[0] > MAX_AVATAR_WIDTH || $imageData[1] > MAX_AVATAR_HEIGHT) { + if ($imageData[0] > UserAvatar::AVATAR_SIZE || $imageData[1] > UserAvatar::AVATAR_SIZE) { try { - $obtainDimensions = true; - if (MAX_AVATAR_WIDTH / $imageData[0] < MAX_AVATAR_HEIGHT / $imageData[1]) { - if (round($imageData[1] * (MAX_AVATAR_WIDTH / $imageData[0])) < 48) $obtainDimensions = false; - } - else { - if (round($imageData[0] * (MAX_AVATAR_HEIGHT / $imageData[1])) < 48) $obtainDimensions = false; - } - $adapter = ImageHandler::getInstance()->getAdapter(); $adapter->loadFile($filename); $filename = FileUtil::getTemporaryFilename(); - $thumbnail = $adapter->createThumbnail(MAX_AVATAR_WIDTH, MAX_AVATAR_HEIGHT, $obtainDimensions); + $thumbnail = $adapter->createThumbnail(UserAvatar::AVATAR_SIZE, UserAvatar::AVATAR_SIZE, false); $adapter->writeImage($thumbnail, $filename); } catch (SystemException $e) { @@ -315,79 +276,4 @@ class UserAvatarAction extends AbstractDatabaseObjectAction { return $filename; } - - /** - * Validates the 'getCropDialog' action. - */ - public function validateGetCropDialog() { - $this->avatar = $this->getSingleObject(); - } - - /** - * Returns the data for the dialog to crop an avatar. - * - * @return array - */ - public function getCropDialog() { - return [ - 'cropX' => $this->avatar->cropX, - 'cropY' => $this->avatar->cropY, - 'template' => WCF::getTPL()->fetch('avatarCropDialog', 'wcf', [ - 'avatar' => $this->avatar - ]) - ]; - } - - /** - * Validates the 'cropAvatar' action. - */ - public function validateCropAvatar() { - $this->avatar = $this->getSingleObject(); - - // check if user can edit the given avatar - if ($this->avatar->userID != WCF::getUser()->userID && !WCF::getSession()->getPermission('admin.user.canEditUser')) { - throw new PermissionDeniedException(); - } - - if (!WCF::getSession()->getPermission('user.profile.avatar.canUploadAvatar') || UserProfileRuntimeCache::getInstance()->getObject($this->avatar->userID)->disableAvatar) { - throw new PermissionDeniedException(); - } - - // check parameters - $this->readInteger('cropX', true); - $this->readInteger('cropY', true); - - if ($this->parameters['cropX'] < 0 || $this->parameters['cropX'] > $this->avatar->width - UserAvatar::$maxThumbnailSize) { - throw new UserInputException('cropX'); - } - if ($this->parameters['cropY'] < 0 || $this->parameters['cropY'] > $this->avatar->height - UserAvatar::$maxThumbnailSize) { - throw new UserInputException('cropY'); - } - } - - /** - * Craps an avatar. - */ - public function cropAvatar() { - // created clipped avatar as base for new thumbnails - $adapter = ImageHandler::getInstance()->getAdapter(); - $adapter->loadFile($this->avatar->getLocation()); - $adapter->clip($this->parameters['cropX'], $this->parameters['cropY'], UserAvatar::$maxThumbnailSize, UserAvatar::$maxThumbnailSize); - - // update thumbnails - foreach (UserAvatar::$avatarThumbnailSizes as $size) { - $thumbnail = $adapter->createThumbnail($size, $size); - $adapter->writeImage($thumbnail, $this->avatar->getLocation($size)); - } - - // update database entry - $this->avatar->update([ - 'cropX' => $this->parameters['cropX'], - 'cropY' => $this->parameters['cropY'] - ]); - - return [ - 'url' => $this->avatar->getURL(96) - ]; - } } diff --git a/wcfsetup/install/files/lib/data/user/avatar/UserAvatarEditor.class.php b/wcfsetup/install/files/lib/data/user/avatar/UserAvatarEditor.class.php index 4a3d9c62e0..2bfb99fd71 100644 --- a/wcfsetup/install/files/lib/data/user/avatar/UserAvatarEditor.class.php +++ b/wcfsetup/install/files/lib/data/user/avatar/UserAvatarEditor.class.php @@ -53,7 +53,7 @@ class UserAvatarEditor extends DatabaseObjectEditor { * Deletes avatar files. */ public function deleteFiles() { - foreach (UserAvatar::$avatarThumbnailSizes as $size) { + foreach (UserAvatar::$avatarThumbnailSizes as $size) { // delete wcf2.1 files if ($this->width < $size && $this->height < $size) break; @unlink($this->getLocation($size)); diff --git a/wcfsetup/install/files/lib/system/upload/AvatarUploadFileValidationStrategy.class.php b/wcfsetup/install/files/lib/system/upload/AvatarUploadFileValidationStrategy.class.php index a01844e2cc..67d501d399 100644 --- a/wcfsetup/install/files/lib/system/upload/AvatarUploadFileValidationStrategy.class.php +++ b/wcfsetup/install/files/lib/system/upload/AvatarUploadFileValidationStrategy.class.php @@ -21,7 +21,7 @@ class AvatarUploadFileValidationStrategy extends DefaultUploadFileValidationStra // check image size try { $imageData = getimagesize($uploadFile->getLocation()); - if ($imageData[0] < UserAvatar::MIN_AVATAR_SIZE || $imageData[1] < UserAvatar::MIN_AVATAR_SIZE) { + if ($imageData[0] < UserAvatar::AVATAR_SIZE || $imageData[1] < UserAvatar::AVATAR_SIZE) { $uploadFile->setValidationErrorType('tooSmall'); return false; } diff --git a/wcfsetup/install/files/lib/system/worker/UserRebuildDataWorker.class.php b/wcfsetup/install/files/lib/system/worker/UserRebuildDataWorker.class.php index dcc82798e1..e5574efa64 100644 --- a/wcfsetup/install/files/lib/system/worker/UserRebuildDataWorker.class.php +++ b/wcfsetup/install/files/lib/system/worker/UserRebuildDataWorker.class.php @@ -1,10 +1,14 @@ prepareStatement($sql); $statement->execute($conditionBuilder->getParameters()); } + + // update old avatars + $avatarList = new UserAvatarList(); + $avatarList->getConditionBuilder()->add('user_avatar.userID IN (?)', [$userIDs]); + $avatarList->getConditionBuilder()->add('(user_avatar.width <> ? OR user_avatar.height <> ?)', [UserAvatar::AVATAR_SIZE, UserAvatar::AVATAR_SIZE]); + $avatarList->readObjects(); + foreach ($avatarList as $avatar) { + $width = $avatar->width; + $height = $avatar->height; + if ($width != $height) { + $width = $height = min($width, $height, UserAvatar::AVATAR_SIZE); + $adapter = ImageHandler::getInstance()->getAdapter(); + $adapter->loadFile($avatar->getLocation()); + $thumbnail = $adapter->createThumbnail($width, $height, false); + $adapter->writeImage($thumbnail, $avatar->getLocation()); + } + + if ($width < UserAvatar::AVATAR_SIZE || $height < UserAvatar::AVATAR_SIZE) { + } + + $editor = new UserAvatarEditor($avatar); + $editor->update([ + ]); + } } } } diff --git a/wcfsetup/install/files/style/ui/userProfile.scss b/wcfsetup/install/files/style/ui/userProfile.scss index e127397ee4..28c8d5169a 100644 --- a/wcfsetup/install/files/style/ui/userProfile.scss +++ b/wcfsetup/install/files/style/ui/userProfile.scss @@ -1,355 +1,9 @@ -.userProfileHeader.box { - margin: 0; -} -.userProfileCoverPhoto { - background-image: url('../images/user-profile-header.jpg'); // todo - background-position: center; - background-size: cover; - - /* adds a box-shadow on the top and bottom of the element to create perspective - and a clear separation if the images color is close to the surrounding elements */ - box-shadow: inset 0 10px 10px -10px rgba(0, 0, 0, .6), inset 0 -10px 10px -10px rgba(0, 0, 0, .6); - - @include screen-md-up { - height: 300px; - } - - @include screen-sm-down { - height: 200px; - } - - &::after { - bottom: 0; - content: ''; - left: 0; - position: absolute; - right: 0; - top: 0; - - @include screen-md-up { - background: linear-gradient(to bottom, rgba(0, 0, 0, 0) 60%, rgba(0, 0, 0, .7) 100%); - } - - @include screen-sm-down { - background: linear-gradient(to bottom, rgba(0, 0, 0, 0) 20%, rgba(0, 0, 0, .7) 100%); - } - } -} - -.userProfileHeaderContainer { - position: relative; -} - .userProfileUser { - position: absolute; - width: 100%; - - @include screen-md-up { - bottom: 0; - } - - @include screen-sm-down { - bottom: 10px; - } - - .contentHeader { - @include screen-md-up { - align-items: flex-end; - } - - @include screen-sm-down { - align-items: center; - display: flex; - } - } - - .contentHeaderIcon { - align-items: center; - background-color: #fff; - border-radius: 50%; - box-sizing: content-box; - box-shadow: 0 3px 6px rgba(0 ,0, 0, 0.16), 0 3px 6px rgba(0, 0, 0, 0.23); - display: flex; - justify-content: center; - - @include screen-md-up { - border: 4px solid transparent; - flex: 0 0 160px; - height: 160px; - margin-right: 20px; - transform: translateY(50%); - width: 160px; - } - - @include screen-sm-down { - border: 2px solid transparent; - flex: 0 0 64px; - height: 64px; - margin-right: 10px; - width: 64px; - } - - img { - @include screen-sm-down { - width: 64px !important; - height: 64px !important; - } - } - } - - .contentHeaderTitle { - align-items: center; - color: #fff; - text-shadow: 0 1px 4px rgba(0, 0, 0, .5); - - @include screen-md-up { - display: flex; - flex-wrap: wrap; - padding-bottom: 10px; - - > .contentTitle { - align-items: center; - display: flex; - flex: 0 0 auto; - margin-right: 10px; - - > .icon { - margin-left: 5px; - } - } - - > .userTitleBadge { - flex: 0 0 auto; - } - } - @include screen-sm-down { - flex: 1 1 auto; - } - - a { - color: #fff; - } - - > .userTitleBadge { - box-shadow: 0 3px 6px rgba(0, 0, 0, 0.16), 0 3px 6px rgba(0, 0, 0, 0.23); - - @include screen-sm-down { - margin-top: 5px; - } - } - - .icon { - color: inherit; - } + .contentHeaderIcon a { + display: block; } .contentDescription { - @include screen-md-up { - flex: 0 0 100%; - margin-top: 5px; - - .inlineList:not(:first-child) { - margin-top: 2px; - - @include wcfFontSmall; - } - } - - @include screen-sm-down { - display: none; - } - } - - .userProfileButtonContainer { - @include screen-md-up { - display: flex; - flex: 0 0 auto; - margin-bottom: 20px; - } - @include screen-sm-down { - display: block; - position: absolute; - right: 10px; - bottom: 0; - } - - > li { - margin-left: 10px; - position: relative; - - @include screen-md-up { - flex: 0 0 auto; - } - @include screen-sm-down { - margin-top: 10px; - } - - > a { - background-color: $wcfHeaderMenuBackground; - border-radius: 50%; - box-shadow: 0 10px 20px rgba(0, 0, 0, 0.19), 0 6px 6px rgba(0, 0, 0, 0.23); - color: $wcfHeaderMenuLink; - display: block; - padding: 10px; - - @include screen-md-up { - padding: 10px; - } - @include screen-sm-down { - padding: 5px; - } - - .icon { - color: inherit; - - @include screen-sm-down { - font-size: 18px; - height: 24px; - line-height: 24px; - width: 24px; - } - } - } - - > .userProfileButtonMenu { - background-color: $wcfHeaderMenuDropdownBackground; - box-shadow: 0 0 3px rgba(0, 0, 0, .12), 0 1px 2px rgba(0, 0, 0, .24); - border-radius: 0 0 3px 3px; - padding: 5px 0; - position: absolute; - visibility: hidden; - z-index: 1; - - @include screen-md-up { - right: 0; - } - @include screen-sm-down { - right: 40px; - top: 0; - } - - > li { - &.divider:not(:first-child) { - border-top: 1px solid $wcfHeaderMenuDropdownBackgroundActive; - margin-top: 5px; - padding-top: 5px; - } - - > a { - color: $wcfHeaderMenuDropdownLink; - } - - > a, - > span { - display: block; - padding: 7px 20px; - white-space: nowrap; - text-align: right; - } - - &.active > a, - > a:hover { - background-color: $wcfHeaderMenuDropdownBackgroundActive; - color: $wcfHeaderMenuDropdownLinkActive; - text-decoration: none; - } - } - } - - &:hover { - > a { - background-color: $wcfHeaderMenuLinkBackgroundActive; - color: $wcfHeaderMenuLinkActive; - } - - > .userProfileButtonMenu { - visibility: visible; - } - } - } - } -} - -.userProfileDescriptionMobile { - @include screen-md-up { - display: none; - } - - @include screen-sm-down { - border-bottom: 1px solid $wcfContentBorderInner; - - > .layoutBoundary { - padding: 20px 10px; - } - } -} - -.userProfileDetails { - @include screen-md-up { - min-height: 94px; - } - - > .layoutBoundary { - @include screen-md-up { - padding: 20px 20px 20px 208px; - } - @include screen-sm-down { - padding: 20px 0; - } - } - - .userStats { - display: flex; - flex-wrap: wrap; - margin-bottom: -20px; - - li { - flex: 0 0 auto; - margin-bottom: 20px; - padding: 0 10px; - text-align: center; - white-space: nowrap; - - @include screen-lg { - width: 12%; - } - @include screen-sm-md { - width: 25%; - } - @include screen-xs { - width: 33%; - } - - a { - display: block; - } - } - - .userStatsValue { - overflow: hidden; - - @include wcfFontTitle; - } - - .userStatsTitle { - color: $wcfContentDimmedText; - display: block; - margin-top: 4px; - overflow: hidden; - text-overflow: ellipsis; - - @include screen-sm-down { - @include wcfFontSmall; - } - } - } -} - -#tpl_wcf_user .main { - padding-top: 0; - - .userProfileContent, - .sidebar { margin-top: 20px; } } diff --git a/wcfsetup/install/lang/de.xml b/wcfsetup/install/lang/de.xml index 798399e30f..aecec6b924 100644 --- a/wcfsetup/install/lang/de.xml +++ b/wcfsetup/install/lang/de.xml @@ -968,8 +968,6 @@ - - @@ -3178,9 +3176,7 @@ Wenn {if LANGUAGE_USE_INFORMAL_VARIANT}du Probleme mit der Aktivierung haben sol user->avatarID || $__wcf->user->enableGravatar} {if LANGUAGE_USE_INFORMAL_VARIANT}deinen{else}Ihren{/if} derzeitigen Avatar gesperrt und{/if} {if LANGUAGE_USE_INFORMAL_VARIANT}dir{else}Ihnen{/if} die weitere Nutzungsberechtigung der Avatar-Funktion {if !$__wcf->user->disableAvatarReason}entzogen.{else} aus folgenden Gründen entzogen: {$__wcf->user->disableAvatarReason}{/if}]]> - - - session->getPermission('user.profile.avatar.allowedFileExtensions')} und maximal eine Größe von {@$__wcf->session->getPermission('user.profile.avatar.maxSize')|filesize} besitzen. Die Mindestgröße für Avatare liegt bei 96×96 Pixel, die empfohlene Maximalgröße bei {@MAX_AVATAR_WIDTH}×{@MAX_AVATAR_HEIGHT} Pixel, größere Avatare werden - sofern möglich - automatisch auf die Maximalgröße verkleinert.]]> + session->getPermission('user.profile.avatar.allowedFileExtensions')} und maximal eine Größe von {@$__wcf->session->getPermission('user.profile.avatar.maxSize')|filesize} besitzen. Die Maximalgröße für Avatare liegt bei 128×128 Pixel, größere Avatare werden - sofern möglich - automatisch auf die Maximalgröße verkleinert.]]> user->email}“) verknüpft ist. Auf der folgenden Website können Sie einen Gravatar anlegen: www.gravatar.com]]> diff --git a/wcfsetup/install/lang/en.xml b/wcfsetup/install/lang/en.xml index 636fec2c12..e44cbb1b47 100644 --- a/wcfsetup/install/lang/en.xml +++ b/wcfsetup/install/lang/en.xml @@ -971,10 +971,6 @@ Examples for medium ID detection: - - - - @@ -3217,9 +3213,7 @@ You can safely ignore this email if you did not register with the website: {@PAG user->avatarID || $__wcf->user->enableGravatar}have banned your avatar and {/if}disallowed you from using an avatar{if $__wcf->user->disableAvatarReason}: {$__wcf->user->disableAvatarReason}{/if}.]]> - - - session->getPermission('user.profile.avatar.allowedFileExtensions')}” for your avatar with a maximum file size of {@$__wcf->session->getPermission('user.profile.avatar.maxSize')|filesize}. The minimum dimensions are 96×96 pixel, it is recommended to provide a dimension of {@MAX_AVATAR_WIDTH}×{@MAX_AVATAR_HEIGHT} pixel. Any avatar that exceeds this limit will be scaled down if possible.]]> + session->getPermission('user.profile.avatar.allowedFileExtensions')}” for your avatar with a maximum file size of {@$__wcf->session->getPermission('user.profile.avatar.maxSize')|filesize}. The maximum dimensions are 128×128 pixel. Any avatar that exceeds this limit will be scaled down if possible.]]> user->email}”. Visit www.gravatar.com to set up or change your avatar.]]> diff --git a/wcfsetup/setup/db/install.sql b/wcfsetup/setup/db/install.sql index 9d3560931e..4f65d1da17 100644 --- a/wcfsetup/setup/db/install.sql +++ b/wcfsetup/setup/db/install.sql @@ -1356,9 +1356,7 @@ CREATE TABLE wcf1_user_avatar ( width SMALLINT(5) NOT NULL DEFAULT 0, height SMALLINT(5) NOT NULL DEFAULT 0, userID INT(10), - fileHash VARCHAR(40) NOT NULL DEFAULT '', - cropX SMALLINT(5) NOT NULL DEFAULT 0, - cropY SMALLINT(5) NOT NULL DEFAULT 0 + fileHash VARCHAR(40) NOT NULL DEFAULT '' ); DROP TABLE IF EXISTS wcf1_user_collapsible_content;