2013-10-17 18:32:34 +02:00
|
|
|
<?php
|
|
|
|
|
2014-02-17 05:17:52 +01:00
|
|
|
final class PhabricatorProjectEditPictureController
|
2013-10-17 18:32:34 +02:00
|
|
|
extends PhabricatorProjectController {
|
|
|
|
|
2015-08-08 19:34:55 +02:00
|
|
|
public function handleRequest(AphrontRequest $request) {
|
|
|
|
$viewer = $request->getViewer();
|
2015-01-12 19:04:01 +01:00
|
|
|
$id = $request->getURIData('id');
|
2013-10-17 18:32:34 +02:00
|
|
|
|
|
|
|
$project = id(new PhabricatorProjectQuery())
|
|
|
|
->setViewer($viewer)
|
2015-08-08 19:34:55 +02:00
|
|
|
->withIDs(array($id))
|
2015-01-12 19:04:01 +01:00
|
|
|
->needImages(true)
|
2013-10-17 18:32:34 +02:00
|
|
|
->requireCapabilities(
|
|
|
|
array(
|
|
|
|
PhabricatorPolicyCapability::CAN_VIEW,
|
|
|
|
PhabricatorPolicyCapability::CAN_EDIT,
|
|
|
|
))
|
|
|
|
->executeOne();
|
|
|
|
if (!$project) {
|
|
|
|
return new Aphront404Response();
|
|
|
|
}
|
|
|
|
|
2016-01-12 23:15:59 +01:00
|
|
|
$this->setProject($project);
|
|
|
|
|
2015-01-19 19:14:27 +01:00
|
|
|
$edit_uri = $this->getApplicationURI('profile/'.$project->getID().'/');
|
2015-01-12 19:04:01 +01:00
|
|
|
$view_uri = $this->getApplicationURI('profile/'.$project->getID().'/');
|
2013-10-17 18:32:34 +02:00
|
|
|
|
|
|
|
$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(),
|
2014-08-08 03:56:19 +02:00
|
|
|
'canCDN' => true,
|
2013-10-17 18:32:34 +02:00
|
|
|
));
|
|
|
|
} else {
|
|
|
|
$e_file = pht('Required');
|
|
|
|
$errors[] = pht(
|
|
|
|
'You must choose a file when uploading a new project 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 {
|
2015-05-13 02:32:06 +02:00
|
|
|
$xform = PhabricatorFileTransform::getTransformByKey(
|
|
|
|
PhabricatorFileThumbnailTransform::TRANSFORM_PROFILE);
|
|
|
|
$xformed = $xform->executeTransform($file);
|
2013-10-17 18:32:34 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!$errors) {
|
|
|
|
if ($is_default) {
|
2014-02-10 23:32:30 +01:00
|
|
|
$new_value = null;
|
2013-10-17 18:32:34 +02:00
|
|
|
} else {
|
2014-02-10 23:32:30 +01:00
|
|
|
$new_value = $xformed->getPHID();
|
2013-10-17 18:32:34 +02:00
|
|
|
}
|
2014-02-10 23:32:30 +01:00
|
|
|
|
|
|
|
$xactions = array();
|
|
|
|
$xactions[] = id(new PhabricatorProjectTransaction())
|
|
|
|
->setTransactionType(PhabricatorProjectTransaction::TYPE_IMAGE)
|
|
|
|
->setNewValue($new_value);
|
|
|
|
|
|
|
|
$editor = id(new PhabricatorProjectTransactionEditor())
|
|
|
|
->setActor($viewer)
|
|
|
|
->setContentSourceFromRequest($request)
|
|
|
|
->setContinueOnMissingFields(true)
|
|
|
|
->setContinueOnNoEffect(true);
|
|
|
|
|
|
|
|
$editor->applyTransactions($project, $xactions);
|
|
|
|
|
2014-02-17 05:17:52 +01:00
|
|
|
return id(new AphrontRedirectResponse())->setURI($edit_uri);
|
2013-10-17 18:32:34 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$title = pht('Edit Project Picture');
|
|
|
|
|
|
|
|
$form = id(new PHUIFormLayoutView())
|
|
|
|
->setUser($viewer);
|
|
|
|
|
|
|
|
$default_image = PhabricatorFile::loadBuiltin($viewer, 'project.png');
|
|
|
|
|
|
|
|
$images = array();
|
|
|
|
|
Migrate project profiles onto projects, and remove ProjectProfile object
Summary:
Ref T4379. Long ago, the "Project" vs "ProjectProfile" split was intended to allow a bunch of special fields on projects without burdening the simple use cases, but CustomField handles that far better and far more generally, and doing this makes using ApplicationTransactions a pain to get right, so get rid of it.
The only remaining field is `profileImagePHID`, which we can just move to the main Project object. This is custom enough that I think it's reasonable not to express it as a custom field.
Test Plan: Created a project, set profile, edited project, viewed in typeahead, ran migration, verified database results.
Reviewers: btrahan
Reviewed By: btrahan
CC: aran
Maniphest Tasks: T4379
Differential Revision: https://secure.phabricator.com/D8183
2014-02-10 23:32:14 +01:00
|
|
|
$current = $project->getProfileImagePHID();
|
2013-10-17 18:32:34 +02:00
|
|
|
$has_current = false;
|
|
|
|
if ($current) {
|
|
|
|
$files = id(new PhabricatorFileQuery())
|
|
|
|
->setViewer($viewer)
|
|
|
|
->withPHIDs(array($current))
|
|
|
|
->execute();
|
|
|
|
if ($files) {
|
|
|
|
$file = head($files);
|
|
|
|
if ($file->isTransformableImage()) {
|
|
|
|
$has_current = true;
|
|
|
|
$images[$current] = array(
|
|
|
|
'uri' => $file->getBestURI(),
|
|
|
|
'tip' => pht('Current Picture'),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$images[PhabricatorPHIDConstants::PHID_VOID] = array(
|
|
|
|
'uri' => $default_image->getBestURI(),
|
2016-01-18 21:58:19 +01:00
|
|
|
'tip' => pht('No Picture'),
|
2013-10-17 18:32:34 +02:00
|
|
|
);
|
|
|
|
|
|
|
|
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,
|
|
|
|
)),
|
2014-10-07 15:01:04 +02:00
|
|
|
$button,
|
|
|
|
);
|
2013-10-17 18:32:34 +02:00
|
|
|
|
|
|
|
$button = phabricator_form(
|
|
|
|
$viewer,
|
|
|
|
array(
|
|
|
|
'class' => 'profile-image-form',
|
|
|
|
'method' => 'POST',
|
|
|
|
),
|
|
|
|
$button);
|
|
|
|
|
|
|
|
$buttons[] = $button;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($has_current) {
|
|
|
|
$form->appendChild(
|
|
|
|
id(new AphrontFormMarkupControl())
|
|
|
|
->setLabel(pht('Current Picture'))
|
|
|
|
->setValue(array_shift($buttons)));
|
|
|
|
}
|
|
|
|
|
|
|
|
$form->appendChild(
|
|
|
|
id(new AphrontFormMarkupControl())
|
|
|
|
->setLabel(pht('Use Picture'))
|
2016-01-18 21:58:19 +01:00
|
|
|
->setValue(
|
|
|
|
array(
|
|
|
|
$this->renderDefaultForm($project),
|
|
|
|
$buttons,
|
|
|
|
)));
|
2013-10-17 18:32:34 +02:00
|
|
|
|
|
|
|
$launch_id = celerity_generate_unique_node_id();
|
|
|
|
$input_id = celerity_generate_unique_node_id();
|
|
|
|
|
|
|
|
Javelin::initBehavior(
|
|
|
|
'launch-icon-composer',
|
|
|
|
array(
|
|
|
|
'launchID' => $launch_id,
|
|
|
|
'inputID' => $input_id,
|
|
|
|
));
|
|
|
|
|
|
|
|
$compose_button = javelin_tag(
|
|
|
|
'button',
|
|
|
|
array(
|
|
|
|
'class' => 'grey',
|
|
|
|
'id' => $launch_id,
|
|
|
|
'sigil' => 'icon-composer',
|
|
|
|
),
|
|
|
|
pht('Choose Icon and Color...'));
|
|
|
|
|
|
|
|
$compose_input = javelin_tag(
|
|
|
|
'input',
|
|
|
|
array(
|
|
|
|
'type' => 'hidden',
|
|
|
|
'id' => $input_id,
|
|
|
|
'name' => 'phid',
|
|
|
|
));
|
|
|
|
|
|
|
|
$compose_form = phabricator_form(
|
|
|
|
$viewer,
|
|
|
|
array(
|
|
|
|
'class' => 'profile-image-form',
|
|
|
|
'method' => 'POST',
|
|
|
|
),
|
|
|
|
array(
|
|
|
|
$compose_input,
|
|
|
|
$compose_button,
|
|
|
|
));
|
|
|
|
|
|
|
|
$form->appendChild(
|
|
|
|
id(new AphrontFormMarkupControl())
|
|
|
|
->setLabel(pht('Quick Create'))
|
|
|
|
->setValue($compose_form));
|
|
|
|
|
|
|
|
$upload_form = id(new AphrontFormView())
|
|
|
|
->setUser($viewer)
|
|
|
|
->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())
|
2014-02-17 05:17:52 +01:00
|
|
|
->addCancelButton($edit_uri)
|
2013-10-17 18:32:34 +02:00
|
|
|
->setValue(pht('Upload Picture')));
|
|
|
|
|
|
|
|
$form_box = id(new PHUIObjectBoxView())
|
|
|
|
->setHeaderText($title)
|
2014-01-10 18:17:37 +01:00
|
|
|
->setFormErrors($errors)
|
2013-10-17 18:32:34 +02:00
|
|
|
->setForm($form);
|
|
|
|
|
|
|
|
$upload_box = id(new PHUIObjectBoxView())
|
|
|
|
->setHeaderText(pht('Upload New Picture'))
|
|
|
|
->setForm($upload_form);
|
|
|
|
|
2016-01-12 23:15:59 +01:00
|
|
|
$nav = $this->getProfileMenu();
|
2016-01-12 19:27:39 +01:00
|
|
|
$nav->selectFilter(PhabricatorProject::PANEL_PROFILE);
|
2015-01-12 19:04:01 +01:00
|
|
|
|
2016-01-12 23:15:59 +01:00
|
|
|
return $this->newPage()
|
|
|
|
->setTitle($title)
|
|
|
|
->setNavigation($nav)
|
|
|
|
->appendChild(
|
|
|
|
array(
|
|
|
|
$form_box,
|
|
|
|
$upload_box,
|
|
|
|
));
|
2013-10-17 18:32:34 +02:00
|
|
|
}
|
2016-01-18 21:58:19 +01:00
|
|
|
|
|
|
|
private function renderDefaultForm(PhabricatorProject $project) {
|
|
|
|
$viewer = $this->getViewer();
|
|
|
|
$compose_color = $project->getDisplayIconComposeColor();
|
|
|
|
$compose_icon = $project->getDisplayIconComposeIcon();
|
|
|
|
|
|
|
|
$default_builtin = id(new PhabricatorFilesComposeIconBuiltinFile())
|
|
|
|
->setColor($compose_color)
|
|
|
|
->setIcon($compose_icon);
|
|
|
|
|
|
|
|
$file_builtins = PhabricatorFile::loadBuiltins(
|
|
|
|
$viewer,
|
|
|
|
array($default_builtin));
|
|
|
|
|
|
|
|
$file_builtin = head($file_builtins);
|
|
|
|
|
|
|
|
$default_button = javelin_tag(
|
|
|
|
'button',
|
|
|
|
array(
|
|
|
|
'class' => 'grey profile-image-button',
|
|
|
|
'sigil' => 'has-tooltip',
|
|
|
|
'meta' => array(
|
|
|
|
'tip' => pht('Use Icon and Color'),
|
|
|
|
'size' => 300,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
phutil_tag(
|
|
|
|
'img',
|
|
|
|
array(
|
|
|
|
'height' => 50,
|
|
|
|
'width' => 50,
|
|
|
|
'src' => $file_builtin->getBestURI(),
|
|
|
|
)));
|
|
|
|
|
|
|
|
$inputs = array(
|
|
|
|
'projectPHID' => $project->getPHID(),
|
|
|
|
'icon' => $compose_icon,
|
|
|
|
'color' => $compose_color,
|
|
|
|
);
|
|
|
|
|
|
|
|
foreach ($inputs as $key => $value) {
|
|
|
|
$inputs[$key] = javelin_tag(
|
|
|
|
'input',
|
|
|
|
array(
|
|
|
|
'type' => 'hidden',
|
|
|
|
'name' => $key,
|
|
|
|
'value' => $value,
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
|
|
|
$default_form = phabricator_form(
|
|
|
|
$viewer,
|
|
|
|
array(
|
|
|
|
'class' => 'profile-image-form',
|
|
|
|
'method' => 'POST',
|
|
|
|
'action' => '/file/compose/',
|
|
|
|
),
|
|
|
|
array(
|
|
|
|
$inputs,
|
|
|
|
$default_button,
|
|
|
|
));
|
|
|
|
|
|
|
|
return $default_form;
|
|
|
|
}
|
|
|
|
|
2013-10-17 18:32:34 +02:00
|
|
|
}
|