2014-02-26 23:46:18 +01:00
|
|
|
<?php
|
|
|
|
|
|
|
|
final class DifferentialJIRAIssuesField
|
2014-02-27 01:53:42 +01:00
|
|
|
extends DifferentialStoredCustomField {
|
2014-02-26 23:46:18 +01:00
|
|
|
|
2014-02-28 00:15:48 +01:00
|
|
|
private $error;
|
|
|
|
|
2014-02-27 01:53:42 +01:00
|
|
|
public function getFieldKey() {
|
|
|
|
return 'phabricator:jira-issues';
|
|
|
|
}
|
2014-02-26 23:46:18 +01:00
|
|
|
|
2014-03-08 02:05:00 +01:00
|
|
|
public function getFieldKeyForConduit() {
|
|
|
|
return 'jira.issues';
|
|
|
|
}
|
|
|
|
|
2014-03-11 21:47:30 +01:00
|
|
|
public function isFieldEnabled() {
|
|
|
|
return (bool)PhabricatorAuthProviderOAuth1JIRA::getJIRAProvider();
|
|
|
|
}
|
|
|
|
|
2014-03-08 18:13:51 +01:00
|
|
|
public function canDisableField() {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2014-02-27 01:53:42 +01:00
|
|
|
public function getValueForStorage() {
|
|
|
|
return json_encode($this->getValue());
|
|
|
|
}
|
2014-02-26 23:46:18 +01:00
|
|
|
|
2014-02-27 01:53:42 +01:00
|
|
|
public function setValueFromStorage($value) {
|
2014-02-28 00:15:48 +01:00
|
|
|
$this->setValue(phutil_json_decode($value));
|
2014-02-27 01:53:42 +01:00
|
|
|
return $this;
|
2014-02-26 23:46:18 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
public function getFieldName() {
|
|
|
|
return pht('JIRA Issues');
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getFieldDescription() {
|
|
|
|
return pht('Lists associated JIRA issues.');
|
|
|
|
}
|
|
|
|
|
|
|
|
public function shouldAppearInPropertyView() {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function renderPropertyViewLabel() {
|
|
|
|
return $this->getFieldName();
|
|
|
|
}
|
|
|
|
|
|
|
|
public function renderPropertyViewValue(array $handles) {
|
2014-02-28 00:15:48 +01:00
|
|
|
$xobjs = $this->loadDoorkeeperExternalObjects($this->getValue());
|
2014-02-26 23:46:18 +01:00
|
|
|
if (!$xobjs) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
$links = array();
|
|
|
|
foreach ($xobjs as $xobj) {
|
|
|
|
$links[] = id(new DoorkeeperTagView())
|
|
|
|
->setExternalObject($xobj);
|
|
|
|
}
|
|
|
|
|
|
|
|
return phutil_implode_html(phutil_tag('br'), $links);
|
|
|
|
}
|
|
|
|
|
2014-02-28 00:15:48 +01:00
|
|
|
private function buildDoorkeeperRefs($value) {
|
2014-02-26 23:46:18 +01:00
|
|
|
$provider = PhabricatorAuthProviderOAuth1JIRA::getJIRAProvider();
|
|
|
|
|
|
|
|
$refs = array();
|
2014-02-28 00:15:48 +01:00
|
|
|
if ($value) {
|
|
|
|
foreach ($value as $jira_key) {
|
2014-02-26 23:46:18 +01:00
|
|
|
$refs[] = id(new DoorkeeperObjectRef())
|
|
|
|
->setApplicationType(DoorkeeperBridgeJIRA::APPTYPE_JIRA)
|
|
|
|
->setApplicationDomain($provider->getProviderDomain())
|
|
|
|
->setObjectType(DoorkeeperBridgeJIRA::OBJTYPE_ISSUE)
|
|
|
|
->setObjectID($jira_key);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return $refs;
|
|
|
|
}
|
|
|
|
|
2014-02-28 00:15:48 +01:00
|
|
|
private function loadDoorkeeperExternalObjects($value) {
|
|
|
|
$refs = $this->buildDoorkeeperRefs($value);
|
2014-02-26 23:46:18 +01:00
|
|
|
if (!$refs) {
|
|
|
|
return array();
|
|
|
|
}
|
|
|
|
|
|
|
|
$xobjs = id(new DoorkeeperExternalObjectQuery())
|
2014-02-27 01:53:42 +01:00
|
|
|
->setViewer($this->getViewer())
|
2014-02-26 23:46:18 +01:00
|
|
|
->withObjectKeys(mpull($refs, 'getObjectKey'))
|
|
|
|
->execute();
|
|
|
|
|
|
|
|
return $xobjs;
|
|
|
|
}
|
|
|
|
|
2014-02-28 00:15:48 +01:00
|
|
|
public function shouldAppearInEditView() {
|
|
|
|
return PhabricatorAuthProviderOAuth1JIRA::getJIRAProvider();
|
|
|
|
}
|
|
|
|
|
|
|
|
public function shouldAppearInApplicationTransactions() {
|
|
|
|
return PhabricatorAuthProviderOAuth1JIRA::getJIRAProvider();
|
|
|
|
}
|
|
|
|
|
|
|
|
public function readValueFromRequest(AphrontRequest $request) {
|
|
|
|
$this->setValue($request->getStrList($this->getFieldKey()));
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function renderEditControl(array $handles) {
|
|
|
|
return id(new AphrontFormTextControl())
|
|
|
|
->setLabel(pht('JIRA Issues'))
|
|
|
|
->setCaption(
|
|
|
|
pht('Example: %s', phutil_tag('tt', array(), 'JIS-3, JIS-9')))
|
|
|
|
->setName($this->getFieldKey())
|
|
|
|
->setValue(implode(', ', nonempty($this->getValue(), array())))
|
|
|
|
->setError($this->error);
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getOldValueForApplicationTransactions() {
|
2014-04-02 21:03:59 +02:00
|
|
|
return array_unique(nonempty($this->getValue(), array()));
|
2014-02-28 00:15:48 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
public function getNewValueForApplicationTransactions() {
|
2014-04-02 21:03:59 +02:00
|
|
|
return array_unique(nonempty($this->getValue(), array()));
|
2014-02-28 00:15:48 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
public function validateApplicationTransactions(
|
|
|
|
PhabricatorApplicationTransactionEditor $editor,
|
|
|
|
$type,
|
|
|
|
array $xactions) {
|
|
|
|
|
|
|
|
$this->error = null;
|
|
|
|
|
|
|
|
$errors = parent::validateApplicationTransactions(
|
|
|
|
$editor,
|
|
|
|
$type,
|
|
|
|
$xactions);
|
|
|
|
|
|
|
|
$transaction = null;
|
|
|
|
foreach ($xactions as $xaction) {
|
2014-04-02 21:03:59 +02:00
|
|
|
$old = $xaction->getOldValue();
|
|
|
|
$new = $xaction->getNewValue();
|
2014-02-28 00:15:48 +01:00
|
|
|
|
2014-04-02 21:03:59 +02:00
|
|
|
$add = array_diff($new, $old);
|
|
|
|
if (!$add) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Only check that the actor can see newly added JIRA refs. You're
|
|
|
|
// allowed to remove refs or make no-op changes even if you aren't
|
|
|
|
// linked to JIRA.
|
|
|
|
|
|
|
|
try {
|
|
|
|
$refs = id(new DoorkeeperImportEngine())
|
|
|
|
->setViewer($this->getViewer())
|
|
|
|
->setRefs($this->buildDoorkeeperRefs($add))
|
|
|
|
->setThrowOnMissingLink(true)
|
|
|
|
->execute();
|
|
|
|
} catch (DoorkeeperMissingLinkException $ex) {
|
|
|
|
$this->error = pht('Not Linked');
|
|
|
|
$errors[] = new PhabricatorApplicationTransactionValidationError(
|
|
|
|
$type,
|
|
|
|
pht('Not Linked'),
|
|
|
|
pht(
|
|
|
|
'You can not add JIRA issues (%s) to this revision because your '.
|
|
|
|
'Phabricator account is not linked to a JIRA account.',
|
|
|
|
implode(', ', $add)),
|
|
|
|
$xaction);
|
|
|
|
continue;
|
|
|
|
}
|
2014-02-28 00:15:48 +01:00
|
|
|
|
|
|
|
$bad = array();
|
|
|
|
foreach ($refs as $ref) {
|
|
|
|
if (!$ref->getIsVisible()) {
|
|
|
|
$bad[] = $ref->getObjectID();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($bad) {
|
|
|
|
$bad = implode(', ', $bad);
|
|
|
|
$this->error = pht('Invalid');
|
|
|
|
|
2014-04-02 21:03:59 +02:00
|
|
|
$errors[] = new PhabricatorApplicationTransactionValidationError(
|
2014-02-28 00:15:48 +01:00
|
|
|
$type,
|
|
|
|
pht('Invalid'),
|
|
|
|
pht(
|
|
|
|
"Some JIRA issues could not be loaded. They may not exist, or ".
|
|
|
|
"you may not have permission to view them: %s",
|
|
|
|
$bad),
|
|
|
|
$xaction);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return $errors;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getApplicationTransactionTitle(
|
|
|
|
PhabricatorApplicationTransaction $xaction) {
|
|
|
|
|
|
|
|
$old = $xaction->getOldValue();
|
|
|
|
if (!is_array($old)) {
|
|
|
|
$old = array();
|
|
|
|
}
|
|
|
|
|
|
|
|
$new = $xaction->getNewValue();
|
|
|
|
if (!is_array($new)) {
|
|
|
|
$new = array();
|
|
|
|
}
|
|
|
|
|
|
|
|
$add = array_diff($new, $old);
|
|
|
|
$rem = array_diff($old, $new);
|
|
|
|
|
|
|
|
$author_phid = $xaction->getAuthorPHID();
|
|
|
|
if ($add && $rem) {
|
|
|
|
return pht(
|
|
|
|
'%s updated JIRA issue(s): added %d %s; removed %d %s.',
|
|
|
|
$xaction->renderHandleLink($author_phid),
|
|
|
|
new PhutilNumber(count($add)),
|
|
|
|
implode(', ', $add),
|
|
|
|
new PhutilNumber(count($rem)),
|
|
|
|
implode(', ', $rem));
|
|
|
|
} else if ($add) {
|
|
|
|
return pht(
|
|
|
|
'%s added %d JIRA issue(s): %s.',
|
|
|
|
$xaction->renderHandleLink($author_phid),
|
|
|
|
new PhutilNumber(count($add)),
|
|
|
|
implode(', ', $add));
|
|
|
|
} else if ($rem) {
|
|
|
|
return pht(
|
|
|
|
'%s removed %d JIRA issue(s): %s.',
|
|
|
|
$xaction->renderHandleLink($author_phid),
|
|
|
|
new PhutilNumber(count($rem)),
|
|
|
|
implode(', ', $rem));
|
|
|
|
}
|
|
|
|
|
|
|
|
return parent::getApplicationTransactionTitle($xaction);
|
|
|
|
}
|
|
|
|
|
|
|
|
public function applyApplicationTransactionExternalEffects(
|
|
|
|
PhabricatorApplicationTransaction $xaction) {
|
|
|
|
|
|
|
|
// Update the CustomField storage.
|
|
|
|
parent::applyApplicationTransactionExternalEffects($xaction);
|
|
|
|
|
|
|
|
// Now, synchronize the Doorkeeper edges.
|
|
|
|
$revision = $this->getObject();
|
|
|
|
$revision_phid = $revision->getPHID();
|
|
|
|
|
|
|
|
$edge_type = PhabricatorEdgeConfig::TYPE_PHOB_HAS_JIRAISSUE;
|
|
|
|
$xobjs = $this->loadDoorkeeperExternalObjects($xaction->getNewValue());
|
|
|
|
$edge_dsts = mpull($xobjs, 'getPHID');
|
|
|
|
|
|
|
|
$edges = PhabricatorEdgeQuery::loadDestinationPHIDs(
|
|
|
|
$revision_phid,
|
|
|
|
$edge_type);
|
|
|
|
|
|
|
|
$editor = id(new PhabricatorEdgeEditor())
|
|
|
|
->setActor($this->getViewer());
|
|
|
|
|
|
|
|
foreach (array_diff($edges, $edge_dsts) as $rem_edge) {
|
|
|
|
$editor->removeEdge($revision_phid, $edge_type, $rem_edge);
|
|
|
|
}
|
|
|
|
|
|
|
|
foreach (array_diff($edge_dsts, $edges) as $add_edge) {
|
|
|
|
$editor->addEdge($revision_phid, $edge_type, $add_edge);
|
|
|
|
}
|
|
|
|
|
|
|
|
$editor->save();
|
|
|
|
}
|
2014-02-27 01:53:42 +01:00
|
|
|
|
2014-03-08 02:05:00 +01:00
|
|
|
public function shouldAppearInCommitMessage() {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function shouldAppearInCommitMessageTemplate() {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getCommitMessageLabels() {
|
|
|
|
return array(
|
|
|
|
'JIRA',
|
|
|
|
'JIRA Issues',
|
|
|
|
'JIRA Issue',
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
public function parseValueFromCommitMessage($value) {
|
|
|
|
return preg_split('/[\s,]+/', $value, $limit = -1, PREG_SPLIT_NO_EMPTY);
|
|
|
|
}
|
|
|
|
|
2014-03-31 18:27:30 +02:00
|
|
|
public function readValueFromCommitMessage($value) {
|
|
|
|
$this->setValue($value);
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2014-03-08 02:05:00 +01:00
|
|
|
public function renderCommitMessageValue(array $handles) {
|
|
|
|
$value = $this->getValue();
|
|
|
|
if (!$value) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
return implode(', ', $value);
|
|
|
|
}
|
|
|
|
|
2014-03-09 19:23:55 +01:00
|
|
|
public function shouldAppearInConduitDictionary() {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2014-03-31 18:27:30 +02:00
|
|
|
|
2014-02-26 23:46:18 +01:00
|
|
|
}
|