public function renderValueForRevisionView()
 {
     $diff = $this->getDiff();
     $ustar = DifferentialRevisionUpdateHistoryView::renderDiffUnitStar($diff);
     $umsg = DifferentialRevisionUpdateHistoryView::getDiffUnitMessage($diff);
     $postponed_count = 0;
     $udata = $this->getDiffProperty('arc:unit');
     $utail = null;
     if ($udata) {
         $unit_messages = array();
         foreach ($udata as $test) {
             $name = idx($test, 'name');
             $result = idx($test, 'result');
             if ($result != DifferentialUnitTestResult::RESULT_POSTPONED && $result != DifferentialUnitTestResult::RESULT_PASS) {
                 $engine = PhabricatorMarkupEngine::newDifferentialMarkupEngine();
                 $userdata = phutil_utf8_shorten(idx($test, 'userdata'), 512);
                 $userdata = $engine->markupText($userdata);
                 $unit_messages[] = '<li>' . '<span class="unit-result-' . phutil_escape_html($result) . '">' . phutil_escape_html(ucwords($result)) . '</span>' . ' ' . phutil_escape_html($name) . '<p>' . $userdata . '</p>' . '</li>';
             } else {
                 if ($result == DifferentialUnitTestResult::RESULT_POSTPONED) {
                     $postponed_count++;
                 }
             }
         }
         $uexcuse = $this->getUnitExcuse();
         if ($unit_messages) {
             $utail = '<div class="differential-unit-block">' . $uexcuse . '<ul>' . implode("\n", $unit_messages) . '</ul>' . '</div>';
         }
     }
     if ($postponed_count > 0 && $diff->getUnitStatus() == DifferentialUnitStatus::UNIT_POSTPONED) {
         $umsg = $postponed_count . ' ' . $umsg;
     }
     return $ustar . ' ' . $umsg . $utail;
 }
 private function fail($near, $message)
 {
     $message = sprintf('%s near: %s', $message, phutil_utf8_shorten($near, 32000));
     if ($this->getEngine()->isTextMode()) {
         return '(' . $message . ')';
     }
     return hsprintf('<div style="color: red;">%s</div>', $message);
 }
 public function getSummary()
 {
     $message = $this->getCommitMessage();
     $lines = explode("\n", $message);
     $summary = head($lines);
     $summary = phutil_utf8_shorten($summary, self::SUMMARY_MAX_LENGTH);
     return $summary;
 }
 public function testUTF8shorten()
 {
     $inputs = array(array("1erp derp derp", 9, "", "1erp derp"), array("2erp derp derp", 12, "...", "2erp derp..."), array("derpxderpxderp", 12, "...", "derpxderp..."), array("derp♃derpderp", 12, "...", "derp♃derp..."), array("", 12, "...", ""), array("derp", 12, "...", "derp"), array("11111", 5, "2222", "11111"), array("111111", 5, "2222", "12222"), array("D1rp. Derp derp.", 7, "...", "D1rp."), array("D2rp. Derp derp.", 5, "...", "D2rp."), array("D3rp. Derp derp.", 4, "...", "D..."), array("D4rp. Derp derp.", 14, "...", "D4rp. Derp..."), array("D5rpderp, derp derp", 16, "...", "D5rpderp..."), array("D6rpderp, derp derp", 17, "...", "D6rpderp, derp..."), array("Gr͠mpyCatSmiles", 8, "...", "Gr͠mpy..."), array("X͠͠͠Y", 1, "", "X͠͠͠"), array("Derp, supercalafragalisticexpialadoshus", 30, "...", "Derp..."), array("((((((((((", 8, '...', '(((((...'), array('derp', 3, 'quack', 'quack'));
     foreach ($inputs as $input) {
         list($string, $length, $terminal, $expect) = $input;
         $result = phutil_utf8_shorten($string, $length, $terminal);
         $this->assertEqual($expect, $result, 'Shortening of ' . $string);
     }
 }
 private function writeAndRead($write, $read)
 {
     $future = new ExecFuture('cat');
     $future->write($write);
     $lines = array();
     foreach (new LinesOfALargeExecFuture($future) as $line) {
         $lines[] = $line;
     }
     $this->assertEqual($read, $lines, "Write: " . phutil_utf8_shorten($write, 32));
 }
 private function writeAndRead($write, $read, $delimiter = "\n")
 {
     $tmp = new TempFile();
     Filesystem::writeFile($tmp, $write);
     $lines = array();
     $iterator = id(new LinesOfALargeFile($tmp))->setDelimiter($delimiter);
     foreach ($iterator as $n => $line) {
         $lines[$n - 1] = $line;
     }
     $this->assertEqual($read, $lines, "Write: " . phutil_utf8_shorten($write, 32));
 }
 private static function flagWasEdited($flag, $verb)
 {
     $color = idx(self::$colorMap, $flag['color'], 'cyan');
     $note = $flag['note'];
     if ($note) {
         // Make sure notes that are long or have line breaks in them or
         // whatever don't mess up the formatting.
         $note = implode(' ', preg_split('/\\s+/', $note));
         $note = ' (' . phutil_utf8_shorten($note, 40, '...') . ')';
     }
     echo phutil_console_format("<fg:{$color}>%s</fg> flag%s {$verb}!\n", $flag['colorName'], $note);
 }
 public function renderView()
 {
     $data = $this->getStoryData();
     $author_phid = $data->getAuthorPHID();
     $owner_phid = $data->getValue('ownerPHID');
     $task_phid = $data->getValue('taskPHID');
     $objects = $this->getObjects();
     $action = $data->getValue('action');
     $view = new PhabricatorFeedStoryView();
     $verb = ManiphestAction::getActionPastTenseVerb($action);
     $extra = null;
     switch ($action) {
         case ManiphestAction::ACTION_ASSIGN:
             if ($owner_phid) {
                 $extra = ' to ' . '<strong>' . $this->getHandle($owner_phid)->renderLink() . '</strong>';
             } else {
                 $verb = 'placed';
                 $extra = ' up for grabs';
             }
             break;
     }
     $title = '<strong>' . $this->getHandle($author_phid)->renderLink() . '</strong>' . " {$verb} task " . '<strong>' . $this->getHandle($task_phid)->renderLink() . '</strong>';
     $title .= $extra;
     $title .= '.';
     $view->setTitle($title);
     switch ($action) {
         case ManiphestAction::ACTION_CREATE:
             $full_size = true;
             break;
         default:
             $full_size = false;
             break;
     }
     $view->setEpoch($data->getEpoch());
     if ($full_size) {
         if (!empty($objects[$author_phid])) {
             $image_phid = $objects[$author_phid]->getProfileImagePHID();
             $image_uri = PhabricatorFileURI::getViewURIForPHID($image_phid);
             $view->setImage($image_uri);
         }
         $content = phutil_escape_html(phutil_utf8_shorten($data->getValue('description'), 128));
         $content = str_replace("\n", '<br />', $content);
         $view->appendChild($content);
     } else {
         $view->setOneLineStory(true);
     }
     return $view;
 }
