public function copyFieldsFromConduit(array $fields) { $revision = $this->revision; $revision->loadRelationships(); $aux_fields = DifferentialFieldSelector::newSelector()->getFieldSpecifications(); $user = id(new PhabricatorUser())->loadOneWhere('phid = %s', $this->actorPHID); foreach ($aux_fields as $key => $aux_field) { $aux_field->setRevision($revision); $aux_field->setUser($user); if (!$aux_field->shouldAppearOnCommitMessage()) { unset($aux_fields[$key]); } } $aux_fields = mpull($aux_fields, null, 'getCommitMessageKey'); foreach ($fields as $field => $value) { if (empty($aux_fields[$field])) { throw new Exception("Parsed commit message contains unrecognized field '{$field}'."); } $aux_fields[$field]->setValueFromParsedCommitMessage($value); } foreach ($aux_fields as $aux_field) { $aux_field->validateField(); } $aux_fields = array_values($aux_fields); $this->setAuxiliaryFields($aux_fields); }
protected function execute(ConduitAPIRequest $request) { $corpus = $request->getValue('corpus'); $aux_fields = DifferentialFieldSelector::newSelector()->getFieldSpecifications(); foreach ($aux_fields as $key => $aux_field) { if (!$aux_field->shouldAppearOnCommitMessage()) { unset($aux_fields[$key]); } } $aux_fields = mpull($aux_fields, null, 'getCommitMessageKey'); // Build a map from labels (like "Test Plan") to field keys // (like "testPlan"). $label_map = $this->buildLabelMap($aux_fields); $field_map = $this->parseCommitMessage($corpus, $label_map); $fields = array(); $errors = array(); foreach ($field_map as $field_key => $field_value) { $field = $aux_fields[$field_key]; try { $fields[$field_key] = $field->parseValueFromCommitMessage($field_value); } catch (DifferentialFieldParseException $ex) { $field_label = $field->renderLabelForCommitMessage(); $errors[] = "Error parsing field '{$field_label}': " . $ex->getMessage(); } } // TODO: This is for backcompat only, remove once Arcanist gets updated. $error = head($errors); return array('error' => $error, 'errors' => $errors, 'fields' => $fields); }
private function loadAuxiliaryFields(DifferentialRevision $revision) { $aux_fields = DifferentialFieldSelector::newSelector()->getFieldSpecifications(); foreach ($aux_fields as $key => $aux_field) { if (!$aux_field->shouldAppearOnConduitView()) { unset($aux_fields[$key]); } } $aux_fields = DifferentialAuxiliaryField::loadFromStorage($revision, $aux_fields); return mpull($aux_fields, 'getValueForConduit', 'getKeyForConduit'); }
protected function execute(ConduitAPIRequest $request) { $corpus = $request->getValue('corpus'); $is_partial = $request->getValue('partial'); $aux_fields = DifferentialFieldSelector::newSelector()->getFieldSpecifications(); foreach ($aux_fields as $key => $aux_field) { if (!$aux_field->shouldAppearOnCommitMessage()) { unset($aux_fields[$key]); } $aux_field->setUser($request->getUser()); } $aux_fields = mpull($aux_fields, null, 'getCommitMessageKey'); $this->errors = array(); // Build a map from labels (like "Test Plan") to field keys // (like "testPlan"). $label_map = $this->buildLabelMap($aux_fields); $field_map = $this->parseCommitMessage($corpus, $label_map); $fields = array(); foreach ($field_map as $field_key => $field_value) { $field = $aux_fields[$field_key]; try { $fields[$field_key] = $field->parseValueFromCommitMessage($field_value); $field->setValueFromParsedCommitMessage($fields[$field_key]); } catch (DifferentialFieldParseException $ex) { $field_label = $field->renderLabelForCommitMessage(); $this->errors[] = "Error parsing field '{$field_label}': " . $ex->getMessage(); } } if (!$is_partial) { foreach ($aux_fields as $field_key => $aux_field) { try { $aux_field->validateField(); } catch (DifferentialFieldValidationException $ex) { $field_label = $aux_field->renderLabelForCommitMessage(); $this->errors[] = "Invalid or missing field '{$field_label}': " . $ex->getMessage(); } } } return array('errors' => $this->errors, 'fields' => $fields); }
private function loadAuxiliaryFields(DifferentialRevision $revision) { $user = $this->getRequest()->getUser(); $aux_fields = DifferentialFieldSelector::newSelector()->getFieldSpecifications(); foreach ($aux_fields as $key => $aux_field) { $aux_field->setRevision($revision); if (!$aux_field->shouldAppearOnEdit()) { unset($aux_fields[$key]); } else { $aux_field->setUser($user); } } return DifferentialAuxiliaryField::loadFromStorage($revision, $aux_fields); }
public static function getDefaultFields() { $selector = DifferentialFieldSelector::newSelector(); $fields = $selector->getFieldSpecifications(); foreach ($fields as $key => $field) { if (!$field->shouldAppearOnRevisionList()) { unset($fields[$key]); } } if (!$fields) { throw new Exception("Phabricator configuration has no fields that appear on the list " . "interface!"); } return $selector->sortFieldsForRevisionList($fields); }
private function loadAuxiliaryFieldsAndProperties(DifferentialRevision $revision, DifferentialDiff $diff, array $special_properties) { $aux_fields = DifferentialFieldSelector::newSelector()->getFieldSpecifications(); foreach ($aux_fields as $key => $aux_field) { if (!$aux_field->shouldAppearOnRevisionView()) { unset($aux_fields[$key]); } else { $aux_field->setUser($this->getRequest()->getUser()); } } $aux_fields = DifferentialAuxiliaryField::loadFromStorage($revision, $aux_fields); $aux_props = array(); foreach ($aux_fields as $key => $aux_field) { $aux_field->setDiff($diff); $aux_props[$key] = $aux_field->getRequiredDiffProperties(); } $required_properties = array_mergev($aux_props); $required_properties = array_merge($required_properties, $special_properties); $property_map = array(); if ($required_properties) { $properties = id(new DifferentialDiffProperty())->loadAllWhere('diffID = %d AND name IN (%Ls)', $diff->getID(), $required_properties); $property_map = mpull($properties, 'getData', 'getName'); } foreach ($aux_fields as $key => $aux_field) { // Give each field only the properties it specifically required, and // set 'null' for each requested key which we didn't actually load a // value for (otherwise, getDiffProperty() will throw). if ($aux_props[$key]) { $props = array_select_keys($property_map, $aux_props[$key]) + array_fill_keys($aux_props[$key], null); } else { $props = array(); } $aux_field->setDiffProperties($props); } return array($aux_fields, array_select_keys($property_map, $special_properties)); }
protected function execute(ConduitAPIRequest $request) { $id = $request->getValue('revision_id'); $revision = id(new DifferentialRevision())->load($id); if (!$revision) { throw new ConduitException('ERR_NOT_FOUND'); } $revision->loadRelationships(); $is_edit = $request->getValue('edit'); $aux_fields = DifferentialFieldSelector::newSelector()->getFieldSpecifications(); foreach ($aux_fields as $key => $aux_field) { $aux_field->setRevision($revision); if (!$aux_field->shouldAppearOnCommitMessage()) { unset($aux_fields[$key]); } } $aux_fields = DifferentialAuxiliaryField::loadFromStorage($revision, $aux_fields); $aux_fields = mpull($aux_fields, null, 'getCommitMessageKey'); if ($is_edit) { $fields = $request->getValue('fields'); foreach ($fields as $field => $value) { $aux_field = idx($aux_fields, $field); if (!$aux_field) { throw new Exception("Commit message includes field '{$field}' which does not " . "correspond to any configured field."); } if ($aux_field->shouldOverwriteWhenCommitMessageIsEdited()) { $aux_field->setValueFromParsedCommitMessage($value); } } } $aux_phids = array(); foreach ($aux_fields as $field_key => $field) { $aux_phids[$field_key] = $field->getRequiredHandlePHIDsForCommitMessage(); } $phids = array_unique(array_mergev($aux_phids)); $handles = id(new PhabricatorObjectHandleData($phids))->loadHandles(); foreach ($aux_fields as $field_key => $field) { $field->setHandles(array_select_keys($handles, $aux_phids[$field_key])); } $commit_message = array(); foreach ($aux_fields as $field_key => $field) { $value = $field->renderValueForCommitMessage($is_edit); $label = $field->renderLabelForCommitMessage(); if ($value === null || !strlen($value)) { if ($field_key === 'title') { $commit_message[] = '<<Enter Revision Title>>'; } else { if ($field->shouldAppearOnCommitMessageTemplate() && $is_edit) { $commit_message[] = $label . ': '; } } } else { if ($field_key === 'title') { $commit_message[] = $value; } else { $value = str_replace(array("\r\n", "\r"), array("\n", "\n"), $value); if (strpos($value, "\n") !== false) { $commit_message[] = "{$label}:\n{$value}"; } else { $commit_message[] = "{$label}: {$value}"; } } } } $commit_message = implode("\n\n", $commit_message); return wordwrap($commit_message, 80); }
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(); }
protected function renderAuxFields($phase) { $selector = DifferentialFieldSelector::newSelector(); $aux_fields = $selector->sortFieldsForMail($selector->getFieldSpecifications()); $body = array(); foreach ($aux_fields as $field) { $field->setRevision($this->getRevision()); // TODO: Introduce and use getRequiredHandlePHIDsForMail() and load all // handles in prepareBody(). $text = $field->renderValueForMail($phase); if ($text !== null) { $body[] = $text; $body[] = null; } } return implode("\n", $body); }