diff --git a/src/applications/config/controller/PhabricatorConfigEditController.php b/src/applications/config/controller/PhabricatorConfigEditController.php index b5ff45195d..1e82c02ace 100644 --- a/src/applications/config/controller/PhabricatorConfigEditController.php +++ b/src/applications/config/controller/PhabricatorConfigEditController.php @@ -1,505 +1,500 @@ key = $data['key']; } public function processRequest() { $request = $this->getRequest(); $user = $request->getUser(); $options = PhabricatorApplicationConfigOptions::loadAllOptions(); if (empty($options[$this->key])) { // This may be a dead config entry, which existed in the past but no // longer exists. Allow it to be edited so it can be reviewed and // deleted. $option = id(new PhabricatorConfigOption()) ->setKey($this->key) ->setType('wild') ->setDefault(null) ->setDescription( pht( "This configuration option is unknown. It may be misspelled, ". "or have existed in a previous version of Phabricator.")); $group = null; $group_uri = $this->getApplicationURI(); } else { $option = $options[$this->key]; $group = $option->getGroup(); $group_uri = $this->getApplicationURI('group/'.$group->getKey().'/'); } $issue = $request->getStr('issue'); if ($issue) { // If the user came here from an open setup issue, send them back. $done_uri = $this->getApplicationURI('issue/'.$issue.'/'); } else { $done_uri = $group_uri; } // Check if the config key is already stored in the database. // Grab the value if it is. $config_entry = id(new PhabricatorConfigEntry()) ->loadOneWhere( 'configKey = %s AND namespace = %s', $this->key, 'default'); if (!$config_entry) { $config_entry = id(new PhabricatorConfigEntry()) ->setConfigKey($this->key) ->setNamespace('default') ->setIsDeleted(true); } $e_value = null; $errors = array(); if ($request->isFormPost() && !$option->getLocked()) { $result = $this->readRequest( $option, $request); list($e_value, $value_errors, $display_value, $xaction) = $result; $errors = array_merge($errors, $value_errors); if (!$errors) { $editor = id(new PhabricatorConfigEditor()) ->setActor($user) ->setContinueOnNoEffect(true) - ->setContentSource( - PhabricatorContentSource::newForSource( - PhabricatorContentSource::SOURCE_WEB, - array( - 'ip' => $request->getRemoteAddr(), - ))); + ->setContentSourceFromRequest($request); try { $editor->applyTransactions($config_entry, array($xaction)); return id(new AphrontRedirectResponse())->setURI($done_uri); } catch (PhabricatorConfigValidationException $ex) { $e_value = pht('Invalid'); $errors[] = $ex->getMessage(); } } } else { $display_value = $this->getDisplayValue($option, $config_entry); } $form = new AphrontFormView(); $form->setFlexible(true); $error_view = null; if ($errors) { $error_view = id(new AphrontErrorView()) ->setTitle(pht('You broke everything!')) ->setErrors($errors); } else if ($option->getHidden()) { $msg = pht( "This configuration is hidden and can not be edited or viewed from ". "the web interface."); $error_view = id(new AphrontErrorView()) ->setTitle(pht('Configuration Hidden')) ->setSeverity(AphrontErrorView::SEVERITY_WARNING) ->appendChild(phutil_tag('p', array(), $msg)); } else if ($option->getLocked()) { $msg = pht( "This configuration is locked and can not be edited from the web ". "interface."); $error_view = id(new AphrontErrorView()) ->setTitle(pht('Configuration Locked')) ->setSeverity(AphrontErrorView::SEVERITY_NOTICE) ->appendChild(phutil_tag('p', array(), $msg)); } if ($option->getHidden()) { $control = null; } else { $control = $this->renderControl( $option, $display_value, $e_value); } $engine = new PhabricatorMarkupEngine(); $engine->setViewer($user); $engine->addObject($option, 'description'); $engine->process(); $description = phutil_tag( 'div', array( 'class' => 'phabricator-remarkup', ), $engine->getOutput($option, 'description')); $form ->setUser($user) ->addHiddenInput('issue', $request->getStr('issue')) ->appendChild( id(new AphrontFormMarkupControl()) ->setLabel(pht('Description')) ->setValue($description)) ->appendChild($control); $submit_control = id(new AphrontFormSubmitControl()) ->addCancelButton($done_uri); if (!$option->getLocked()) { $submit_control->setValue(pht('Save Config Entry')); } $form->appendChild($submit_control); $examples = $this->renderExamples($option); if ($examples) { $form->appendChild( id(new AphrontFormMarkupControl()) ->setLabel(pht('Examples')) ->setValue($examples)); } if (!$option->getHidden()) { $form->appendChild( id(new AphrontFormMarkupControl()) ->setLabel(pht('Default')) ->setValue($this->renderDefaults($option))); } $title = pht('Edit %s', $this->key); $short = pht('Edit'); $crumbs = $this->buildApplicationCrumbs(); $crumbs->addCrumb( id(new PhabricatorCrumbView()) ->setName(pht('Config')) ->setHref($this->getApplicationURI())); if ($group) { $crumbs->addCrumb( id(new PhabricatorCrumbView()) ->setName($group->getName()) ->setHref($group_uri)); } $crumbs->addCrumb( id(new PhabricatorCrumbView()) ->setName($this->key) ->setHref('/config/edit/'.$this->key)); $xactions = id(new PhabricatorConfigTransactionQuery()) ->withObjectPHIDs(array($config_entry->getPHID())) ->setViewer($user) ->execute(); $xaction_view = id(new PhabricatorApplicationTransactionView()) ->setUser($user) ->setTransactions($xactions); return $this->buildApplicationPage( array( $crumbs, id(new PhabricatorHeaderView())->setHeader($title), $error_view, $form, $xaction_view, ), array( 'title' => $title, 'device' => true, )); } private function readRequest( PhabricatorConfigOption $option, AphrontRequest $request) { $xaction = new PhabricatorConfigTransaction(); $xaction->setTransactionType(PhabricatorConfigTransaction::TYPE_EDIT); $e_value = null; $errors = array(); $value = $request->getStr('value'); if (!strlen($value)) { $value = null; $xaction->setNewValue( array( 'deleted' => true, 'value' => null, )); return array($e_value, $errors, $value, $xaction); } $type = $option->getType(); $set_value = null; switch ($type) { case 'int': if (preg_match('/^-?[0-9]+$/', trim($value))) { $set_value = (int)$value; } else { $e_value = pht('Invalid'); $errors[] = pht('Value must be an integer.'); } break; case 'string': case 'enum': $set_value = (string)$value; break; case 'list': $set_value = $request->getStrList('value'); break; case 'set': $set_value = array_fill_keys($request->getStrList('value'), true); break; case 'bool': switch ($value) { case 'true': $set_value = true; break; case 'false': $set_value = false; break; default: $e_value = pht('Invalid'); $errors[] = pht('Value must be boolean, "true" or "false".'); break; } break; case 'class': if (!class_exists($value)) { $e_value = pht('Invalid'); $errors[] = pht('Class does not exist.'); } else { $base = $option->getBaseClass(); if (!is_subclass_of($value, $base)) { $e_value = pht('Invalid'); $errors[] = pht('Class is not of valid type.'); } else { $set_value = $value; } } break; default: $json = json_decode($value, true); if ($json === null && strtolower($value) != 'null') { $e_value = pht('Invalid'); $errors[] = pht( 'The given value must be valid JSON. This means, among '. 'other things, that you must wrap strings in double-quotes.'); } else { $set_value = $json; } break; } if (!$errors) { $xaction->setNewValue( array( 'deleted' => false, 'value' => $set_value, )); } else { $xaction = null; } return array($e_value, $errors, $value, $xaction); } private function getDisplayValue( PhabricatorConfigOption $option, PhabricatorConfigEntry $entry) { if ($entry->getIsDeleted()) { return null; } $type = $option->getType(); $value = $entry->getValue(); switch ($type) { case 'int': case 'string': case 'enum': case 'class': return $value; case 'bool': return $value ? 'true' : 'false'; case 'list': return implode("\n", nonempty($value, array())); case 'set': return implode("\n", nonempty(array_keys($value), array())); default: return PhabricatorConfigJSON::prettyPrintJSON($value); } } private function renderControl( PhabricatorConfigOption $option, $display_value, $e_value) { $type = $option->getType(); switch ($type) { case 'int': case 'string': $control = id(new AphrontFormTextControl()); break; case 'bool': $control = id(new AphrontFormSelectControl()) ->setOptions( array( '' => pht('(Use Default)'), 'true' => idx($option->getBoolOptions(), 0), 'false' => idx($option->getBoolOptions(), 1), )); break; case 'enum': $options = array_mergev( array( array('' => pht('(Use Default)')), $option->getEnumOptions(), )); $control = id(new AphrontFormSelectControl()) ->setOptions($options); break; case 'class': $symbols = id(new PhutilSymbolLoader()) ->setType('class') ->setAncestorClass($option->getBaseClass()) ->setConcreteOnly(true) ->selectSymbolsWithoutLoading(); $names = ipull($symbols, 'name', 'name'); asort($names); $names = array( '' => pht('(Use Default)'), ) + $names; $control = id(new AphrontFormSelectControl()) ->setOptions($names); break; case 'list': case 'set': $control = id(new AphrontFormTextAreaControl()) ->setCaption(pht('Separate values with newlines or commas.')); break; default: $control = id(new AphrontFormTextAreaControl()) ->setHeight(AphrontFormTextAreaControl::HEIGHT_VERY_TALL) ->setCustomClass('PhabricatorMonospaced') ->setCaption(pht('Enter value in JSON.')); break; } $control ->setLabel(pht('Value')) ->setError($e_value) ->setValue($display_value) ->setName('value'); if ($option->getLocked()) { $control->setDisabled(true); } return $control; } private function renderExamples(PhabricatorConfigOption $option) { $examples = $option->getExamples(); if (!$examples) { return null; } $table = array(); $table[] = hsprintf( '%s%s', pht('Example'), pht('Value')); foreach ($examples as $example) { list($value, $description) = $example; if ($value === null) { $value = phutil_tag('em', array(), pht('(empty)')); } else { $value = phutil_escape_html_newlines($value); } $table[] = hsprintf( '%s%s', $description, $value); } require_celerity_resource('config-options-css'); return phutil_tag( 'table', array( 'class' => 'config-option-table', ), $table); } private function renderDefaults(PhabricatorConfigOption $option) { $stack = PhabricatorEnv::getConfigSourceStack(); $stack = $stack->getStack(); /* TODO: Once DatabaseSource lands, do this: foreach ($stack as $key => $source) { unset($stack[$key]); if ($source instanceof PhabricatorConfigDatabaseSource) { break; } } */ $table = array(); $table[] = hsprintf( '%s%s', pht('Source'), pht('Value')); foreach ($stack as $key => $source) { $value = $source->getKeys( array( $option->getKey(), )); if (!array_key_exists($option->getKey(), $value)) { $value = phutil_tag('em', array(), pht('(empty)')); } else { $value = PhabricatorConfigJSON::prettyPrintJSON( $value[$option->getKey()]); } $table[] = hsprintf( '%s%s', $source->getName(), $value); } require_celerity_resource('config-options-css'); return phutil_tag( 'table', array( 'class' => 'config-option-table', ), $table); } } diff --git a/src/applications/config/editor/PhabricatorConfigEditor.php b/src/applications/config/editor/PhabricatorConfigEditor.php index 2468e9eb79..48533de047 100644 --- a/src/applications/config/editor/PhabricatorConfigEditor.php +++ b/src/applications/config/editor/PhabricatorConfigEditor.php @@ -1,131 +1,125 @@ getTransactionType()) { case PhabricatorConfigTransaction::TYPE_EDIT: return array( 'deleted' => (int)$object->getIsDeleted(), 'value' => $object->getValue(), ); } } protected function getCustomTransactionNewValue( PhabricatorLiskDAO $object, PhabricatorApplicationTransaction $xaction) { switch ($xaction->getTransactionType()) { case PhabricatorConfigTransaction::TYPE_EDIT: return $xaction->getNewValue(); } } protected function applyCustomInternalTransaction( PhabricatorLiskDAO $object, PhabricatorApplicationTransaction $xaction) { switch ($xaction->getTransactionType()) { case PhabricatorConfigTransaction::TYPE_EDIT: $v = $xaction->getNewValue(); // If this is a defined configuration option (vs a straggler from an // old version of Phabricator or a configuration file misspelling) // submit it to the validation gauntlet. $key = $object->getConfigKey(); $all_options = PhabricatorApplicationConfigOptions::loadAllOptions(); $option = idx($all_options, $key); if ($option) { $option->getGroup()->validateOption( $option, $v['value']); } $object->setIsDeleted((int)$v['deleted']); $object->setValue($v['value']); break; } } protected function applyCustomExternalTransaction( PhabricatorLiskDAO $object, PhabricatorApplicationTransaction $xaction) { return; } protected function mergeTransactions( PhabricatorApplicationTransaction $u, PhabricatorApplicationTransaction $v) { $type = $u->getTransactionType(); switch ($type) { case PhabricatorConfigTransaction::TYPE_EDIT: return $v; } return parent::mergeTransactions($u, $v); } protected function transactionHasEffect( PhabricatorLiskDAO $object, PhabricatorApplicationTransaction $xaction) { $old = $xaction->getOldValue(); $new = $xaction->getNewValue(); $type = $xaction->getTransactionType(); switch ($type) { case PhabricatorConfigTransaction::TYPE_EDIT: // If an edit deletes an already-deleted entry, no-op it. if (idx($old, 'deleted') && idx($new, 'deleted')) { return false; } break; } return parent::transactionHasEffect($object, $xaction); } protected function didApplyTransactions(array $xactions) { // Force all the setup checks to run on the next page load. PhabricatorSetupCheck::deleteSetupCheckCache(); } public static function storeNewValue( PhabricatorConfigEntry $config_entry, $value, AphrontRequest $request) { $xaction = id(new PhabricatorConfigTransaction()) ->setTransactionType(PhabricatorConfigTransaction::TYPE_EDIT) ->setNewValue( array( 'deleted' => false, 'value' => $value )); $editor = id(new PhabricatorConfigEditor()) ->setActor($request->getUser()) ->setContinueOnNoEffect(true) - ->setContentSource( - PhabricatorContentSource::newForSource( - PhabricatorContentSource::SOURCE_WEB, - array( - 'ip' => $request->getRemoteAddr(), - ))); - + ->setContentSourceFromRequest($request); $editor->applyTransactions($config_entry, array($xaction)); } } diff --git a/src/applications/conpherence/controller/ConpherenceNewController.php b/src/applications/conpherence/controller/ConpherenceNewController.php index 83b3c8af7f..7c9d553cba 100644 --- a/src/applications/conpherence/controller/ConpherenceNewController.php +++ b/src/applications/conpherence/controller/ConpherenceNewController.php @@ -1,149 +1,145 @@ getRequest(); $user = $request->getUser(); $conpherence = id(new ConpherenceThread()) ->attachParticipants(array()) ->attachFilePHIDs(array()) ->setMessageCount(0); $title = pht('New Message'); $participants = array(); $message = ''; $files = array(); $errors = array(); $e_participants = null; $e_message = null; // this comes from ajax requests from all over. should be a single phid. $participant_prefill = $request->getStr('participant'); if ($participant_prefill) { $participants[] = $participant_prefill; } if ($request->isFormPost()) { $participants = $request->getArr('participants'); if (empty($participants)) { $e_participants = true; $errors[] = pht('You must specify participants.'); } else { $participants[] = $user->getPHID(); $participants = array_unique($participants); $conpherence->setRecentParticipantPHIDs( array_slice($participants, 0, 10)); } $message = $request->getStr('message'); if (empty($message)) { $e_message = true; $errors[] = pht('You must write a message.'); } $file_phids = PhabricatorMarkupEngine::extractFilePHIDsFromEmbeddedFiles( array($message)); if ($file_phids) { $files = id(new PhabricatorFileQuery()) ->setViewer($user) ->withPHIDs($file_phids) ->execute(); } if (!$errors) { $conpherence->openTransaction(); $conpherence->save(); $xactions = array(); $xactions[] = id(new ConpherenceTransaction()) ->setTransactionType(ConpherenceTransactionType::TYPE_PARTICIPANTS) ->setNewValue(array('+' => $participants)); if ($files) { $xactions[] = id(new ConpherenceTransaction()) ->setTransactionType(ConpherenceTransactionType::TYPE_FILES) ->setNewValue(array('+' => mpull($files, 'getPHID'))); } $xactions[] = id(new ConpherenceTransaction()) ->setTransactionType(PhabricatorTransactions::TYPE_COMMENT) ->attachComment( id(new ConpherenceTransactionComment()) ->setContent($message) ->setConpherencePHID($conpherence->getPHID())); - $content_source = PhabricatorContentSource::newForSource( - PhabricatorContentSource::SOURCE_WEB, - array( - 'ip' => $request->getRemoteAddr() - )); + id(new ConpherenceEditor()) - ->setContentSource($content_source) + ->setContentSourceFromRequest($request) ->setContinueOnNoEffect(true) ->setActor($user) ->applyTransactions($conpherence, $xactions); $conpherence->saveTransaction(); $uri = $this->getApplicationURI($conpherence->getID()); return id(new AphrontRedirectResponse()) ->setURI($uri); } } $error_view = null; if ($errors) { $error_view = id(new AphrontErrorView()) ->setTitle(pht('Conpherence Errors')) ->setErrors($errors); } $participant_handles = array(); if ($participants) { $handles = id(new PhabricatorObjectHandleData($participants)) ->setViewer($user) ->loadHandles(); $participant_handles = mpull($handles, 'getFullName', 'getPHID'); } $submit_uri = $this->getApplicationURI('new/'); $cancel_uri = $this->getApplicationURI(); // TODO - we can get a better cancel_uri once we get better at crazy // ajax jonx T2086 if ($participant_prefill) { $handle = $handles[$participant_prefill]; $cancel_uri = $handle->getURI(); } $dialog = id(new AphrontDialogView()) ->setWidth(AphrontDialogView::WIDTH_FORM) ->setUser($user) ->setTitle($title) ->addCancelButton($cancel_uri) ->addSubmitButton(pht('Send Message')); $form = id(new AphrontFormLayoutView()) ->setUser($user) ->setFullWidth(true) ->appendChild( id(new AphrontFormTokenizerControl()) ->setName('participants') ->setValue($participant_handles) ->setUser($user) ->setDatasource('/typeahead/common/users/') ->setLabel(pht('To')) ->setError($e_participants)) ->appendChild( id(new PhabricatorRemarkupControl()) ->setName('message') ->setValue($message) ->setLabel(pht('Message')) ->setError($e_message)); $dialog->appendChild($form); return id(new AphrontDialogResponse())->setDialog($dialog); } } diff --git a/src/applications/conpherence/controller/ConpherenceUpdateController.php b/src/applications/conpherence/controller/ConpherenceUpdateController.php index 889e635602..06116f2527 100644 --- a/src/applications/conpherence/controller/ConpherenceUpdateController.php +++ b/src/applications/conpherence/controller/ConpherenceUpdateController.php @@ -1,313 +1,308 @@ conpherenceID = $conpherence_id; return $this; } public function getConpherenceID() { return $this->conpherenceID; } public function willProcessRequest(array $data) { $this->setConpherenceID(idx($data, 'id')); } public function processRequest() { $request = $this->getRequest(); $user = $request->getUser(); $conpherence_id = $this->getConpherenceID(); if (!$conpherence_id) { return new Aphront404Response(); } $conpherence = id(new ConpherenceThreadQuery()) ->setViewer($user) ->withIDs(array($conpherence_id)) ->needFilePHIDs(true) ->executeOne(); $action = $request->getStr('action', ConpherenceUpdateActions::METADATA); $latest_transaction_id = null; $response_mode = 'ajax'; $error_view = null; $e_file = array(); $errors = array(); if ($request->isFormPost()) { - $content_source = PhabricatorContentSource::newForSource( - PhabricatorContentSource::SOURCE_WEB, - array( - 'ip' => $request->getRemoteAddr() - )); $editor = id(new ConpherenceEditor()) ->setContinueOnNoEffect($request->isContinueRequest()) - ->setContentSource($content_source) + ->setContentSourceFromRequest($request) ->setActor($user); switch ($action) { case ConpherenceUpdateActions::MESSAGE: $message = $request->getStr('text'); $xactions = $editor->generateTransactionsFromText( $conpherence, $message); break; case ConpherenceUpdateActions::ADD_PERSON: $xactions = array(); $person_tokenizer = $request->getArr('add_person'); $person_phid = reset($person_tokenizer); if ($person_phid) { $xactions[] = id(new ConpherenceTransaction()) ->setTransactionType( ConpherenceTransactionType::TYPE_PARTICIPANTS) ->setNewValue(array('+' => array($person_phid))); } break; case ConpherenceUpdateActions::REMOVE_PERSON: $xactions = array(); if (!$request->isContinueRequest()) { // do nothing; we'll display a confirmation dialogue instead break; } $person_phid = $request->getStr('remove_person'); if ($person_phid && $person_phid == $user->getPHID()) { $xactions[] = id(new ConpherenceTransaction()) ->setTransactionType( ConpherenceTransactionType::TYPE_PARTICIPANTS) ->setNewValue(array('-' => array($person_phid))); $response_mode = 'go-home'; } break; case ConpherenceUpdateActions::NOTIFICATIONS: $notifications = $request->getStr('notifications'); $participant = $conpherence->getParticipant($user->getPHID()); $participant->setSettings(array('notifications' => $notifications)); $participant->save(); $result = pht( 'Updated notification settings to "%s".', ConpherenceSettings::getHumanString($notifications)); return id(new AphrontAjaxResponse()) ->setContent($result); break; case ConpherenceUpdateActions::METADATA: $xactions = array(); $updated = false; // all other metadata updates are continue requests if (!$request->isContinueRequest()) { break; } $title = $request->getStr('title'); if ($title != $conpherence->getTitle()) { $xactions[] = id(new ConpherenceTransaction()) ->setTransactionType(ConpherenceTransactionType::TYPE_TITLE) ->setNewValue($title); $updated = true; } if (!$updated) { $errors[] = pht( 'That was a non-update. Try cancel.'); } break; default: throw new Exception('Unknown action: '.$action); break; } if ($xactions) { try { $xactions = $editor->applyTransactions($conpherence, $xactions); } catch (PhabricatorApplicationTransactionNoEffectException $ex) { return id(new PhabricatorApplicationTransactionNoEffectResponse()) ->setCancelURI($this->getApplicationURI($conpherence_id.'/')) ->setException($ex); } switch ($response_mode) { case 'ajax': $latest_transaction_id = $request->getInt('latest_transaction_id'); $content = $this->loadAndRenderUpdates( $action, $conpherence_id, $latest_transaction_id); return id(new AphrontAjaxResponse()) ->setContent($content); break; case 'go-home': return id(new AphrontRedirectResponse()) ->setURI($this->getApplicationURI()); break; case 'redirect': default: return id(new AphrontRedirectResponse()) ->setURI($this->getApplicationURI($conpherence->getID().'/')); break; } } } if ($errors) { $error_view = id(new AphrontErrorView()) ->setTitle(pht('Errors editing conpherence.')) ->setInsideDialogue(true) ->setErrors($errors); } switch ($action) { case ConpherenceUpdateActions::REMOVE_PERSON: $dialogue = $this->renderRemovePersonDialogue($conpherence); break; case ConpherenceUpdateActions::METADATA: default: $dialogue = $this->renderMetadataDialogue($conpherence, $error_view); break; } return id(new AphrontDialogResponse()) ->setDialog($dialogue ->setUser($user) ->setWidth(AphrontDialogView::WIDTH_FORM) ->setSubmitURI($this->getApplicationURI('update/'.$conpherence_id.'/')) ->addSubmitButton() ->addCancelButton($this->getApplicationURI($conpherence->getID().'/'))); } private function renderRemovePersonDialogue( ConpherenceThread $conpherence) { $request = $this->getRequest(); $user = $request->getUser(); $remove_person = $request->getStr('remove_person'); $participants = $conpherence->getParticipants(); $message = pht( 'Are you sure you want to remove yourself from this conpherence? '); if (count($participants) == 1) { $message .= pht( 'The conpherence will be inaccessible forever and ever.'); } else { $message .= pht( 'Someone else in the conpherence can add you back later.'); } $body = phutil_tag( 'p', array( ), $message); require_celerity_resource('conpherence-update-css'); return id(new AphrontDialogView()) ->setTitle(pht('Update Conpherence Participants')) ->addHiddenInput('action', 'remove_person') ->addHiddenInput('__continue__', true) ->addHiddenInput('remove_person', $remove_person) ->appendChild($body); } private function renderMetadataDialogue( ConpherenceThread $conpherence, $error_view) { $form = id(new AphrontFormLayoutView()) ->appendChild($error_view) ->appendChild( id(new AphrontFormTextControl()) ->setLabel(pht('Title')) ->setName('title') ->setValue($conpherence->getTitle())); require_celerity_resource('conpherence-update-css'); return id(new AphrontDialogView()) ->setTitle(pht('Update Conpherence')) ->addHiddenInput('action', 'metadata') ->addHiddenInput('__continue__', true) ->appendChild($form); } private function loadAndRenderUpdates( $action, $conpherence_id, $latest_transaction_id) { $need_widget_data = false; $need_transactions = false; switch ($action) { case ConpherenceUpdateActions::METADATA: $need_transactions = true; break; case ConpherenceUpdateActions::MESSAGE: case ConpherenceUpdateActions::ADD_PERSON: $need_transactions = true; $need_widget_data = true; break; case ConpherenceUpdateActions::REMOVE_PERSON: case ConpherenceUpdateActions::NOTIFICATIONS: default: break; } $user = $this->getRequest()->getUser(); $conpherence = id(new ConpherenceThreadQuery()) ->setViewer($user) ->setAfterTransactionID($latest_transaction_id) ->needWidgetData($need_widget_data) ->needTransactions($need_transactions) ->withIDs(array($conpherence_id)) ->executeOne(); if ($need_transactions) { $data = $this->renderConpherenceTransactions($conpherence); } else { $data = array(); } $rendered_transactions = idx($data, 'transactions'); $new_latest_transaction_id = idx($data, 'latest_transaction_id'); $widget_uri = $this->getApplicationURI('update/'.$conpherence->getID().'/'); $nav_item = null; $header = null; $people_widget = null; $file_widget = null; switch ($action) { case ConpherenceUpdateActions::METADATA: $header = $this->buildHeaderPaneContent($conpherence); $nav_item = id(new ConpherenceThreadListView()) ->setUser($user) ->setBaseURI($this->getApplicationURI()) ->renderSingleThread($conpherence); break; case ConpherenceUpdateActions::MESSAGE: $file_widget = id(new ConpherenceFileWidgetView()) ->setUser($this->getRequest()->getUser()) ->setConpherence($conpherence) ->setUpdateURI($widget_uri); break; case ConpherenceUpdateActions::ADD_PERSON: $people_widget = id(new ConpherencePeopleWidgetView()) ->setUser($user) ->setConpherence($conpherence) ->setUpdateURI($widget_uri); break; case ConpherenceUpdateActions::REMOVE_PERSON: case ConpherenceUpdateActions::NOTIFICATIONS: default: break; } $content = array( 'transactions' => $rendered_transactions, 'latest_transaction_id' => $new_latest_transaction_id, 'nav_item' => hsprintf('%s', $nav_item), 'conpherence_phid' => $conpherence->getPHID(), 'header' => hsprintf('%s', $header), 'file_widget' => $file_widget ? $file_widget->render() : null, 'people_widget' => $people_widget ? $people_widget->render() : null, ); return $content; } } diff --git a/src/applications/macro/controller/PhabricatorMacroCommentController.php b/src/applications/macro/controller/PhabricatorMacroCommentController.php index bccc40c65c..3d40791c1d 100644 --- a/src/applications/macro/controller/PhabricatorMacroCommentController.php +++ b/src/applications/macro/controller/PhabricatorMacroCommentController.php @@ -1,75 +1,70 @@ id = idx($data, 'id'); } public function processRequest() { $request = $this->getRequest(); $user = $request->getUser(); if (!$request->isFormPost()) { return new Aphront400Response(); } $macro = id(new PhabricatorMacroQuery()) ->setViewer($user) ->withIDs(array($this->id)) ->executeOne(); if (!$macro) { return new Aphront404Response(); } $is_preview = $request->isPreviewRequest(); $draft = PhabricatorDraft::buildFromRequest($request); $view_uri = $this->getApplicationURI('/view/'.$macro->getID().'/'); $xactions = array(); $xactions[] = id(new PhabricatorMacroTransaction()) ->setTransactionType(PhabricatorTransactions::TYPE_COMMENT) ->attachComment( id(new PhabricatorMacroTransactionComment()) ->setContent($request->getStr('comment'))); $editor = id(new PhabricatorMacroEditor()) ->setActor($user) ->setContinueOnNoEffect($request->isContinueRequest()) - ->setContentSource( - PhabricatorContentSource::newForSource( - PhabricatorContentSource::SOURCE_WEB, - array( - 'ip' => $request->getRemoteAddr(), - ))) + ->setContentSourceFromRequest($request) ->setIsPreview($is_preview); try { $xactions = $editor->applyTransactions($macro, $xactions); } catch (PhabricatorApplicationTransactionNoEffectException $ex) { return id(new PhabricatorApplicationTransactionNoEffectResponse()) ->setCancelURI($view_uri) ->setException($ex); } if ($draft) { $draft->replaceOrDelete(); } if ($request->isAjax()) { return id(new PhabricatorApplicationTransactionResponse()) ->setViewer($user) ->setTransactions($xactions) ->setIsPreview($is_preview) ->setAnchorOffset($request->getStr('anchor')); } else { return id(new AphrontRedirectResponse()) ->setURI($view_uri); } } } diff --git a/src/applications/macro/controller/PhabricatorMacroDisableController.php b/src/applications/macro/controller/PhabricatorMacroDisableController.php index a0f386afe8..ef9076688d 100644 --- a/src/applications/macro/controller/PhabricatorMacroDisableController.php +++ b/src/applications/macro/controller/PhabricatorMacroDisableController.php @@ -1,64 +1,59 @@ id = $data['id']; } public function processRequest() { $request = $this->getRequest(); $user = $request->getUser(); $macro = id(new PhabricatorMacroQuery()) ->setViewer($user) ->requireCapabilities( array( PhabricatorPolicyCapability::CAN_VIEW, PhabricatorPolicyCapability::CAN_EDIT, )) ->withIDs(array($this->id)) ->executeOne(); if (!$macro) { return new Aphront404Response(); } $view_uri = $this->getApplicationURI('/view/'.$this->id.'/'); if ($request->isDialogFormPost() || $macro->getIsDisabled()) { $xaction = id(new PhabricatorMacroTransaction()) ->setTransactionType(PhabricatorMacroTransactionType::TYPE_DISABLED) ->setNewValue($macro->getIsDisabled() ? 0 : 1); $editor = id(new PhabricatorMacroEditor()) ->setActor($user) - ->setContentSource( - PhabricatorContentSource::newForSource( - PhabricatorContentSource::SOURCE_WEB, - array( - 'ip' => $request->getRemoteAddr(), - ))); + ->setContentSourceFromRequest($request); $xactions = $editor->applyTransactions($macro, array($xaction)); return id(new AphrontRedirectResponse())->setURI($view_uri); } $dialog = new AphrontDialogView(); $dialog ->setUser($request->getUser()) ->setTitle(pht('Really disable macro?')) ->appendChild(phutil_tag('p', array(), pht( 'Really disable the much-beloved image macro %s? '. 'It will be sorely missed.', $macro->getName()))) ->setSubmitURI($this->getApplicationURI('/disable/'.$this->id.'/')) ->addSubmitButton(pht('Disable')) ->addCancelButton($view_uri); return id(new AphrontDialogResponse())->setDialog($dialog); } } diff --git a/src/applications/macro/controller/PhabricatorMacroEditController.php b/src/applications/macro/controller/PhabricatorMacroEditController.php index be3ebc6ab9..d7130f191c 100644 --- a/src/applications/macro/controller/PhabricatorMacroEditController.php +++ b/src/applications/macro/controller/PhabricatorMacroEditController.php @@ -1,282 +1,277 @@ id = idx($data, 'id'); } public function processRequest() { $request = $this->getRequest(); $user = $request->getUser(); if ($this->id) { $macro = id(new PhabricatorMacroQuery()) ->setViewer($user) ->requireCapabilities( array( PhabricatorPolicyCapability::CAN_VIEW, PhabricatorPolicyCapability::CAN_EDIT, )) ->withIDs(array($this->id)) ->executeOne(); if (!$macro) { return new Aphront404Response(); } } else { $macro = new PhabricatorFileImageMacro(); } $errors = array(); $e_name = true; $e_file = pht('Provide a URL or a file'); $file = null; $can_fetch = PhabricatorEnv::getEnvConfig('security.allow-outbound-http'); if ($request->isFormPost()) { $original = clone $macro; $new_name = null; if ($request->getBool('name_form') || !$macro->getID()) { $new_name = $request->getStr('name'); $macro->setName($new_name); if (!strlen($macro->getName())) { $errors[] = pht('Macro name is required.'); $e_name = pht('Required'); } else if (!preg_match('/^[a-z0-9:_-]{3,}$/', $macro->getName())) { $errors[] = pht( 'Macro must be at least three characters long and contain only '. 'lowercase letters, digits, hyphens, colons and underscores.'); $e_name = pht('Invalid'); } else { $e_name = null; } } $file = null; if ($request->getFileExists('file')) { $file = PhabricatorFile::newFromPHPUpload( $_FILES['file'], array( 'name' => $request->getStr('name'), 'authorPHID' => $user->getPHID(), )); } else if ($request->getStr('url')) { try { $file = PhabricatorFile::newFromFileDownload( $request->getStr('url'), array( 'name' => $request->getStr('name'), 'authorPHID' => $user->getPHID(), )); } catch (Exception $ex) { $errors[] = pht('Could not fetch URL: %s', $ex->getMessage()); } } else if ($request->getStr('phid')) { $file = id(new PhabricatorFile())->loadOneWhere( 'phid = %s', $request->getStr('phid')); } if ($file) { if (!$file->isViewableInBrowser()) { $errors[] = pht('You must upload an image.'); $e_file = pht('Invalid'); } else { $macro->setFilePHID($file->getPHID()); $macro->attachFile($file); $e_file = null; } } if (!$macro->getID() && !$file) { $errors[] = pht('You must upload an image to create a macro.'); } if (!$errors) { try { $xactions = array(); if ($new_name !== null) { $xactions[] = id(new PhabricatorMacroTransaction()) ->setTransactionType(PhabricatorMacroTransactionType::TYPE_NAME) ->setNewValue($new_name); } if ($file) { $xactions[] = id(new PhabricatorMacroTransaction()) ->setTransactionType(PhabricatorMacroTransactionType::TYPE_FILE) ->setNewValue($file->getPHID()); } $editor = id(new PhabricatorMacroEditor()) ->setActor($user) ->setContinueOnNoEffect(true) - ->setContentSource( - PhabricatorContentSource::newForSource( - PhabricatorContentSource::SOURCE_WEB, - array( - 'ip' => $request->getRemoteAddr(), - ))); + ->setContentSourceFromRequest($request); $xactions = $editor->applyTransactions($original, $xactions); $view_uri = $this->getApplicationURI('/view/'.$original->getID().'/'); return id(new AphrontRedirectResponse())->setURI($view_uri); } catch (AphrontQueryDuplicateKeyException $ex) { throw $ex; $errors[] = pht('Macro name is not unique!'); $e_name = pht('Duplicate'); } } } if ($errors) { $error_view = new AphrontErrorView(); $error_view->setTitle(pht('Form Errors')); $error_view->setErrors($errors); } else { $error_view = null; } $current_file = null; if ($macro->getFilePHID()) { $current_file = $macro->getFile(); } $form = new AphrontFormView(); $form->addHiddenInput('name_form', 1); $form->setUser($request->getUser()); $form ->setEncType('multipart/form-data') ->setFlexible(true) ->appendChild( id(new AphrontFormTextControl()) ->setLabel(pht('Name')) ->setName('name') ->setValue($macro->getName()) ->setCaption( pht('This word or phrase will be replaced with the image.')) ->setError($e_name)); if (!$macro->getID()) { if ($current_file) { $current_file_view = id(new PhabricatorFileLinkView()) ->setFilePHID($current_file->getPHID()) ->setFileName($current_file->getName()) ->setFileViewable(true) ->setFileViewURI($current_file->getBestURI()) ->render(); $form->addHiddenInput('phid', $current_file->getPHID()); $form->appendChild( id(new AphrontFormMarkupControl()) ->setLabel(pht('Selected File')) ->setValue($current_file_view)); $other_label = pht('Change File'); } else { $other_label = pht('File'); } if ($can_fetch) { $form->appendChild( id(new AphrontFormTextControl()) ->setLabel(pht('URL')) ->setName('url') ->setValue($request->getStr('url')) ->setError($request->getFileExists('file') ? false : $e_file)); } $form->appendChild( id(new AphrontFormFileControl()) ->setLabel($other_label) ->setName('file') ->setError($request->getStr('url') ? false : $e_file)); } $view_uri = $this->getApplicationURI('/view/'.$macro->getID().'/'); if ($macro->getID()) { $cancel_uri = $view_uri; } else { $cancel_uri = $this->getApplicationURI(); } $form ->appendChild( id(new AphrontFormSubmitControl()) ->setValue(pht('Save Image Macro')) ->addCancelButton($cancel_uri)); $crumbs = $this->buildApplicationCrumbs(); if ($macro->getID()) { $title = pht('Edit Image Macro'); $crumb = pht('Edit Macro'); $crumbs->addCrumb( id(new PhabricatorCrumbView()) ->setHref($view_uri) ->setName(pht('Macro "%s"', $macro->getName()))); } else { $title = pht('Create Image Macro'); $crumb = pht('Create Macro'); } $crumbs->addCrumb( id(new PhabricatorCrumbView()) ->setHref($request->getRequestURI()) ->setName($crumb)); $upload = null; if ($macro->getID()) { $upload_header = id(new PhabricatorHeaderView()) ->setHeader(pht('Upload New File')); $upload_form = id(new AphrontFormView()) ->setFlexible(true) ->setEncType('multipart/form-data') ->setUser($request->getUser()); if ($can_fetch) { $upload_form ->appendChild( id(new AphrontFormTextControl()) ->setLabel(pht('URL')) ->setName('url') ->setValue($request->getStr('url'))); } $upload_form ->appendChild( id(new AphrontFormFileControl()) ->setLabel(pht('File')) ->setName('file')) ->appendChild( id(new AphrontFormSubmitControl()) ->setValue(pht('Upload File'))); $upload = array($upload_header, $upload_form); } return $this->buildApplicationPage( array( $crumbs, $error_view, $form, $upload, ), array( 'title' => $title, 'device' => true, )); } } diff --git a/src/applications/metamta/contentsource/PhabricatorContentSource.php b/src/applications/metamta/contentsource/PhabricatorContentSource.php index aae5d4291d..414016fbcc 100644 --- a/src/applications/metamta/contentsource/PhabricatorContentSource.php +++ b/src/applications/metamta/contentsource/PhabricatorContentSource.php @@ -1,60 +1,68 @@ } public static function newForSource($source, array $params) { $obj = new PhabricatorContentSource(); $obj->source = $source; $obj->params = $params; return $obj; } public static function newFromSerialized($serialized) { $dict = json_decode($serialized, true); if (!is_array($dict)) { $dict = array(); } $obj = new PhabricatorContentSource(); $obj->source = idx($dict, 'source', self::SOURCE_UNKNOWN); $obj->params = idx($dict, 'params', array()); return $obj; } + public static function newFromRequest(AphrontRequest $request) { + return self::newForSource( + PhabricatorContentSource::SOURCE_WEB, + array( + 'ip' => $request->getRemoteAddr(), + )); + } + public function serialize() { return json_encode(array( 'source' => $this->getSource(), 'params' => $this->getParams(), )); } public function getSource() { return $this->source; } public function getParams() { return $this->params; } public function getParam($key, $default = null) { return idx($this->params, $key, $default); } } diff --git a/src/applications/phlux/controller/PhluxEditController.php b/src/applications/phlux/controller/PhluxEditController.php index f6efd6f156..93f5afa7bc 100644 --- a/src/applications/phlux/controller/PhluxEditController.php +++ b/src/applications/phlux/controller/PhluxEditController.php @@ -1,201 +1,196 @@ key = idx($data, 'key'); } public function processRequest() { $request = $this->getRequest(); $user = $request->getUser(); $is_new = ($this->key === null); if ($is_new) { $var = new PhluxVariable(); $var->setViewPolicy(PhabricatorPolicies::POLICY_USER); $var->setEditPolicy(PhabricatorPolicies::POLICY_USER); } else { $var = id(new PhluxVariableQuery()) ->setViewer($user) ->requireCapabilities( array( PhabricatorPolicyCapability::CAN_VIEW, PhabricatorPolicyCapability::CAN_EDIT, )) ->withKeys(array($this->key)) ->executeOne(); if (!$var) { return new Aphront404Response(); } $view_uri = $this->getApplicationURI('/view/'.$this->key.'/'); } $e_key = ($is_new ? true : null); $e_value = true; $errors = array(); $key = $var->getVariableKey(); $display_value = null; $value = $var->getVariableValue(); if ($request->isFormPost()) { if ($is_new) { $key = $request->getStr('key'); if (!strlen($key)) { $errors[] = pht('Variable key is required.'); $e_key = pht('Required'); } else if (!preg_match('/^[a-z0-9.-]+$/', $key)) { $errors[] = pht( 'Variable key "%s" must contain only lowercase letters, digits, '. 'period, and hyphen.', $key); $e_key = pht('Invalid'); } } $raw_value = $request->getStr('value'); $value = json_decode($raw_value, true); if ($value === null && strtolower($raw_value) !== 'null') { $e_value = pht('Invalid'); $errors[] = pht('Variable value must be valid JSON.'); $display_value = $raw_value; } if (!$errors) { $editor = id(new PhluxVariableEditor()) ->setActor($user) ->setContinueOnNoEffect(true) - ->setContentSource( - PhabricatorContentSource::newForSource( - PhabricatorContentSource::SOURCE_WEB, - array( - 'ip' => $request->getRemoteAddr(), - ))); + ->setContentSourceFromRequest($request); $xactions = array(); $xactions[] = id(new PhluxTransaction()) ->setTransactionType(PhluxTransaction::TYPE_EDIT_KEY) ->setNewValue($key); $xactions[] = id(new PhluxTransaction()) ->setTransactionType(PhluxTransaction::TYPE_EDIT_VALUE) ->setNewValue($value); $xactions[] = id(new PhluxTransaction()) ->setTransactionType(PhabricatorTransactions::TYPE_VIEW_POLICY) ->setNewValue($request->getStr('viewPolicy')); $xactions[] = id(new PhluxTransaction()) ->setTransactionType(PhabricatorTransactions::TYPE_EDIT_POLICY) ->setNewValue($request->getStr('editPolicy')); try { $editor->applyTransactions($var, $xactions); $view_uri = $this->getApplicationURI('/view/'.$key.'/'); return id(new AphrontRedirectResponse())->setURI($view_uri); } catch (AphrontQueryDuplicateKeyException $ex) { $e_key = pht('Not Unique'); $errors[] = pht('Variable key must be unique.'); } } } if ($display_value === null) { if (is_array($value) && (array_keys($value) !== array_keys(array_values($value)))) { $json = new PhutilJSON(); $display_value = $json->encodeFormatted($value); } else { $display_value = json_encode($value); } } if ($errors) { $errors = id(new AphrontErrorView()) ->setErrors($errors); } $policies = id(new PhabricatorPolicyQuery()) ->setViewer($user) ->setObject($var) ->execute(); $form = id(new AphrontFormView()) ->setUser($user) ->appendChild( id(new AphrontFormTextControl()) ->setValue($var->getVariableKey()) ->setLabel(pht('Key')) ->setName('key') ->setError($e_key) ->setCaption(pht('Lowercase letters, digits, dot and hyphen only.')) ->setDisabled(!$is_new)) ->appendChild( id(new AphrontFormTextAreaControl()) ->setValue($display_value) ->setLabel(pht('Value')) ->setName('value') ->setCaption(pht('Enter value as JSON.')) ->setError($e_value)) ->appendChild( id(new AphrontFormPolicyControl()) ->setName('viewPolicy') ->setPolicyObject($var) ->setCapability(PhabricatorPolicyCapability::CAN_VIEW) ->setPolicies($policies)) ->appendChild( id(new AphrontFormPolicyControl()) ->setName('editPolicy') ->setPolicyObject($var) ->setCapability(PhabricatorPolicyCapability::CAN_EDIT) ->setPolicies($policies)); if ($is_new) { $form->appendChild( id(new AphrontFormSubmitControl()) ->setValue(pht('Create Variable'))); } else { $form->appendChild( id(new AphrontFormSubmitControl()) ->setValue(pht('Update Variable')) ->addCancelButton($view_uri)); } $crumbs = $this->buildApplicationCrumbs(); if ($is_new) { $title = pht('Create Variable'); $crumbs->addCrumb( id(new PhabricatorCrumbView()) ->setName($title) ->setHref($request->getRequestURI())); } else { $title = pht('Edit %s', $this->key); $crumbs->addCrumb( id(new PhabricatorCrumbView()) ->setName($title) ->setHref($request->getRequestURI())); } $header = id(new PhabricatorHeaderView()) ->setHeader($title); return $this->buildApplicationPage( array( $crumbs, $header, $errors, $form, ), array( 'title' => $title, 'device' => true, 'dust' => true, )); } } diff --git a/src/applications/pholio/controller/PholioInlineSaveController.php b/src/applications/pholio/controller/PholioInlineSaveController.php index 54fda6cbd3..300e39be70 100644 --- a/src/applications/pholio/controller/PholioInlineSaveController.php +++ b/src/applications/pholio/controller/PholioInlineSaveController.php @@ -1,101 +1,94 @@ operation; } public function processRequest() { $request = $this->getRequest(); $user = $request->getUser(); $mock = id(new PholioMockQuery()) ->setViewer($user) ->withIDs(array($request->getInt('mockID'))) ->executeOne(); if (!$mock) { return new Aphront404Response(); } $this->operation = $request->getStr('op'); if ($this->getOperation() == 'save') { $new_content = $request->getStr('text'); if (!strlen($new_content)) { throw new Exception("Content must not be empty."); } $draft = id(new PholioTransactionComment()); $draft->setImageID($request->getInt('imageID')); $draft->setX($request->getInt('startX')); $draft->setY($request->getInt('startY')); $draft->setCommentVersion(1); $draft->setAuthorPHID($user->getPHID()); $draft->setEditPolicy($user->getPHID()); $draft->setViewPolicy(PhabricatorPolicies::POLICY_PUBLIC); - - $content_source = PhabricatorContentSource::newForSource( - PhabricatorContentSource::SOURCE_WEB, - array( - 'ip' => $request->getRemoteAddr(), - )); - - $draft->setContentSource($content_source); + $draft->setContentSourceFromRequest($request); $draft->setWidth($request->getInt('endX') - $request->getInt('startX')); $draft->setHeight($request->getInt('endY') - $request->getInt('startY')); $draft->setContent($new_content); $draft->save(); $handle = head($this->loadViewerHandles(array($user->getPHID()))); $inline_view = id(new PholioInlineCommentView()) ->setInlineComment($draft) ->setEngine(new PhabricatorMarkupEngine()) ->setUser($user) ->setHandle($handle); return id(new AphrontAjaxResponse()) ->setContent( $draft->toDictionary() + array( 'contentHTML' => $inline_view->render(), )); } else { $dialog = new PholioInlineCommentSaveView(); $dialog->setUser($user); $dialog->setSubmitURI($request->getRequestURI()); $dialog->setTitle(pht('Add Inline Comment')); $dialog->addHiddenInput('op', 'save'); $dialog->appendChild($this->renderTextArea('')); return id(new AphrontAjaxResponse())->setContent($dialog->render()); } } private function renderTextArea($text) { return javelin_tag( 'textarea', array( 'class' => 'pholio-inline-comment-dialog-textarea', 'name' => 'text', ), $text); } } diff --git a/src/applications/pholio/controller/PholioMockCommentController.php b/src/applications/pholio/controller/PholioMockCommentController.php index af0b1b6e73..3fd514205e 100644 --- a/src/applications/pholio/controller/PholioMockCommentController.php +++ b/src/applications/pholio/controller/PholioMockCommentController.php @@ -1,96 +1,90 @@ id = $data['id']; } public function processRequest() { $request = $this->getRequest(); $user = $request->getUser(); if (!$request->isFormPost()) { return new Aphront400Response(); } $mock = id(new PholioMockQuery()) ->setViewer($user) ->withIDs(array($this->id)) ->needImages(true) ->executeOne(); if (!$mock) { return new Aphront404Response(); } $is_preview = $request->isPreviewRequest(); $draft = PhabricatorDraft::buildFromRequest($request); $mock_uri = '/M'.$mock->getID(); $comment = $request->getStr('comment'); - $content_source = PhabricatorContentSource::newForSource( - PhabricatorContentSource::SOURCE_WEB, - array( - 'ip' => $request->getRemoteAddr(), - )); - $xactions = array(); $inline_comments = id(new PholioTransactionComment())->loadAllWhere( 'authorphid = %s AND transactionphid IS NULL AND imageid IN (%Ld)', $user->getPHID(), mpull($mock->getImages(), 'getID')); if (!$inline_comments || strlen($comment)) { $xactions[] = id(new PholioTransaction()) ->setTransactionType(PhabricatorTransactions::TYPE_COMMENT) ->attachComment( id(new PholioTransactionComment()) ->setContent($comment)); } foreach ($inline_comments as $inline_comment) { $xactions[] = id(new PholioTransaction()) ->setTransactionType(PholioTransactionType::TYPE_INLINE) ->attachComment($inline_comment); } $editor = id(new PholioMockEditor()) ->setActor($user) - ->setContentSource($content_source) + ->setContentSourceFromRequest($request) ->setContinueOnNoEffect($request->isContinueRequest()) ->setIsPreview($is_preview); try { $xactions = $editor->applyTransactions($mock, $xactions); } catch (PhabricatorApplicationTransactionNoEffectException $ex) { return id(new PhabricatorApplicationTransactionNoEffectResponse()) ->setCancelURI($mock_uri) ->setException($ex); } if ($draft) { $draft->replaceOrDelete(); } if ($request->isAjax()) { return id(new PhabricatorApplicationTransactionResponse()) ->setViewer($user) ->setTransactions($xactions) ->setIsPreview($is_preview) ->setAnchorOffset($request->getStr('anchor')); } else { return id(new AphrontRedirectResponse())->setURI($mock_uri); } } } diff --git a/src/applications/pholio/controller/PholioMockEditController.php b/src/applications/pholio/controller/PholioMockEditController.php index 571a40feee..cc6453d7a0 100644 --- a/src/applications/pholio/controller/PholioMockEditController.php +++ b/src/applications/pholio/controller/PholioMockEditController.php @@ -1,246 +1,240 @@ id = idx($data, 'id'); } public function processRequest() { $request = $this->getRequest(); $user = $request->getUser(); if ($this->id) { $mock = id(new PholioMockQuery()) ->setViewer($user) ->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; } else { $mock = new PholioMock(); $mock->setAuthorPHID($user->getPHID()); $mock->setViewPolicy(PhabricatorPolicies::POLICY_USER); $title = pht('Create Mock'); $is_new = true; } $e_name = true; $e_images = true; $errors = array(); $v_name = $mock->getName(); $v_desc = $mock->getDescription(); $v_view = $mock->getViewPolicy(); $v_cc = PhabricatorSubscribersQuery::loadSubscribersForPHID( $mock->getPHID()); $files = array(); if ($request->isFormPost()) { $xactions = array(); $type_name = PholioTransactionType::TYPE_NAME; $type_desc = PholioTransactionType::TYPE_DESCRIPTION; $type_view = PhabricatorTransactions::TYPE_VIEW_POLICY; $type_cc = PhabricatorTransactions::TYPE_SUBSCRIBERS; $v_name = $request->getStr('name'); $v_desc = $request->getStr('description'); $v_view = $request->getStr('can_view'); $v_cc = $request->getArr('cc'); $xactions[$type_name] = $v_name; $xactions[$type_desc] = $v_desc; $xactions[$type_view] = $v_view; $xactions[$type_cc] = array('=' => $v_cc); if (!strlen($request->getStr('name'))) { $e_name = 'Required'; $errors[] = pht('You must name the mock.'); } $images = array(); if ($is_new) { // TODO: Make this transactional and allow edits? $file_phids = $request->getArr('file_phids'); if ($file_phids) { $files = id(new PhabricatorFileQuery()) ->setViewer($user) ->withPHIDs($file_phids) ->execute(); } if (!$files) { $e_images = pht('Required'); $errors[] = pht('You must add at least one image to the mock.'); } else { $mock->setCoverPHID(head($files)->getPHID()); } $sequence = 0; foreach ($files as $file) { $image = new PholioImage(); $image->setFilePHID($file->getPHID()); $image->setSequence($sequence++); $images[] = $image; } } if (!$errors) { - $content_source = PhabricatorContentSource::newForSource( - PhabricatorContentSource::SOURCE_WEB, - array( - 'ip' => $request->getRemoteAddr(), - )); - foreach ($xactions as $type => $value) { $xactions[$type] = id(new PholioTransaction()) ->setTransactionType($type) ->setNewValue($value); } $mock->openTransaction(); $editor = id(new PholioMockEditor()) - ->setContentSource($content_source) + ->setContentSourceFromRequest($request) ->setContinueOnNoEffect(true) ->setActor($user); $xactions = $editor->applyTransactions($mock, $xactions); if ($images) { foreach ($images as $image) { // TODO: Move into editor? $image->setMockID($mock->getID()); $image->save(); } } $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(); // NOTE: Make this show up correctly on the rendered form. $mock->setViewPolicy($v_view); $handles = id(new PhabricatorObjectHandleData($v_cc)) ->setViewer($user) ->loadHandles(); $cc_tokens = mpull($handles, 'getFullName', 'getPHID'); $images_controller = ''; if ($is_new) { $images_controller = id(new AphrontFormDragAndDropUploadControl($request)) ->setValue($files) ->setName('file_phids') ->setLabel(pht('Images')) ->setActivatedClass('aphront-textarea-drag-and-drop') ->setError($e_images); } $form = id(new AphrontFormView()) ->setUser($user) ->setFlexible(true) ->appendChild( id(new AphrontFormTextControl()) ->setName('name') ->setValue($v_name) ->setLabel(pht('Name')) ->setError($e_name)) ->appendChild( id(new PhabricatorRemarkupControl()) ->setName('description') ->setValue($v_desc) ->setLabel(pht('Description')) ->setUser($user)) ->appendChild($images_controller) ->appendChild( id(new AphrontFormTokenizerControl()) ->setLabel(pht('CC')) ->setName('cc') ->setValue($cc_tokens) ->setUser($user) ->setDatasource('/typeahead/common/mailable/')) ->appendChild( id(new AphrontFormPolicyControl()) ->setUser($user) ->setCapability(PhabricatorPolicyCapability::CAN_VIEW) ->setPolicyObject($mock) ->setPolicies($policies) ->setName('can_view')) ->appendChild($submit); $crumbs = $this->buildApplicationCrumbs($this->buildSideNav()); $crumbs->addCrumb( id(new PhabricatorCrumbView()) ->setName($title) ->setHref($this->getApplicationURI())); $content = array( $crumbs, $error_view, $form, ); $nav = $this->buildSideNav(); $nav->selectFilter(null); $nav->appendChild($content); return $this->buildApplicationPage( $nav, array( 'title' => $title, 'device' => true, )); } } diff --git a/src/applications/phortune/controller/PhortuneController.php b/src/applications/phortune/controller/PhortuneController.php index 70c0d44e93..c11ddb3326 100644 --- a/src/applications/phortune/controller/PhortuneController.php +++ b/src/applications/phortune/controller/PhortuneController.php @@ -1,60 +1,55 @@ setViewer($user) ->withMemberPHIDs(array($user->getPHID())) ->execute(); if (!$accounts) { return $this->createUserAccount($user); } else if (count($accounts) == 1) { return head($accounts); } else { throw new Exception("TODO: No account selection yet."); } } protected function createUserAccount(PhabricatorUser $user) { $request = $this->getRequest(); $xactions = array(); $xactions[] = id(new PhortuneAccountTransaction()) ->setTransactionType(PhortuneAccountTransaction::TYPE_NAME) ->setNewValue(pht('Account (%s)', $user->getUserName())); $xactions[] = id(new PhortuneAccountTransaction()) ->setTransactionType(PhabricatorTransactions::TYPE_EDGE) ->setMetadataValue( 'edge:type', PhabricatorEdgeConfig::TYPE_ACCOUNT_HAS_MEMBER) ->setNewValue( array( '=' => array($user->getPHID() => $user->getPHID()), )); $account = new PhortuneAccount(); $editor = id(new PhortuneAccountEditor()) ->setActor($user) - ->setContentSource( - PhabricatorContentSource::newForSource( - PhabricatorContentSource::SOURCE_WEB, - array( - 'ip' => $request->getRemoteAddr(), - ))); + ->setContentSourceFromRequest($request); // We create an account for you the first time you visit Phortune. $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); $editor->applyTransactions($account, $xactions); unset($unguarded); return $account; } } diff --git a/src/applications/releeph/controller/request/ReleephRequestActionController.php b/src/applications/releeph/controller/request/ReleephRequestActionController.php index 1c9daea570..64e1945e06 100644 --- a/src/applications/releeph/controller/request/ReleephRequestActionController.php +++ b/src/applications/releeph/controller/request/ReleephRequestActionController.php @@ -1,109 +1,104 @@ action = $data['action']; } public function processRequest() { $request = $this->getRequest(); $releeph_project = $this->getReleephProject(); $releeph_branch = $this->getReleephBranch(); $releeph_request = $this->getReleephRequest(); $releeph_branch->populateReleephRequestHandles( $request->getUser(), array($releeph_request)); $action = $this->action; $user = $request->getUser(); $origin_uri = $releeph_request->loadReleephBranch()->getURI(); $editor = id(new ReleephRequestTransactionalEditor()) ->setActor($user) ->setContinueOnNoEffect(true) - ->setContentSource( - PhabricatorContentSource::newForSource( - PhabricatorContentSource::SOURCE_WEB, - array( - 'ip' => $request->getRemoteAddr(), - ))); + ->setContentSourceFromRequest($request); $xactions = array(); switch ($action) { case 'want': case 'pass': static $action_map = array( 'want' => ReleephRequest::INTENT_WANT, 'pass' => ReleephRequest::INTENT_PASS); $intent = $action_map[$action]; $xactions[] = id(new ReleephRequestTransaction()) ->setTransactionType(ReleephRequestTransaction::TYPE_USER_INTENT) ->setMetadataValue( 'isAuthoritative', $releeph_project->isAuthoritative($user)) ->setNewValue($intent); break; case 'mark-manually-picked': case 'mark-manually-reverted': if (!$releeph_project->isAuthoritative($user)) { throw new Exception( "Bug! Only authoritative users (pushers, or users in pusherless ". "Releeph projects) can manually change a request's in-branch ". "status!"); } if ($action === 'mark-manually-picked') { $in_branch = 1; $intent = ReleephRequest::INTENT_WANT; } else { $in_branch = 0; $intent = ReleephRequest::INTENT_PASS; } $xactions[] = id(new ReleephRequestTransaction()) ->setTransactionType(ReleephRequestTransaction::TYPE_USER_INTENT) ->setMetadataValue('isManual', true) ->setMetadataValue('isAuthoritative', true) ->setNewValue($intent); $xactions[] = id(new ReleephRequestTransaction()) ->setTransactionType(ReleephRequestTransaction::TYPE_MANUAL_IN_BRANCH) ->setNewValue($in_branch); break; default: throw new Exception("unknown or unimplemented action {$action}"); } $editor->applyTransactions($releeph_request, $xactions); // If we're adding a new user to userIntents, we'll have to re-populate // request handles to load that user's data. // // This is cheap enough to do every time. $this->getReleephBranch()->populateReleephRequestHandles( $user, array($releeph_request)); $list = id(new ReleephRequestHeaderListView()) ->setReleephProject($this->getReleephProject()) ->setReleephBranch($this->getReleephBranch()) ->setReleephRequests(array($releeph_request)) ->setUser($request->getUser()) ->setAphrontRequest($this->getRequest()) ->setOriginType('request'); return id(new AphrontAjaxResponse())->setContent(array( 'markup' => head($list->renderInner()) )); } } diff --git a/src/applications/releeph/controller/request/ReleephRequestCommentController.php b/src/applications/releeph/controller/request/ReleephRequestCommentController.php index 09e1f6186c..bcb16b9808 100644 --- a/src/applications/releeph/controller/request/ReleephRequestCommentController.php +++ b/src/applications/releeph/controller/request/ReleephRequestCommentController.php @@ -1,63 +1,58 @@ getRequest(); $user = $request->getUser(); $rq = $this->getReleephRequest(); if (!$request->isFormPost()) { return new Aphront400Response(); } $is_preview = $request->isPreviewRequest(); $draft = PhabricatorDraft::buildFromRequest($request); $view_uri = $this->getApplicationURI('/RQ'.$rq->getID()); $xactions = array(); $xactions[] = id(new ReleephRequestTransaction()) ->setTransactionType(PhabricatorTransactions::TYPE_COMMENT) ->attachComment( id(new ReleephRequestTransactionComment()) ->setContent($request->getStr('comment'))); $editor = id(new ReleephRequestTransactionalEditor()) ->setActor($user) ->setContinueOnNoEffect($request->isContinueRequest()) - ->setContentSource( - PhabricatorContentSource::newForSource( - PhabricatorContentSource::SOURCE_WEB, - array( - 'ip' => $request->getRemoteAddr(), - ))) + ->setContentSourceFromRequest($request) ->setIsPreview($is_preview); try { $xactions = $editor->applyTransactions($rq, $xactions); } catch (PhabricatorApplicationTransactionNoEffectException $ex) { return id(new PhabricatorApplicationTransactionNoEffectResponse()) ->setCancelURI($view_uri) ->setException($ex); } if ($draft) { $draft->replaceOrDelete(); } if ($request->isAjax()) { return id(new PhabricatorApplicationTransactionResponse()) ->setViewer($user) ->setTransactions($xactions) ->setIsPreview($is_preview) ->setAnchorOffset($request->getStr('anchor')); } else { return id(new AphrontRedirectResponse()) ->setURI($view_uri); } } } diff --git a/src/applications/releeph/controller/request/ReleephRequestEditController.php b/src/applications/releeph/controller/request/ReleephRequestEditController.php index 95775d2bcd..e53ac2955f 100644 --- a/src/applications/releeph/controller/request/ReleephRequestEditController.php +++ b/src/applications/releeph/controller/request/ReleephRequestEditController.php @@ -1,274 +1,269 @@ id = idx($data, 'requestID'); parent::willProcessRequest($data); } public function processRequest() { $request = $this->getRequest(); $user = $request->getUser(); $releeph_project = $this->getReleephProject(); $releeph_branch = $this->getReleephBranch(); $request_identifier = $request->getStr('requestIdentifierRaw'); $e_request_identifier = true; // Load the RQ we're editing, or create a new one if ($this->id) { $rq = id(new ReleephRequest())->load($this->id); $is_edit = true; } else { $is_edit = false; $rq = id(new ReleephRequest()) ->setRequestUserPHID($user->getPHID()) ->setBranchID($releeph_branch->getID()) ->setInBranch(0); } // Load all the ReleephFieldSpecifications $selector = $this->getReleephProject()->getReleephFieldSelector(); $fields = $selector->getFieldSpecifications(); foreach ($fields as $field) { $field ->setReleephProject($releeph_project) ->setReleephBranch($releeph_branch) ->setReleephRequest($rq); } // epriestley: Is it common to pass around a referer URL to // return from whence one came? [...] // If you only have two places, maybe consider some parameter // rather than the full URL. switch ($request->getStr('origin')) { case 'request': $origin_uri = '/RQ'.$rq->getID(); break; case 'branch': default: $origin_uri = $releeph_branch->getURI(); break; } // Make edits $errors = array(); if ($request->isFormPost()) { $xactions = array(); // The commit-identifier being requested... if (!$is_edit) { if ($request_identifier === ReleephRequestTypeaheadControl::PLACEHOLDER) { $errors[] = "No commit ID was provided."; $e_request_identifier = 'Required'; } else { $pr_commit = null; $finder = id(new ReleephCommitFinder()) ->setUser($user) ->setReleephProject($releeph_project); try { $pr_commit = $finder->fromPartial($request_identifier); } catch (Exception $e) { $e_request_identifier = 'Invalid'; $errors[] = "Request {$request_identifier} is probably not a valid commit"; $errors[] = $e->getMessage(); } $pr_commit_data = null; if (!$errors) { $pr_commit_data = $pr_commit->loadCommitData(); if (!$pr_commit_data) { $e_request_identifier = 'Not parsed yet'; $errors[] = "The requested commit hasn't been parsed yet."; } } } if (!$errors) { $existing = id(new ReleephRequest()) ->loadOneWhere('requestCommitPHID = %s AND branchID = %d', $pr_commit->getPHID(), $releeph_branch->getID()); if ($existing) { return id(new AphrontRedirectResponse()) ->setURI('/releeph/request/edit/'.$existing->getID(). '?existing=1'); } $xactions[] = id(new ReleephRequestTransaction()) ->setTransactionType(ReleephRequestTransaction::TYPE_REQUEST) ->setNewValue($pr_commit->getPHID()); $xactions[] = id(new ReleephRequestTransaction()) ->setTransactionType(ReleephRequestTransaction::TYPE_USER_INTENT) // To help hide these implicit intents... ->setMetadataValue('isRQCreate', true) ->setMetadataValue('userPHID', $user->getPHID()) ->setMetadataValue( 'isAuthoritative', $releeph_project->isAuthoritative($user)) ->setNewValue(ReleephRequest::INTENT_WANT); } } if (!$errors) { foreach ($fields as $field) { if ($field->isEditable()) { try { $data = $request->getRequestData(); $value = idx($data, $field->getRequiredStorageKey()); $field->validate($value); $xactions[] = id(new ReleephRequestTransaction()) ->setTransactionType(ReleephRequestTransaction::TYPE_EDIT_FIELD) ->setMetadataValue('fieldClass', get_class($field)) ->setNewValue($value); } catch (ReleephFieldParseException $ex) { $errors[] = $ex->getMessage(); } } } } if (!$errors) { $editor = id(new ReleephRequestTransactionalEditor()) ->setActor($user) ->setContinueOnNoEffect(true) - ->setContentSource( - PhabricatorContentSource::newForSource( - PhabricatorContentSource::SOURCE_WEB, - array( - 'ip' => $request->getRemoteAddr(), - ))); + ->setContentSourceFromRequest($request); $editor->applyTransactions($rq, $xactions); return id(new AphrontRedirectResponse())->setURI($origin_uri); } } $releeph_branch->populateReleephRequestHandles($user, array($rq)); $handles = $rq->getHandles(); $age_string = ''; if ($is_edit) { $age_string = phabricator_format_relative_time( time() - $rq->getDateCreated()) . ' ago'; } // Warn the user if we've been redirected here because we tried to // re-request something. $notice_view = null; if ($request->getInt('existing')) { $notice_messages = array( 'You are editing an existing pick request!', hsprintf( "Requested %s by %s", $age_string, $handles[$rq->getRequestUserPHID()]->renderLink()) ); $notice_view = id(new AphrontErrorView()) ->setSeverity(AphrontErrorView::SEVERITY_NOTICE) ->setErrors($notice_messages); } /** * Build the rest of the page */ $error_view = null; if ($errors) { $error_view = new AphrontErrorView(); $error_view->setErrors($errors); $error_view->setTitle('Form Errors'); } $form = id(new AphrontFormView()) ->setUser($user); if ($is_edit) { $form ->appendChild( id(new AphrontFormMarkupControl()) ->setLabel('Original Commit') ->setValue( $handles[$rq->getRequestCommitPHID()]->renderLink())) ->appendChild( id(new AphrontFormMarkupControl()) ->setLabel('Requestor') ->setValue(hsprintf( '%s %s', $handles[$rq->getRequestUserPHID()]->renderLink(), $age_string))); } else { $origin = null; $diff_rev_id = $request->getStr('D'); if ($diff_rev_id) { $diff_rev = id(new DifferentialRevision())->load($diff_rev_id); $origin = '/D'.$diff_rev->getID(); $title = sprintf( 'D%d: %s', $diff_rev_id, $diff_rev->getTitle()); $form ->addHiddenInput('requestIdentifierRaw', 'D'.$diff_rev_id) ->appendChild( id(new AphrontFormStaticControl()) ->setLabel('Diff') ->setValue($title)); } else { $origin = $releeph_branch->getURI(); $repo = $releeph_project->loadPhabricatorRepository(); $branch_cut_point = id(new PhabricatorRepositoryCommit()) ->loadOneWhere( 'phid = %s', $releeph_branch->getCutPointCommitPHID()); $form->appendChild( id(new ReleephRequestTypeaheadControl()) ->setName('requestIdentifierRaw') ->setLabel('Commit ID') ->setRepo($repo) ->setValue($request_identifier) ->setError($e_request_identifier) ->setStartTime($branch_cut_point->getEpoch()) ->setCaption( 'Start typing to autocomplete on commit title, '. 'or give a Phabricator commit identifier like rFOO1234')); } } // Fields foreach ($fields as $field) { if ($field->isEditable()) { $control = $field->renderEditControl($request); $form->appendChild($control); } } if ($is_edit) { $title = pht('Edit Releeph Request'); $submit_name = pht('Save'); } else { $title = pht('Create Releeph Request'); $submit_name = pht('Create'); } $form ->appendChild( id(new AphrontFormSubmitControl()) ->addCancelButton($origin_uri, 'Cancel') ->setValue($submit_name)); $panel = id(new AphrontPanelView()) ->setHeader($title) ->setWidth(AphrontPanelView::WIDTH_FORM) ->appendChild($form); return $this->buildStandardPageResponse( array($notice_view, $error_view, $panel), array('title', $title)); } } diff --git a/src/applications/subscriptions/controller/PhabricatorSubscriptionsEditController.php b/src/applications/subscriptions/controller/PhabricatorSubscriptionsEditController.php index e3f5824374..ace77ff978 100644 --- a/src/applications/subscriptions/controller/PhabricatorSubscriptionsEditController.php +++ b/src/applications/subscriptions/controller/PhabricatorSubscriptionsEditController.php @@ -1,123 +1,118 @@ phid = idx($data, 'phid'); $this->action = idx($data, 'action'); } public function processRequest() { $request = $this->getRequest(); if (!$request->isFormPost()) { return new Aphront400Response(); } switch ($this->action) { case 'add': $is_add = true; break; case 'delete': $is_add = false; break; default: return new Aphront400Response(); } $user = $request->getUser(); $phid = $this->phid; // TODO: This is a policy test because `loadObjects()` is not currently // policy-aware. Once it is, we can collapse this. $handle = PhabricatorObjectHandleData::loadOneHandle($phid, $user); if (!$handle->isComplete()) { return new Aphront404Response(); } $objects = id(new PhabricatorObjectHandleData(array($phid))) ->setViewer($user) ->loadObjects(); $object = idx($objects, $phid); if (!($object instanceof PhabricatorSubscribableInterface)) { return $this->buildErrorResponse( pht('Bad Object'), pht('This object is not subscribable.'), $handle->getURI()); } if ($object->isAutomaticallySubscribed($user->getPHID())) { return $this->buildErrorResponse( pht('Automatically Subscribed'), pht('You are automatically subscribed to this object.'), $handle->getURI()); } if ($object instanceof PhabricatorApplicationTransactionInterface) { if ($is_add) { $xaction_value = array( '+' => array($user->getPHID()), ); } else { $xaction_value = array( '-' => array($user->getPHID()), ); } $xaction = id($object->getApplicationTransactionObject()) ->setTransactionType(PhabricatorTransactions::TYPE_SUBSCRIBERS) ->setNewValue($xaction_value); $editor = id($object->getApplicationTransactionEditor()) ->setActor($user) ->setContinueOnNoEffect(true) - ->setContentSource( - PhabricatorContentSource::newForSource( - PhabricatorContentSource::SOURCE_WEB, - array( - 'ip' => $request->getRemoteAddr(), - ))); + ->setContentSourceFromRequest($request); $editor->applyTransactions($object, array($xaction)); } else { // TODO: Eventually, get rid of this once everything implements // PhabriatorApplicationTransactionInterface. $editor = id(new PhabricatorSubscriptionsEditor()) ->setActor($user) ->setObject($object); if ($is_add) { $editor->subscribeExplicit(array($user->getPHID()), $explicit = true); } else { $editor->unsubscribe(array($user->getPHID())); } $editor->save(); } // TODO: We should just render the "Unsubscribe" action and swap it out // in the document for Ajax requests. return id(new AphrontReloadResponse())->setURI($handle->getURI()); } private function buildErrorResponse($title, $message, $uri) { $request = $this->getRequest(); $user = $request->getUser(); $dialog = id(new AphrontDialogView()) ->setUser($user) ->setTitle($title) ->appendChild($message) ->addCancelButton($uri); return id(new AphrontDialogResponse())->setDialog($dialog); } } diff --git a/src/applications/transactions/controller/PhabricatorApplicationTransactionCommentEditController.php b/src/applications/transactions/controller/PhabricatorApplicationTransactionCommentEditController.php index f89e274b5c..022b482caf 100644 --- a/src/applications/transactions/controller/PhabricatorApplicationTransactionCommentEditController.php +++ b/src/applications/transactions/controller/PhabricatorApplicationTransactionCommentEditController.php @@ -1,88 +1,83 @@ phid = $data['phid']; } public function processRequest() { $request = $this->getRequest(); $user = $request->getUser(); $xactions = id(new PhabricatorObjectHandleData(array($this->phid))) ->setViewer($user) ->loadObjects(); $xaction = idx($xactions, $this->phid); if (!$xaction) { // TODO: This may also mean you don't have permission to edit the object, // but we can't make that distinction via PhabricatorObjectHandleData // at the moment. return new Aphront404Response(); } if (!$xaction->getComment()) { // You can't currently edit a transaction which doesn't have a comment. // Some day you may be able to edit the visibility. return new Aphront404Response(); } $obj_phid = $xaction->getObjectPHID(); $obj_handle = PhabricatorObjectHandleData::loadOneHandle($obj_phid, $user); if (!$obj_handle) { // Require the corresponding object exist and be visible to the user. return new Aphront404Response(); } if ($request->isDialogFormPost()) { $text = $request->getStr('text'); $comment = $xaction->getApplicationTransactionCommentObject(); $comment->setContent($text); if (!strlen($text)) { $comment->setIsDeleted(true); } $editor = id(new PhabricatorApplicationTransactionCommentEditor()) ->setActor($user) - ->setContentSource( - $content_source = PhabricatorContentSource::newForSource( - PhabricatorContentSource::SOURCE_WEB, - array( - 'ip' => $request->getRemoteAddr(), - ))) + ->setContentSourceFromRequest($request) ->applyEdit($xaction, $comment); if ($request->isAjax()) { return id(new PhabricatorApplicationTransactionResponse()) ->setViewer($user) ->setTransactions(array($xaction)) ->setAnchorOffset($request->getStr('anchor')); } else { return id(new AphrontReloadResponse())->setURI($obj_handle->getURI()); } } $dialog = id(new AphrontDialogView()) ->setUser($user) ->setTitle(pht('Edit Comment')); $dialog ->addHiddenInput('anchor', $request->getStr('anchor')) ->appendChild( id(new PhabricatorRemarkupControl()) ->setName('text') ->setValue($xaction->getComment()->getContent())); $dialog ->addSubmitButton(pht('Edit Comment')) ->addCancelButton($obj_handle->getURI()); return id(new AphrontDialogResponse())->setDialog($dialog); } } diff --git a/src/applications/transactions/storage/PhabricatorApplicationTransactionComment.php b/src/applications/transactions/storage/PhabricatorApplicationTransactionComment.php index 7b526f7758..818beb9c4d 100644 --- a/src/applications/transactions/storage/PhabricatorApplicationTransactionComment.php +++ b/src/applications/transactions/storage/PhabricatorApplicationTransactionComment.php @@ -1,111 +1,116 @@ true, ) + parent::getConfiguration(); } public function getApplicationName() { return $this->getApplicationTransactionObject()->getApplicationName(); } public function getTableName() { $xaction = $this->getApplicationTransactionObject(); return self::getTableNameFromTransaction($xaction); } public static function getTableNameFromTransaction( PhabricatorApplicationTransaction $xaction) { return $xaction->getTableName().'_comment'; } public function setContentSource(PhabricatorContentSource $content_source) { $this->contentSource = $content_source->serialize(); return $this; } + public function setContentSourceFromRequest(AphrontRequest $request) { + return $this->setContentSource( + PhabricatorContentSource::newFromRequest($request)); + } + public function getContentSource() { return PhabricatorContentSource::newFromSerialized($this->contentSource); } /* -( PhabricatorMarkupInterface )----------------------------------------- */ public function getMarkupFieldKey($field) { return PhabricatorPHIDConstants::PHID_TYPE_XCMT.':'.$this->getPHID(); } public function newMarkupEngine($field) { return PhabricatorMarkupEngine::getEngine(); } public function getMarkupText($field) { return $this->getContent(); } public function didMarkupText($field, $output, PhutilMarkupEngine $engine) { require_celerity_resource('phabricator-remarkup-css'); return phutil_tag( 'div', array( 'class' => 'phabricator-remarkup', ), $output); } public function shouldUseMarkupCache($field) { return (bool)$this->getPHID(); } /* -( PhabricatorPolicyInterface Implementation )-------------------------- */ public function getCapabilities() { return array( PhabricatorPolicyCapability::CAN_VIEW, PhabricatorPolicyCapability::CAN_EDIT, ); } public function getPolicy($capability) { switch ($capability) { case PhabricatorPolicyCapability::CAN_VIEW: return $this->getViewPolicy(); case PhabricatorPolicyCapability::CAN_EDIT: return $this->getEditPolicy(); } } public function hasAutomaticCapability($capability, PhabricatorUser $viewer) { return ($viewer->getPHID() == $this->getAuthorPHID()); } }