Example #9
0
 public function testUTF8shorten()
 {
     $inputs = array(array("1erp derp derp", 9, "", "1erp derp"), array("2erp derp derp", 12, "...", "2erp derp..."), array("derpxderpxderp", 12, "...", "derpxderp..."), array("derp♃derpderp", 12, "...", "derp♃derp..."), array("", 12, "...", ""), array("derp", 12, "...", "derp"), array("11111", 5, "2222", "11111"), array("111111", 5, "2222", "12222"), array("D1rp. Derp derp.", 7, "...", "D1rp."), array("D2rp. Derp derp.", 5, "...", "D2rp."), array("D3rp. Derp derp.", 4, "...", "D..."), array("D4rp. Derp derp.", 14, "...", "D4rp. Derp..."), array("D5rpderp, derp derp", 16, "...", "D5rpderp..."), array("D6rpderp, derp derp", 17, "...", "D6rpderp, derp..."), array("Derp, supercalafragalisticexpialadoshus", 30, "...", "Derp..."), array("((((((((((", 8, '...', '(((((...'));
     foreach ($inputs as $input) {
         list($string, $length, $terminal, $expect) = $input;
         $result = phutil_utf8_shorten($string, $length, $terminal);
         $this->assertEqual($expect, $result, 'Shortening of ' . $string);
     }
     try {
         phutil_utf8_shorten('derp', 3, 'quack');
         $caught = false;
     } catch (Exception $ex) {
         $caught = true;
     }
     $this->assertEqual(true, $caught, 'Expect exception for terminal.');
 }
 public function processRequest()
 {
     $projects = id(new PhabricatorProject())->loadAllWhere('1 = 1 ORDER BY id DESC limit 100');
     $project_phids = mpull($projects, 'getPHID');
     $profiles = array();
     if ($projects) {
         $profiles = id(new PhabricatorProjectProfile())->loadAllWhere('projectPHID in (%Ls)', $project_phids);
         $profiles = mpull($profiles, null, 'getProjectPHID');
     }
     $affil_groups = array();
     if ($projects) {
         $affil_groups = PhabricatorProjectAffiliation::loadAllForProjectPHIDs($project_phids);
     }
     $author_phids = mpull($projects, 'getAuthorPHID');
     $handles = id(new PhabricatorObjectHandleData($author_phids))->loadHandles();
     $query = id(new ManiphestTaskQuery())->withProjects($project_phids)->withAnyProject(true)->withStatus(ManiphestTaskQuery::STATUS_OPEN)->setLimit(PHP_INT_MAX);
     $tasks = $query->execute();
     $groups = array();
     foreach ($tasks as $task) {
         foreach ($task->getProjectPHIDs() as $phid) {
             $groups[$phid][] = $task;
         }
     }
     $rows = array();
     foreach ($projects as $project) {
         $phid = $project->getPHID();
         $profile = $profiles[$phid];
         $affiliations = $affil_groups[$phid];
         $group = idx($groups, $phid, array());
         $task_count = count($group);
         $population = count($affiliations);
         $status = PhabricatorProjectStatus::getNameForStatus($project->getStatus());
         $blurb = $profile->getBlurb();
         $blurb = phutil_utf8_shorten($blurb, $columns = 100);
         $rows[] = array(phutil_escape_html($project->getName()), phutil_escape_html($blurb), $handles[$project->getAuthorPHID()]->renderLink(), phutil_escape_html($population), phutil_escape_html($status), phutil_render_tag('a', array('href' => '/maniphest/view/all/?projects=' . $phid), phutil_escape_html($task_count)), phutil_render_tag('a', array('class' => 'small grey button', 'href' => '/project/view/' . $project->getID() . '/'), 'View Project Profile'));
     }
     $table = new AphrontTableView($rows);
     $table->setHeaders(array('Project', 'Description', 'Mastermind', 'Population', 'Status', 'Open Tasks', ''));
     $table->setColumnClasses(array('pri', 'wide', '', 'right', '', 'right', 'action'));
     $panel = new AphrontPanelView();
     $panel->appendChild($table);
     $panel->setHeader('Project');
     $panel->setCreateButton('Create New Project', '/project/create/');
     return $this->buildStandardPageResponse($panel, array('title' => 'Projects'));
 }
 public function render()
 {
     require_celerity_resource('phabricator-project-tag-css');
     $show = array_slice($this->handles, 0, 2);
     $tags = array();
     foreach ($show as $handle) {
         $tags[] = phutil_render_tag('a', array('href' => $handle->getURI(), 'class' => 'phabricator-project-tag'), phutil_escape_html(phutil_utf8_shorten($handle->getName(), 24)));
     }
     if (count($this->handles) > 2) {
         require_celerity_resource('aphront-tooltip-css');
         Javelin::initBehavior('phabricator-tooltips');
         $all = array();
         foreach ($this->handles as $handle) {
             $all[] = $handle->getName();
         }
         $tags[] = javelin_render_tag('span', array('class' => 'phabricator-project-tag', 'sigil' => 'has-tooltip', 'meta' => array('tip' => implode(', ', $all), 'size' => 200)), "…");
     }
     return implode("\n", $tags);
 }
 public function processRequest()
 {
     $request = $this->getRequest();
     $user = $request->getUser();
     $offset = $request->getInt('offset', 0);
     $page_size = 1000;
     $pager = new AphrontPagerView();
     $request_uri = $request->getRequestURI();
     $pager->setURI($request_uri, 'offset');
     $pager->setPageSize($page_size);
     $pager->setOffset($offset);
     $query = new PhabricatorChatLogQuery();
     $query->withChannels(array($this->channel));
     $logs = $query->executeWithPager($pager);
     require_celerity_resource('phabricator-chatlog-css');
     $last_author = null;
     $last_epoch = null;
     $row_idx = 0;
     $row_colors = array('normal', 'alternate');
     $out = array();
     $out[] = '<table class="phabricator-chat-log">';
     foreach ($logs as $log) {
         $this_author = $log->getAuthor();
         $this_epoch = $log->getEpoch();
         if ($this_author !== $last_author || $this_epoch - 60 * 5 > $last_epoch) {
             ++$row_idx;
             $out[] = '<tr class="initial ' . $row_colors[$row_idx % 2] . '">';
             $out[] = '<td class="timestamp">' . phabricator_datetime($log->getEpoch(), $user) . '</td>';
             $author = $log->getAuthor();
             $author = phutil_utf8_shorten($author, 18);
             $out[] = '<td class="author">' . phutil_escape_html($author) . '</td>';
         } else {
             $out[] = '<tr class="' . $row_colors[$row_idx % 2] . '">';
             $out[] = '<td class="similar" colspan="2"></td>';
         }
         $out[] = '<td class="message">' . phutil_escape_html($log->getMessage()) . '</td>';
         $out[] = '</tr>';
         $last_author = $this_author;
         $last_epoch = $this_epoch;
     }
     $out[] = '</table>';
     return $this->buildStandardPageResponse(array(implode("\n", $out), $pager), array('title' => 'Channel Log'));
 }
 private function buildQuestionListView(array $questions)
 {
     assert_instances_of($questions, 'PonderQuestion');
     $user = $this->getRequest()->getUser();
     $view = new PhabricatorObjectItemListView();
     $view->setNoDataString(pht('No matching questions.'));
     foreach ($questions as $question) {
         $item = new PhabricatorObjectItemView();
         $item->setHeader('Q' . $question->getID() . ' ' . $question->getTitle());
         $item->setHref('/Q' . $question->getID());
         $desc = $question->getContent();
         if ($desc) {
             $item->addDetail(pht('Description'), phutil_escape_html(phutil_utf8_shorten($desc, 128)));
         }
         $item->addDetail(pht('Author'), $this->getHandle($question->getAuthorPHID())->renderLink());
         $item->addDetail(pht('Votes'), $question->getVoteCount());
         $item->addDetail(pht('Answers'), $question->getAnswerCount());
         $created = pht('Created %s', phabricator_date($question->getDateCreated(), $user));
         $item->addAttribute($created);
         $view->addItem($item);
     }
     return $view;
 }
 private function buildDisplayRows(array $text_list, array $rev_list, array $blame_dict, $needs_blame, DiffusionRequest $drequest, DiffusionFileContentQuery $file_query, $selected)
 {
     if ($blame_dict) {
         $epoch_list = ipull(ifilter($blame_dict, 'epoch'), 'epoch');
         $epoch_min = min($epoch_list);
         $epoch_max = max($epoch_list);
         $epoch_range = $epoch_max - $epoch_min + 1;
     }
     $line_arr = array();
     $line_str = $drequest->getLine();
     $ranges = explode(',', $line_str);
     foreach ($ranges as $range) {
         if (strpos($range, '-') !== false) {
             list($min, $max) = explode('-', $range, 2);
             $line_arr[] = array('min' => min($min, $max), 'max' => max($min, $max));
         } else {
             if (strlen($range)) {
                 $line_arr[] = array('min' => $range, 'max' => $range);
             }
         }
     }
     $display = array();
     $line_number = 1;
     $last_rev = null;
     $color = null;
     foreach ($text_list as $k => $line) {
         $display_line = array('color' => null, 'epoch' => null, 'commit' => null, 'author' => null, 'target' => null, 'highlighted' => null, 'line' => $line_number, 'data' => $line);
         if ($needs_blame) {
             // If the line's rev is same as the line above, show empty content
             // with same color; otherwise generate blame info. The newer a change
             // is, the more saturated the color.
             // TODO: SVN doesn't always give us blame for the last line, if empty?
             // Bug with our stuff or with SVN?
             $rev = idx($rev_list, $k, $last_rev);
             if ($last_rev == $rev) {
                 $display_line['color'] = $color;
             } else {
                 $blame = $blame_dict[$rev];
                 if (!isset($blame['epoch'])) {
                     $color = '#ffd';
                     // Render as warning.
                 } else {
                     $color_ratio = ($blame['epoch'] - $epoch_min) / $epoch_range;
                     $color_value = 0xf6 * (1.0 - $color_ratio);
                     $color = sprintf('#%02x%02x%02x', $color_value, 0xf6, $color_value);
                 }
                 $display_line['epoch'] = idx($blame, 'epoch');
                 $display_line['color'] = $color;
                 $display_line['commit'] = $rev;
                 if (isset($blame['handle'])) {
                     $author_link = $blame['handle']->renderLink();
                 } else {
                     $author_link = phutil_render_tag('span', array(), phutil_escape_html($blame['author']));
                 }
                 $display_line['author'] = $author_link;
                 $last_rev = $rev;
             }
         }
         if ($line_arr) {
             if ($line_number == $line_arr[0]['min']) {
                 $display_line['target'] = true;
             }
             foreach ($line_arr as $range) {
                 if ($line_number >= $range['min'] && $line_number <= $range['max']) {
                     $display_line['highlighted'] = true;
                 }
             }
         }
         $display[] = $display_line;
         ++$line_number;
     }
     $commits = array_filter(ipull($display, 'commit'));
     if ($commits) {
         $commits = id(new PhabricatorAuditCommitQuery())->withIdentifiers($drequest->getRepository()->getID(), $commits)->needCommitData(true)->execute();
         $commits = mpull($commits, null, 'getCommitIdentifier');
     }
     $revision_ids = id(new DifferentialRevision())->loadIDsByCommitPHIDs(mpull($commits, 'getPHID'));
     $revisions = array();
     if ($revision_ids) {
         $revisions = id(new DifferentialRevision())->loadAllWhere('id IN (%Ld)', $revision_ids);
     }
     $request = $this->getRequest();
     $user = $request->getUser();
     Javelin::initBehavior('phabricator-oncopy', array());
     $rows = array();
     foreach ($display as $line) {
         $line_href = $drequest->generateURI(array('action' => 'browse', 'line' => $line['line'], 'stable' => true));
         $blame = array();
         if ($line['color']) {
             $color = $line['color'];
             $before_link = null;
             $commit_link = null;
             $revision_link = null;
             if (idx($line, 'commit')) {
                 $commit = $line['commit'];
                 $summary = 'Unknown';
                 if (idx($commits, $commit)) {
                     $summary = $commits[$commit]->getCommitData()->getSummary();
                 }
                 $tooltip = phabricator_date($line['epoch'], $user) . " · " . $summary;
                 Javelin::initBehavior('phabricator-tooltips', array());
                 require_celerity_resource('aphront-tooltip-css');
                 $commit_link = javelin_render_tag('a', array('href' => $drequest->generateURI(array('action' => 'commit', 'commit' => $line['commit'])), 'sigil' => 'has-tooltip', 'meta' => array('tip' => $tooltip, 'align' => 'E', 'size' => 600)), phutil_escape_html(phutil_utf8_shorten($line['commit'], 9, '')));
                 $revision_id = null;
                 if (idx($commits, $commit)) {
                     $revision_id = idx($revision_ids, $commits[$commit]->getPHID());
                 }
                 if ($revision_id) {
                     $revision = idx($revisions, $revision_id);
                     if (!$revision) {
                         $tooltip = '(Invalid revision)';
                     } else {
                         $tooltip = phabricator_date($revision->getDateModified(), $user) . " · " . $revision->getTitle();
                     }
                     $revision_link = javelin_render_tag('a', array('href' => '/D' . $revision_id, 'sigil' => 'has-tooltip', 'meta' => array('tip' => $tooltip, 'align' => 'E', 'size' => 600)), 'D' . $revision_id);
                 }
                 $uri = $line_href->alter('before', $commit);
                 $before_link = javelin_render_tag('a', array('href' => $uri->setQueryParam('view', 'blame'), 'sigil' => 'has-tooltip', 'meta' => array('tip' => 'Skip Past This Commit', 'align' => 'E', 'size' => 300)), "«");
             }
             $blame[] = phutil_render_tag('th', array('class' => 'diffusion-blame-link', 'style' => 'background: ' . $color), $before_link);
             $blame[] = phutil_render_tag('th', array('class' => 'diffusion-rev-link', 'style' => 'background: ' . $color), $commit_link);
             $blame[] = phutil_render_tag('th', array('class' => 'diffusion-rev-link', 'style' => 'background: ' . $color), $revision_link);
             $blame[] = phutil_render_tag('th', array('class' => 'diffusion-author-link', 'style' => 'background: ' . $color), idx($line, 'author'));
         }
         $line_link = phutil_render_tag('a', array('href' => $line_href), phutil_escape_html($line['line']));
         $blame[] = javelin_render_tag('th', array('class' => 'diffusion-line-link', 'sigil' => 'diffusion-line-link', 'style' => isset($color) ? 'background: ' . $color : null), $line_link);
         Javelin::initBehavior('diffusion-line-linker');
         $blame = implode('', $blame);
         if ($line['target']) {
             Javelin::initBehavior('diffusion-jump-to', array('target' => 'scroll_target'));
             $anchor_text = '<a id="scroll_target"></a>';
         } else {
             $anchor_text = null;
         }
         $line_text = phutil_render_tag('td', array(), $anchor_text . "​" . $line['data']);
         $rows[] = phutil_render_tag('tr', array('class' => $line['highlighted'] ? 'highlighted' : null), $blame . $line_text);
     }
     return $rows;
 }
 public function loadHandles()
 {
     $types = phid_group_by_type($this->phids);
     $handles = array();
     $external_loaders = PhabricatorEnv::getEnvConfig('phid.external-loaders');
     foreach ($types as $type => $phids) {
         switch ($type) {
             case PhabricatorPHIDConstants::PHID_TYPE_MAGIC:
                 // Black magic!
                 foreach ($phids as $phid) {
                     $handle = new PhabricatorObjectHandle();
                     $handle->setPHID($phid);
                     $handle->setType($type);
                     switch ($phid) {
                         case ManiphestTaskOwner::OWNER_UP_FOR_GRABS:
                             $handle->setName('Up For Grabs');
                             $handle->setFullName('upforgrabs (Up For Grabs)');
                             $handle->setComplete(true);
                             break;
                         case ManiphestTaskOwner::PROJECT_NO_PROJECT:
                             $handle->setName('No Project');
                             $handle->setFullName('noproject (No Project)');
                             $handle->setComplete(true);
                             break;
                         default:
                             $handle->setName('Foul Magicks');
                             break;
                     }
                     $handles[$phid] = $handle;
                 }
                 break;
             case PhabricatorPHIDConstants::PHID_TYPE_USER:
                 $object = new PhabricatorUser();
                 $users = $object->loadAllWhere('phid IN (%Ls)', $phids);
                 $users = mpull($users, null, 'getPHID');
                 $image_phids = mpull($users, 'getProfileImagePHID');
                 $image_phids = array_unique(array_filter($image_phids));
                 $images = array();
                 if ($image_phids) {
                     $images = id(new PhabricatorFile())->loadAllWhere('phid IN (%Ls)', $image_phids);
                     $images = mpull($images, 'getBestURI', 'getPHID');
                 }
                 $statuses = id(new PhabricatorUserStatus())->loadCurrentStatuses($phids);
                 foreach ($phids as $phid) {
                     $handle = new PhabricatorObjectHandle();
                     $handle->setPHID($phid);
                     $handle->setType($type);
                     if (empty($users[$phid])) {
                         $handle->setName('Unknown User');
                     } else {
                         $user = $users[$phid];
                         $handle->setName($user->getUsername());
                         $handle->setURI('/p/' . $user->getUsername() . '/');
                         $handle->setFullName($user->getUsername() . ' (' . $user->getRealName() . ')');
                         $handle->setAlternateID($user->getID());
                         $handle->setComplete(true);
                         if (isset($statuses[$phid])) {
                             $handle->setStatus($statuses[$phid]->getTextStatus());
                         }
                         $handle->setDisabled($user->getIsDisabled());
                         $img_uri = idx($images, $user->getProfileImagePHID());
                         if ($img_uri) {
                             $handle->setImageURI($img_uri);
                         } else {
                             $handle->setImageURI(PhabricatorUser::getDefaultProfileImageURI());
                         }
                     }
                     $handles[$phid] = $handle;
                 }
                 break;
             case PhabricatorPHIDConstants::PHID_TYPE_MLST:
                 $object = new PhabricatorMetaMTAMailingList();
                 $lists = $object->loadAllWhere('phid IN (%Ls)', $phids);
                 $lists = mpull($lists, null, 'getPHID');
                 foreach ($phids as $phid) {
                     $handle = new PhabricatorObjectHandle();
                     $handle->setPHID($phid);
                     $handle->setType($type);
                     if (empty($lists[$phid])) {
                         $handle->setName('Unknown Mailing List');
                     } else {
                         $list = $lists[$phid];
                         $handle->setName($list->getName());
                         $handle->setURI($list->getURI());
                         $handle->setFullName($list->getName());
                         $handle->setComplete(true);
                     }
                     $handles[$phid] = $handle;
                 }
                 break;
             case PhabricatorPHIDConstants::PHID_TYPE_DREV:
                 $object = new DifferentialRevision();
                 $revs = $object->loadAllWhere('phid in (%Ls)', $phids);
                 $revs = mpull($revs, null, 'getPHID');
                 foreach ($phids as $phid) {
                     $handle = new PhabricatorObjectHandle();
                     $handle->setPHID($phid);
                     $handle->setType($type);
                     if (empty($revs[$phid])) {
                         $handle->setName('Unknown Revision');
                     } else {
                         $rev = $revs[$phid];
                         $handle->setName($rev->getTitle());
                         $handle->setURI('/D' . $rev->getID());
                         $handle->setFullName('D' . $rev->getID() . ': ' . $rev->getTitle());
                         $handle->setComplete(true);
                         $status = $rev->getStatus();
                         if ($status == ArcanistDifferentialRevisionStatus::CLOSED || $status == ArcanistDifferentialRevisionStatus::ABANDONED) {
                             $closed = PhabricatorObjectHandleStatus::STATUS_CLOSED;
                             $handle->setStatus($closed);
                         }
                     }
                     $handles[$phid] = $handle;
                 }
                 break;
             case PhabricatorPHIDConstants::PHID_TYPE_CMIT:
                 $object = new PhabricatorRepositoryCommit();
                 $commits = $object->loadAllWhere('phid in (%Ls)', $phids);
                 $commits = mpull($commits, null, 'getPHID');
                 $repository_ids = array();
                 $callsigns = array();
                 if ($commits) {
                     $repository_ids = mpull($commits, 'getRepositoryID');
                     $repositories = id(new PhabricatorRepository())->loadAllWhere('id in (%Ld)', array_unique($repository_ids));
                     $callsigns = mpull($repositories, 'getCallsign');
                 }
                 foreach ($phids as $phid) {
                     $handle = new PhabricatorObjectHandle();
                     $handle->setPHID($phid);
                     $handle->setType($type);
                     if (empty($commits[$phid]) || !isset($callsigns[$repository_ids[$phid]])) {
                         $handle->setName('Unknown Commit');
                     } else {
                         $commit = $commits[$phid];
                         $callsign = $callsigns[$repository_ids[$phid]];
                         $repository = $repositories[$repository_ids[$phid]];
                         $commit_identifier = $commit->getCommitIdentifier();
                         // In case where the repository for the commit was deleted,
                         // we don't have have info about the repository anymore.
                         if ($repository) {
                             $name = $repository->formatCommitName($commit_identifier);
                             $handle->setName($name);
                         } else {
                             $handle->setName('Commit ' . 'r' . $callsign . $commit_identifier);
                         }
                         $handle->setURI('/r' . $callsign . $commit_identifier);
                         $handle->setFullName('r' . $callsign . $commit_identifier);
                         $handle->setTimestamp($commit->getEpoch());
                         $handle->setComplete(true);
                     }
                     $handles[$phid] = $handle;
                 }
                 break;
             case PhabricatorPHIDConstants::PHID_TYPE_TASK:
                 $object = new ManiphestTask();
                 $tasks = $object->loadAllWhere('phid in (%Ls)', $phids);
                 $tasks = mpull($tasks, null, 'getPHID');
                 foreach ($phids as $phid) {
                     $handle = new PhabricatorObjectHandle();
                     $handle->setPHID($phid);
                     $handle->setType($type);
                     if (empty($tasks[$phid])) {
                         $handle->setName('Unknown Revision');
                     } else {
                         $task = $tasks[$phid];
                         $handle->setName($task->getTitle());
                         $handle->setURI('/T' . $task->getID());
                         $handle->setFullName('T' . $task->getID() . ': ' . $task->getTitle());
                         $handle->setComplete(true);
                         $handle->setAlternateID($task->getID());
                         if ($task->getStatus() != ManiphestTaskStatus::STATUS_OPEN) {
                             $closed = PhabricatorObjectHandleStatus::STATUS_CLOSED;
                             $handle->setStatus($closed);
                         }
                     }
                     $handles[$phid] = $handle;
                 }
                 break;
             case PhabricatorPHIDConstants::PHID_TYPE_FILE:
                 $object = new PhabricatorFile();
                 $files = $object->loadAllWhere('phid IN (%Ls)', $phids);
                 $files = mpull($files, null, 'getPHID');
                 foreach ($phids as $phid) {
                     $handle = new PhabricatorObjectHandle();
                     $handle->setPHID($phid);
                     $handle->setType($type);
                     if (empty($files[$phid])) {
                         $handle->setName('Unknown File');
                     } else {
                         $file = $files[$phid];
                         $handle->setName($file->getName());
                         $handle->setURI($file->getBestURI());
                         $handle->setComplete(true);
                     }
                     $handles[$phid] = $handle;
                 }
                 break;
             case PhabricatorPHIDConstants::PHID_TYPE_PROJ:
                 $object = new PhabricatorProject();
                 $projects = $object->loadAllWhere('phid IN (%Ls)', $phids);
                 $projects = mpull($projects, null, 'getPHID');
                 foreach ($phids as $phid) {
                     $handle = new PhabricatorObjectHandle();
                     $handle->setPHID($phid);
                     $handle->setType($type);
                     if (empty($projects[$phid])) {
                         $handle->setName('Unknown Project');
                     } else {
                         $project = $projects[$phid];
                         $handle->setName($project->getName());
                         $handle->setURI('/project/view/' . $project->getID() . '/');
                         $handle->setComplete(true);
                     }
                     $handles[$phid] = $handle;
                 }
                 break;
             case PhabricatorPHIDConstants::PHID_TYPE_REPO:
                 $object = new PhabricatorRepository();
                 $repositories = $object->loadAllWhere('phid in (%Ls)', $phids);
                 $repositories = mpull($repositories, null, 'getPHID');
                 foreach ($phids as $phid) {
                     $handle = new PhabricatorObjectHandle();
                     $handle->setPHID($phid);
                     $handle->setType($type);
                     if (empty($repositories[$phid])) {
                         $handle->setName('Unknown Repository');
                     } else {
                         $repository = $repositories[$phid];
                         $handle->setName($repository->getCallsign());
                         $handle->setURI('/diffusion/' . $repository->getCallsign() . '/');
                         $handle->setComplete(true);
                     }
                     $handles[$phid] = $handle;
                 }
                 break;
             case PhabricatorPHIDConstants::PHID_TYPE_OPKG:
                 $object = new PhabricatorOwnersPackage();
                 $packages = $object->loadAllWhere('phid in (%Ls)', $phids);
                 $packages = mpull($packages, null, 'getPHID');
                 foreach ($phids as $phid) {
                     $handle = new PhabricatorObjectHandle();
                     $handle->setPHID($phid);
                     $handle->setType($type);
                     if (empty($packages[$phid])) {
                         $handle->setName('Unknown Package');
                     } else {
                         $package = $packages[$phid];
                         $handle->setName($package->getName());
                         $handle->setURI('/owners/package/' . $package->getID() . '/');
                         $handle->setComplete(true);
                     }
                     $handles[$phid] = $handle;
                 }
                 break;
             case PhabricatorPHIDConstants::PHID_TYPE_APRJ:
                 $project_dao = new PhabricatorRepositoryArcanistProject();
                 $projects = $project_dao->loadAllWhere('phid IN (%Ls)', $phids);
                 $projects = mpull($projects, null, 'getPHID');
                 foreach ($phids as $phid) {
                     $handle = new PhabricatorObjectHandle();
                     $handle->setPHID($phid);
                     $handle->setType($type);
                     if (empty($projects[$phid])) {
                         $handle->setName('Unknown Arcanist Project');
                     } else {
                         $project = $projects[$phid];
                         $handle->setName($project->getName());
                         $handle->setComplete(true);
                     }
                     $handles[$phid] = $handle;
                 }
                 break;
             case PhabricatorPHIDConstants::PHID_TYPE_WIKI:
                 $document_dao = new PhrictionDocument();
                 $content_dao = new PhrictionContent();
                 $conn = $document_dao->establishConnection('r');
                 $documents = queryfx_all($conn, 'SELECT * FROM %T document JOIN %T content
           ON document.contentID = content.id
           WHERE document.phid IN (%Ls)', $document_dao->getTableName(), $content_dao->getTableName(), $phids);
                 $documents = ipull($documents, null, 'phid');
                 foreach ($phids as $phid) {
                     $handle = new PhabricatorObjectHandle();
                     $handle->setPHID($phid);
                     $handle->setType($type);
                     if (empty($documents[$phid])) {
                         $handle->setName('Unknown Document');
                     } else {
                         $info = $documents[$phid];
                         $handle->setName($info['title']);
                         $handle->setURI(PhrictionDocument::getSlugURI($info['slug']));
                         $handle->setComplete(true);
                     }
                     $handles[$phid] = $handle;
                 }
                 break;
             case PhabricatorPHIDConstants::PHID_TYPE_QUES:
                 $questions = id(new PonderQuestionQuery())->withPHIDs($phids)->execute();
                 $questions = mpull($questions, null, 'getPHID');
                 foreach ($phids as $phid) {
                     $handle = new PhabricatorObjectHandle();
                     $handle->setPHID($phid);
                     $handle->setType($type);
                     if (empty($questions[$phid])) {
                         $handle->setName('Unknown Ponder Question');
                     } else {
                         $question = $questions[$phid];
                         $handle->setName(phutil_utf8_shorten($question->getTitle(), 60));
                         $handle->setURI(new PhutilURI('Q' . $question->getID()));
                         $handle->setComplete(true);
                     }
                     $handles[$phid] = $handle;
                 }
                 break;
             case PhabricatorPHIDConstants::PHID_TYPE_PSTE:
                 $pastes = id(new PhabricatorPasteQuery())->withPHIDs($phids)->setViewer($this->viewer)->execute();
                 $pastes = mpull($pastes, null, 'getPHID');
                 foreach ($phids as $phid) {
                     $handle = new PhabricatorObjectHandle();
                     $handle->setPHID($phid);
                     $handle->setType($type);
                     if (empty($pastes[$phid])) {
                         $handle->setName('Unknown Paste');
                     } else {
                         $paste = $pastes[$phid];
                         $handle->setName($paste->getTitle());
                         $handle->setFullName('P' . $paste->getID() . ': ' . $paste->getTitle());
                         $handle->setURI('/P' . $paste->getID());
                         $handle->setComplete(true);
                     }
                     $handles[$phid] = $handle;
                 }
                 break;
             default:
                 $loader = null;
                 if (isset($external_loaders[$type])) {
                     $loader = $external_loaders[$type];
                 } else {
                     if (isset($external_loaders['*'])) {
                         $loader = $external_loaders['*'];
                     }
                 }
                 if ($loader) {
                     $object = newv($loader, array());
                     $handles += $object->loadHandles($phids);
                     break;
                 }
                 foreach ($phids as $phid) {
                     $handle = new PhabricatorObjectHandle();
                     $handle->setType($type);
                     $handle->setPHID($phid);
                     $handle->setName('Unknown Object');
                     $handle->setFullName('An Unknown Object');
                     $handles[$phid] = $handle;
                 }
                 break;
         }
     }
     return $handles;
 }
 public function render()
 {
     $user = $this->user;
     if (!$user) {
         throw new Exception("Call setUser() before render()-ing this view.");
     }
     $local = $this->localCommits;
     if (!$local) {
         return null;
     }
     require_celerity_resource('differential-local-commits-view-css');
     $has_tree = false;
     $has_local = false;
     foreach ($local as $commit) {
         if (idx($commit, 'tree')) {
             $has_tree = true;
         }
         if (idx($commit, 'local')) {
             $has_local = true;
         }
     }
     $rows = array();
     foreach ($local as $commit) {
         $row = array();
         if (idx($commit, 'commit')) {
             $commit_hash = substr($commit['commit'], 0, 16);
         } else {
             if (isset($commit['rev'])) {
                 $commit_hash = substr($commit['rev'], 0, 16);
             } else {
                 $commit_hash = null;
             }
         }
         $row[] = '<td>' . phutil_escape_html($commit_hash) . '</td>';
         if ($has_tree) {
             $tree = idx($commit, 'tree');
             $tree = substr($tree, 0, 16);
             $row[] = '<td>' . phutil_escape_html($tree) . '</td>';
         }
         if ($has_local) {
             $local_rev = idx($commit, 'local', null);
             $row[] = '<td>' . phutil_escape_html($local_rev) . '</td>';
         }
         $parents = idx($commit, 'parents', array());
         foreach ($parents as $k => $parent) {
             if (is_array($parent)) {
                 $parent = idx($parent, 'rev');
             }
             $parents[$k] = phutil_escape_html(substr($parent, 0, 16));
         }
         $parents = implode('<br />', $parents);
         $row[] = '<td>' . $parents . '</td>';
         $author = nonempty(idx($commit, 'user'), idx($commit, 'author'));
         $row[] = '<td>' . phutil_escape_html($author) . '</td>';
         $message = idx($commit, 'message');
         $summary = idx($commit, 'summary');
         $summary = phutil_utf8_shorten($summary, 80);
         $view = new AphrontMoreView();
         $view->setSome(phutil_escape_html($summary));
         if ($message && trim($summary) != trim($message)) {
             $view->setMore(nl2br(phutil_escape_html($message)));
         }
         $row[] = phutil_render_tag('td', array('class' => 'summary'), $view->render());
         $date = nonempty(idx($commit, 'date'), idx($commit, 'time'));
         if ($date) {
             $date = phabricator_datetime($date, $user);
         }
         $row[] = '<td>' . $date . '</td>';
         $rows[] = '<tr>' . implode('', $row) . '</tr>';
     }
     $headers = array();
     $headers[] = '<th>Commit</th>';
     if ($has_tree) {
         $headers[] = '<th>Tree</th>';
     }
     if ($has_local) {
         $headers[] = '<th>Local</th>';
     }
     $headers[] = '<th>Parents</th>';
     $headers[] = '<th>Author</th>';
     $headers[] = '<th>Summary</th>';
     $headers[] = '<th>Date</th>';
     $headers = '<tr>' . implode('', $headers) . '</tr>';
     return '<div class="differential-panel">' . '<h1>Local Commits</h1>' . '<table class="differential-local-commits-table">' . $headers . implode("\n", $rows) . '</table>' . '</div>';
 }
Example #17
0
 /**
  * @task message
  */
 private function getCommitMessageFromUser()
 {
     $conduit = $this->getConduit();
     $template = null;
     if (!$this->getArgument('verbatim')) {
         $saved = $this->readScratchFile('create-message');
         if ($saved) {
             $where = $this->getReadableScratchFilePath('create-message');
             $preview = explode("\n", $saved);
             $preview = array_shift($preview);
             $preview = trim($preview);
             $preview = phutil_utf8_shorten($preview, 64);
             if ($preview) {
                 $preview = "Message begins:\n\n       {$preview}\n\n";
             } else {
                 $preview = null;
             }
             echo "You have a saved revision message in '{$where}'.\n" . "{$preview}" . "You can use this message, or discard it.";
             $use = phutil_console_confirm("Do you want to use this message?", $default_no = false);
             if ($use) {
                 $template = $saved;
             } else {
                 $this->removeScratchFile('create-message');
             }
         }
     }
     $template_is_default = false;
     $notes = array();
     $included = array();
     list($fields, $notes, $included) = $this->getDefaultCreateFields();
     if ($template) {
         $fields = array();
         $notes = array();
     } else {
         if (!$fields) {
             $template_is_default = true;
         }
         if ($notes) {
             $commit = head($this->getRepositoryAPI()->getLocalCommitInformation());
             $template = $commit['message'];
         } else {
             $template = $conduit->callMethodSynchronous('differential.getcommitmessage', array('revision_id' => null, 'edit' => 'create', 'fields' => $fields));
         }
     }
     if ($included) {
         foreach ($included as $k => $commit) {
             $included[$k] = '        ' . $commit;
         }
         $in_branch = '';
         if (!$this->isRawDiffSource()) {
             $in_branch = ' in branch ' . $this->getRepositoryAPI()->getBranchName();
         }
         $included = array_merge(array("", "Included commits{$in_branch}:", ""), $included, array(""));
     } else {
         $included = array('');
     }
     $issues = array_merge(array('NEW DIFFERENTIAL REVISION', 'Describe the changes in this new revision.'), $included, array('arc could not identify any existing revision in your working copy.', 'If you intended to update an existing revision, use:', '', '  $ arc diff --update <revision>'));
     if ($notes) {
         $issues = array_merge($issues, array(''), $notes);
     }
     $done = false;
     $first = true;
     while (!$done) {
         $template = rtrim($template, "\r\n") . "\n\n";
         foreach ($issues as $issue) {
             $template .= '# ' . $issue . "\n";
         }
         $template .= "\n";
         if ($first && $this->getArgument('verbatim') && !$template_is_default) {
             $new_template = $template;
         } else {
             $new_template = $this->newInteractiveEditor($template)->setName('new-commit')->editInteractively();
         }
         $first = false;
         if ($template_is_default && $new_template == $template) {
             throw new ArcanistUsageException("Template not edited.");
         }
         $template = ArcanistCommentRemover::removeComments($new_template);
         $wrote = $this->writeScratchFile('create-message', $template);
         $where = $this->getReadableScratchFilePath('create-message');
         try {
             $message = ArcanistDifferentialCommitMessage::newFromRawCorpus($template);
             $message->pullDataFromConduit($conduit);
             $this->validateCommitMessage($message);
             $done = true;
         } catch (ArcanistDifferentialCommitMessageParserException $ex) {
             echo "Commit message has errors:\n\n";
             $issues = array('Resolve these errors:');
             foreach ($ex->getParserErrors() as $error) {
                 echo phutil_console_wrap("- " . $error . "\n", 6);
                 $issues[] = '  - ' . $error;
             }
             echo "\n";
             echo "You must resolve these errors to continue.";
             $again = phutil_console_confirm("Do you want to edit the message?", $default_no = false);
             if ($again) {
                 // Keep going.
             } else {
                 $saved = null;
                 if ($wrote) {
                     $saved = "A copy was saved to '{$where}'.";
                 }
                 throw new ArcanistUsageException("Message has unresolved errrors. {$saved}");
             }
         } catch (Exception $ex) {
             if ($wrote) {
                 echo phutil_console_wrap("(Commit messaged saved to '{$where}'.)\n");
             }
             throw $ex;
         }
     }
     return $message;
 }
 public function processRequest()
 {
     $request = $this->getRequest();
     $user = $request->getUser();
     $project = id(new PhabricatorProject())->load($this->id);
     if (!$project) {
         return new Aphront404Response();
     }
     $profile = $project->loadProfile();
     if (!$profile) {
         $profile = new PhabricatorProjectProfile();
     }
     $src_phid = $profile->getProfileImagePHID();
     if (!$src_phid) {
         $src_phid = $user->getProfileImagePHID();
     }
     $file = id(new PhabricatorFile())->loadOneWhere('phid = %s', $src_phid);
     if ($file) {
         $picture = $file->getBestURI();
     } else {
         $picture = null;
     }
     $members = mpull($project->loadAffiliations(), null, 'getUserPHID');
     $nav_view = new AphrontSideNavFilterView();
     $uri = new PhutilURI('/project/view/' . $project->getID() . '/');
     $nav_view->setBaseURI($uri);
     $external_arrow = "↗";
     $tasks_uri = '/maniphest/view/all/?projects=' . $project->getPHID();
     $slug = PhabricatorSlug::normalize($project->getName());
     $phriction_uri = '/w/projects/' . $slug;
     $edit_uri = '/project/edit/' . $project->getID() . '/';
     $nav_view->addFilter('dashboard', 'Dashboard');
     $nav_view->addSpacer();
     $nav_view->addFilter('feed', 'Feed');
     $nav_view->addFilter(null, 'Tasks ' . $external_arrow, $tasks_uri);
     $nav_view->addFilter(null, 'Wiki ' . $external_arrow, $phriction_uri);
     $nav_view->addFilter('people', 'People');
     $nav_view->addFilter('about', 'About');
     $nav_view->addSpacer();
     $nav_view->addFilter(null, "Edit Project…", $edit_uri);
     $this->page = $nav_view->selectFilter($this->page, 'dashboard');
     require_celerity_resource('phabricator-profile-css');
     switch ($this->page) {
         case 'dashboard':
             $content = $this->renderTasksPage($project, $profile);
             $query = new PhabricatorFeedQuery();
             $query->setFilterPHIDs(array($project->getPHID()));
             $stories = $query->execute();
             $content .= $this->renderStories($stories);
             break;
         case 'about':
             $content = $this->renderAboutPage($project, $profile);
             break;
         case 'people':
             $content = $this->renderPeoplePage($project, $profile);
             break;
         case 'feed':
             $content = $this->renderFeedPage($project, $profile);
             break;
         default:
             throw new Exception("Unimplemented filter '{$this->page}'.");
     }
     $content = '<div style="padding: 1em;">' . $content . '</div>';
     $nav_view->appendChild($content);
     $header = new PhabricatorProfileHeaderView();
     $header->setName($project->getName());
     $header->setDescription(phutil_utf8_shorten($profile->getBlurb(), 1024));
     $header->setProfilePicture($picture);
     $action = null;
     if (empty($members[$user->getPHID()])) {
         $action = phabricator_render_form($user, array('action' => '/project/update/' . $project->getID() . '/join/', 'method' => 'post'), phutil_render_tag('button', array('class' => 'green'), 'Join Project'));
     } else {
         $action = javelin_render_tag('a', array('href' => '/project/update/' . $project->getID() . '/leave/', 'sigil' => 'workflow', 'class' => 'grey button'), 'Leave Project...');
     }
     $header->addAction($action);
     $header->appendChild($nav_view);
     return $this->buildStandardPageResponse($header, array('title' => $project->getName() . ' Project'));
 }
 public function processRequest()
 {
     $request = $this->getRequest();
     $nav = new AphrontSideNavFilterView();
     $nav->setBaseURI(new PhutilURI('/project/filter/'))->addLabel('User')->addFilter('active', 'Active')->addFilter('owned', 'Owned')->addSpacer()->addLabel('All')->addFilter('all', 'All Projects');
     $this->filter = $nav->selectFilter($this->filter, 'active');
     $pager = new AphrontPagerView();
     $pager->setPageSize(250);
     $pager->setURI($request->getRequestURI(), 'page');
     $pager->setOffset($request->getInt('page'));
     $query = new PhabricatorProjectQuery();
     $query->setOffset($pager->getOffset());
     $query->setLimit($pager->getPageSize() + 1);
     $view_phid = $request->getUser()->getPHID();
     switch ($this->filter) {
         case 'active':
             $table_header = 'Active Projects';
             $query->setMembers(array($view_phid));
             break;
         case 'owned':
             $table_header = 'Owned Projects';
             $query->setOwners(array($view_phid));
             break;
         case 'all':
             $table_header = 'All Projects';
             break;
     }
     $projects = $query->execute();
     $projects = $pager->sliceResults($projects);
     $project_phids = mpull($projects, 'getPHID');
     $profiles = array();
     if ($projects) {
         $profiles = id(new PhabricatorProjectProfile())->loadAllWhere('projectPHID in (%Ls)', $project_phids);
         $profiles = mpull($profiles, null, 'getProjectPHID');
     }
     $affil_groups = array();
     if ($projects) {
         $affil_groups = PhabricatorProjectAffiliation::loadAllForProjectPHIDs($project_phids);
     }
     $tasks = array();
     $groups = array();
     if ($project_phids) {
         $query = id(new ManiphestTaskQuery())->withProjects($project_phids)->withAnyProject(true)->withStatus(ManiphestTaskQuery::STATUS_OPEN)->setLimit(PHP_INT_MAX);
         $tasks = $query->execute();
         foreach ($tasks as $task) {
             foreach ($task->getProjectPHIDs() as $phid) {
                 $groups[$phid][] = $task;
             }
         }
     }
     $rows = array();
     foreach ($projects as $project) {
         $phid = $project->getPHID();
         $profile = $profiles[$phid];
         $affiliations = $affil_groups[$phid];
         $group = idx($groups, $phid, array());
         $task_count = count($group);
         $population = count($affiliations);
         $blurb = $profile->getBlurb();
         $blurb = phutil_utf8_shorten($blurb, 64);
         $rows[] = array(phutil_render_tag('a', array('href' => '/project/view/' . $project->getID() . '/'), phutil_escape_html($project->getName())), phutil_escape_html($blurb), phutil_escape_html($population), phutil_render_tag('a', array('href' => '/maniphest/view/all/?projects=' . $phid), phutil_escape_html($task_count)));
     }
     $table = new AphrontTableView($rows);
     $table->setHeaders(array('Project', 'Description', 'Population', 'Open Tasks'));
     $table->setColumnClasses(array('pri', 'wide', '', ''));
     $panel = new AphrontPanelView();
     $panel->setHeader($table_header);
     $panel->setCreateButton('Create New Project', '/project/create/');
     $panel->appendChild($table);
     $panel->appendChild($pager);
     $nav->appendChild($panel);
     return $this->buildStandardPageResponse($nav, array('title' => 'Projects'));
 }
 public function save()
 {
     if (!$this->user) {
         throw new Exception("Call setUser() before save()!");
     }
     $document = $this->document;
     $content = $this->content;
     $new_content = new PhrictionContent();
     $new_content->setSlug($document->getSlug());
     $new_content->setAuthorPHID($this->user->getPHID());
     $new_content->setTitle(coalesce($this->newTitle, $content->getTitle()));
     $new_content->setContent(coalesce($this->newContent, $content->getContent()));
     if (strlen($this->description)) {
         $new_content->setDescription($this->description);
     }
     $new_content->setVersion($content->getVersion() + 1);
     // TODO: This should be transactional.
     $is_new = false;
     if (!$document->getID()) {
         $is_new = true;
         $document->save();
     }
     $new_content->setDocumentID($document->getID());
     $new_content->save();
     $document->setContentID($new_content->getID());
     $document->save();
     $document->attachContent($new_content);
     PhabricatorSearchPhrictionIndexer::indexDocument($document);
     id(new PhabricatorFeedStoryPublisher())->setRelatedPHIDs(array($document->getPHID(), $this->user->getPHID()))->setStoryAuthorPHID($this->user->getPHID())->setStoryTime(time())->setStoryType(PhabricatorFeedStoryTypeConstants::STORY_PHRICTION)->setStoryData(array('phid' => $document->getPHID(), 'action' => $is_new ? PhrictionActionConstants::ACTION_CREATE : PhrictionActionConstants::ACTION_EDIT, 'content' => phutil_utf8_shorten($new_content->getContent(), 140)))->publish();
     return $this;
 }
 public function save()
 {
     $revision = $this->getRevision();
     $is_new = $this->isNewRevision();
     if ($is_new) {
         $this->initializeNewRevision($revision);
     }
     $revision->loadRelationships();
     $this->willWriteRevision();
     if ($this->reviewers === null) {
         $this->reviewers = $revision->getReviewers();
     }
     if ($this->cc === null) {
         $this->cc = $revision->getCCPHIDs();
     }
     $diff = $this->getDiff();
     if ($diff) {
         $revision->setLineCount($diff->getLineCount());
     }
     // Save the revision, to generate its ID and PHID if it is new. We need
     // the ID/PHID in order to record them in Herald transcripts, but don't
     // want to hold a transaction open while running Herald because it is
     // potentially somewhat slow. The downside is that we may end up with a
     // saved revision/diff pair without appropriate CCs. We could be better
     // about this -- for example:
     //
     //  - Herald can't affect reviewers, so we could compute them before
     //    opening the transaction and then save them in the transaction.
     //  - Herald doesn't *really* need PHIDs to compute its effects, we could
     //    run it before saving these objects and then hand over the PHIDs later.
     //
     // But this should address the problem of orphaned revisions, which is
     // currently the only problem we experience in practice.
     $revision->openTransaction();
     if ($diff) {
         $revision->setBranchName($diff->getBranch());
         $revision->setArcanistProjectPHID($diff->getArcanistProjectPHID());
     }
     $revision->save();
     if ($diff) {
         $diff->setRevisionID($revision->getID());
         $diff->save();
     }
     $revision->saveTransaction();
     // We're going to build up three dictionaries: $add, $rem, and $stable. The
     // $add dictionary has added reviewers/CCs. The $rem dictionary has
     // reviewers/CCs who have been removed, and the $stable array is
     // reviewers/CCs who haven't changed. We're going to send new reviewers/CCs
     // a different ("welcome") email than we send stable reviewers/CCs.
     $old = array('rev' => array_fill_keys($revision->getReviewers(), true), 'ccs' => array_fill_keys($revision->getCCPHIDs(), true));
     $xscript_header = null;
     $xscript_uri = null;
     $new = array('rev' => array_fill_keys($this->reviewers, true), 'ccs' => array_fill_keys($this->cc, true));
     $rem_ccs = array();
     $xscript_phid = null;
     if ($diff) {
         $adapter = new HeraldDifferentialRevisionAdapter($revision, $diff);
         $adapter->setExplicitCCs($new['ccs']);
         $adapter->setExplicitReviewers($new['rev']);
         $adapter->setForbiddenCCs($revision->getUnsubscribedPHIDs());
         $xscript = HeraldEngine::loadAndApplyRules($adapter);
         $xscript_uri = '/herald/transcript/' . $xscript->getID() . '/';
         $xscript_phid = $xscript->getPHID();
         $xscript_header = $xscript->getXHeraldRulesHeader();
         $xscript_header = HeraldTranscript::saveXHeraldRulesHeader($revision->getPHID(), $xscript_header);
         $sub = array('rev' => array(), 'ccs' => $adapter->getCCsAddedByHerald());
         $rem_ccs = $adapter->getCCsRemovedByHerald();
     } else {
         $sub = array('rev' => array(), 'ccs' => array());
     }
     // Remove any CCs which are prevented by Herald rules.
     $sub['ccs'] = array_diff_key($sub['ccs'], $rem_ccs);
     $new['ccs'] = array_diff_key($new['ccs'], $rem_ccs);
     $add = array();
     $rem = array();
     $stable = array();
     foreach (array('rev', 'ccs') as $key) {
         $add[$key] = array();
         if ($new[$key] !== null) {
             $add[$key] += array_diff_key($new[$key], $old[$key]);
         }
         $add[$key] += array_diff_key($sub[$key], $old[$key]);
         $combined = $sub[$key];
         if ($new[$key] !== null) {
             $combined += $new[$key];
         }
         $rem[$key] = array_diff_key($old[$key], $combined);
         $stable[$key] = array_diff_key($old[$key], $add[$key] + $rem[$key]);
     }
     self::alterReviewers($revision, $this->reviewers, array_keys($rem['rev']), array_keys($add['rev']), $this->actorPHID);
     // We want to attribute new CCs to a "reasonPHID", representing the reason
     // they were added. This is either a user (if some user explicitly CCs
     // them, or uses "Add CCs...") or a Herald transcript PHID, indicating that
     // they were added by a Herald rule.
     if ($add['ccs'] || $rem['ccs']) {
         $reasons = array();
         foreach ($add['ccs'] as $phid => $ignored) {
             if (empty($new['ccs'][$phid])) {
                 $reasons[$phid] = $xscript_phid;
             } else {
                 $reasons[$phid] = $this->actorPHID;
             }
         }
         foreach ($rem['ccs'] as $phid => $ignored) {
             if (empty($new['ccs'][$phid])) {
                 $reasons[$phid] = $this->actorPHID;
             } else {
                 $reasons[$phid] = $xscript_phid;
             }
         }
     } else {
         $reasons = $this->actorPHID;
     }
     self::alterCCs($revision, $this->cc, array_keys($rem['ccs']), array_keys($add['ccs']), $reasons);
     $this->updateAuxiliaryFields();
     // Add the author and users included from Herald rules to the relevant set
     // of users so they get a copy of the email.
     if (!$this->silentUpdate) {
         if ($is_new) {
             $add['rev'][$this->getActorPHID()] = true;
             if ($diff) {
                 $add['rev'] += $adapter->getEmailPHIDsAddedByHerald();
             }
         } else {
             $stable['rev'][$this->getActorPHID()] = true;
             if ($diff) {
                 $stable['rev'] += $adapter->getEmailPHIDsAddedByHerald();
             }
         }
     }
     $mail = array();
     $phids = array($this->getActorPHID());
     $handles = id(new PhabricatorObjectHandleData($phids))->loadHandles();
     $actor_handle = $handles[$this->getActorPHID()];
     $changesets = null;
     $comment = null;
     if ($diff) {
         $changesets = $diff->loadChangesets();
         // TODO: This should probably be in DifferentialFeedbackEditor?
         if (!$is_new) {
             $comment = $this->createComment();
         }
         if ($comment) {
             $mail[] = id(new DifferentialNewDiffMail($revision, $actor_handle, $changesets))->setIsFirstMailAboutRevision($is_new)->setIsFirstMailToRecipients($is_new)->setComments($this->getComments())->setToPHIDs(array_keys($stable['rev']))->setCCPHIDs(array_keys($stable['ccs']));
         }
         // Save the changes we made above.
         $diff->setDescription(preg_replace('/\\n.*/s', '', $this->getComments()));
         $diff->save();
         $this->updateAffectedPathTable($revision, $diff, $changesets);
         $this->updateRevisionHashTable($revision, $diff);
         // An updated diff should require review, as long as it's not closed
         // or accepted. The "accepted" status is "sticky" to encourage courtesy
         // re-diffs after someone accepts with minor changes/suggestions.
         $status = $revision->getStatus();
         if ($status != ArcanistDifferentialRevisionStatus::CLOSED && $status != ArcanistDifferentialRevisionStatus::ACCEPTED) {
             $revision->setStatus(ArcanistDifferentialRevisionStatus::NEEDS_REVIEW);
         }
     } else {
         $diff = $revision->loadActiveDiff();
         if ($diff) {
             $changesets = $diff->loadChangesets();
         } else {
             $changesets = array();
         }
     }
     $revision->save();
     $this->didWriteRevision();
     $event_data = array('revision_id' => $revision->getID(), 'revision_phid' => $revision->getPHID(), 'revision_name' => $revision->getTitle(), 'revision_author_phid' => $revision->getAuthorPHID(), 'action' => $is_new ? DifferentialAction::ACTION_CREATE : DifferentialAction::ACTION_UPDATE, 'feedback_content' => $is_new ? phutil_utf8_shorten($revision->getSummary(), 140) : $this->getComments(), 'actor_phid' => $revision->getAuthorPHID());
     id(new PhabricatorTimelineEvent('difx', $event_data))->recordEvent();
     id(new PhabricatorFeedStoryPublisher())->setStoryType(PhabricatorFeedStoryTypeConstants::STORY_DIFFERENTIAL)->setStoryData($event_data)->setStoryTime(time())->setStoryAuthorPHID($revision->getAuthorPHID())->setRelatedPHIDs(array($revision->getPHID(), $revision->getAuthorPHID()))->setPrimaryObjectPHID($revision->getPHID())->setSubscribedPHIDs(array_merge(array($revision->getAuthorPHID()), $revision->getReviewers(), $revision->getCCPHIDs()))->publish();
     //  TODO: Move this into a worker task thing.
     PhabricatorSearchDifferentialIndexer::indexRevision($revision);
     if ($this->silentUpdate) {
         return;
     }
     $revision->loadRelationships();
     if ($add['rev']) {
         $message = id(new DifferentialNewDiffMail($revision, $actor_handle, $changesets))->setIsFirstMailAboutRevision($is_new)->setIsFirstMailToRecipients(true)->setToPHIDs(array_keys($add['rev']));
         if ($is_new) {
             // The first time we send an email about a revision, put the CCs in
             // the "CC:" field of the same "Review Requested" email that reviewers
             // get, so you don't get two initial emails if you're on a list that
             // is CC'd.
             $message->setCCPHIDs(array_keys($add['ccs']));
         }
         $mail[] = $message;
     }
     // If we added CCs, we want to send them an email, but only if they were not
     // already a reviewer and were not added as one (in these cases, they got
     // a "NewDiff" mail, either in the past or just a moment ago). You can still
     // get two emails, but only if a revision is updated and you are added as a
     // reviewer at the same time a list you are on is added as a CC, which is
     // rare and reasonable.
     $implied_ccs = self::getImpliedCCs($revision);
     $implied_ccs = array_fill_keys($implied_ccs, true);
     $add['ccs'] = array_diff_key($add['ccs'], $implied_ccs);
     if (!$is_new && $add['ccs']) {
         $mail[] = id(new DifferentialCCWelcomeMail($revision, $actor_handle, $changesets))->setIsFirstMailToRecipients(true)->setToPHIDs(array_keys($add['ccs']));
     }
     foreach ($mail as $message) {
         $message->setHeraldTranscriptURI($xscript_uri);
         $message->setXHeraldRulesHeader($xscript_header);
         $message->send();
     }
 }
 public function save()
 {
     $revision = $this->getRevision();
     // TODO
     //    $revision->openTransaction();
     $is_new = $this->isNewRevision();
     if ($is_new) {
         // These fields aren't nullable; set them to sensible defaults if they
         // haven't been configured. We're just doing this so we can generate an
         // ID for the revision if we don't have one already.
         $revision->setLineCount(0);
         if ($revision->getStatus() === null) {
             $revision->setStatus(DifferentialRevisionStatus::NEEDS_REVIEW);
         }
         if ($revision->getTitle() === null) {
             $revision->setTitle('Untitled Revision');
         }
         if ($revision->getAuthorPHID() === null) {
             $revision->setAuthorPHID($this->getActorPHID());
         }
         if ($revision->getSummary() === null) {
             $revision->setSummary('');
         }
         if ($revision->getTestPlan() === null) {
             $revision->setTestPlan('');
         }
         $revision->save();
     }
     $revision->loadRelationships();
     $this->willWriteRevision();
     if ($this->reviewers === null) {
         $this->reviewers = $revision->getReviewers();
     }
     if ($this->cc === null) {
         $this->cc = $revision->getCCPHIDs();
     }
     // We're going to build up three dictionaries: $add, $rem, and $stable. The
     // $add dictionary has added reviewers/CCs. The $rem dictionary has
     // reviewers/CCs who have been removed, and the $stable array is
     // reviewers/CCs who haven't changed. We're going to send new reviewers/CCs
     // a different ("welcome") email than we send stable reviewers/CCs.
     $old = array('rev' => array_fill_keys($revision->getReviewers(), true), 'ccs' => array_fill_keys($revision->getCCPHIDs(), true));
     $diff = $this->getDiff();
     $xscript_header = null;
     $xscript_uri = null;
     $new = array('rev' => array_fill_keys($this->reviewers, true), 'ccs' => array_fill_keys($this->cc, true));
     $rem_ccs = array();
     if ($diff) {
         $diff->setRevisionID($revision->getID());
         $revision->setLineCount($diff->getLineCount());
         $adapter = new HeraldDifferentialRevisionAdapter($revision, $diff);
         $adapter->setExplicitCCs($new['ccs']);
         $adapter->setExplicitReviewers($new['rev']);
         $adapter->setForbiddenCCs($revision->getUnsubscribedPHIDs());
         $xscript = HeraldEngine::loadAndApplyRules($adapter);
         $xscript_uri = PhabricatorEnv::getProductionURI('/herald/transcript/' . $xscript->getID() . '/');
         $xscript_phid = $xscript->getPHID();
         $xscript_header = $xscript->getXHeraldRulesHeader();
         $xscript_header = HeraldTranscript::saveXHeraldRulesHeader($revision->getPHID(), $xscript_header);
         $sub = array('rev' => array(), 'ccs' => $adapter->getCCsAddedByHerald());
         $rem_ccs = $adapter->getCCsRemovedByHerald();
     } else {
         $sub = array('rev' => array(), 'ccs' => array());
     }
     // Remove any CCs which are prevented by Herald rules.
     $sub['ccs'] = array_diff_key($sub['ccs'], $rem_ccs);
     $new['ccs'] = array_diff_key($new['ccs'], $rem_ccs);
     $add = array();
     $rem = array();
     $stable = array();
     foreach (array('rev', 'ccs') as $key) {
         $add[$key] = array();
         if ($new[$key] !== null) {
             $add[$key] += array_diff_key($new[$key], $old[$key]);
         }
         $add[$key] += array_diff_key($sub[$key], $old[$key]);
         $combined = $sub[$key];
         if ($new[$key] !== null) {
             $combined += $new[$key];
         }
         $rem[$key] = array_diff_key($old[$key], $combined);
         $stable[$key] = array_diff_key($old[$key], $add[$key] + $rem[$key]);
     }
     self::alterReviewers($revision, $this->reviewers, array_keys($rem['rev']), array_keys($add['rev']), $this->actorPHID);
     /*
     
         // TODO: When Herald is brought over, run through this stuff to figure
         // out which adds are Herald's fault.
     
         // TODO: Still need to do this.
     
         if ($add['ccs'] || $rem['ccs']) {
           foreach (array_keys($add['ccs']) as $id) {
             if (empty($new['ccs'][$id])) {
               $reason_phid = 'TODO';//$xscript_phid;
             } else {
               $reason_phid = $this->getActorPHID();
             }
           }
           foreach (array_keys($rem['ccs']) as $id) {
             if (empty($new['ccs'][$id])) {
               $reason_phid = $this->getActorPHID();
             } else {
               $reason_phid = 'TODO';//$xscript_phid;
             }
           }
         }
     */
     self::alterCCs($revision, $this->cc, array_keys($rem['ccs']), array_keys($add['ccs']), $this->actorPHID);
     $this->updateAuxiliaryFields();
     // Add the author and users included from Herald rules to the relevant set
     // of users so they get a copy of the email.
     if (!$this->silentUpdate) {
         if ($is_new) {
             $add['rev'][$this->getActorPHID()] = true;
             if ($diff) {
                 $add['rev'] += $adapter->getEmailPHIDsAddedByHerald();
             }
         } else {
             $stable['rev'][$this->getActorPHID()] = true;
             if ($diff) {
                 $stable['rev'] += $adapter->getEmailPHIDsAddedByHerald();
             }
         }
     }
     $mail = array();
     $phids = array($this->getActorPHID());
     $handles = id(new PhabricatorObjectHandleData($phids))->loadHandles();
     $actor_handle = $handles[$this->getActorPHID()];
     $changesets = null;
     $comment = null;
     if ($diff) {
         $changesets = $diff->loadChangesets();
         // TODO: This should probably be in DifferentialFeedbackEditor?
         if (!$is_new) {
             $comment = $this->createComment();
         }
         if ($comment) {
             $mail[] = id(new DifferentialNewDiffMail($revision, $actor_handle, $changesets))->setIsFirstMailAboutRevision($is_new)->setIsFirstMailToRecipients($is_new)->setComments($this->getComments())->setToPHIDs(array_keys($stable['rev']))->setCCPHIDs(array_keys($stable['ccs']));
         }
         // Save the changes we made above.
         $diff->setDescription(substr($this->getComments(), 0, 80));
         $diff->save();
         // An updated diff should require review, as long as it's not committed
         // or accepted. The "accepted" status is "sticky" to encourage courtesy
         // re-diffs after someone accepts with minor changes/suggestions.
         $status = $revision->getStatus();
         if ($status != DifferentialRevisionStatus::COMMITTED && $status != DifferentialRevisionStatus::ACCEPTED) {
             $revision->setStatus(DifferentialRevisionStatus::NEEDS_REVIEW);
         }
     } else {
         $diff = $revision->loadActiveDiff();
         if ($diff) {
             $changesets = $diff->loadChangesets();
         } else {
             $changesets = array();
         }
     }
     $revision->save();
     $this->didWriteRevision();
     $event_data = array('revision_id' => $revision->getID(), 'revision_phid' => $revision->getPHID(), 'revision_name' => $revision->getTitle(), 'revision_author_phid' => $revision->getAuthorPHID(), 'action' => $is_new ? DifferentialAction::ACTION_CREATE : DifferentialAction::ACTION_UPDATE, 'feedback_content' => $is_new ? phutil_utf8_shorten($revision->getSummary(), 140) : $this->getComments(), 'actor_phid' => $revision->getAuthorPHID());
     id(new PhabricatorTimelineEvent('difx', $event_data))->recordEvent();
     id(new PhabricatorFeedStoryPublisher())->setStoryType(PhabricatorFeedStoryTypeConstants::STORY_DIFFERENTIAL)->setStoryData($event_data)->setStoryTime(time())->setStoryAuthorPHID($revision->getAuthorPHID())->setRelatedPHIDs(array($revision->getPHID(), $revision->getAuthorPHID()))->publish();
     // TODO
     //    $revision->saveTransaction();
     //  TODO: Move this into a worker task thing.
     PhabricatorSearchDifferentialIndexer::indexRevision($revision);
     if ($this->silentUpdate) {
         return;
     }
     $revision->loadRelationships();
     if ($add['rev']) {
         $message = id(new DifferentialNewDiffMail($revision, $actor_handle, $changesets))->setIsFirstMailAboutRevision($is_new)->setIsFirstMailToRecipients(true)->setToPHIDs(array_keys($add['rev']));
         if ($is_new) {
             // The first time we send an email about a revision, put the CCs in
             // the "CC:" field of the same "Review Requested" email that reviewers
             // get, so you don't get two initial emails if you're on a list that
             // is CC'd.
             $message->setCCPHIDs(array_keys($add['ccs']));
         }
         $mail[] = $message;
     }
     // If you were added as a reviewer and a CC, just give you the reviewer
     // email. We could go to greater lengths to prevent this, but there's
     // bunch of stuff with list subscriptions anyway. You can still get two
     // emails, but only if a revision is updated and you are added as a reviewer
     // at the same time a list you are on is added as a CC, which is rare and
     // reasonable.
     $add['ccs'] = array_diff_key($add['ccs'], $add['rev']);
     if (!$is_new && $add['ccs']) {
         $mail[] = id(new DifferentialCCWelcomeMail($revision, $actor_handle, $changesets))->setIsFirstMailToRecipients(true)->setToPHIDs(array_keys($add['ccs']));
     }
     foreach ($mail as $message) {
         $message->setHeraldTranscriptURI($xscript_uri);
         $message->setXHeraldRulesHeader($xscript_header);
         $message->send();
     }
 }
 private function fail($near, $message)
 {
     return '<div style="color: red;">' . phutil_escape_html($message) . ' near: ' . phutil_escape_html(phutil_utf8_shorten($near, 32000)) . '</div>';
 }
 public function processRequest()
 {
     $request = $this->getRequest();
     $user = $request->getUser();
     $query = id(new PhabricatorProjectQuery())->setViewer($user)->withIDs(array($this->id));
     if ($this->page == 'people') {
         $query->needMembers(true);
     }
     $project = $query->executeOne();
     if (!$project) {
         return new Aphront404Response();
     }
     $profile = $project->loadProfile();
     if (!$profile) {
         $profile = new PhabricatorProjectProfile();
     }
     $picture = $profile->loadProfileImageURI();
     $nav_view = $this->buildLocalNavigation($project);
     $this->page = $nav_view->selectFilter($this->page, 'dashboard');
     require_celerity_resource('phabricator-profile-css');
     switch ($this->page) {
         case 'dashboard':
             $content = $this->renderTasksPage($project, $profile);
             $query = new PhabricatorFeedQuery();
             $query->setFilterPHIDs(array($project->getPHID()));
             $query->setLimit(50);
             $query->setViewer($this->getRequest()->getUser());
             $stories = $query->execute();
             $content .= $this->renderStories($stories);
             break;
         case 'about':
             $content = $this->renderAboutPage($project, $profile);
             break;
         case 'people':
             $content = $this->renderPeoplePage($project, $profile);
             break;
         case 'feed':
             $content = $this->renderFeedPage($project, $profile);
             break;
         default:
             throw new Exception("Unimplemented filter '{$this->page}'.");
     }
     $content = '<div style="padding: 1em;">' . $content . '</div>';
     $nav_view->appendChild($content);
     $header = new PhabricatorProfileHeaderView();
     $header->setName($project->getName());
     $header->setDescription(phutil_utf8_shorten($profile->getBlurb(), 1024));
     $header->setProfilePicture($picture);
     $action = null;
     if (!$project->isUserMember($user->getPHID())) {
         $can_join = PhabricatorPolicyCapability::CAN_JOIN;
         if (PhabricatorPolicyFilter::hasCapability($user, $project, $can_join)) {
             $class = 'green';
         } else {
             $class = 'grey disabled';
         }
         $action = phabricator_render_form($user, array('action' => '/project/update/' . $project->getID() . '/join/', 'method' => 'post'), phutil_render_tag('button', array('class' => $class), 'Join Project'));
     } else {
         $action = javelin_render_tag('a', array('href' => '/project/update/' . $project->getID() . '/leave/', 'sigil' => 'workflow', 'class' => 'grey button'), 'Leave Project...');
     }
     $header->addAction($action);
     $header->appendChild($nav_view);
     return $this->buildStandardPageResponse($header, array('title' => $project->getName() . ' Project'));
 }
 protected final function renderSummary($text, $len = 128)
 {
     if ($len) {
         $text = phutil_utf8_shorten($text, $len);
     }
     $text = phutil_escape_html($text);
     $text = str_replace("\n", '<br />', $text);
     return $text;
 }
 /**
  * @phutil-external-symbol class PHPExcel
  * @phutil-external-symbol class PHPExcel_IOFactory
  * @phutil-external-symbol class PHPExcel_Style_NumberFormat
  */
 public function processRequest()
 {
     $request = $this->getRequest();
     $user = $request->getUser();
     $ok = @(include_once 'PHPExcel.php');
     if (!$ok) {
         $dialog = new AphrontDialogView();
         $dialog->setUser($user);
         $dialog->setTitle('Excel Export Not Configured');
         $dialog->appendChild('<p>This system does not have PHPExcel installed. This software ' . 'component is required to export tasks to Excel. Have your system ' . 'administrator install it from:</p>' . '<br />' . '<p>' . '<a href="http://www.phpexcel.net/">http://www.phpexcel.net/</a>' . '</p>' . '<br />' . '<p>Your PHP "include_path" needs to be updated to include the ' . 'PHPExcel Classes/ directory.</p>');
         $dialog->addCancelButton('/maniphest/');
         return id(new AphrontDialogResponse())->setDialog($dialog);
     }
     $query = id(new PhabricatorSearchQuery())->loadOneWhere('queryKey = %s', $this->key);
     if (!$query) {
         return new Aphront404Response();
     }
     if (!$request->isDialogFormPost()) {
         $dialog = new AphrontDialogView();
         $dialog->setUser($user);
         $dialog->setTitle('Export Tasks to Excel');
         $dialog->appendChild('<p>Do you want to export the query results to Excel?</p>');
         $dialog->addCancelButton('/maniphest/');
         $dialog->addSubmitButton('Export to Excel');
         return id(new AphrontDialogResponse())->setDialog($dialog);
     }
     $query->setParameter('limit', null);
     $query->setParameter('offset', null);
     $query->setParameter('order', 'p');
     $query->setParameter('group', 'n');
     list($tasks, $handles) = ManiphestTaskListController::loadTasks($query);
     // Ungroup tasks.
     $tasks = array_mergev($tasks);
     $all_projects = array_mergev(mpull($tasks, 'getProjectPHIDs'));
     $project_handles = $this->loadViewerHandles($all_projects);
     $handles += $project_handles;
     $workbook = new PHPExcel();
     $sheet = $workbook->setActiveSheetIndex(0);
     $sheet->setTitle('Tasks');
     $widths = array(null, 15, null, 10, 15, 15, 60, 30, 20, 100);
     foreach ($widths as $col => $width) {
         if ($width !== null) {
             $sheet->getColumnDimension($this->col($col))->setWidth($width);
         }
     }
     $status_map = ManiphestTaskStatus::getTaskStatusMap();
     $pri_map = ManiphestTaskPriority::getTaskPriorityMap();
     $date_format = null;
     $rows = array();
     $rows[] = array('ID', 'Owner', 'Status', 'Priority', 'Date Created', 'Date Updated', 'Title', 'Projects', 'URI', 'Description');
     $is_date = array(false, false, false, false, true, true, false, false, false, false);
     $header_format = array('font' => array('bold' => true));
     foreach ($tasks as $task) {
         $task_owner = null;
         if ($task->getOwnerPHID()) {
             $task_owner = $handles[$task->getOwnerPHID()]->getName();
         }
         $projects = array();
         foreach ($task->getProjectPHIDs() as $phid) {
             $projects[] = $handles[$phid]->getName();
         }
         $projects = implode(', ', $projects);
         $rows[] = array('T' . $task->getID(), $task_owner, idx($status_map, $task->getStatus(), '?'), idx($pri_map, $task->getPriority(), '?'), $this->computeExcelDate($task->getDateCreated()), $this->computeExcelDate($task->getDateModified()), $task->getTitle(), $projects, PhabricatorEnv::getProductionURI('/T' . $task->getID()), phutil_utf8_shorten($task->getDescription(), 512));
     }
     foreach ($rows as $row => $cols) {
         foreach ($cols as $col => $spec) {
             $cell_name = $this->col($col) . ($row + 1);
             $sheet->setCellValue($cell_name, $spec);
             if ($row == 0) {
                 $sheet->getStyle($cell_name)->applyFromArray($header_format);
             }
             if ($is_date[$col]) {
                 $code = PHPExcel_Style_NumberFormat::FORMAT_DATE_YYYYMMDD2;
                 $sheet->getStyle($cell_name)->getNumberFormat()->setFormatCode($code);
             }
         }
     }
     $writer = PHPExcel_IOFactory::createWriter($workbook, 'Excel2007');
     ob_start();
     $writer->save('php://output');
     $data = ob_get_clean();
     $mime = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
     return id(new AphrontFileResponse())->setMimeType($mime)->setDownload('maniphest_tasks_' . date('Ymd') . '.xlsx')->setContent($data);
 }
 private function updateDocument($document, $content, $new_content)
 {
     $is_new = false;
     if (!$document->getID()) {
         $is_new = true;
     }
     $new_content->setVersion($content->getVersion() + 1);
     $change_type = $new_content->getChangeType();
     switch ($change_type) {
         case PhrictionChangeType::CHANGE_EDIT:
             $doc_status = PhrictionDocumentStatus::STATUS_EXISTS;
             $feed_action = $is_new ? PhrictionActionConstants::ACTION_CREATE : PhrictionActionConstants::ACTION_EDIT;
             break;
         case PhrictionChangeType::CHANGE_DELETE:
             $doc_status = PhrictionDocumentStatus::STATUS_DELETED;
             $feed_action = PhrictionActionConstants::ACTION_DELETE;
             if ($is_new) {
                 throw new Exception("You can not delete a document which doesn't exist yet!");
             }
             break;
         default:
             throw new Exception("Unsupported content change type '{$change_type}'!");
     }
     $document->setStatus($doc_status);
     // TODO: This should be transactional.
     if ($is_new) {
         $document->save();
     }
     $new_content->setDocumentID($document->getID());
     $new_content->save();
     $document->setContentID($new_content->getID());
     $document->save();
     $document->attachContent($new_content);
     PhabricatorSearchPhrictionIndexer::indexDocument($document);
     $project_phid = null;
     $slug = $document->getSlug();
     if (PhrictionDocument::isProjectSlug($slug)) {
         $project = id(new PhabricatorProject())->loadOneWhere('phrictionSlug = %s', PhrictionDocument::getProjectSlugIdentifier($slug));
         if ($project) {
             $project_phid = $project->getPHID();
         }
     }
     $related_phids = array($document->getPHID(), $this->user->getPHID());
     if ($project_phid) {
         $related_phids[] = $project_phid;
     }
     id(new PhabricatorFeedStoryPublisher())->setRelatedPHIDs($related_phids)->setStoryAuthorPHID($this->user->getPHID())->setStoryTime(time())->setStoryType(PhabricatorFeedStoryTypeConstants::STORY_PHRICTION)->setStoryData(array('phid' => $document->getPHID(), 'action' => $feed_action, 'content' => phutil_utf8_shorten($new_content->getContent(), 140), 'project' => $project_phid))->publish();
     return $this;
 }
 private function renderEvent(AphrontCalendarEventView $event, $epoch_start, $epoch_end)
 {
     $user = $this->user;
     $event_start = $event->getEpochStart();
     $event_end = $event->getEpochEnd();
     $classes = array();
     $when = array();
     $classes[] = 'aphront-calendar-event';
     if ($event_start < $epoch_start) {
         $classes[] = 'aphront-calendar-event-continues-before';
         $when[] = 'Started ' . phabricator_datetime($event_start, $user);
     } else {
         $when[] = 'Starts at ' . phabricator_time($event_start, $user);
     }
     if ($event_end > $epoch_end) {
         $classes[] = 'aphront-calendar-event-continues-after';
         $when[] = 'Ends ' . phabricator_datetime($event_end, $user);
     } else {
         $when[] = 'Ends at ' . phabricator_time($event_end, $user);
     }
     Javelin::initBehavior('phabricator-tooltips');
     $info = $event->getName();
     if ($event->getDescription()) {
         $info .= "\n\n" . $event->getDescription();
     }
     $text_div = javelin_render_tag('div', array('sigil' => 'has-tooltip', 'meta' => array('tip' => $info . "\n\n" . implode("\n", $when), 'size' => 240), 'class' => 'aphront-calendar-event-text'), phutil_escape_html(phutil_utf8_shorten($event->getName(), 32)));
     return javelin_render_tag('div', array('class' => implode(' ', $classes)), $text_div);
 }
 private static function abbreviate($w)
 {
     return phutil_utf8_shorten($w, 60);
 }
 public function run()
 {
     $output = array();
     $status = $this->getArgument('status');
     $owner = $this->getArgument('owner');
     $order = $this->getArgument('order');
     $limit = $this->getArgument('limit');
     $this->tasks = $this->loadManiphestTasks($status == 'all' ? 'any' : $status, $owner ? $this->findOwnerPhid($owner) : $this->getUserPHID(), $order, $limit);
     if (!$this->tasks) {
         echo "No tasks found.\n";
         return 0;
     }
     $task_rows = array();
     foreach ($this->tasks as $task) {
         $output = array();
         // Render the "T123" column.
         $task_id = "T" . $task['id'];
         $formatted_task_id = phutil_console_format('**%s**', $task_id);
         $output['id'] = array('text' => $formatted_task_id, 'len' => phutil_utf8_console_strlen($task_id));
         // Render the "Title" column.
         $formatted_title = rtrim($task['title']);
         $output['title'] = array('text' => $formatted_title, 'len' => phutil_utf8_console_strlen($formatted_title));
         // Render the "Priority" column.
         switch ($task['priority']) {
             case 'Needs Triage':
                 $color = 'magenta';
                 break;
             case 'Unbreak Now!':
                 $color = 'red';
                 break;
             case 'High':
                 $color = 'yellow';
                 break;
             case 'Normal':
                 $color = 'green';
                 break;
             case 'Low':
                 $color = 'blue';
                 break;
             case 'Wishlist':
                 $color = 'cyan';
                 break;
             default:
                 $color = 'white';
                 break;
         }
         $formatted_priority = phutil_console_format("<bg:{$color}> </bg> %s", $task['priority']);
         $output['priority'] = array('text' => $formatted_priority, 'len' => phutil_utf8_console_strlen($task['priority']) + 2);
         // Render the "Status" column.
         if ($task['status']) {
             $status_text = 'Closed';
             $status_color = 'red';
         } else {
             $status_text = 'Open';
             $status_color = 'green';
         }
         $formatted_status = phutil_console_format("<bg:{$status_color}> </bg> %s", $status_text);
         $output['status'] = array('text' => $formatted_status, 'len' => phutil_utf8_console_strlen('status') + 2);
         $task_rows[] = $output;
     }
     // Find the longest string in each column.
     $col_size = array();
     foreach ($task_rows as $row) {
         foreach ($row as $key => $col) {
             if (empty($col_size[$key])) {
                 $col_size[$key] = 0;
             }
             $col_size[$key] = max($col_size[$key], $col['len']);
         }
     }
     // Determine the terminal width. If we can't figure it out, assume 80.
     $width = nonempty(phutil_console_get_terminal_width(), 80);
     // We're going to clip the titles so they'll all fit in one line on the
     // terminal. Figure out where to clip them.
     $padding_between_columns = 4;
     $clip_title_at = max(16, $width - ($col_size['id'] + $col_size['priority'] + $col_size['status'] + $padding_between_columns * 3));
     $col_size['title'] = min($col_size['title'], $clip_title_at);
     foreach ($task_rows as $key => $cols) {
         $new_title = phutil_utf8_shorten($cols['title']['text'], $clip_title_at);
         $task_rows[$key]['title']['len'] = phutil_utf8_console_strlen($new_title);
         $task_rows[$key]['title']['text'] = $new_title;
     }
     $table = array();
     foreach ($task_rows as $row) {
         $trow = array();
         foreach ($row as $col => $cell) {
             $text = $cell['text'];
             $pad_len = $col_size[$col] - $cell['len'];
             if ($pad_len) {
                 $text .= str_repeat(' ', $pad_len);
             }
             $trow[] = $text;
         }
         $table[] = implode(str_repeat(' ', $padding_between_columns), $trow);
     }
     $table = implode("\n", $table) . "\n";
     echo $table;
 }