diff --git a/src/applications/maniphest/controller/taskdetail/ManiphestTaskDetailController.php b/src/applications/maniphest/controller/taskdetail/ManiphestTaskDetailController.php index 9cc754185..08fec3140 100644 --- a/src/applications/maniphest/controller/taskdetail/ManiphestTaskDetailController.php +++ b/src/applications/maniphest/controller/taskdetail/ManiphestTaskDetailController.php @@ -1,235 +1,240 @@ <?php /* * Copyright 2011 Facebook, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ class ManiphestTaskDetailController extends ManiphestController { private $id; public function willProcessRequest(array $data) { $this->id = $data['id']; } public function processRequest() { $request = $this->getRequest(); $user = $request->getUser(); $e_title = null; $priority_map = ManiphestTaskPriority::getTaskPriorityMap(); $task = id(new ManiphestTask())->load($this->id); $transactions = id(new ManiphestTransaction())->loadAllWhere( 'taskID = %d', $task->getID()); $phids = array(); foreach ($transactions as $transaction) { foreach ($transaction->extractPHIDs() as $phid) { $phids[$phid] = true; } } foreach ($task->getCCPHIDs() as $phid) { $phids[$phid] = true; } if ($task->getOwnerPHID()) { $phids[$task->getOwnerPHID()] = true; } $phids[$task->getAuthorPHID()] = true; $phids = array_keys($phids); $handles = id(new PhabricatorObjectHandleData($phids)) ->loadHandles(); $factory = new DifferentialMarkupEngineFactory(); $engine = $factory->newDifferentialCommentMarkupEngine(); $dict = array(); $dict['Status'] = '<strong>'. ManiphestTaskStatus::getTaskStatusFullName($task->getStatus()). '</strong>'; $dict['Assigned To'] = $task->getOwnerPHID() ? $handles[$task->getOwnerPHID()]->renderLink() : '<em>None</em>'; $dict['Priority'] = ManiphestTaskPriority::getTaskPriorityName( $task->getPriority()); $cc = $task->getCCPHIDs(); if ($cc) { $cc_links = array(); foreach ($cc as $phid) { $cc_links[] = $handles[$phid]->renderLink(); } $dict['CC'] = implode(', ', $cc_links); } else { $dict['CC'] = '<em>None</em>'; } $dict['Author'] = $handles[$task->getAuthorPHID()]->renderLink(); - $dict['Description'] = $engine->markupText($task->getDescription()); + $dict['Description'] = + '<div class="maniphest-task-description">'. + '<div class="phabricator-remarkup">'. + $engine->markupText($task->getDescription()). + '</div>'. + '</div>'; require_celerity_resource('mainphest-task-detail-css'); $table = array(); foreach ($dict as $key => $value) { $table[] = '<tr>'. '<th>'.phutil_escape_html($key).':</th>'. '<td>'.$value.'</td>'. '</tr>'; } $table = '<table class="maniphest-task-properties">'. implode("\n", $table). '</table>'; $panel = '<div class="maniphest-panel">'. '<div class="maniphest-task-detail-core">'. '<h1>'. phutil_escape_html('T'.$task->getID().' '.$task->getTitle()). '</h1>'. $table. '</div>'. '</div>'; $transaction_types = ManiphestTransactionType::getTransactionTypeMap(); $resolution_types = ManiphestTaskStatus::getTaskStatusMap(); if ($task->getStatus() == ManiphestTaskStatus::STATUS_OPEN) { $resolution_types = array_select_keys( $resolution_types, array( ManiphestTaskStatus::STATUS_CLOSED_RESOLVED, ManiphestTaskStatus::STATUS_CLOSED_WONTFIX, ManiphestTaskStatus::STATUS_CLOSED_INVALID, ManiphestTaskStatus::STATUS_CLOSED_SPITE, )); } else { $resolution_types = array( ManiphestTaskStatus::STATUS_OPEN => 'Reopened', ); $transaction_types[ManiphestTransactionType::TYPE_STATUS] = 'Reopen Task'; unset($transaction_types[ManiphestTransactionType::TYPE_PRIORITY]); unset($transaction_types[ManiphestTransactionType::TYPE_OWNER]); } $default_claim = array( $user->getPHID() => $user->getUsername().' ('.$user->getRealName().')', ); $comment_form = new AphrontFormView(); $comment_form ->setUser($user) ->setAction('/maniphest/transaction/save/') ->addHiddenInput('taskID', $task->getID()) ->appendChild( id(new AphrontFormSelectControl()) ->setLabel('Action') ->setName('action') ->setOptions($transaction_types) ->setID('transaction-action')) ->appendChild( id(new AphrontFormSelectControl()) ->setLabel('Resolution') ->setName('resolution') ->setControlID('resolution') ->setControlStyle('display: none') ->setOptions($resolution_types)) ->appendChild( id(new AphrontFormTokenizerControl()) ->setLabel('Assign To') ->setName('assign_to') ->setControlID('assign_to') ->setControlStyle('display: none') ->setID('assign-tokenizer') ->setDisableBehavior(true)) ->appendChild( id(new AphrontFormTokenizerControl()) ->setLabel('CCs') ->setName('ccs') ->setControlID('ccs') ->setControlStyle('display: none') ->setID('cc-tokenizer') ->setDisableBehavior(true)) ->appendChild( id(new AphrontFormSelectControl()) ->setLabel('Priority') ->setName('priority') ->setOptions($priority_map) ->setControlID('priority') ->setControlStyle('display: none') ->setValue($task->getPriority())) ->appendChild( id(new AphrontFormTextAreaControl()) ->setLabel('Comments') ->setName('comments') ->setValue('')) ->appendChild( id(new AphrontFormSubmitControl()) ->setValue('Avast!')); Javelin::initBehavior('maniphest-transaction-controls', array( 'select' => 'transaction-action', 'controlMap' => array( ManiphestTransactionType::TYPE_STATUS => 'resolution', ManiphestTransactionType::TYPE_OWNER => 'assign_to', ManiphestTransactionType::TYPE_CCS => 'ccs', ManiphestTransactionType::TYPE_PRIORITY => 'priority', ), 'tokenizers' => array( ManiphestTransactionType::TYPE_OWNER => array( 'id' => 'assign-tokenizer', 'src' => '/typeahead/common/users/', 'value' => $default_claim, 'limit' => 1, ), ManiphestTransactionType::TYPE_CCS => array( 'id' => 'cc-tokenizer', 'src' => '/typeahead/common/mailable/', ), ), )); $comment_panel = new AphrontPanelView(); $comment_panel->appendChild($comment_form); $comment_panel->setHeader('Leap Into Action'); $transaction_view = new ManiphestTransactionListView(); $transaction_view->setTransactions($transactions); $transaction_view->setHandles($handles); $transaction_view->setUser($user); $transaction_view->setMarkupEngine($engine); return $this->buildStandardPageResponse( array( $panel, $transaction_view, $comment_panel, ), array( 'title' => 'Create Task', )); } } diff --git a/src/applications/maniphest/view/transactiondetail/ManiphestTransactionDetailView.php b/src/applications/maniphest/view/transactiondetail/ManiphestTransactionDetailView.php index 69cf33507..1ffc2ecb3 100644 --- a/src/applications/maniphest/view/transactiondetail/ManiphestTransactionDetailView.php +++ b/src/applications/maniphest/view/transactiondetail/ManiphestTransactionDetailView.php @@ -1,249 +1,247 @@ <?php /* * Copyright 2011 Facebook, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ class ManiphestTransactionDetailView extends AphrontView { private $transactions; private $handles; private $markupEngine; private $forEmail; public function setTransactionGroup(array $transactions) { $this->transactions = $transactions; return $this; } public function setHandles(array $handles) { $this->handles = $handles; return $this; } public function setMarkupEngine(PhutilMarkupEngine $engine) { $this->markupEngine = $engine; return $this; } public function renderForEmail($with_date) { $this->forEmail = true; $transaction = reset($this->transactions); $author = $this->renderHandles(array($transaction->getAuthorPHID())); $action = null; $descs = array(); $comments = null; foreach ($this->transactions as $transaction) { list($verb, $desc, $classes) = $this->describeAction($transaction); if ($action === null) { $action = $verb; } $desc = $author.' '.$desc.'.'; if ($with_date) { $desc = 'On '.date('M jS \a\t g:i A', $transaction->getDateCreated()). ', '.$desc; } $descs[] = $desc; if ($transaction->hasComments()) { $comments = $transaction->getComments(); } } $descs = implode("\n", $descs); if ($comments) { $descs .= "\n".$comments; } $this->forEmail = false; return array($action, $descs); } public function render() { $handles = $this->handles; $transactions = $this->transactions; require_celerity_resource('maniphest-transaction-detail-css'); foreach ($this->transactions as $transaction) { if ($transaction->hasComments()) { break; } } $author = $this->handles[$transaction->getAuthorPHID()]; $comments = $transaction->getCache(); if (!strlen($comments)) { $comments = $transaction->getComments(); if (strlen($comments)) { $comments = $this->markupEngine->markupText($comments); $transaction->setCache($comments); $transaction->save(); } } $more_classes = array(); $descs = array(); foreach ($transactions as $transaction) { list($verb, $desc, $classes) = $this->describeAction($transaction); $more_classes = array_merge($more_classes, $classes); $descs[] = $author->renderLink().' '.$desc.'.'; } $descs = implode('<br />', $descs); $more_classes = implode(' ', $classes); if ($transaction->hasComments()) { $comment_block = '<div class="maniphest-transaction-comments phabricator-remarkup">'. $comments. '</div>'; } else { $comment_block = null; } return phutil_render_tag( 'div', array( 'class' => "maniphest-transaction-detail-container", 'style' => "background-image: url('".$author->getImageURI()."')", ), '<div class="maniphest-transaction-detail-view '.$more_classes.'">'. '<div class="maniphest-transaction-header">'. '<div class="maniphest-transaction-timestamp">'. phabricator_format_timestamp($transaction->getDateCreated()). '</div>'. - '<strong>'. - $descs. - '</strong>'. + $descs. '</div>'. $comment_block. '</div>'); } private function describeAction($transaction) { $verb = null; $desc = null; $classes = array(); $handles = $this->handles; $type = $transaction->getTransactionType(); $author_phid = $transaction->getAuthorPHID(); $new = $transaction->getNewValue(); $old = $transaction->getOldValue(); switch ($type) { case ManiphestTransactionType::TYPE_NONE: $verb = 'Commented On'; $desc = 'added a comment'; break; case ManiphestTransactionType::TYPE_OWNER: if ($transaction->getAuthorPHID() == $new) { $verb = 'Claimed'; $desc = 'claimed this task'; } else if (!$new) { $verb = 'Up For Grabs'; $desc = 'placed this task up for grabs'; } else if (!$old) { $verb = 'Assigned'; $desc = 'assigned this task to '.$this->renderHandles(array($new)); } else { $verb = 'Reassigned'; $desc = 'reassigned this task from '. $this->renderHandles(array($old)). ' to '. $this->renderHandles(array($new)); } break; case ManiphestTransactionType::TYPE_CCS: $added = array_diff($new, $old); $removed = array_diff($old, $new); if ($added && !$removed) { $verb = 'Added CC'; if (count($added) == 1) { $desc = 'added '.$this->renderHandles($added).' to CC'; } else { $desc = 'added CCs: '.$this->renderHandles($added); } } else if ($removed && !$added) { $verb = 'Removed CC'; if (count($removed) == 1) { $desc = 'removed '.$this->renderHandles($removed).' from CC'; } else { $desc = 'removed CCs: '.$this->renderHandles($removed); } } else { $verb = 'Changed CC'; $desc = 'changed CCs, added: '.$this->renderHandles($added).'; '. 'removed: '.$this->renderHandles($removed); } break; case ManiphestTransactionType::TYPE_STATUS: if ($new == ManiphestTaskStatus::STATUS_OPEN) { if ($old) { $verb = 'Reopened'; $desc = 'reopened this task'; } else { $verb = 'Created'; $desc = 'created this task'; } } else if ($new == ManiphestTaskStatus::STATUS_CLOSED_SPITE) { $verb = 'Spited'; $desc = 'closed this task out of spite'; } else { $verb = 'Closed'; $full = idx(ManiphestTaskStatus::getTaskStatusMap(), $new, '???'); $desc = 'closed this task as "'.$full.'"'; } break; case ManiphestTransactionType::TYPE_PRIORITY: $old_name = ManiphestTaskPriority::getTaskPriorityName($old); $new_name = ManiphestTaskPriority::getTaskPriorityName($new); if ($old == ManiphestTaskPriority::PRIORITY_TRIAGE) { $verb = 'Triaged'; $desc = 'triaged this task as "'.$new_name.'" priority'; } else if ($old > $new) { $verb = 'Lowered Priority'; $desc = 'lowered the priority of this task from "'.$old_name.'" to '. '"'.$new_name.'"'; } else { $verb = 'Raised Priority'; $desc = 'raised the priority of this task from "'.$old_name.'" to '. '"'.$new_name.'"'; } break; default: return ' brazenly '.$type."'d"; } return array($verb, $desc, $classes); } private function renderHandles($phids) { $links = array(); foreach ($phids as $phid) { if ($this->forEmail) { $links[] = $this->handles[$phid]->getName(); } else { $links[] = $this->handles[$phid]->renderLink(); } } return implode(', ', $links); } } diff --git a/webroot/rsrc/css/application/maniphest/transaction-detail.css b/webroot/rsrc/css/application/maniphest/transaction-detail.css index 9c89bb825..0c0f522ac 100644 --- a/webroot/rsrc/css/application/maniphest/transaction-detail.css +++ b/webroot/rsrc/css/application/maniphest/transaction-detail.css @@ -1,36 +1,42 @@ /** * @provides maniphest-transaction-detail-css */ .maniphest-transaction-detail-container { margin: 2px 1em 3px; background: 0px 0px no-repeat; min-height: 50px; } .maniphest-transaction-header { - background: #e0e0e0; + background: #e6e6e6; padding: 4px 1em; } +.maniphest-transaction-header a { + font-weight: bold; +} + .maniphest-transaction-detail-view { margin-left: 54px; border: 1px solid #bbbbbb; border-width: 1px 0 0; min-height: 40px; } .maniphest-transaction-timestamp { float: right; font-size: 11px; color: #666666; } .maniphest-transaction-comments { padding: 4px 1em; background: #f3f3f3; } + +.maniphest-task-description p, .maniphest-transaction-comments p { margin: .25em 0 .5em; }