From 37b13ef2c9242b0c8b7ac3032eacbad17bb4e23b Mon Sep 17 00:00:00 2001 From: epriestley Date: Tue, 9 Jul 2013 16:23:54 -0700 Subject: [PATCH] Improve UI for selecting profile pictures Summary: Ref T1703. Move profile pictures to a separate, dedicated interface. Instead of the 35 controls we currently provide, just show all the possible images we can find and then let the user upload an additional one if they want. Possible improvements to this interface: - Write an edge so we can show old profile pictures too. - The cropping/scaling got a bit buggy at some point, fix that. - Refresh OAuth sources which we're capable of refreshing before showing images (more work than I really want to deal with). - We could show little inset icons for the image source ("f" for Facebook, etc.) instead of just the tooltips. Test Plan: Chose images, uploaded new images, hit various error cases. {F49344} Reviewers: chad, btrahan Reviewed By: btrahan CC: aran Maniphest Tasks: T2919, T1703 Differential Revision: https://secure.phabricator.com/D6398 --- src/__celerity_resource_map__.php | 99 +++--- src/__phutil_library_map__.php | 2 + .../files/PhabricatorImageTransformer.php | 1 - .../PhabricatorApplicationPeople.php | 2 + .../PhabricatorPeopleProfileController.php | 8 + ...bricatorPeopleProfilePictureController.php | 292 ++++++++++++++++++ .../panel/PhabricatorSettingsPanelProfile.php | 93 +----- .../css/application/people/people-profile.css | 13 + 8 files changed, 373 insertions(+), 137 deletions(-) create mode 100644 src/applications/people/controller/PhabricatorPeopleProfilePictureController.php create mode 100644 webroot/rsrc/css/application/people/people-profile.css 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; +}