2012-11-22 02:23:28 +01:00
|
|
|
<?php
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @group pholio
|
|
|
|
*/
|
|
|
|
final class PholioMockEditController extends PholioController {
|
|
|
|
|
|
|
|
private $id;
|
|
|
|
|
|
|
|
public function willProcessRequest(array $data) {
|
|
|
|
$this->id = idx($data, 'id');
|
|
|
|
}
|
|
|
|
|
|
|
|
public function processRequest() {
|
|
|
|
$request = $this->getRequest();
|
|
|
|
$user = $request->getUser();
|
|
|
|
|
|
|
|
if ($this->id) {
|
2012-11-22 02:27:44 +01:00
|
|
|
$mock = id(new PholioMockQuery())
|
|
|
|
->setViewer($user)
|
2013-07-16 22:31:20 +02:00
|
|
|
->needImages(true)
|
2012-11-22 02:27:44 +01:00
|
|
|
->requireCapabilities(
|
|
|
|
array(
|
|
|
|
PhabricatorPolicyCapability::CAN_VIEW,
|
|
|
|
PhabricatorPolicyCapability::CAN_EDIT,
|
|
|
|
))
|
|
|
|
->withIDs(array($this->id))
|
|
|
|
->executeOne();
|
|
|
|
|
|
|
|
if (!$mock) {
|
|
|
|
return new Aphront404Response();
|
|
|
|
}
|
|
|
|
|
|
|
|
$title = pht('Edit Mock');
|
|
|
|
|
|
|
|
$is_new = false;
|
2013-07-16 22:31:20 +02:00
|
|
|
$mock_images = $mock->getImages();
|
|
|
|
$files = mpull($mock_images, 'getFile');
|
|
|
|
$mock_images = mpull($mock_images, null, 'getFilePHID');
|
2012-11-22 02:23:28 +01:00
|
|
|
} else {
|
2013-07-16 22:31:20 +02:00
|
|
|
$mock = id(new PholioMock())
|
|
|
|
->setAuthorPHID($user->getPHID())
|
|
|
|
->attachImages(array())
|
|
|
|
->setViewPolicy(PhabricatorPolicies::POLICY_USER);
|
2012-11-22 02:23:28 +01:00
|
|
|
|
|
|
|
$title = pht('Create Mock');
|
2012-11-22 02:27:44 +01:00
|
|
|
|
|
|
|
$is_new = true;
|
2013-07-16 22:31:20 +02:00
|
|
|
$files = array();
|
|
|
|
$mock_images = array();
|
2012-11-22 02:23:28 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
$e_name = true;
|
|
|
|
$e_images = true;
|
|
|
|
$errors = array();
|
|
|
|
|
2012-11-22 02:27:44 +01:00
|
|
|
$v_name = $mock->getName();
|
|
|
|
$v_desc = $mock->getDescription();
|
|
|
|
$v_view = $mock->getViewPolicy();
|
2013-02-22 14:44:46 +01:00
|
|
|
$v_cc = PhabricatorSubscribersQuery::loadSubscribersForPHID(
|
|
|
|
$mock->getPHID());
|
2012-11-22 02:27:44 +01:00
|
|
|
|
2012-11-22 02:23:28 +01:00
|
|
|
if ($request->isFormPost()) {
|
2012-11-22 02:27:44 +01:00
|
|
|
$xactions = array();
|
2012-11-22 02:23:28 +01:00
|
|
|
|
2012-11-22 02:27:44 +01:00
|
|
|
$type_name = PholioTransactionType::TYPE_NAME;
|
|
|
|
$type_desc = PholioTransactionType::TYPE_DESCRIPTION;
|
2012-12-11 22:59:20 +01:00
|
|
|
$type_view = PhabricatorTransactions::TYPE_VIEW_POLICY;
|
2013-02-22 14:44:46 +01:00
|
|
|
$type_cc = PhabricatorTransactions::TYPE_SUBSCRIBERS;
|
2012-11-22 02:23:28 +01:00
|
|
|
|
2012-11-22 02:27:44 +01:00
|
|
|
$v_name = $request->getStr('name');
|
|
|
|
$v_desc = $request->getStr('description');
|
|
|
|
$v_view = $request->getStr('can_view');
|
2013-02-22 14:44:46 +01:00
|
|
|
$v_cc = $request->getArr('cc');
|
2012-11-22 02:23:28 +01:00
|
|
|
|
2013-07-16 22:31:20 +02:00
|
|
|
$mock_xactions = array();
|
|
|
|
$mock_xactions[$type_name] = $v_name;
|
|
|
|
$mock_xactions[$type_desc] = $v_desc;
|
|
|
|
$mock_xactions[$type_view] = $v_view;
|
|
|
|
$mock_xactions[$type_cc] = array('=' => $v_cc);
|
2012-11-22 02:23:28 +01:00
|
|
|
|
2012-11-22 02:27:44 +01:00
|
|
|
if (!strlen($request->getStr('name'))) {
|
|
|
|
$e_name = 'Required';
|
2013-07-18 23:19:43 +02:00
|
|
|
$errors[] = pht('You must give the mock a name.');
|
2012-11-22 02:23:28 +01:00
|
|
|
}
|
|
|
|
|
2013-07-16 22:31:20 +02:00
|
|
|
$file_phids = $request->getArr('file_phids');
|
|
|
|
if ($file_phids) {
|
|
|
|
$files = id(new PhabricatorFileQuery())
|
|
|
|
->setViewer($user)
|
|
|
|
->withPHIDs($file_phids)
|
|
|
|
->execute();
|
|
|
|
$files = mpull($files, null, 'getPHID');
|
|
|
|
$files = array_select_keys($files, $file_phids);
|
2013-07-19 00:04:08 +02:00
|
|
|
} else {
|
|
|
|
$files = array();
|
2013-07-16 22:31:20 +02:00
|
|
|
}
|
2012-11-22 02:27:44 +01:00
|
|
|
|
2013-07-16 22:31:20 +02:00
|
|
|
if (!$files) {
|
|
|
|
$e_images = pht('Required');
|
|
|
|
$errors[] = pht('You must add at least one image to the mock.');
|
|
|
|
} else {
|
|
|
|
$mock->setCoverPHID(head($files)->getPHID());
|
2012-11-22 02:23:28 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!$errors) {
|
2013-07-16 22:31:20 +02:00
|
|
|
foreach ($mock_xactions as $type => $value) {
|
2012-11-22 02:27:44 +01:00
|
|
|
$xactions[$type] = id(new PholioTransaction())
|
|
|
|
->setTransactionType($type)
|
|
|
|
->setNewValue($value);
|
|
|
|
}
|
|
|
|
|
2013-08-12 22:09:07 +02:00
|
|
|
$order = $request->getStrList('imageOrder');
|
|
|
|
$sequence_map = array_flip($order);
|
2013-07-26 01:59:25 +02:00
|
|
|
$replaces = $request->getArr('replaces');
|
|
|
|
$replaces_map = array_flip($replaces);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Foreach file posted, check to see whether we are replacing an image,
|
|
|
|
* adding an image, or simply updating image metadata. Create
|
|
|
|
* transactions for these cases as appropos.
|
|
|
|
*/
|
2013-07-16 22:31:20 +02:00
|
|
|
foreach ($files as $file_phid => $file) {
|
2013-07-26 01:59:25 +02:00
|
|
|
$replaces_image_phid = null;
|
|
|
|
if (isset($replaces_map[$file_phid])) {
|
|
|
|
$old_file_phid = $replaces_map[$file_phid];
|
|
|
|
$old_image = idx($mock_images, $old_file_phid);
|
|
|
|
if ($old_image) {
|
|
|
|
$replaces_image_phid = $old_image->getPHID();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$existing_image = idx($mock_images, $file_phid);
|
|
|
|
|
2013-07-19 00:04:08 +02:00
|
|
|
$title = (string)$request->getStr('title_'.$file_phid);
|
|
|
|
$description = (string)$request->getStr('description_'.$file_phid);
|
2013-08-12 22:09:07 +02:00
|
|
|
$sequence = $sequence_map[$file_phid];
|
2013-07-26 01:59:25 +02:00
|
|
|
|
|
|
|
if ($replaces_image_phid) {
|
|
|
|
$replace_image = id(new PholioImage())
|
|
|
|
->setReplacesImagePHID($replaces_image_phid)
|
|
|
|
->setFilePhid($file_phid)
|
|
|
|
->setName(strlen($title) ? $title : $file->getName())
|
|
|
|
->setDescription($description)
|
|
|
|
->setSequence($sequence);
|
|
|
|
$xactions[] = id(new PholioTransaction())
|
|
|
|
->setTransactionType(
|
|
|
|
PholioTransactionType::TYPE_IMAGE_REPLACE)
|
|
|
|
->setNewValue($replace_image);
|
|
|
|
} else if (!$existing_image) { // this is an add
|
2013-07-16 22:31:20 +02:00
|
|
|
$add_image = id(new PholioImage())
|
|
|
|
->setFilePhid($file_phid)
|
|
|
|
->setName(strlen($title) ? $title : $file->getName())
|
|
|
|
->setDescription($description)
|
|
|
|
->setSequence($sequence);
|
|
|
|
$xactions[] = id(new PholioTransaction())
|
|
|
|
->setTransactionType(PholioTransactionType::TYPE_IMAGE_FILE)
|
|
|
|
->setNewValue(
|
|
|
|
array('+' => array($add_image)));
|
|
|
|
} else {
|
|
|
|
$xactions[] = id(new PholioTransaction())
|
|
|
|
->setTransactionType(PholioTransactionType::TYPE_IMAGE_NAME)
|
|
|
|
->setNewValue(
|
2013-07-26 01:59:25 +02:00
|
|
|
array($existing_image->getPHID() => $title));
|
2013-07-16 22:31:20 +02:00
|
|
|
$xactions[] = id(new PholioTransaction())
|
|
|
|
->setTransactionType(
|
|
|
|
PholioTransactionType::TYPE_IMAGE_DESCRIPTION)
|
2013-07-26 01:59:25 +02:00
|
|
|
->setNewValue(
|
|
|
|
array($existing_image->getPHID() => $description));
|
2013-08-12 22:09:07 +02:00
|
|
|
$xactions[] = id(new PholioTransaction())
|
|
|
|
->setTransactionType(
|
|
|
|
PholioTransactionType::TYPE_IMAGE_SEQUENCE)
|
|
|
|
->setNewValue(
|
|
|
|
array($existing_image->getPHID() => $sequence));
|
2013-07-16 22:31:20 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
foreach ($mock_images as $file_phid => $mock_image) {
|
2013-07-26 01:59:25 +02:00
|
|
|
if (!isset($files[$file_phid]) && !isset($replaces[$file_phid])) {
|
|
|
|
// this is an outright delete
|
2013-07-16 22:31:20 +02:00
|
|
|
$xactions[] = id(new PholioTransaction())
|
|
|
|
->setTransactionType(PholioTransactionType::TYPE_IMAGE_FILE)
|
|
|
|
->setNewValue(
|
|
|
|
array('-' => array($mock_image)));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-11-22 02:23:28 +01:00
|
|
|
$mock->openTransaction();
|
2012-11-22 02:27:44 +01:00
|
|
|
$editor = id(new PholioMockEditor())
|
2013-05-24 19:48:34 +02:00
|
|
|
->setContentSourceFromRequest($request)
|
2012-12-21 14:51:33 +01:00
|
|
|
->setContinueOnNoEffect(true)
|
2012-11-22 02:27:44 +01:00
|
|
|
->setActor($user);
|
|
|
|
|
Add ApplicationTransaction handling for transactions with no effect
Summary:
When a user submits an action with no effect (like an empty comment, an "abandon" on an already-accepted revision, or a "close, resolved" on a closed task) we want to alert them that their action isn't effective. These warnings fall into two general buckets:
- User is submitting two or more actions, and some aren't effective but some are. Prompt them to apply the effective actions only.
- A special case of this is where the only effective action is a comment. We provide tailored text ("Post Comment") in this case.
- User is submitting one action, which isn't effective. Tell them they're out of luck.
- A special case of this is an empty comment. We provide tailored text in this case.
By default, the transaction editor throws when transactions have no effect. The caller can then deal with this, or use `PhabricatorApplicationTransactionNoEffectResponse` to provide a standard dialog that gives the user information as above. For cases where we expect transactions to have no effect (notably, "edit" forms) we just continue on no-effect unconditionally.
Also fix an issue where new, combined or filtered transactions would not be represented properly in the Ajax response (i.e., return final transactions from `applyTransactions()`), and translate some strings.
Test Plan:
- Submitted empty and nonempy comments in Macro and Pholio.
- Submitted comments with new and existing "@mentions".
- Submitted edits in both applications.
Reviewers: btrahan, vrana
Reviewed By: btrahan
CC: aran
Maniphest Tasks: T912, T2104
Differential Revision: https://secure.phabricator.com/D4160
2012-12-12 02:27:40 +01:00
|
|
|
$xactions = $editor->applyTransactions($mock, $xactions);
|
2012-11-22 02:27:44 +01:00
|
|
|
|
2012-11-22 02:23:28 +01:00
|
|
|
$mock->saveTransaction();
|
|
|
|
|
|
|
|
return id(new AphrontRedirectResponse())
|
|
|
|
->setURI('/M'.$mock->getID());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($errors) {
|
|
|
|
$error_view = id(new AphrontErrorView())
|
|
|
|
->setTitle(pht('Form Errors'))
|
|
|
|
->setErrors($errors);
|
|
|
|
} else {
|
|
|
|
$error_view = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($this->id) {
|
|
|
|
$submit = id(new AphrontFormSubmitControl())
|
|
|
|
->addCancelButton('/M'.$this->id)
|
|
|
|
->setValue(pht('Save'));
|
|
|
|
} else {
|
|
|
|
$submit = id(new AphrontFormSubmitControl())
|
|
|
|
->addCancelButton($this->getApplicationURI())
|
|
|
|
->setValue(pht('Create'));
|
|
|
|
}
|
|
|
|
|
|
|
|
$policies = id(new PhabricatorPolicyQuery())
|
|
|
|
->setViewer($user)
|
|
|
|
->setObject($mock)
|
|
|
|
->execute();
|
|
|
|
|
2012-11-22 02:27:44 +01:00
|
|
|
// NOTE: Make this show up correctly on the rendered form.
|
|
|
|
$mock->setViewPolicy($v_view);
|
|
|
|
|
2013-09-11 21:27:28 +02:00
|
|
|
$handles = id(new PhabricatorHandleQuery())
|
2013-02-22 14:44:46 +01:00
|
|
|
->setViewer($user)
|
2013-09-11 21:27:28 +02:00
|
|
|
->withPHIDs($v_cc)
|
|
|
|
->execute();
|
2013-02-22 14:44:46 +01:00
|
|
|
|
|
|
|
$cc_tokens = mpull($handles, 'getFullName', 'getPHID');
|
|
|
|
|
2013-07-19 00:04:08 +02:00
|
|
|
$image_elements = array();
|
|
|
|
foreach ($mock_images as $mock_image) {
|
|
|
|
$image_elements[] = id(new PholioUploadedImageView())
|
|
|
|
->setUser($user)
|
|
|
|
->setImage($mock_image);
|
|
|
|
}
|
|
|
|
|
|
|
|
$list_id = celerity_generate_unique_node_id();
|
|
|
|
$drop_id = celerity_generate_unique_node_id();
|
2013-08-12 21:08:54 +02:00
|
|
|
$order_id = celerity_generate_unique_node_id();
|
2013-07-19 00:04:08 +02:00
|
|
|
|
|
|
|
$list_control = phutil_tag(
|
|
|
|
'div',
|
|
|
|
array(
|
|
|
|
'id' => $list_id,
|
|
|
|
'class' => 'pholio-edit-list',
|
|
|
|
),
|
|
|
|
$image_elements);
|
|
|
|
|
|
|
|
$drop_control = phutil_tag(
|
|
|
|
'div',
|
|
|
|
array(
|
|
|
|
'id' => $drop_id,
|
|
|
|
'class' => 'pholio-edit-drop',
|
|
|
|
),
|
|
|
|
'Drag and drop images here to add them to the mock.');
|
|
|
|
|
2013-08-12 21:08:54 +02:00
|
|
|
$order_control = phutil_tag(
|
|
|
|
'input',
|
|
|
|
array(
|
|
|
|
'type' => 'hidden',
|
|
|
|
'name' => 'imageOrder',
|
|
|
|
'id' => $order_id,
|
|
|
|
));
|
|
|
|
|
2013-07-19 00:04:08 +02:00
|
|
|
Javelin::initBehavior(
|
|
|
|
'pholio-mock-edit',
|
|
|
|
array(
|
|
|
|
'listID' => $list_id,
|
|
|
|
'dropID' => $drop_id,
|
2013-08-12 21:08:54 +02:00
|
|
|
'orderID' => $order_id,
|
2013-07-19 00:04:08 +02:00
|
|
|
'uploadURI' => '/file/dropupload/',
|
|
|
|
'renderURI' => $this->getApplicationURI('image/upload/'),
|
|
|
|
'pht' => array(
|
|
|
|
'uploading' => pht('Uploading Image...'),
|
|
|
|
'uploaded' => pht('Upload Complete...'),
|
2013-07-19 17:50:49 +02:00
|
|
|
'undo' => pht('Undo'),
|
|
|
|
'removed' => pht('This image will be removed from the mock.'),
|
2013-07-19 00:04:08 +02:00
|
|
|
),
|
|
|
|
));
|
2013-07-16 22:31:20 +02:00
|
|
|
|
|
|
|
require_celerity_resource('pholio-edit-css');
|
2012-11-22 02:23:28 +01:00
|
|
|
$form = id(new AphrontFormView())
|
|
|
|
->setUser($user)
|
2013-08-12 21:08:54 +02:00
|
|
|
->appendChild($order_control)
|
2012-11-22 02:23:28 +01:00
|
|
|
->appendChild(
|
|
|
|
id(new AphrontFormTextControl())
|
|
|
|
->setName('name')
|
2012-11-22 02:27:44 +01:00
|
|
|
->setValue($v_name)
|
2012-11-22 02:23:28 +01:00
|
|
|
->setLabel(pht('Name'))
|
|
|
|
->setError($e_name))
|
|
|
|
->appendChild(
|
|
|
|
id(new PhabricatorRemarkupControl())
|
|
|
|
->setName('description')
|
2012-11-22 02:27:44 +01:00
|
|
|
->setValue($v_desc)
|
2012-11-27 23:06:31 +01:00
|
|
|
->setLabel(pht('Description'))
|
|
|
|
->setUser($user))
|
2013-02-22 14:44:46 +01:00
|
|
|
->appendChild(
|
|
|
|
id(new AphrontFormTokenizerControl())
|
|
|
|
->setLabel(pht('CC'))
|
|
|
|
->setName('cc')
|
|
|
|
->setValue($cc_tokens)
|
|
|
|
->setUser($user)
|
|
|
|
->setDatasource('/typeahead/common/mailable/'))
|
2012-11-22 02:23:28 +01:00
|
|
|
->appendChild(
|
|
|
|
id(new AphrontFormPolicyControl())
|
|
|
|
->setUser($user)
|
|
|
|
->setCapability(PhabricatorPolicyCapability::CAN_VIEW)
|
|
|
|
->setPolicyObject($mock)
|
|
|
|
->setPolicies($policies)
|
|
|
|
->setName('can_view'))
|
2013-07-19 00:04:08 +02:00
|
|
|
->appendChild(
|
|
|
|
id(new AphrontFormMarkupControl())
|
|
|
|
->setValue($list_control))
|
|
|
|
->appendChild(
|
|
|
|
id(new AphrontFormMarkupControl())
|
|
|
|
->setValue($drop_control)
|
|
|
|
->setError($e_images))
|
2012-11-22 02:23:28 +01:00
|
|
|
->appendChild($submit);
|
|
|
|
|
2013-09-25 20:23:29 +02:00
|
|
|
$form_box = id(new PHUIObjectBoxView())
|
2013-08-26 20:53:11 +02:00
|
|
|
->setHeaderText($title)
|
|
|
|
->setFormError($error_view)
|
|
|
|
->setForm($form);
|
|
|
|
|
2013-07-22 21:21:15 +02:00
|
|
|
$crumbs = $this->buildApplicationCrumbs();
|
2013-03-26 21:15:15 +01:00
|
|
|
$crumbs->addCrumb(
|
|
|
|
id(new PhabricatorCrumbView())
|
|
|
|
->setName($title)
|
|
|
|
->setHref($this->getApplicationURI()));
|
2012-11-22 02:23:28 +01:00
|
|
|
|
|
|
|
$content = array(
|
2013-03-26 21:15:15 +01:00
|
|
|
$crumbs,
|
2013-08-26 20:53:11 +02:00
|
|
|
$form_box,
|
2012-11-22 02:23:28 +01:00
|
|
|
);
|
|
|
|
|
|
|
|
return $this->buildApplicationPage(
|
2013-07-22 21:21:15 +02:00
|
|
|
$content,
|
2012-11-22 02:23:28 +01:00
|
|
|
array(
|
|
|
|
'title' => $title,
|
2013-02-15 19:17:31 +01:00
|
|
|
'device' => true,
|
2012-11-22 02:23:28 +01:00
|
|
|
));
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|