diff --git a/src/__celerity_resource_map__.php b/src/__celerity_resource_map__.php index 27bf87dd23..1a5c66e9db 100644 --- a/src/__celerity_resource_map__.php +++ b/src/__celerity_resource_map__.php @@ -3009,6 +3009,15 @@ celerity_register_resource_map(array( ), 'disk' => '/rsrc/js/application/herald/PathTypeahead.js', ), + 'people-profile-css' => + array( + 'uri' => '/res/1f0e94c5/rsrc/css/application/people/people-profile.css', + 'type' => 'css', + 'requires' => + array( + ), + 'disk' => '/rsrc/css/application/people/people-profile.css', + ), 'phabricator-action-header-view-css' => array( 'uri' => '/res/3b701648/rsrc/css/layout/phabricator-action-header-view.css', @@ -3197,7 +3206,7 @@ celerity_register_resource_map(array( ), 'phabricator-header-view-css' => array( - 'uri' => '/res/76173bb6/rsrc/css/layout/phabricator-header-view.css', + 'uri' => '/res/da35cfa0/rsrc/css/layout/phabricator-header-view.css', 'type' => 'css', 'requires' => array( @@ -4140,7 +4149,7 @@ celerity_register_resource_map(array( ), array( 'packages' => array( - 'd7254b92' => + '680ace9b' => array( 'name' => 'core.pkg.css', 'symbols' => @@ -4188,7 +4197,7 @@ celerity_register_resource_map(array( 40 => 'phabricator-property-list-view-css', 41 => 'phabricator-tag-view-css', ), - 'uri' => '/res/pkg/d7254b92/core.pkg.css', + 'uri' => '/res/pkg/680ace9b/core.pkg.css', 'type' => 'css', ), '75ccea43' => @@ -4382,16 +4391,16 @@ celerity_register_resource_map(array( 'reverse' => array( 'aphront-attached-file-view-css' => 'adc3c36d', - 'aphront-dialog-view-css' => 'd7254b92', - 'aphront-error-view-css' => 'd7254b92', - 'aphront-form-view-css' => 'd7254b92', - 'aphront-list-filter-view-css' => 'd7254b92', - 'aphront-pager-view-css' => 'd7254b92', - 'aphront-panel-view-css' => 'd7254b92', - 'aphront-table-view-css' => 'd7254b92', - 'aphront-tokenizer-control-css' => 'd7254b92', - 'aphront-tooltip-css' => 'd7254b92', - 'aphront-typeahead-control-css' => 'd7254b92', + 'aphront-dialog-view-css' => '680ace9b', + 'aphront-error-view-css' => '680ace9b', + 'aphront-form-view-css' => '680ace9b', + 'aphront-list-filter-view-css' => '680ace9b', + 'aphront-pager-view-css' => '680ace9b', + 'aphront-panel-view-css' => '680ace9b', + 'aphront-table-view-css' => '680ace9b', + 'aphront-tokenizer-control-css' => '680ace9b', + 'aphront-tooltip-css' => '680ace9b', + 'aphront-typeahead-control-css' => '680ace9b', 'differential-changeset-view-css' => 'dd27a69b', 'differential-core-view-css' => 'dd27a69b', 'differential-inline-comment-editor' => '4ad86dee', @@ -4405,7 +4414,7 @@ celerity_register_resource_map(array( 'differential-table-of-contents-css' => 'dd27a69b', 'diffusion-commit-view-css' => 'c8ce2d88', 'diffusion-icons-css' => 'c8ce2d88', - 'global-drag-and-drop-css' => 'd7254b92', + 'global-drag-and-drop-css' => '680ace9b', 'inline-comment-summary-css' => 'dd27a69b', 'javelin-aphlict' => '75ccea43', 'javelin-behavior' => 'a9f14d76', @@ -4479,55 +4488,55 @@ celerity_register_resource_map(array( 'javelin-util' => 'a9f14d76', 'javelin-vector' => 'a9f14d76', 'javelin-workflow' => 'a9f14d76', - 'lightbox-attachment-css' => 'd7254b92', + 'lightbox-attachment-css' => '680ace9b', 'maniphest-task-summary-css' => 'adc3c36d', 'maniphest-transaction-detail-css' => 'adc3c36d', - 'phabricator-action-list-view-css' => 'd7254b92', - 'phabricator-application-launch-view-css' => 'd7254b92', + 'phabricator-action-list-view-css' => '680ace9b', + 'phabricator-application-launch-view-css' => '680ace9b', 'phabricator-busy' => '75ccea43', 'phabricator-content-source-view-css' => 'dd27a69b', - 'phabricator-core-css' => 'd7254b92', - 'phabricator-crumbs-view-css' => 'd7254b92', + 'phabricator-core-css' => '680ace9b', + 'phabricator-crumbs-view-css' => '680ace9b', 'phabricator-drag-and-drop-file-upload' => '4ad86dee', 'phabricator-dropdown-menu' => '75ccea43', 'phabricator-file-upload' => '75ccea43', - 'phabricator-filetree-view-css' => 'd7254b92', - 'phabricator-flag-css' => 'd7254b92', - 'phabricator-form-view-css' => 'd7254b92', - 'phabricator-header-view-css' => 'd7254b92', + 'phabricator-filetree-view-css' => '680ace9b', + 'phabricator-flag-css' => '680ace9b', + 'phabricator-form-view-css' => '680ace9b', + 'phabricator-header-view-css' => '680ace9b', 'phabricator-hovercard' => '75ccea43', - 'phabricator-jump-nav' => 'd7254b92', + 'phabricator-jump-nav' => '680ace9b', 'phabricator-keyboard-shortcut' => '75ccea43', 'phabricator-keyboard-shortcut-manager' => '75ccea43', - 'phabricator-main-menu-view' => 'd7254b92', + 'phabricator-main-menu-view' => '680ace9b', 'phabricator-menu-item' => '75ccea43', - 'phabricator-nav-view-css' => 'd7254b92', + 'phabricator-nav-view-css' => '680ace9b', 'phabricator-notification' => '75ccea43', - 'phabricator-notification-css' => 'd7254b92', - 'phabricator-notification-menu-css' => 'd7254b92', - 'phabricator-object-item-list-view-css' => 'd7254b92', + 'phabricator-notification-css' => '680ace9b', + 'phabricator-notification-menu-css' => '680ace9b', + 'phabricator-object-item-list-view-css' => '680ace9b', 'phabricator-object-selector-css' => 'dd27a69b', 'phabricator-phtize' => '75ccea43', 'phabricator-prefab' => '75ccea43', 'phabricator-project-tag-css' => 'adc3c36d', - 'phabricator-property-list-view-css' => 'd7254b92', - 'phabricator-remarkup-css' => 'd7254b92', + 'phabricator-property-list-view-css' => '680ace9b', + 'phabricator-remarkup-css' => '680ace9b', 'phabricator-shaped-request' => '4ad86dee', - 'phabricator-side-menu-view-css' => 'd7254b92', - 'phabricator-standard-page-view' => 'd7254b92', - 'phabricator-tag-view-css' => 'd7254b92', + 'phabricator-side-menu-view-css' => '680ace9b', + 'phabricator-standard-page-view' => '680ace9b', + 'phabricator-tag-view-css' => '680ace9b', 'phabricator-textareautils' => '75ccea43', 'phabricator-tooltip' => '75ccea43', - 'phabricator-transaction-view-css' => 'd7254b92', - 'phabricator-zindex-css' => 'd7254b92', - 'phui-button-css' => 'd7254b92', - 'phui-form-css' => 'd7254b92', - 'phui-icon-view-css' => 'd7254b92', - 'phui-spacing-css' => 'd7254b92', - 'sprite-apps-large-css' => 'd7254b92', - 'sprite-gradient-css' => 'd7254b92', - 'sprite-icons-css' => 'd7254b92', - 'sprite-menu-css' => 'd7254b92', - 'syntax-highlighting-css' => 'd7254b92', + 'phabricator-transaction-view-css' => '680ace9b', + 'phabricator-zindex-css' => '680ace9b', + 'phui-button-css' => '680ace9b', + 'phui-form-css' => '680ace9b', + 'phui-icon-view-css' => '680ace9b', + 'phui-spacing-css' => '680ace9b', + 'sprite-apps-large-css' => '680ace9b', + 'sprite-gradient-css' => '680ace9b', + 'sprite-icons-css' => '680ace9b', + 'sprite-menu-css' => '680ace9b', + 'syntax-highlighting-css' => '680ace9b', ), )); diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 3310ddd1f2..e8a3e85f2c 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -1344,6 +1344,7 @@ phutil_register_library_map(array( 'PhabricatorPeopleLogsController' => 'applications/people/controller/PhabricatorPeopleLogsController.php', 'PhabricatorPeopleProfileController' => 'applications/people/controller/PhabricatorPeopleProfileController.php', 'PhabricatorPeopleProfileEditController' => 'applications/people/controller/PhabricatorPeopleProfileEditController.php', + 'PhabricatorPeopleProfilePictureController' => 'applications/people/controller/PhabricatorPeopleProfilePictureController.php', 'PhabricatorPeopleQuery' => 'applications/people/query/PhabricatorPeopleQuery.php', 'PhabricatorPeopleSearchEngine' => 'applications/people/query/PhabricatorPeopleSearchEngine.php', 'PhabricatorPeopleTestDataGenerator' => 'applications/people/lipsum/PhabricatorPeopleTestDataGenerator.php', @@ -3303,6 +3304,7 @@ phutil_register_library_map(array( 'PhabricatorPeopleLogsController' => 'PhabricatorPeopleController', 'PhabricatorPeopleProfileController' => 'PhabricatorPeopleController', 'PhabricatorPeopleProfileEditController' => 'PhabricatorPeopleController', + 'PhabricatorPeopleProfilePictureController' => 'PhabricatorPeopleController', 'PhabricatorPeopleQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 'PhabricatorPeopleSearchEngine' => 'PhabricatorApplicationSearchEngine', 'PhabricatorPeopleTestDataGenerator' => 'PhabricatorTestDataGenerator', diff --git a/src/applications/files/PhabricatorImageTransformer.php b/src/applications/files/PhabricatorImageTransformer.php index fcb92a79a8..a51ad82208 100644 --- a/src/applications/files/PhabricatorImageTransformer.php +++ b/src/applications/files/PhabricatorImageTransformer.php @@ -94,7 +94,6 @@ final class PhabricatorImageTransformer { } $cropped = $this->applyScaleWithImagemagick($file, $x, $scaled_y); - if ($cropped != null) { return $cropped; } diff --git a/src/applications/people/application/PhabricatorApplicationPeople.php b/src/applications/people/application/PhabricatorApplicationPeople.php index de1f738c13..8c055e904b 100644 --- a/src/applications/people/application/PhabricatorApplicationPeople.php +++ b/src/applications/people/application/PhabricatorApplicationPeople.php @@ -46,6 +46,8 @@ final class PhabricatorApplicationPeople extends PhabricatorApplication { 'ldap/' => 'PhabricatorPeopleLdapController', 'editprofile/(?P[1-9]\d*)/' => 'PhabricatorPeopleProfileEditController', + 'picture/(?P[1-9]\d*)/' => + 'PhabricatorPeopleProfilePictureController', ), '/p/(?P[\w._-]+)/(?:(?P\w+)/)?' => 'PhabricatorPeopleProfileController', diff --git a/src/applications/people/controller/PhabricatorPeopleProfileController.php b/src/applications/people/controller/PhabricatorPeopleProfileController.php index aeca967ca2..0b1f33314d 100644 --- a/src/applications/people/controller/PhabricatorPeopleProfileController.php +++ b/src/applications/people/controller/PhabricatorPeopleProfileController.php @@ -117,6 +117,14 @@ final class PhabricatorPeopleProfileController ->setDisabled(!$can_edit) ->setWorkflow(!$can_edit)); + $actions->addAction( + id(new PhabricatorActionView()) + ->setIcon('image') + ->setName(pht('Edit Profile Picture')) + ->setHref($this->getApplicationURI('picture/'.$user->getID().'/')) + ->setDisabled(!$can_edit) + ->setWorkflow(!$can_edit)); + if ($viewer->getIsAdmin()) { $actions->addAction( id(new PhabricatorActionView()) diff --git a/src/applications/people/controller/PhabricatorPeopleProfilePictureController.php b/src/applications/people/controller/PhabricatorPeopleProfilePictureController.php new file mode 100644 index 0000000000..3320f339bd --- /dev/null +++ b/src/applications/people/controller/PhabricatorPeopleProfilePictureController.php @@ -0,0 +1,292 @@ +id = $data['id']; + } + + public function processRequest() { + $request = $this->getRequest(); + $viewer = $request->getUser(); + + $user = id(new PhabricatorPeopleQuery()) + ->setViewer($viewer) + ->withIDs(array($this->id)) + ->requireCapabilities( + array( + PhabricatorPolicyCapability::CAN_VIEW, + PhabricatorPolicyCapability::CAN_EDIT, + )) + ->executeOne(); + if (!$user) { + return new Aphront404Response(); + } + + $profile_uri = '/p/'.$user->getUsername().'/'; + + $supported_formats = PhabricatorFile::getTransformableImageFormats(); + $e_file = true; + $errors = array(); + + if ($request->isFormPost()) { + $phid = $request->getStr('phid'); + $is_default = false; + if ($phid == PhabricatorPHIDConstants::PHID_VOID) { + $phid = null; + $is_default = true; + } else if ($phid) { + $file = id(new PhabricatorFileQuery()) + ->setViewer($viewer) + ->withPHIDs(array($phid)) + ->executeOne(); + } else { + if ($request->getFileExists('picture')) { + $file = PhabricatorFile::newFromPHPUpload( + $_FILES['picture'], + array( + 'authorPHID' => $viewer->getPHID(), + )); + } else { + $e_file = pht('Required'); + $errors[] = pht( + 'You must choose a file when uploading a new profile picture.'); + } + } + + if (!$errors && !$is_default) { + if (!$file->isTransformableImage()) { + $e_file = pht('Not Supported'); + $errors[] = pht( + 'This server only supports these image formats: %s.', + implode(', ', $supported_formats)); + } else { + $xformer = new PhabricatorImageTransformer(); + $xformed = $xformer->executeProfileTransform( + $file, + $width = 50, + $min_height = 50, + $max_height = 50); + } + } + + if (!$errors) { + $user->setProfileImagePHID($xformed->getPHID()); + $user->save(); + return id(new AphrontRedirectResponse())->setURI($profile_uri); + } + } + + $title = pht('Edit Profile Picture'); + $crumbs = $this->buildApplicationCrumbs(); + $crumbs->addCrumb( + id(new PhabricatorCrumbView()) + ->setName($user->getUsername()) + ->setHref($profile_uri)); + $crumbs->addCrumb( + id(new PhabricatorCrumbView()) + ->setName($title)); + + $form = id(new AphrontFormLayoutView()) + ->setUser($viewer); + + $default_image = PhabricatorFile::loadBuiltin($viewer, 'profile.png'); + + $images = array(); + + $current = $user->getProfileImagePHID(); + if ($current) { + $files = id(new PhabricatorFileQuery()) + ->setViewer($viewer) + ->withPHIDs(array($current)) + ->execute(); + if ($files) { + $file = head($files); + if ($file->isTransformableImage()) { + $images[$current] = array( + 'uri' => $file->getBestURI(), + 'tip' => pht('Current Picture'), + ); + } + } + } + + // Try to add external account images for any associated external accounts. + $accounts = id(new PhabricatorExternalAccountQuery()) + ->setViewer($viewer) + ->withUserPHIDs(array($user->getPHID())) + ->needImages(true) + ->execute(); + + foreach ($accounts as $account) { + $file = $account->getProfileImageFile(); + if ($account->getProfileImagePHID() != $file->getPHID()) { + // This is a default image, just skip it. + continue; + } + + $provider = PhabricatorAuthProvider::getEnabledProviderByKey( + $account->getProviderKey()); + if ($provider) { + $tip = pht('Picture From %s', $provider->getProviderName()); + } else { + $tip = pht('Picture From External Account'); + } + + if ($file->isTransformableImage()) { + $images[$file->getPHID()] = array( + 'uri' => $file->getBestURI(), + 'tip' => $tip, + ); + } + } + + // Try to add Gravatar images for any email addresses associated with the + // account. + if (PhabricatorEnv::getEnvConfig('security.allow-outbound-http')) { + $emails = id(new PhabricatorUserEmail())->loadAllWhere( + 'userPHID = %s ORDER BY address', + $viewer->getPHID()); + + $futures = array(); + foreach ($emails as $email_object) { + $email = $email_object->getAddress(); + + $hash = md5(strtolower(trim($email))); + $uri = id(new PhutilURI("https://secure.gravatar.com/avatar/{$hash}")) + ->setQueryParams( + array( + 'size' => 200, + 'default' => '404', + 'rating' => 'x', + )); + $futures[$email] = new HTTPSFuture($uri); + } + + $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); + foreach (Futures($futures) as $email => $future) { + try { + list($body) = $future->resolvex(); + $file = PhabricatorFile::newFromFileData( + $body, + array( + 'name' => 'profile-gravatar', + 'ttl' => (60 * 60 * 4), + )); + if ($file->isTransformableImage()) { + $images[$file->getPHID()] = array( + 'uri' => $file->getBestURI(), + 'tip' => pht('Gravatar for %s', $email), + ); + } + } catch (Exception $ex) { + // Just continue. + } + } + unset($unguarded); + } + + $images[PhabricatorPHIDConstants::PHID_VOID] = array( + 'uri' => $default_image->getBestURI(), + 'tip' => pht('Default Picture'), + ); + + require_celerity_resource('people-profile-css'); + Javelin::initBehavior('phabricator-tooltips', array()); + + $buttons = array(); + foreach ($images as $phid => $spec) { + $button = javelin_tag( + 'button', + array( + 'class' => 'grey profile-image-button', + 'sigil' => 'has-tooltip', + 'meta' => array( + 'tip' => $spec['tip'], + 'size' => 300, + ), + ), + phutil_tag( + 'img', + array( + 'height' => 50, + 'width' => 50, + 'src' => $spec['uri'], + ))); + + $button = array( + phutil_tag( + 'input', + array( + 'type' => 'hidden', + 'name' => 'phid', + 'value' => $phid, + )), + $button); + + $button = phabricator_form( + $viewer, + array( + 'class' => 'profile-image-form', + 'method' => 'POST', + ), + $button); + + $buttons[] = $button; + } + + $form->appendChild( + id(new AphrontFormMarkupControl()) + ->setLabel(pht('Current Picture')) + ->setValue(array_slice($buttons, 0, 1))); + + $form->appendChild( + id(new AphrontFormMarkupControl()) + ->setLabel(pht('Use Picture')) + ->setValue(array_slice($buttons, 1))); + + $upload_head = id(new PhabricatorHeaderView()) + ->setHeader(pht('Upload New Picture')); + + $upload_form = id(new AphrontFormView()) + ->setUser($user) + ->setFlexible(true) + ->setEncType('multipart/form-data') + ->appendChild( + id(new AphrontFormFileControl()) + ->setName('picture') + ->setLabel(pht('Upload Picture')) + ->setError($e_file) + ->setCaption( + pht('Supported formats: %s', implode(', ', $supported_formats)))) + ->appendChild( + id(new AphrontFormSubmitControl()) + ->addCancelButton($profile_uri) + ->setValue(pht('Upload Picture'))); + + if ($errors) { + $errors = id(new AphrontErrorView())->setErrors($errors); + } + + return $this->buildApplicationPage( + array( + $crumbs, + $errors, + $form, + $upload_head, + $upload_form, + ), + array( + 'title' => $title, + 'device' => true, + 'dust' => true, + )); + } +} diff --git a/src/applications/settings/panel/PhabricatorSettingsPanelProfile.php b/src/applications/settings/panel/PhabricatorSettingsPanelProfile.php index 0ef509e1c2..e4a71a5106 100644 --- a/src/applications/settings/panel/PhabricatorSettingsPanelProfile.php +++ b/src/applications/settings/panel/PhabricatorSettingsPanelProfile.php @@ -18,11 +18,6 @@ final class PhabricatorSettingsPanelProfile public function processRequest(AphrontRequest $request) { $user = $request->getUser(); - $profile = $user->loadUserProfile(); - - $supported_formats = PhabricatorFile::getTransformableImageFormats(); - - $e_image = null; $errors = array(); if ($request->isFormPost()) { $sex = $request->getStr('sex'); @@ -36,64 +31,8 @@ final class PhabricatorSettingsPanelProfile // Checked in runtime. $user->setTranslation($request->getStr('translation')); - $default_image = $request->getExists('default_image'); - $gravatar_email = $request->getStr('gravatar'); - if ($default_image) { - $profile->setProfileImagePHID(null); - $user->setProfileImagePHID(null); - } else if (!empty($gravatar_email) || $request->getFileExists('image')) { - $file = null; - if (!empty($gravatar_email)) { - // These steps recommended by: - // https://en.gravatar.com/site/implement/hash/ - $trimmed = trim($gravatar_email); - $lower_cased = strtolower($trimmed); - $hash = md5($lower_cased); - $url = 'http://www.gravatar.com/avatar/'.($hash).'?s=200'; - $file = PhabricatorFile::newFromFileDownload( - $url, - array( - 'name' => 'gravatar', - 'authorPHID' => $user->getPHID(), - )); - } else if ($request->getFileExists('image')) { - $file = PhabricatorFile::newFromPHPUpload( - $_FILES['image'], - array( - 'authorPHID' => $user->getPHID(), - )); - } - - $okay = $file->isTransformableImage(); - if ($okay) { - $xformer = new PhabricatorImageTransformer(); - - // Generate the large picture for the profile page. - $large_xformed = $xformer->executeProfileTransform( - $file, - $width = 280, - $min_height = 140, - $max_height = 420); - $profile->setProfileImagePHID($large_xformed->getPHID()); - - // Generate the small picture for comments, etc. - $small_xformed = $xformer->executeProfileTransform( - $file, - $width = 50, - $min_height = 50, - $max_height = 50); - $user->setProfileImagePHID($small_xformed->getPHID()); - } else { - $e_image = pht('Not Supported'); - $errors[] = - pht('This server only supports these image formats:'). - ' ' .implode(', ', $supported_formats); - } - } - if (!$errors) { $user->save(); - $profile->save(); $response = id(new AphrontRedirectResponse()) ->setURI($this->getPanelURI('?saved=true')); return $response; @@ -116,7 +55,6 @@ final class PhabricatorSettingsPanelProfile } } - $img_src = $user->loadProfileImageURI(); $profile_uri = PhabricatorEnv::getURI('/p/'.$user->getUsername().'/'); $sexes = array( @@ -144,7 +82,6 @@ final class PhabricatorSettingsPanelProfile $form = new AphrontFormView(); $form ->setUser($request->getUser()) - ->setEncType('multipart/form-data') ->appendChild( id(new AphrontFormSelectControl()) ->setOptions($sexes) @@ -156,37 +93,11 @@ final class PhabricatorSettingsPanelProfile ->setOptions($translations) ->setLabel(pht('Translation')) ->setName('translation') - ->setValue($user->getTranslation())) - ->appendChild( - id(new AphrontFormMarkupControl()) - ->setLabel(pht('Profile Image')) - ->setValue( - phutil_tag( - 'img', - array( - 'src' => $img_src, - )))) - ->appendChild( - id(new AphrontFormImageControl()) - ->setLabel(pht('Change Image')) - ->setName('image') - ->setError($e_image) - ->setCaption( - pht('Supported formats: %s', implode(', ', $supported_formats)))); - - if (PhabricatorEnv::getEnvConfig('security.allow-outbound-http')) { - $form->appendChild( - id(new AphrontFormTextControl()) - ->setLabel(pht('Import Gravatar')) - ->setName('gravatar') - ->setError($e_image) - ->setCaption(pht('Enter gravatar email address'))); - } + ->setValue($user->getTranslation())); $form->appendChild( id(new AphrontFormSubmitControl()) - ->setValue(pht('Save')) - ->addCancelButton('/p/'.$user->getUsername().'/')); + ->setValue(pht('Save'))); $header = new PhabricatorHeaderView(); $header->setHeader(pht('Edit Profile Details')); diff --git a/webroot/rsrc/css/application/people/people-profile.css b/webroot/rsrc/css/application/people/people-profile.css new file mode 100644 index 0000000000..9a6ea0e22d --- /dev/null +++ b/webroot/rsrc/css/application/people/people-profile.css @@ -0,0 +1,13 @@ +/** + * @provides people-profile-css + */ + +form.profile-image-form { + display: inline-block; + margin: 0 8px 8px 0; +} + +button.profile-image-button { + padding: 4px; + margin: 0; +}