public function getValueControlTemplate() { $users_datasource = new PhabricatorPeopleDatasource(); return array('markup' => new AphrontTokenizerTemplateView(), 'uri' => $users_datasource->getDatasourceURI(), 'placeholder' => $users_datasource->getPlaceholderText()); }
protected function renderInput() { $id = $this->getID(); if (!$id) { $id = celerity_generate_unique_node_id(); $this->setID($id); } $viewer = $this->getUser(); if (!$viewer) { throw new PhutilInvalidStateException('setUser'); } // We need to have this if previews render images, since Ajax can not // currently ship JS or CSS. require_celerity_resource('lightbox-attachment-css'); if (!$this->getDisabled()) { Javelin::initBehavior('aphront-drag-and-drop-textarea', array('target' => $id, 'activatedClass' => 'aphront-textarea-drag-and-drop', 'uri' => '/file/dropupload/', 'chunkThreshold' => PhabricatorFileStorageEngine::getChunkThreshold())); } $root_id = celerity_generate_unique_node_id(); $user_datasource = new PhabricatorPeopleDatasource(); $proj_datasource = id(new PhabricatorProjectDatasource())->setParameters(array('autocomplete' => 1)); Javelin::initBehavior('phabricator-remarkup-assist', array('pht' => array('bold text' => pht('bold text'), 'italic text' => pht('italic text'), 'monospaced text' => pht('monospaced text'), 'List Item' => pht('List Item'), 'Quoted Text' => pht('Quoted Text'), 'data' => pht('data'), 'name' => pht('name'), 'URL' => pht('URL')), 'disabled' => $this->getDisabled(), 'rootID' => $root_id, 'autocompleteMap' => (object) array(64 => array('datasourceURI' => $user_datasource->getDatasourceURI(), 'headerIcon' => 'fa-user', 'headerText' => pht('Find User:'******'hintText' => $user_datasource->getPlaceholderText()), 35 => array('datasourceURI' => $proj_datasource->getDatasourceURI(), 'headerIcon' => 'fa-briefcase', 'headerText' => pht('Find Project:'), 'hintText' => $proj_datasource->getPlaceholderText())))); Javelin::initBehavior('phabricator-tooltips', array()); $actions = array('fa-bold' => array('tip' => pht('Bold'), 'nodevice' => true), 'fa-italic' => array('tip' => pht('Italics'), 'nodevice' => true), 'fa-text-width' => array('tip' => pht('Monospaced'), 'nodevice' => true), 'fa-link' => array('tip' => pht('Link'), 'nodevice' => true), array('spacer' => true, 'nodevice' => true), 'fa-list-ul' => array('tip' => pht('Bulleted List'), 'nodevice' => true), 'fa-list-ol' => array('tip' => pht('Numbered List'), 'nodevice' => true), 'fa-code' => array('tip' => pht('Code Block'), 'nodevice' => true), 'fa-quote-right' => array('tip' => pht('Quote'), 'nodevice' => true), 'fa-table' => array('tip' => pht('Table'), 'nodevice' => true), 'fa-cloud-upload' => array('tip' => pht('Upload File'))); $can_use_macros = !$this->disableMacro && function_exists('imagettftext'); if ($can_use_macros) { $can_use_macros = PhabricatorApplication::isClassInstalledForViewer('PhabricatorMacroApplication', $viewer); } if ($can_use_macros) { $actions[] = array('spacer' => true); $actions['fa-meh-o'] = array('tip' => pht('Meme')); } $actions['fa-eye'] = array('tip' => pht('Preview'), 'align' => 'right'); $actions[] = array('spacer' => true, 'align' => 'right'); $actions['fa-life-bouy'] = array('tip' => pht('Help'), 'align' => 'right', 'href' => PhabricatorEnv::getDoclink('Remarkup Reference')); if (!$this->disableFullScreen) { $actions[] = array('spacer' => true, 'align' => 'right'); $actions['fa-arrows-alt'] = array('tip' => pht('Fullscreen Mode'), 'align' => 'right'); } $buttons = array(); foreach ($actions as $action => $spec) { $classes = array(); if (idx($spec, 'align') == 'right') { $classes[] = 'remarkup-assist-right'; } if (idx($spec, 'nodevice')) { $classes[] = 'remarkup-assist-nodevice'; } if (idx($spec, 'spacer')) { $classes[] = 'remarkup-assist-separator'; $buttons[] = phutil_tag('span', array('class' => implode(' ', $classes)), ''); continue; } else { $classes[] = 'remarkup-assist-button'; } $href = idx($spec, 'href', '#'); if ($href == '#') { $meta = array('action' => $action); $mustcapture = true; $target = null; } else { $meta = array(); $mustcapture = null; $target = '_blank'; } $content = null; $tip = idx($spec, 'tip'); if ($tip) { $meta['tip'] = $tip; $content = javelin_tag('span', array('aural' => true), $tip); } $sigils = array(); $sigils[] = 'remarkup-assist'; if (!$this->getDisabled()) { $sigils[] = 'has-tooltip'; } $buttons[] = javelin_tag('a', array('class' => implode(' ', $classes), 'href' => $href, 'sigil' => implode(' ', $sigils), 'meta' => $meta, 'mustcapture' => $mustcapture, 'target' => $target, 'tabindex' => -1), phutil_tag('div', array('class' => 'remarkup-assist phui-icon-view phui-font-fa bluegrey ' . $action), $content)); } $buttons = phutil_tag('div', array('class' => 'remarkup-assist-bar'), $buttons); $use_monospaced = $viewer->compareUserSetting(PhabricatorMonospacedTextareasSetting::SETTINGKEY, PhabricatorMonospacedTextareasSetting::VALUE_TEXT_MONOSPACED); if ($use_monospaced) { $monospaced_textareas_class = 'PhabricatorMonospaced'; } else { $monospaced_textareas_class = null; } $this->setCustomClass('remarkup-assist-textarea ' . $monospaced_textareas_class); return javelin_tag('div', array('sigil' => 'remarkup-assist-control', 'class' => $this->getDisabled() ? 'disabled-control' : null, 'id' => $root_id), array($buttons, parent::renderInput())); }
public function processRequest() { $request = $this->getRequest(); $user = $request->getUser(); $e_title = null; $priority_map = ManiphestTaskPriority::getTaskPriorityMap(); $task = id(new ManiphestTaskQuery())->setViewer($user)->withIDs(array($this->id))->executeOne(); if (!$task) { return new Aphront404Response(); } $workflow = $request->getStr('workflow'); $parent_task = null; if ($workflow && is_numeric($workflow)) { $parent_task = id(new ManiphestTaskQuery())->setViewer($user)->withIDs(array($workflow))->executeOne(); } $transactions = id(new ManiphestTransactionQuery())->setViewer($user)->withObjectPHIDs(array($task->getPHID()))->needComments(true)->execute(); $field_list = PhabricatorCustomField::getObjectFields($task, PhabricatorCustomField::ROLE_VIEW); $field_list->setViewer($user)->readFieldsFromStorage($task); $e_commit = ManiphestTaskHasCommitEdgeType::EDGECONST; $e_dep_on = PhabricatorEdgeConfig::TYPE_TASK_DEPENDS_ON_TASK; $e_dep_by = PhabricatorEdgeConfig::TYPE_TASK_DEPENDED_ON_BY_TASK; $e_rev = ManiphestTaskHasRevisionEdgeType::EDGECONST; $e_mock = PhabricatorEdgeConfig::TYPE_TASK_HAS_MOCK; $phid = $task->getPHID(); $query = id(new PhabricatorEdgeQuery())->withSourcePHIDs(array($phid))->withEdgeTypes(array($e_commit, $e_dep_on, $e_dep_by, $e_rev, $e_mock)); $edges = idx($query->execute(), $phid); $phids = array_fill_keys($query->getDestinationPHIDs(), true); foreach ($task->getCCPHIDs() as $phid) { $phids[$phid] = true; } foreach ($task->getProjectPHIDs() as $phid) { $phids[$phid] = true; } if ($task->getOwnerPHID()) { $phids[$task->getOwnerPHID()] = true; } $phids[$task->getAuthorPHID()] = true; $attached = $task->getAttached(); foreach ($attached as $type => $list) { foreach ($list as $phid => $info) { $phids[$phid] = true; } } if ($parent_task) { $phids[$parent_task->getPHID()] = true; } $phids = array_keys($phids); $this->loadHandles($phids); $handles = $this->getLoadedHandles(); $context_bar = null; if ($parent_task) { $context_bar = new AphrontContextBarView(); $context_bar->addButton(phutil_tag('a', array('href' => '/maniphest/task/create/?parent=' . $parent_task->getID(), 'class' => 'green button'), pht('Create Another Subtask'))); $context_bar->appendChild(hsprintf('Created a subtask of <strong>%s</strong>', $this->getHandle($parent_task->getPHID())->renderLink())); } else { if ($workflow == 'create') { $context_bar = new AphrontContextBarView(); $context_bar->addButton(phutil_tag('label', array(), 'Create Another')); $context_bar->addButton(phutil_tag('a', array('href' => '/maniphest/task/create/?template=' . $task->getID(), 'class' => 'green button'), pht('Similar Task'))); $context_bar->addButton(phutil_tag('a', array('href' => '/maniphest/task/create/', 'class' => 'green button'), pht('Empty Task'))); $context_bar->appendChild(pht('New task created.')); } } $engine = new PhabricatorMarkupEngine(); $engine->setViewer($user); $engine->addObject($task, ManiphestTask::MARKUP_FIELD_DESCRIPTION); foreach ($transactions as $modern_xaction) { if ($modern_xaction->getComment()) { $engine->addObject($modern_xaction->getComment(), PhabricatorApplicationTransactionComment::MARKUP_FIELD_COMMENT); } } $engine->process(); $resolution_types = ManiphestTaskStatus::getTaskStatusMap(); $transaction_types = array(PhabricatorTransactions::TYPE_COMMENT => pht('Comment'), ManiphestTransaction::TYPE_STATUS => pht('Change Status'), ManiphestTransaction::TYPE_OWNER => pht('Reassign / Claim'), ManiphestTransaction::TYPE_CCS => pht('Add CCs'), ManiphestTransaction::TYPE_PRIORITY => pht('Change Priority'), ManiphestTransaction::TYPE_PROJECTS => pht('Associate Projects')); // Remove actions the user doesn't have permission to take. $requires = array(ManiphestTransaction::TYPE_OWNER => ManiphestEditAssignCapability::CAPABILITY, ManiphestTransaction::TYPE_PRIORITY => ManiphestEditPriorityCapability::CAPABILITY, ManiphestTransaction::TYPE_PROJECTS => ManiphestEditProjectsCapability::CAPABILITY, ManiphestTransaction::TYPE_STATUS => ManiphestEditStatusCapability::CAPABILITY); foreach ($transaction_types as $type => $name) { if (isset($requires[$type])) { if (!$this->hasApplicationCapability($requires[$type])) { unset($transaction_types[$type]); } } } // Don't show an option to change to the current status, or to change to // the duplicate status explicitly. unset($resolution_types[$task->getStatus()]); unset($resolution_types[ManiphestTaskStatus::getDuplicateStatus()]); // Don't show owner/priority changes for closed tasks, as they don't make // much sense. if ($task->isClosed()) { unset($transaction_types[ManiphestTransaction::TYPE_PRIORITY]); unset($transaction_types[ManiphestTransaction::TYPE_OWNER]); } $default_claim = array($user->getPHID() => $user->getUsername() . ' (' . $user->getRealName() . ')'); $draft = id(new PhabricatorDraft())->loadOneWhere('authorPHID = %s AND draftKey = %s', $user->getPHID(), $task->getPHID()); if ($draft) { $draft_text = $draft->getDraft(); } else { $draft_text = null; } $comment_form = new AphrontFormView(); $comment_form->setUser($user)->setWorkflow(true)->setAction('/maniphest/transaction/save/')->setEncType('multipart/form-data')->addHiddenInput('taskID', $task->getID())->appendChild(id(new AphrontFormSelectControl())->setLabel(pht('Action'))->setName('action')->setOptions($transaction_types)->setID('transaction-action'))->appendChild(id(new AphrontFormSelectControl())->setLabel(pht('Status'))->setName('resolution')->setControlID('resolution')->setControlStyle('display: none')->setOptions($resolution_types))->appendChild(id(new AphrontFormTokenizerControl())->setLabel(pht('Assign To'))->setName('assign_to')->setControlID('assign_to')->setControlStyle('display: none')->setID('assign-tokenizer')->setDisableBehavior(true))->appendChild(id(new AphrontFormTokenizerControl())->setLabel(pht('CCs'))->setName('ccs')->setControlID('ccs')->setControlStyle('display: none')->setID('cc-tokenizer')->setDisableBehavior(true))->appendChild(id(new AphrontFormSelectControl())->setLabel(pht('Priority'))->setName('priority')->setOptions($priority_map)->setControlID('priority')->setControlStyle('display: none')->setValue($task->getPriority()))->appendChild(id(new AphrontFormTokenizerControl())->setLabel(pht('Projects'))->setName('projects')->setControlID('projects')->setControlStyle('display: none')->setID('projects-tokenizer')->setDisableBehavior(true))->appendChild(id(new AphrontFormFileControl())->setLabel(pht('File'))->setName('file')->setControlID('file')->setControlStyle('display: none'))->appendChild(id(new PhabricatorRemarkupControl())->setLabel(pht('Comments'))->setName('comments')->setValue($draft_text)->setID('transaction-comments')->setUser($user))->appendChild(id(new AphrontFormSubmitControl())->setValue(pht('Submit'))); $control_map = array(ManiphestTransaction::TYPE_STATUS => 'resolution', ManiphestTransaction::TYPE_OWNER => 'assign_to', ManiphestTransaction::TYPE_CCS => 'ccs', ManiphestTransaction::TYPE_PRIORITY => 'priority', ManiphestTransaction::TYPE_PROJECTS => 'projects'); $projects_source = new PhabricatorProjectDatasource(); $users_source = new PhabricatorPeopleDatasource(); $mailable_source = new PhabricatorMetaMTAMailableDatasource(); $tokenizer_map = array(ManiphestTransaction::TYPE_PROJECTS => array('id' => 'projects-tokenizer', 'src' => $projects_source->getDatasourceURI(), 'placeholder' => $projects_source->getPlaceholderText()), ManiphestTransaction::TYPE_OWNER => array('id' => 'assign-tokenizer', 'src' => $users_source->getDatasourceURI(), 'value' => $default_claim, 'limit' => 1, 'placeholder' => $users_source->getPlaceholderText()), ManiphestTransaction::TYPE_CCS => array('id' => 'cc-tokenizer', 'src' => $mailable_source->getDatasourceURI(), 'placeholder' => $mailable_source->getPlaceholderText())); // TODO: Initializing these behaviors for logged out users fatals things. if ($user->isLoggedIn()) { Javelin::initBehavior('maniphest-transaction-controls', array('select' => 'transaction-action', 'controlMap' => $control_map, 'tokenizers' => $tokenizer_map)); Javelin::initBehavior('maniphest-transaction-preview', array('uri' => '/maniphest/transaction/preview/' . $task->getID() . '/', 'preview' => 'transaction-preview', 'comments' => 'transaction-comments', 'action' => 'transaction-action', 'map' => $control_map, 'tokenizers' => $tokenizer_map)); } $is_serious = PhabricatorEnv::getEnvConfig('phabricator.serious-business'); $comment_header = $is_serious ? pht('Add Comment') : pht('Weigh In'); $preview_panel = phutil_tag_div('aphront-panel-preview', phutil_tag('div', array('id' => 'transaction-preview'), phutil_tag_div('aphront-panel-preview-loading-text', pht('Loading preview...')))); $timeline = id(new PhabricatorApplicationTransactionView())->setUser($user)->setObjectPHID($task->getPHID())->setTransactions($transactions)->setMarkupEngine($engine); $object_name = 'T' . $task->getID(); $actions = $this->buildActionView($task); $crumbs = $this->buildApplicationCrumbs()->addTextCrumb($object_name, '/' . $object_name)->setActionList($actions); $header = $this->buildHeaderView($task); $properties = $this->buildPropertyView($task, $field_list, $edges, $actions); $description = $this->buildDescriptionView($task, $engine); if (!$user->isLoggedIn()) { // TODO: Eventually, everything should run through this. For now, we're // only using it to get a consistent "Login to Comment" button. $comment_box = id(new PhabricatorApplicationTransactionCommentView())->setUser($user)->setRequestURI($request->getRequestURI()); $preview_panel = null; } else { $comment_box = id(new PHUIObjectBoxView())->setFlush(true)->setHeaderText($comment_header)->appendChild($comment_form); $timeline->setQuoteTargetID('transaction-comments'); $timeline->setQuoteRef($object_name); } $object_box = id(new PHUIObjectBoxView())->setHeader($header)->addPropertyList($properties); if ($description) { $object_box->addPropertyList($description); } return $this->buildApplicationPage(array($crumbs, $context_bar, $object_box, $timeline, $comment_box, $preview_panel), array('title' => 'T' . $task->getID() . ' ' . $task->getTitle(), 'pageObjects' => array($task->getPHID()))); }