PageRenderTime 28ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/src/applications/maniphest/controller/transactionsave/ManiphestTransactionSaveController.php

http://github.com/facebook/phabricator
PHP | 261 lines | 175 code | 33 blank | 53 comment | 29 complexity | db857484e43ff67e549f1bb451e4a5bb 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. /*
  3. * Copyright 2011 Facebook, Inc.
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. /**
  18. * @group maniphest
  19. */
  20. class ManiphestTransactionSaveController extends ManiphestController {
  21. public function processRequest() {
  22. $request = $this->getRequest();
  23. $user = $request->getUser();
  24. $task = id(new ManiphestTask())->load($request->getStr('taskID'));
  25. if (!$task) {
  26. return new Aphront404Response();
  27. }
  28. $transactions = array();
  29. $action = $request->getStr('action');
  30. // If we have drag-and-dropped files, attach them first in a separate
  31. // transaction. These can come in on any transaction type, which is why we
  32. // handle them separately.
  33. $files = array();
  34. // Look for drag-and-drop uploads first.
  35. $file_phids = $request->getArr('files');
  36. if ($file_phids) {
  37. $files = id(new PhabricatorFile())->loadAllWhere(
  38. 'phid in (%Ls)',
  39. $file_phids);
  40. }
  41. // This means "attach a file" even though we store other types of data
  42. // as 'attached'.
  43. if ($action == ManiphestTransactionType::TYPE_ATTACH) {
  44. if (!empty($_FILES['file'])) {
  45. $err = idx($_FILES['file'], 'error');
  46. if ($err != UPLOAD_ERR_NO_FILE) {
  47. $file = PhabricatorFile::newFromPHPUpload(
  48. $_FILES['file'],
  49. array(
  50. 'authorPHID' => $user->getPHID(),
  51. ));
  52. $files[] = $file;
  53. }
  54. }
  55. }
  56. // If we had explicit or drag-and-drop files, create a transaction
  57. // for those before we deal with whatever else might have happened.
  58. $file_transaction = null;
  59. if ($files) {
  60. $files = mpull($files, 'getPHID', 'getPHID');
  61. $new = $task->getAttached();
  62. foreach ($files as $phid) {
  63. if (empty($new[PhabricatorPHIDConstants::PHID_TYPE_FILE])) {
  64. $new[PhabricatorPHIDConstants::PHID_TYPE_FILE] = array();
  65. }
  66. $new[PhabricatorPHIDConstants::PHID_TYPE_FILE][$phid] = array();
  67. }
  68. $transaction = new ManiphestTransaction();
  69. $transaction
  70. ->setAuthorPHID($user->getPHID())
  71. ->setTransactionType(ManiphestTransactionType::TYPE_ATTACH);
  72. $transaction->setNewValue($new);
  73. $transactions[] = $transaction;
  74. $file_transaction = $transaction;
  75. }
  76. // Compute new CCs added by @mentions. Several things can cause CCs to
  77. // be added as side effects: mentions, explicit CCs, users who aren't
  78. // CC'd interacting with the task, and ownership changes. We build up a
  79. // list of all the CCs and then construct a transaction for them at the
  80. // end if necessary.
  81. $added_ccs = PhabricatorMarkupEngine::extractPHIDsFromMentions(
  82. array(
  83. $request->getStr('comments'),
  84. ));
  85. $cc_transaction = new ManiphestTransaction();
  86. $cc_transaction
  87. ->setAuthorPHID($user->getPHID())
  88. ->setTransactionType(ManiphestTransactionType::TYPE_CCS);
  89. $force_cc_transaction = false;
  90. $transaction = new ManiphestTransaction();
  91. $transaction
  92. ->setAuthorPHID($user->getPHID())
  93. ->setComments($request->getStr('comments'))
  94. ->setTransactionType($action);
  95. switch ($action) {
  96. case ManiphestTransactionType::TYPE_STATUS:
  97. $transaction->setNewValue($request->getStr('resolution'));
  98. break;
  99. case ManiphestTransactionType::TYPE_OWNER:
  100. $assign_to = $request->getArr('assign_to');
  101. $assign_to = reset($assign_to);
  102. $transaction->setNewValue($assign_to);
  103. break;
  104. case ManiphestTransactionType::TYPE_PROJECTS:
  105. $projects = $request->getArr('projects');
  106. $projects = array_merge($projects, $task->getProjectPHIDs());
  107. $projects = array_filter($projects);
  108. $projects = array_unique($projects);
  109. $transaction->setNewValue($projects);
  110. break;
  111. case ManiphestTransactionType::TYPE_CCS:
  112. // Accumulate the new explicit CCs into the array that we'll add in
  113. // the CC transaction later.
  114. $added_ccs = array_merge($added_ccs, $request->getArr('ccs'));
  115. // Transfer any comments over to the CC transaction.
  116. $cc_transaction->setComments($transaction->getComments());
  117. // Make sure we include this transaction, even if the user didn't
  118. // actually add any CC's, because we'll discard their comment otherwise.
  119. $force_cc_transaction = true;
  120. // Throw away the primary transaction.
  121. $transaction = null;
  122. break;
  123. case ManiphestTransactionType::TYPE_PRIORITY:
  124. $transaction->setNewValue($request->getInt('priority'));
  125. break;
  126. case ManiphestTransactionType::TYPE_NONE:
  127. case ManiphestTransactionType::TYPE_ATTACH:
  128. // If we have a file transaction, just get rid of this secondary
  129. // transaction and put the comments on it instead.
  130. if ($file_transaction) {
  131. $file_transaction->setComments($transaction->getComments());
  132. $transaction = null;
  133. }
  134. break;
  135. default:
  136. throw new Exception('unknown action');
  137. }
  138. if ($transaction) {
  139. $transactions[] = $transaction;
  140. }
  141. // When you interact with a task, we add you to the CC list so you get
  142. // further updates, and possibly assign the task to you if you took an
  143. // ownership action (closing it) but it's currently unowned. We also move
  144. // previous owners to CC if ownership changes. Detect all these conditions
  145. // and create side-effect transactions for them.
  146. $implicitly_claimed = false;
  147. switch ($action) {
  148. case ManiphestTransactionType::TYPE_OWNER:
  149. if ($task->getOwnerPHID() == $transaction->getNewValue()) {
  150. // If this is actually no-op, don't generate the side effect.
  151. break;
  152. }
  153. // Otherwise, when a task is reassigned, move the previous owner to CC.
  154. $added_ccs[] = $task->getOwnerPHID();
  155. break;
  156. case ManiphestTransactionType::TYPE_STATUS:
  157. if (!$task->getOwnerPHID() &&
  158. $request->getStr('resolution') !=
  159. ManiphestTaskStatus::STATUS_OPEN) {
  160. // Closing an unassigned task. Assign the user as the owner of
  161. // this task.
  162. $assign = new ManiphestTransaction();
  163. $assign->setAuthorPHID($user->getPHID());
  164. $assign->setTransactionType(ManiphestTransactionType::TYPE_OWNER);
  165. $assign->setNewValue($user->getPHID());
  166. $transactions[] = $assign;
  167. $implicitly_claimed = true;
  168. }
  169. break;
  170. }
  171. $user_owns_task = false;
  172. if ($implicitly_claimed) {
  173. $user_owns_task = true;
  174. } else {
  175. if ($action == ManiphestTransactionType::TYPE_OWNER) {
  176. if ($transaction->getNewValue() == $user->getPHID()) {
  177. $user_owns_task = true;
  178. }
  179. } else if ($task->getOwnerPHID() == $user->getPHID()) {
  180. $user_owns_task = true;
  181. }
  182. }
  183. if (!$user_owns_task) {
  184. // If we aren't making the user the new task owner and they aren't the
  185. // existing task owner, add them to CC.
  186. $added_ccs[] = $user->getPHID();
  187. }
  188. if ($added_ccs || $force_cc_transaction) {
  189. // We've added CCs, so include a CC transaction. It's safe to do this even
  190. // if we're just "adding" CCs which already exist, because the
  191. // ManiphestTransactionEditor is smart enough to ignore them.
  192. $all_ccs = array_merge($task->getCCPHIDs(), $added_ccs);
  193. $cc_transaction->setNewValue($all_ccs);
  194. $transactions[] = $cc_transaction;
  195. }
  196. $content_source = PhabricatorContentSource::newForSource(
  197. PhabricatorContentSource::SOURCE_WEB,
  198. array(
  199. 'ip' => $request->getRemoteAddr(),
  200. ));
  201. foreach ($transactions as $transaction) {
  202. $transaction->setContentSource($content_source);
  203. }
  204. $event = new PhabricatorEvent(
  205. PhabricatorEventType::TYPE_MANIPHEST_WILLEDITTASK,
  206. array(
  207. 'task' => $task,
  208. 'new' => false,
  209. 'transactions' => $transactions,
  210. ));
  211. $event->setUser($user);
  212. $event->setAphrontRequest($request);
  213. PhutilEventEngine::dispatchEvent($event);
  214. $task = $event->getValue('task');
  215. $transactions = $event->getValue('transactions');
  216. $editor = new ManiphestTransactionEditor();
  217. $editor->applyTransactions($task, $transactions);
  218. $draft = id(new PhabricatorDraft())->loadOneWhere(
  219. 'authorPHID = %s AND draftKey = %s',
  220. $user->getPHID(),
  221. $task->getPHID());
  222. if ($draft) {
  223. $draft->delete();
  224. }
  225. return id(new AphrontRedirectResponse())
  226. ->setURI('/T'.$task->getID());
  227. }
  228. }