PageRenderTime 47ms CodeModel.GetById 16ms RepoModel.GetById 1ms app.codeStats 0ms

/src/applications/differential/customfield/DifferentialJIRAIssuesField.php

http://github.com/facebook/phabricator
PHP | 285 lines | 223 code | 57 blank | 5 comment | 14 complexity | 725ec0f9689a69c9d07e02ca4db11c27 MD5 | raw file
Possible License(s): JSON, MPL-2.0-no-copyleft-exception, Apache-2.0, BSD-3-Clause, LGPL-2.0, MIT, LGPL-2.1, LGPL-3.0
  1. <?php
  2. final class DifferentialJIRAIssuesField
  3. extends DifferentialStoredCustomField {
  4. private $error;
  5. public function getFieldKey() {
  6. return 'phabricator:jira-issues';
  7. }
  8. public function getFieldKeyForConduit() {
  9. return 'jira.issues';
  10. }
  11. public function isFieldEnabled() {
  12. return (bool)PhabricatorJIRAAuthProvider::getJIRAProvider();
  13. }
  14. public function canDisableField() {
  15. return false;
  16. }
  17. public function getValueForStorage() {
  18. return json_encode($this->getValue());
  19. }
  20. public function setValueFromStorage($value) {
  21. try {
  22. $this->setValue(phutil_json_decode($value));
  23. } catch (PhutilJSONParserException $ex) {
  24. $this->setValue(array());
  25. }
  26. return $this;
  27. }
  28. public function getFieldName() {
  29. return pht('JIRA Issues');
  30. }
  31. public function getFieldDescription() {
  32. return pht('Lists associated JIRA issues.');
  33. }
  34. public function shouldAppearInPropertyView() {
  35. return true;
  36. }
  37. public function renderPropertyViewLabel() {
  38. return $this->getFieldName();
  39. }
  40. public function renderPropertyViewValue(array $handles) {
  41. $xobjs = $this->loadDoorkeeperExternalObjects($this->getValue());
  42. if (!$xobjs) {
  43. return null;
  44. }
  45. $links = array();
  46. foreach ($xobjs as $xobj) {
  47. $links[] = id(new DoorkeeperTagView())
  48. ->setExternalObject($xobj);
  49. }
  50. return phutil_implode_html(phutil_tag('br'), $links);
  51. }
  52. private function buildDoorkeeperRefs($value) {
  53. $provider = PhabricatorJIRAAuthProvider::getJIRAProvider();
  54. $refs = array();
  55. if ($value) {
  56. foreach ($value as $jira_key) {
  57. $refs[] = id(new DoorkeeperObjectRef())
  58. ->setApplicationType(DoorkeeperBridgeJIRA::APPTYPE_JIRA)
  59. ->setApplicationDomain($provider->getProviderDomain())
  60. ->setObjectType(DoorkeeperBridgeJIRA::OBJTYPE_ISSUE)
  61. ->setObjectID($jira_key);
  62. }
  63. }
  64. return $refs;
  65. }
  66. private function loadDoorkeeperExternalObjects($value) {
  67. $refs = $this->buildDoorkeeperRefs($value);
  68. if (!$refs) {
  69. return array();
  70. }
  71. $xobjs = id(new DoorkeeperExternalObjectQuery())
  72. ->setViewer($this->getViewer())
  73. ->withObjectKeys(mpull($refs, 'getObjectKey'))
  74. ->execute();
  75. return $xobjs;
  76. }
  77. public function shouldAppearInEditView() {
  78. return PhabricatorJIRAAuthProvider::getJIRAProvider();
  79. }
  80. public function shouldAppearInApplicationTransactions() {
  81. return PhabricatorJIRAAuthProvider::getJIRAProvider();
  82. }
  83. public function readValueFromRequest(AphrontRequest $request) {
  84. $this->setValue($request->getStrList($this->getFieldKey()));
  85. return $this;
  86. }
  87. public function renderEditControl(array $handles) {
  88. return id(new AphrontFormTextControl())
  89. ->setLabel(pht('JIRA Issues'))
  90. ->setCaption(
  91. pht('Example: %s', phutil_tag('tt', array(), 'JIS-3, JIS-9')))
  92. ->setName($this->getFieldKey())
  93. ->setValue(implode(', ', nonempty($this->getValue(), array())))
  94. ->setError($this->error);
  95. }
  96. public function getOldValueForApplicationTransactions() {
  97. return array_unique(nonempty($this->getValue(), array()));
  98. }
  99. public function getNewValueForApplicationTransactions() {
  100. return array_unique(nonempty($this->getValue(), array()));
  101. }
  102. public function validateApplicationTransactions(
  103. PhabricatorApplicationTransactionEditor $editor,
  104. $type,
  105. array $xactions) {
  106. $this->error = null;
  107. $errors = parent::validateApplicationTransactions(
  108. $editor,
  109. $type,
  110. $xactions);
  111. $transaction = null;
  112. foreach ($xactions as $xaction) {
  113. $old = $xaction->getOldValue();
  114. $new = $xaction->getNewValue();
  115. $add = array_diff($new, $old);
  116. if (!$add) {
  117. continue;
  118. }
  119. // Only check that the actor can see newly added JIRA refs. You're
  120. // allowed to remove refs or make no-op changes even if you aren't
  121. // linked to JIRA.
  122. try {
  123. $refs = id(new DoorkeeperImportEngine())
  124. ->setViewer($this->getViewer())
  125. ->setRefs($this->buildDoorkeeperRefs($add))
  126. ->setThrowOnMissingLink(true)
  127. ->execute();
  128. } catch (DoorkeeperMissingLinkException $ex) {
  129. $this->error = pht('Not Linked');
  130. $errors[] = new PhabricatorApplicationTransactionValidationError(
  131. $type,
  132. pht('Not Linked'),
  133. pht(
  134. 'You can not add JIRA issues (%s) to this revision because your '.
  135. 'Phabricator account is not linked to a JIRA account.',
  136. implode(', ', $add)),
  137. $xaction);
  138. continue;
  139. }
  140. $bad = array();
  141. foreach ($refs as $ref) {
  142. if (!$ref->getIsVisible()) {
  143. $bad[] = $ref->getObjectID();
  144. }
  145. }
  146. if ($bad) {
  147. $bad = implode(', ', $bad);
  148. $this->error = pht('Invalid');
  149. $errors[] = new PhabricatorApplicationTransactionValidationError(
  150. $type,
  151. pht('Invalid'),
  152. pht(
  153. 'Some JIRA issues could not be loaded. They may not exist, or '.
  154. 'you may not have permission to view them: %s',
  155. $bad),
  156. $xaction);
  157. }
  158. }
  159. return $errors;
  160. }
  161. public function getApplicationTransactionTitle(
  162. PhabricatorApplicationTransaction $xaction) {
  163. $old = $xaction->getOldValue();
  164. if (!is_array($old)) {
  165. $old = array();
  166. }
  167. $new = $xaction->getNewValue();
  168. if (!is_array($new)) {
  169. $new = array();
  170. }
  171. $add = array_diff($new, $old);
  172. $rem = array_diff($old, $new);
  173. $author_phid = $xaction->getAuthorPHID();
  174. if ($add && $rem) {
  175. return pht(
  176. '%s updated JIRA issue(s): added %d %s; removed %d %s.',
  177. $xaction->renderHandleLink($author_phid),
  178. phutil_count($add),
  179. implode(', ', $add),
  180. phutil_count($rem),
  181. implode(', ', $rem));
  182. } else if ($add) {
  183. return pht(
  184. '%s added %d JIRA issue(s): %s.',
  185. $xaction->renderHandleLink($author_phid),
  186. phutil_count($add),
  187. implode(', ', $add));
  188. } else if ($rem) {
  189. return pht(
  190. '%s removed %d JIRA issue(s): %s.',
  191. $xaction->renderHandleLink($author_phid),
  192. phutil_count($rem),
  193. implode(', ', $rem));
  194. }
  195. return parent::getApplicationTransactionTitle($xaction);
  196. }
  197. public function applyApplicationTransactionExternalEffects(
  198. PhabricatorApplicationTransaction $xaction) {
  199. // Update the CustomField storage.
  200. parent::applyApplicationTransactionExternalEffects($xaction);
  201. // Now, synchronize the Doorkeeper edges.
  202. $revision = $this->getObject();
  203. $revision_phid = $revision->getPHID();
  204. $edge_type = PhabricatorJiraIssueHasObjectEdgeType::EDGECONST;
  205. $xobjs = $this->loadDoorkeeperExternalObjects($xaction->getNewValue());
  206. $edge_dsts = mpull($xobjs, 'getPHID');
  207. $edges = PhabricatorEdgeQuery::loadDestinationPHIDs(
  208. $revision_phid,
  209. $edge_type);
  210. $editor = new PhabricatorEdgeEditor();
  211. foreach (array_diff($edges, $edge_dsts) as $rem_edge) {
  212. $editor->removeEdge($revision_phid, $edge_type, $rem_edge);
  213. }
  214. foreach (array_diff($edge_dsts, $edges) as $add_edge) {
  215. $editor->addEdge($revision_phid, $edge_type, $add_edge);
  216. }
  217. $editor->save();
  218. }
  219. public function shouldAppearInConduitDictionary() {
  220. return true;
  221. }
  222. public function shouldAppearInConduitTransactions() {
  223. return true;
  224. }
  225. protected function newConduitEditParameterType() {
  226. return new ConduitStringListParameterType();
  227. }
  228. }