private function attemptLand($revision, $request) { $status = $revision->getStatus(); if ($status != ArcanistDifferentialRevisionStatus::ACCEPTED) { throw new Exception(pht('Only Accepted revisions can be landed.')); } $repository = $revision->getRepository(); if ($repository === null) { throw new Exception(pht('Revision is not attached to a repository.')); } $can_push = PhabricatorPolicyFilter::hasCapability($request->getUser(), $repository, DiffusionPushCapability::CAPABILITY); if (!$can_push) { throw new Exception(pht('You do not have permission to push to this repository.')); } $lock = $this->lockRepository($repository); try { $response = $this->pushStrategy->processLandRequest($request, $revision, $repository); } catch (Exception $e) { $lock->unlock(); throw $e; } $lock->unlock(); $looksoon = new ConduitCall('diffusion.looksoon', array('repositories' => array($repository->getPHID()))); $looksoon->setUser($request->getUser()); $looksoon->execute(); return $response; }
private function getBlockers(PhabricatorRepositoryCommit $commit, HarbormasterBuildPlan $plan, HarbormasterBuild $source) { $call = new ConduitCall('diffusion.commitparentsquery', array('commit' => $commit->getCommitIdentifier(), 'repository' => $commit->getRepository()->getPHID())); $call->setUser(PhabricatorUser::getOmnipotentUser()); $parents = $call->execute(); $parents = id(new DiffusionCommitQuery())->setViewer(PhabricatorUser::getOmnipotentUser())->withRepository($commit->getRepository())->withIdentifiers($parents)->execute(); $blockers = array(); $build_objects = array(); foreach ($parents as $parent) { if (!$parent->isImported()) { $blockers[] = pht('Commit %s', $parent->getCommitIdentifier()); } else { $build_objects[] = $parent->getPHID(); } } if ($build_objects) { $buildables = id(new HarbormasterBuildableQuery())->setViewer(PhabricatorUser::getOmnipotentUser())->withBuildablePHIDs($build_objects)->withManualBuildables(false)->execute(); $buildable_phids = mpull($buildables, 'getPHID'); if ($buildable_phids) { $builds = id(new HarbormasterBuildQuery())->setViewer(PhabricatorUser::getOmnipotentUser())->withBuildablePHIDs($buildable_phids)->withBuildPlanPHIDs(array($plan->getPHID()))->execute(); foreach ($builds as $build) { if (!$build->isComplete()) { $blockers[] = pht('Build %d', $build->getID()); } } } } return $blockers; }
public function processRequest() { $request = $this->getRequest(); if ($request->isFormPost()) { $diff = null; try { $diff = PhabricatorFile::readUploadedFileData($_FILES['diff-file']); } catch (Exception $ex) { $diff = $request->getStr('diff'); } $call = new ConduitCall('differential.createrawdiff', array('diff' => $diff)); $call->setUser($request->getUser()); $result = $call->execute(); $path = id(new PhutilURI($result['uri']))->getPath(); return id(new AphrontRedirectResponse())->setURI($path); } $form = new AphrontFormView(); $arcanist_href = PhabricatorEnv::getDoclink('article/Arcanist_User_Guide.html'); $arcanist_link = phutil_render_tag('a', array('href' => $arcanist_href, 'target' => '_blank'), 'Arcanist'); $form->setAction('/differential/diff/create/')->setEncType('multipart/form-data')->setUser($request->getUser())->appendChild('<p class="aphront-form-instructions">The best way to create a ' . "Differential diff is by using {$arcanist_link}, but you " . 'can also just paste a diff (e.g., from <tt>svn diff</tt> or ' . '<tt>git diff</tt>) into this box or upload it as a file if you ' . 'really want.</p>')->appendChild(id(new AphrontFormTextAreaControl())->setLabel('Raw Diff')->setName('diff')->setHeight(AphrontFormTextAreaControl::HEIGHT_VERY_TALL))->appendChild(id(new AphrontFormFileControl())->setLabel('Raw Diff from file')->setName('diff-file'))->appendChild(id(new AphrontFormSubmitControl())->setValue("Create Diff »")); $panel = new AphrontPanelView(); $panel->setHeader('Create New Diff'); $panel->appendChild($form); $panel->setWidth(AphrontPanelView::WIDTH_FORM); return $this->buildStandardPageResponse($panel, array('title' => 'Create Diff', 'tab' => 'create')); }
protected function execute(ConduitAPIRequest $request) { $diff_id = $request->getValue('diffID'); $linter_map = $request->getValue('linters'); $diff = id(new DifferentialDiffQuery())->setViewer($request->getUser())->withIDs(array($diff_id))->executeOne(); if (!$diff) { throw new ConduitException('ERR-BAD-DIFF'); } // Extract the finished linters and messages from the linter map. $finished_linters = array_keys($linter_map); $new_messages = array(); foreach ($linter_map as $linter => $messages) { $new_messages = array_merge($new_messages, $messages); } // Load the postponed linters attached to this diff. $postponed_linters_property = id(new DifferentialDiffProperty())->loadOneWhere('diffID = %d AND name = %s', $diff_id, 'arc:lint-postponed'); if ($postponed_linters_property) { $postponed_linters = $postponed_linters_property->getData(); } else { $postponed_linters = array(); } foreach ($finished_linters as $linter) { if (!in_array($linter, $postponed_linters)) { throw new ConduitException('ERR-BAD-LINTER'); } } foreach ($postponed_linters as $idx => $linter) { if (in_array($linter, $finished_linters)) { unset($postponed_linters[$idx]); } } // Load the lint messages currenty attached to the diff. If this // diff property doesn't exist, create it. $messages_property = id(new DifferentialDiffProperty())->loadOneWhere('diffID = %d AND name = %s', $diff_id, 'arc:lint'); if ($messages_property) { $messages = $messages_property->getData(); } else { $messages = array(); } // Add new lint messages, removing duplicates. foreach ($new_messages as $new_message) { if (!in_array($new_message, $messages)) { $messages[] = $new_message; } } // Use setdiffproperty to update the postponed linters and messages, // as these will also update the lint status correctly. $call = new ConduitCall('differential.setdiffproperty', array('diff_id' => $diff_id, 'name' => 'arc:lint', 'data' => json_encode($messages))); $call->setForceLocal(true); $call->setUser($request->getUser()); $call->execute(); $call = new ConduitCall('differential.setdiffproperty', array('diff_id' => $diff_id, 'name' => 'arc:lint-postponed', 'data' => json_encode($postponed_linters))); $call->setForceLocal(true); $call->setUser($request->getUser()); $call->execute(); }
public function execute(PhutilArgumentParser $args) { $time_start = microtime(true); $methodv = $args->getArg('method'); if (!$methodv) { throw new Exception(pht('No Conduit method provided.')); } else { if (count($methodv) > 1) { throw new Exception(pht('Too many Conduit methods provided.')); } } $method = head($methodv); $json = $this->readAllInput(); $raw_params = null; try { $raw_params = phutil_json_decode($json); } catch (PhutilJSONParserException $ex) { throw new PhutilProxyException(pht('Invalid JSON input.'), $ex); } $params = idx($raw_params, 'params', '[]'); $params = phutil_json_decode($params); $metadata = idx($params, '__conduit__', array()); unset($params['__conduit__']); $call = null; $error_code = null; $error_info = null; try { $call = new ConduitCall($method, $params); $call->setUser($this->getUser()); $result = $call->execute(); } catch (ConduitException $ex) { $result = null; $error_code = $ex->getMessage(); if ($ex->getErrorDescription()) { $error_info = $ex->getErrorDescription(); } else { if ($call) { $error_info = $call->getErrorDescription($error_code); } } } $response = id(new ConduitAPIResponse())->setResult($result)->setErrorCode($error_code)->setErrorInfo($error_info); $json_out = json_encode($response->toDictionary()); $json_out = $json_out . "\n"; $this->getIOChannel()->write($json_out); // NOTE: Flush here so we can get an accurate result for the duration, // if the response is large and the receiver is slow to read it. $this->getIOChannel()->flush(); $time_end = microtime(true); $connection_id = idx($metadata, 'connectionID'); $log = id(new PhabricatorConduitMethodCallLog())->setCallerPHID($this->getUser()->getPHID())->setConnectionID($connection_id)->setMethod($method)->setError((string) $error_code)->setDuration(1000000 * ($time_end - $time_start))->save(); }
public function didParseCommit(PhabricatorRepository $repository, PhabricatorRepositoryCommit $commit, PhabricatorRepositoryCommitData $data) { $user = id(new PhabricatorUser())->loadOneWhere('phid = %s', $data->getCommitDetail('authorPHID')); if (!$user) { return; } $prefixes = array('resolves' => ManiphestTaskStatus::STATUS_CLOSED_RESOLVED, 'fixes' => ManiphestTaskStatus::STATUS_CLOSED_RESOLVED, 'wontfix' => ManiphestTaskStatus::STATUS_CLOSED_WONTFIX, 'wontfixes' => ManiphestTaskStatus::STATUS_CLOSED_WONTFIX, 'spite' => ManiphestTaskStatus::STATUS_CLOSED_SPITE, 'spites' => ManiphestTaskStatus::STATUS_CLOSED_SPITE, 'invalidate' => ManiphestTaskStatus::STATUS_CLOSED_INVALID, 'invaldiates' => ManiphestTaskStatus::STATUS_CLOSED_INVALID, 'close' => ManiphestTaskStatus::STATUS_CLOSED_RESOLVED, 'closes' => ManiphestTaskStatus::STATUS_CLOSED_RESOLVED, 'ref' => null, 'refs' => null, 'references' => null, 'cf.' => null); $suffixes = array('as resolved' => ManiphestTaskStatus::STATUS_CLOSED_RESOLVED, 'as fixed' => ManiphestTaskStatus::STATUS_CLOSED_RESOLVED, 'as wontfix' => ManiphestTaskStatus::STATUS_CLOSED_WONTFIX, 'as spite' => ManiphestTaskStatus::STATUS_CLOSED_SPITE, 'out of spite' => ManiphestTaskStatus::STATUS_CLOSED_SPITE, 'as invalid' => ManiphestTaskStatus::STATUS_CLOSED_INVALID, '' => null); $prefix_regex = array(); foreach ($prefixes as $prefix => $resolution) { $prefix_regex[] = preg_quote($prefix, '/'); } $prefix_regex = implode('|', $prefix_regex); $suffix_regex = array(); foreach ($suffixes as $suffix => $resolution) { $suffix_regex[] = preg_quote($suffix, '/'); } $suffix_regex = implode('|', $suffix_regex); $matches = null; $ok = preg_match_all("/({$prefix_regex})\\s+T(\\d+)\\s*({$suffix_regex})/i", $this->renderValueForCommitMessage($is_edit = false), $matches, PREG_SET_ORDER); if (!$ok) { return; } foreach ($matches as $set) { $prefix = strtolower($set[1]); $task_id = (int) $set[2]; $suffix = strtolower($set[3]); $status = idx($suffixes, $suffix); if (!$status) { $status = idx($prefixes, $prefix); } $tasks = id(new ManiphestTaskQuery())->withTaskIDs(array($task_id))->execute(); $task = idx($tasks, $task_id); if (!$task) { // Task doesn't exist, or the user can't see it. continue; } id(new PhabricatorEdgeEditor())->setUser($user)->addEdge($task->getPHID(), PhabricatorEdgeConfig::TYPE_TASK_HAS_COMMIT, $commit->getPHID())->save(); if (!$status) { // Text like "Ref T123", don't change the task status. continue; } if ($task->getStatus() != ManiphestTaskStatus::STATUS_OPEN) { // Task is already closed. continue; } $commit_name = $repository->formatCommitName($commit->getCommitIdentifier()); $call = new ConduitCall('maniphest.update', array('id' => $task->getID(), 'status' => $status, 'comments' => "Closed by commit {$commit_name}.")); $call->setUser($user); $call->execute(); } }
public function generateDiff($author) { $paste_generator = new PhabricatorPasteTestDataGenerator(); $languages = $paste_generator->supportedLanguages; $lang = array_rand($languages); $code = $paste_generator->generateContent($lang); $altcode = $paste_generator->generateContent($lang); $newcode = $this->randomlyModify($code, $altcode); $diff = id(new PhabricatorDifferenceEngine())->generateRawDiffFromFileContent($code, $newcode); $call = new ConduitCall('differential.createrawdiff', array('diff' => $diff)); $call->setUser($author); $result = $call->execute(); $thediff = id(new DifferentialDiff())->load($result['id']); $thediff->setDescription($this->generateTitle())->save(); return $thediff; }
protected function execute(ConduitAPIRequest $request) { $viewer = $request->getUser(); $call = new ConduitCall('harbormaster.build.search', array_filter(array('constraints' => array_filter(array('ids' => $request->getValue('ids'), 'phids' => $request->getValue('phids'), 'statuses' => $request->getValue('buildStatuses'), 'buildables' => $request->getValue('buildablePHIDs'), 'plans' => $request->getValue('buildPlanPHIDs'))), 'attachments' => array('querybuilds' => true), 'limit' => $request->getValue('limit'), 'before' => $request->getValue('before'), 'after' => $request->getValue('after')))); $subsumption = $call->setUser($viewer)->execute(); $data = array(); foreach ($subsumption['data'] as $build_data) { $querybuilds = idxv($build_data, array('attachments', 'querybuilds'), array()); $fields = idx($build_data, 'fields', array()); unset($build_data['fields']); unset($build_data['attachments']); $data[] = array_mergev(array($build_data, $querybuilds, $fields)); } $subsumption['data'] = $data; return $subsumption; }
public function commitRevisionToWorkspace(DifferentialRevision $revision, ArcanistRepositoryAPI $workspace, PhabricatorUser $user) { $diff_id = $revision->loadActiveDiff()->getID(); $call = new ConduitCall('differential.getrawdiff', array('diffID' => $diff_id)); $call->setUser($user); $raw_diff = $call->execute(); $future = $workspace->execFutureLocal('patch --no-commit -'); $future->write($raw_diff); $future->resolvex(); $workspace->reloadWorkingCopy(); $call = new ConduitCall('differential.getcommitmessage', array('revision_id' => $revision->getID())); $call->setUser($user); $message = $call->execute(); $author = id(new PhabricatorUser())->loadOneWhere('phid = %s', $revision->getAuthorPHID()); $author_string = sprintf('%s <%s>', $author->getRealName(), $author->loadPrimaryEmailAddress()); $author_date = $revision->getDateCreated(); $workspace->execxLocal('commit --date=%s --user=%s ' . '--message=%s', $author_date . ' 0', $author_string, $message); }
public function commitRevisionToWorkspace(DifferentialRevision $revision, ArcanistRepositoryAPI $workspace, PhabricatorUser $user) { $diff_id = $revision->loadActiveDiff()->getID(); $call = new ConduitCall('differential.getrawdiff', array('diffID' => $diff_id)); $call->setUser($user); $raw_diff = $call->execute(); $missing_binary = "\nindex " . "0000000000000000000000000000000000000000.." . "0000000000000000000000000000000000000000\n"; if (strpos($raw_diff, $missing_binary) !== false) { throw new Exception(pht('Patch is missing content for a binary file')); } $future = $workspace->execFutureLocal('apply --index -'); $future->write($raw_diff); $future->resolvex(); $workspace->reloadWorkingCopy(); $call = new ConduitCall('differential.getcommitmessage', array('revision_id' => $revision->getID())); $call->setUser($user); $message = $call->execute(); $author = id(new PhabricatorUser())->loadOneWhere('phid = %s', $revision->getAuthorPHID()); $author_string = sprintf('%s <%s>', $author->getRealName(), $author->loadPrimaryEmailAddress()); $author_date = $revision->getDateCreated(); $workspace->execxLocal('-c user.name=%s -c user.email=%s ' . 'commit --date=%s --author=%s ' . '--message=%s', $user->getRealName(), $user->loadPrimaryEmailAddress(), $author_date, $author_string, $message); }
public function processRequest() { $request = $this->getRequest(); $errors = array(); $e_diff = null; $e_file = null; if ($request->isFormPost()) { $diff = null; if ($request->getFileExists('diff-file')) { $diff = PhabricatorFile::readUploadedFileData($_FILES['diff-file']); } else { $diff = $request->getStr('diff'); } if (!strlen($diff)) { $errors[] = pht('You can not create an empty diff. Copy/paste a diff, or upload a ' . 'diff file.'); $e_diff = pht('Required'); $e_file = pht('Required'); } if (!$errors) { $call = new ConduitCall('differential.createrawdiff', array('diff' => $diff)); $call->setUser($request->getUser()); $result = $call->execute(); $path = id(new PhutilURI($result['uri']))->getPath(); return id(new AphrontRedirectResponse())->setURI($path); } } $form = new AphrontFormView(); $arcanist_href = PhabricatorEnv::getDoclink('Arcanist User Guide'); $arcanist_link = phutil_tag('a', array('href' => $arcanist_href, 'target' => '_blank'), 'Arcanist'); $cancel_uri = $this->getApplicationURI(); $form->setAction('/differential/diff/create/')->setEncType('multipart/form-data')->setUser($request->getUser())->appendInstructions(pht('The best way to create a Differential diff is by using %s, but you ' . 'can also just paste a diff (for example, from %s, %s or %s) into ' . 'this box, or upload a diff file.', $arcanist_link, phutil_tag('tt', array(), 'svn diff'), phutil_tag('tt', array(), 'git diff'), phutil_tag('tt', array(), 'hg diff --git')))->appendChild(id(new AphrontFormTextAreaControl())->setLabel(pht('Raw Diff'))->setName('diff')->setHeight(AphrontFormTextAreaControl::HEIGHT_VERY_TALL)->setError($e_diff))->appendChild(id(new AphrontFormFileControl())->setLabel(pht('Raw Diff From File'))->setName('diff-file')->setError($e_file))->appendChild(id(new AphrontFormSubmitControl())->addCancelButton($cancel_uri)->setValue(pht('Create Diff'))); $form_box = id(new PHUIObjectBoxView())->setHeaderText(pht('Create New Diff'))->setForm($form)->setFormErrors($errors); $crumbs = $this->buildApplicationCrumbs(); $crumbs->addTextCrumb(pht('Create Diff')); return $this->buildApplicationPage(array($crumbs, $form_box), array('title' => pht('Create Diff'))); }
public function processRequest() { $time_start = microtime(true); $request = $this->getRequest(); $method = $this->method; $api_request = null; $log = new PhabricatorConduitMethodCallLog(); $log->setMethod($method); $metadata = array(); try { $params = $this->decodeConduitParams($request, $method); $metadata = idx($params, '__conduit__', array()); unset($params['__conduit__']); $call = new ConduitCall($method, $params); $result = null; // TODO: Straighten out the auth pathway here. We shouldn't be creating // a ConduitAPIRequest at this level, but some of the auth code expects // it. Landing a halfway version of this to unblock T945. $api_request = new ConduitAPIRequest($params); $allow_unguarded_writes = false; $auth_error = null; $conduit_username = '******'; if ($call->shouldRequireAuthentication()) { $metadata['scope'] = $call->getRequiredScope(); $auth_error = $this->authenticateUser($api_request, $metadata); // If we've explicitly authenticated the user here and either done // CSRF validation or are using a non-web authentication mechanism. $allow_unguarded_writes = true; if (isset($metadata['actAsUser'])) { $this->actAsUser($api_request, $metadata['actAsUser']); } if ($auth_error === null) { $conduit_user = $api_request->getUser(); if ($conduit_user && $conduit_user->getPHID()) { $conduit_username = $conduit_user->getUsername(); } $call->setUser($api_request->getUser()); } } $access_log = PhabricatorAccessLog::getLog(); if ($access_log) { $access_log->setData(array('u' => $conduit_username, 'm' => $method)); } if ($call->shouldAllowUnguardedWrites()) { $allow_unguarded_writes = true; } if ($auth_error === null) { if ($allow_unguarded_writes) { $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); } try { $result = $call->execute(); $error_code = null; $error_info = null; } catch (ConduitException $ex) { $result = null; $error_code = $ex->getMessage(); if ($ex->getErrorDescription()) { $error_info = $ex->getErrorDescription(); } else { $error_info = $call->getErrorDescription($error_code); } } if ($allow_unguarded_writes) { unset($unguarded); } } else { list($error_code, $error_info) = $auth_error; } } catch (Exception $ex) { phlog($ex); $result = null; $error_code = 'ERR-CONDUIT-CORE'; $error_info = $ex->getMessage(); } $time_end = microtime(true); $connection_id = null; if (idx($metadata, 'connectionID')) { $connection_id = $metadata['connectionID']; } else { if ($method == 'conduit.connect' && $result) { $connection_id = idx($result, 'connectionID'); } } $log->setConnectionID($connection_id); $log->setError((string) $error_code); $log->setDuration(1000000 * ($time_end - $time_start)); // TODO: This is a hack, but the insert is comparatively expensive and // we only really care about having these logs for real CLI clients, if // even that. if (empty($metadata['authToken'])) { $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); $log->save(); unset($unguarded); } $response = id(new ConduitAPIResponse())->setResult($result)->setErrorCode($error_code)->setErrorInfo($error_info); switch ($request->getStr('output')) { case 'human': return $this->buildHumanReadableResponse($method, $api_request, $response->toDictionary()); case 'json': default: return id(new AphrontJSONResponse())->setAddJSONShield(false)->setContent($response->toDictionary()); } }
public function processRequest() { $time_start = microtime(true); $request = $this->getRequest(); $method = $this->method; $api_request = null; $method_implementation = null; $log = new PhabricatorConduitMethodCallLog(); $log->setMethod($method); $metadata = array(); $multimeter = MultimeterControl::getInstance(); if ($multimeter) { $multimeter->setEventContext('api.' . $method); } try { list($metadata, $params) = $this->decodeConduitParams($request, $method); $call = new ConduitCall($method, $params); $method_implementation = $call->getMethodImplementation(); $result = null; // TODO: The relationship between ConduitAPIRequest and ConduitCall is a // little odd here and could probably be improved. Specifically, the // APIRequest is a sub-object of the Call, which does not parallel the // role of AphrontRequest (which is an indepenent object). // In particular, the setUser() and getUser() existing independently on // the Call and APIRequest is very awkward. $api_request = $call->getAPIRequest(); $allow_unguarded_writes = false; $auth_error = null; $conduit_username = '******'; if ($call->shouldRequireAuthentication()) { $metadata['scope'] = $call->getRequiredScope(); $auth_error = $this->authenticateUser($api_request, $metadata); // If we've explicitly authenticated the user here and either done // CSRF validation or are using a non-web authentication mechanism. $allow_unguarded_writes = true; if ($auth_error === null) { $conduit_user = $api_request->getUser(); if ($conduit_user && $conduit_user->getPHID()) { $conduit_username = $conduit_user->getUsername(); } $call->setUser($api_request->getUser()); } } $access_log = PhabricatorAccessLog::getLog(); if ($access_log) { $access_log->setData(array('u' => $conduit_username, 'm' => $method)); } if ($call->shouldAllowUnguardedWrites()) { $allow_unguarded_writes = true; } if ($auth_error === null) { if ($allow_unguarded_writes) { $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); } try { $result = $call->execute(); $error_code = null; $error_info = null; } catch (ConduitException $ex) { $result = null; $error_code = $ex->getMessage(); if ($ex->getErrorDescription()) { $error_info = $ex->getErrorDescription(); } else { $error_info = $call->getErrorDescription($error_code); } } if ($allow_unguarded_writes) { unset($unguarded); } } else { list($error_code, $error_info) = $auth_error; } } catch (Exception $ex) { if (!$ex instanceof ConduitMethodNotFoundException) { phlog($ex); } $result = null; $error_code = $ex instanceof ConduitException ? 'ERR-CONDUIT-CALL' : 'ERR-CONDUIT-CORE'; $error_info = $ex->getMessage(); } $time_end = microtime(true); $connection_id = null; if (idx($metadata, 'connectionID')) { $connection_id = $metadata['connectionID']; } else { if ($method == 'conduit.connect' && $result) { $connection_id = idx($result, 'connectionID'); } } $log->setCallerPHID(isset($conduit_user) ? $conduit_user->getPHID() : null)->setConnectionID($connection_id)->setError((string) $error_code)->setDuration(1000000 * ($time_end - $time_start)); $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); $log->save(); unset($unguarded); $response = id(new ConduitAPIResponse())->setResult($result)->setErrorCode($error_code)->setErrorInfo($error_info); switch ($request->getStr('output')) { case 'human': return $this->buildHumanReadableResponse($method, $api_request, $response->toDictionary(), $method_implementation); case 'json': default: return id(new AphrontJSONResponse())->setAddJSONShield(false)->setContent($response->toDictionary()); } }
public function handleRequest(AphrontRequest $request) { $viewer = $this->getViewer(); // If we're on the "Update Diff" workflow, load the revision we're going // to update. $revision = null; $revision_id = $request->getURIData('revisionID'); if ($revision_id) { $revision = id(new DifferentialRevisionQuery())->setViewer($viewer)->withIDs(array($revision_id))->requireCapabilities(array(PhabricatorPolicyCapability::CAN_VIEW, PhabricatorPolicyCapability::CAN_EDIT))->executeOne(); if (!$revision) { return new Aphront404Response(); } } $diff = null; // This object is just for policy stuff $diff_object = DifferentialDiff::initializeNewDiff($viewer); $repository_phid = null; $errors = array(); $e_diff = null; $e_file = null; $validation_exception = null; if ($request->isFormPost()) { $repository_tokenizer = $request->getArr(id(new DifferentialRepositoryField())->getFieldKey()); if ($repository_tokenizer) { $repository_phid = reset($repository_tokenizer); } if ($request->getFileExists('diff-file')) { $diff = PhabricatorFile::readUploadedFileData($_FILES['diff-file']); } else { $diff = $request->getStr('diff'); } if (!strlen($diff)) { $errors[] = pht('You can not create an empty diff. Paste a diff or upload a ' . 'file containing a diff.'); $e_diff = pht('Required'); $e_file = pht('Required'); } if (!$errors) { try { $call = new ConduitCall('differential.createrawdiff', array('diff' => $diff, 'repositoryPHID' => $repository_phid, 'viewPolicy' => $request->getStr('viewPolicy'))); $call->setUser($viewer); $result = $call->execute(); $diff_id = $result['id']; $uri = $this->getApplicationURI("diff/{$diff_id}/"); $uri = new PhutilURI($uri); if ($revision) { $uri->setQueryParam('revisionID', $revision->getID()); } return id(new AphrontRedirectResponse())->setURI($uri); } catch (PhabricatorApplicationTransactionValidationException $ex) { $validation_exception = $ex; } } } $form = new AphrontFormView(); $arcanist_href = PhabricatorEnv::getDoclink('Arcanist User Guide'); $arcanist_link = phutil_tag('a', array('href' => $arcanist_href, 'target' => '_blank'), pht('Learn More')); $cancel_uri = $this->getApplicationURI(); $policies = id(new PhabricatorPolicyQuery())->setViewer($viewer)->setObject($diff_object)->execute(); $info_view = null; if (!$request->isFormPost()) { $info_view = id(new PHUIInfoView())->setSeverity(PHUIInfoView::SEVERITY_NOTICE)->setErrors(array(array(pht('The best way to create a diff is to use the Arcanist ' . 'command-line tool.'), ' ', $arcanist_link), pht('You can also paste a diff below, or upload a file ' . 'containing a diff (for example, from %s, %s or %s).', phutil_tag('tt', array(), 'svn diff'), phutil_tag('tt', array(), 'git diff'), phutil_tag('tt', array(), 'hg diff --git')))); } if ($revision) { $title = pht('Update Diff'); $header = pht('Update Diff'); $button = pht('Continue'); } else { $title = pht('Create Diff'); $header = pht('Create New Diff'); $button = pht('Create Diff'); } $form->setEncType('multipart/form-data')->setUser($viewer); if ($revision) { $form->appendChild(id(new AphrontFormMarkupControl())->setLabel(pht('Updating Revision'))->setValue($viewer->renderHandle($revision->getPHID()))); } if ($repository_phid) { $repository_value = array($repository_phid); } else { $repository_value = array(); } $form->appendChild(id(new AphrontFormTextAreaControl())->setLabel(pht('Raw Diff'))->setName('diff')->setValue($diff)->setHeight(AphrontFormTextAreaControl::HEIGHT_VERY_TALL)->setError($e_diff))->appendChild(id(new AphrontFormFileControl())->setLabel(pht('Raw Diff From File'))->setName('diff-file')->setError($e_file))->appendControl(id(new AphrontFormTokenizerControl())->setName(id(new DifferentialRepositoryField())->getFieldKey())->setLabel(pht('Repository'))->setDatasource(new DiffusionRepositoryDatasource())->setValue($repository_value)->setLimit(1))->appendChild(id(new AphrontFormPolicyControl())->setUser($viewer)->setName('viewPolicy')->setPolicyObject($diff_object)->setPolicies($policies)->setCapability(PhabricatorPolicyCapability::CAN_VIEW))->appendChild(id(new AphrontFormSubmitControl())->addCancelButton($cancel_uri)->setValue($button)); $form_box = id(new PHUIObjectBoxView())->setHeaderText($header)->setValidationException($validation_exception)->setForm($form)->setFormErrors($errors); if ($info_view) { $form_box->setInfoView($info_view); } $crumbs = $this->buildApplicationCrumbs(); if ($revision) { $crumbs->addTextCrumb($revision->getMonogram(), '/' . $revision->getMonogram()); } $crumbs->addTextCrumb($title); return $this->buildApplicationPage(array($crumbs, $form_box), array('title' => $title)); }
protected function processReceivedMail(PhabricatorMetaMTAReceivedMail $mail, PhabricatorUser $sender) { $attachments = $mail->getAttachments(); $files = array(); $errors = array(); if ($attachments) { $files = id(new PhabricatorFileQuery())->setViewer($sender)->withPHIDs($attachments)->execute(); foreach ($files as $index => $file) { if ($file->getMimeType() != 'text/plain') { $errors[] = pht('Could not parse file %s; only files with mimetype text/plain ' . 'can be parsed via email.', $file->getName()); unset($files[$index]); } } } $diffs = array(); foreach ($files as $file) { $call = new ConduitCall('differential.createrawdiff', array('diff' => $file->loadFileData())); $call->setUser($sender); try { $result = $call->execute(); $diffs[$file->getName()] = $result['uri']; } catch (Exception $e) { $errors[] = pht('Could not parse attachment %s; only attachments (and mail bodies) ' . 'generated via "diff" commands can be parsed.', $file->getName()); } } $body = $mail->getCleanTextBody(); if ($body) { $call = new ConduitCall('differential.createrawdiff', array('diff' => $body)); $call->setUser($sender); try { $result = $call->execute(); $diffs[pht('Mail Body')] = $result['uri']; } catch (Exception $e) { $errors[] = pht('Could not parse mail body; only mail bodies (and attachments) ' . 'generated via "diff" commands can be parsed.'); } } $subject_prefix = PhabricatorEnv::getEnvConfig('metamta.differential.subject-prefix'); if (count($diffs)) { $subject = pht('You successfully created %d diff(s).', count($diffs)); } else { $subject = pht('Diff creation failed; see body for %s error(s).', new PhutilNumber(count($errors))); } $body = new PhabricatorMetaMTAMailBody(); $body->addRawSection($subject); if (count($diffs)) { $text_body = ''; $html_body = array(); $body_label = pht('%s DIFF LINK(S)', new PhutilNumber(count($diffs))); foreach ($diffs as $filename => $diff_uri) { $text_body .= $filename . ': ' . $diff_uri . "\n"; $html_body[] = phutil_tag('a', array('href' => $diff_uri), $filename); $html_body[] = phutil_tag('br'); } $body->addTextSection($body_label, $text_body); $body->addHTMLSection($body_label, $html_body); } if (count($errors)) { $body_section = new PhabricatorMetaMTAMailSection(); $body_label = pht('%s ERROR(S)', new PhutilNumber(count($errors))); foreach ($errors as $error) { $body_section->addFragment($error); } $body->addTextSection($body_label, $body_section); } id(new PhabricatorMetaMTAMail())->addTos(array($sender->getPHID()))->setSubject($subject)->setSubjectPrefix($subject_prefix)->setFrom($sender->getPHID())->setBody($body->render())->saveAndSend(); }
protected final function updateCommitData($author, $message, $committer = null) { $commit = $this->commit; $data = id(new PhabricatorRepositoryCommitData())->loadOneWhere('commitID = %d', $commit->getID()); if (!$data) { $data = new PhabricatorRepositoryCommitData(); } $data->setCommitID($commit->getID()); $data->setAuthorName($author); $data->setCommitMessage($message); if ($committer) { $data->setCommitDetail('committer', $committer); } $repository = $this->repository; $detail_parser = $repository->getDetail('detail-parser', 'PhabricatorRepositoryDefaultCommitMessageDetailParser'); if ($detail_parser) { $parser_obj = newv($detail_parser, array($commit, $data)); $parser_obj->parseCommitDetails(); } $author_phid = $this->lookupUser($commit, $data->getAuthorName(), $data->getCommitDetail('authorPHID')); $data->setCommitDetail('authorPHID', $author_phid); $committer_phid = $this->lookupUser($commit, $data->getCommitDetail('committer'), $data->getCommitDetail('committerPHID')); $data->setCommitDetail('committerPHID', $committer_phid); if ($author_phid != $commit->getAuthorPHID()) { $commit->setAuthorPHID($author_phid); $commit->save(); } $conn_w = id(new DifferentialRevision())->establishConnection('w'); // NOTE: The `differential_commit` table has a unique ID on `commitPHID`, // preventing more than one revision from being associated with a commit. // Generally this is good and desirable, but with the advent of hash // tracking we may end up in a situation where we match several different // revisions. We just kind of ignore this and pick one, we might want to // revisit this and do something differently. (If we match several revisions // someone probably did something very silly, though.) $revision = null; $should_autoclose = $repository->shouldAutocloseCommit($commit, $data); $revision_id = $data->getCommitDetail('differential.revisionID'); if (!$revision_id) { $hashes = $this->getCommitHashes($this->repository, $this->commit); if ($hashes) { $query = new DifferentialRevisionQuery(); $query->withCommitHashes($hashes); $revisions = $query->execute(); if (!empty($revisions)) { $revision = $this->identifyBestRevision($revisions); $revision_id = $revision->getID(); } } } if ($revision_id) { $lock = PhabricatorGlobalLock::newLock(get_class($this) . ':' . $revision_id); $lock->lock(5 * 60); $revision = id(new DifferentialRevision())->load($revision_id); if ($revision) { $revision->loadRelationships(); queryfx($conn_w, 'INSERT IGNORE INTO %T (revisionID, commitPHID) VALUES (%d, %s)', DifferentialRevision::TABLE_COMMIT, $revision->getID(), $commit->getPHID()); $status_closed = ArcanistDifferentialRevisionStatus::CLOSED; $should_close = $revision->getStatus() != $status_closed && $should_autoclose; if ($should_close) { $actor_phid = nonempty($committer_phid, $author_phid, $revision->getAuthorPHID()); $diff = $this->attachToRevision($revision, $actor_phid); $revision->setDateCommitted($commit->getEpoch()); $editor = new DifferentialCommentEditor($revision, $actor_phid, DifferentialAction::ACTION_CLOSE); $editor->setIsDaemonWorkflow(true); $vs_diff = $this->loadChangedByCommit($diff); if ($vs_diff) { $data->setCommitDetail('vsDiff', $vs_diff->getID()); $changed_by_commit = PhabricatorEnv::getProductionURI('/D' . $revision->getID() . '?vs=' . $vs_diff->getID() . '&id=' . $diff->getID() . '#toc'); $editor->setChangedByCommit($changed_by_commit); } $commit_name = $repository->formatCommitName($commit->getCommitIdentifier()); $committer_name = $this->loadUserName($committer_phid, $data->getCommitDetail('committer')); $author_name = $this->loadUserName($author_phid, $data->getAuthorName()); $info = array(); $info[] = "authored by {$author_name}"; if ($committer_name && $committer_name != $author_name) { $info[] = "committed by {$committer_name}"; } $info = implode(', ', $info); $editor->setMessage("Closed by commit {$commit_name} ({$info}).")->save(); } } $lock->unlock(); } if ($should_autoclose && $author_phid) { $user = id(new PhabricatorUser())->loadOneWhere('phid = %s', $author_phid); $call = new ConduitCall('differential.parsecommitmessage', array('corpus' => $message, 'partial' => true)); $call->setUser($user); $result = $call->execute(); $field_values = $result['fields']; $fields = DifferentialFieldSelector::newSelector()->getFieldSpecifications(); foreach ($fields as $key => $field) { if (!$field->shouldAppearOnCommitMessage()) { continue; } $field->setUser($user); $value = idx($field_values, $field->getCommitMessageKey()); $field->setValueFromParsedCommitMessage($value); if ($revision) { $field->setRevision($revision); } $field->didParseCommit($repository, $commit, $data); } } $data->save(); }