private function renderServerStatus(array $status) { $rows = array(); foreach ($status as $key => $value) { switch ($key) { case 'uptime': $value /= 1000; $value = phutil_format_relative_time_detailed($value); break; case 'log': break; default: $value = number_format($value); break; } $rows[] = array($key, $value); } $table = new AphrontTableView($rows); $table->setColumnClasses(array('header', 'wide')); $test_icon = id(new PHUIIconView())->setIconFont('fa-exclamation-triangle'); $test_button = id(new PHUIButtonView())->setTag('a')->setWorkflow(true)->setText(pht('Send Test Notification'))->setHref($this->getApplicationURI('test/'))->setIcon($test_icon); $header = id(new PHUIHeaderView())->setHeader(pht('Notification Server Status'))->addActionLink($test_button); $box = id(new PHUIObjectBoxView())->setHeader($header)->appendChild($table); return $box; }
private function buildClusterNotificationStatus() { $viewer = $this->getViewer(); $servers = PhabricatorNotificationServerRef::newRefs(); Javelin::initBehavior('phabricator-tooltips'); $rows = array(); foreach ($servers as $server) { if ($server->isAdminServer()) { $type_icon = 'fa-database sky'; $type_tip = pht('Admin Server'); } else { $type_icon = 'fa-bell sky'; $type_tip = pht('Client Server'); } $type_icon = id(new PHUIIconView())->setIcon($type_icon)->addSigil('has-tooltip')->setMetadata(array('tip' => $type_tip)); $messages = array(); $details = array(); if ($server->isAdminServer()) { try { $details = $server->loadServerStatus(); $status_icon = 'fa-exchange green'; $status_label = pht('Version %s', idx($details, 'version')); } catch (Exception $ex) { $status_icon = 'fa-times red'; $status_label = pht('Connection Error'); $messages[] = $ex->getMessage(); } } else { try { $server->testClient(); $status_icon = 'fa-exchange green'; $status_label = pht('Connected'); } catch (Exception $ex) { $status_icon = 'fa-times red'; $status_label = pht('Connection Error'); $messages[] = $ex->getMessage(); } } if ($details) { $uptime = idx($details, 'uptime'); $uptime = $uptime / 1000; $uptime = phutil_format_relative_time_detailed($uptime); $clients = pht('%s Active / %s Total', new PhutilNumber(idx($details, 'clients.active')), new PhutilNumber(idx($details, 'clients.total'))); $stats = pht('%s In / %s Out', new PhutilNumber(idx($details, 'messages.in')), new PhutilNumber(idx($details, 'messages.out'))); } else { $uptime = null; $clients = null; $stats = null; } $status_view = array(id(new PHUIIconView())->setIcon($status_icon), ' ', $status_label); $messages = phutil_implode_html(phutil_tag('br'), $messages); $rows[] = array($type_icon, $server->getProtocol(), $server->getHost(), $server->getPort(), $status_view, $uptime, $clients, $stats, $messages); } $table = id(new AphrontTableView($rows))->setNoDataString(pht('No notification servers are configured.'))->setHeaders(array(null, pht('Proto'), pht('Host'), pht('Port'), pht('Status'), pht('Uptime'), pht('Clients'), pht('Messages'), null))->setColumnClasses(array(null, null, null, null, null, null, null, null, 'wide')); $doc_href = PhabricatorEnv::getDoclink('Cluster: Notifications'); $header = id(new PHUIHeaderView())->setHeader(pht('Cluster Notification Status'))->addActionLink(id(new PHUIButtonView())->setIcon('fa-book')->setHref($doc_href)->setTag('a')->setText(pht('Documentation'))); return id(new PHUIObjectBoxView())->setHeader($header)->setTable($table); }
private function buildPropertyListView(PhabricatorWorkerTask $task) { $viewer = $this->getRequest()->getUser(); $view = new PHUIPropertyListView(); if ($task->isArchived()) { switch ($task->getResult()) { case PhabricatorWorkerArchiveTask::RESULT_SUCCESS: $status = pht('Complete'); break; case PhabricatorWorkerArchiveTask::RESULT_FAILURE: $status = pht('Failed'); break; case PhabricatorWorkerArchiveTask::RESULT_CANCELLED: $status = pht('Cancelled'); break; default: throw new Exception(pht('Unknown task status!')); } } else { $status = pht('Queued'); } $view->addProperty(pht('Task Status'), $status); $view->addProperty(pht('Task Class'), $task->getTaskClass()); if ($task->getLeaseExpires()) { if ($task->getLeaseExpires() > time()) { $lease_status = pht('Leased'); } else { $lease_status = pht('Lease Expired'); } } else { $lease_status = phutil_tag('em', array(), pht('Not Leased')); } $view->addProperty(pht('Lease Status'), $lease_status); $view->addProperty(pht('Lease Owner'), $task->getLeaseOwner() ? $task->getLeaseOwner() : phutil_tag('em', array(), pht('None'))); if ($task->getLeaseExpires() && $task->getLeaseOwner()) { $expires = $task->getLeaseExpires() - time(); $expires = phutil_format_relative_time_detailed($expires); } else { $expires = phutil_tag('em', array(), pht('None')); } $view->addProperty(pht('Lease Expires'), $expires); if ($task->isArchived()) { $duration = pht('%s us', new PhutilNumber($task->getDuration())); } else { $duration = phutil_tag('em', array(), pht('Not Completed')); } $view->addProperty(pht('Duration'), $duration); $data = id(new PhabricatorWorkerTaskData())->load($task->getDataID()); $task->setData($data->getData()); $worker = $task->getWorkerInstance(); $data = $worker->renderForDisplay($viewer); if ($data !== null) { $view->addProperty(pht('Data'), $data); } return $view; }
public function testDetailedDurationFormatting() { $expected_zero = 'now'; $tests = array(12095939 => '19 w, 6 d', -12095939 => '19 w, 6 d ago', 3380521 => '5 w, 4 d', -3380521 => '5 w, 4 d ago', 0 => $expected_zero); foreach ($tests as $duration => $expect) { $this->assertEqual($expect, phutil_format_relative_time_detailed($duration), 'phutil_format_relative_time_detailed(' . $duration . ')'); } $tests = array(3380521 => array(-1 => '5 w', 0 => '5 w', 1 => '5 w', 2 => '5 w, 4 d', 3 => '5 w, 4 d, 3 h', 4 => '5 w, 4 d, 3 h, 2 m', 5 => '5 w, 4 d, 3 h, 2 m, 1 s', 6 => '5 w, 4 d, 3 h, 2 m, 1 s'), -3380521 => array(-1 => '5 w ago', 0 => '5 w ago', 1 => '5 w ago', 2 => '5 w, 4 d ago', 3 => '5 w, 4 d, 3 h ago', 4 => '5 w, 4 d, 3 h, 2 m ago', 5 => '5 w, 4 d, 3 h, 2 m, 1 s ago', 6 => '5 w, 4 d, 3 h, 2 m, 1 s ago'), 0 => array(-1 => $expected_zero, 0 => $expected_zero, 1 => $expected_zero, 2 => $expected_zero, 3 => $expected_zero, 4 => $expected_zero, 5 => $expected_zero, 6 => $expected_zero)); foreach ($tests as $duration => $sub_tests) { if (is_array($sub_tests)) { foreach ($sub_tests as $levels => $expect) { $this->assertEqual($expect, phutil_format_relative_time_detailed($duration, $levels), 'phutil_format_relative_time_detailed(' . $duration . ', ' . $levels . ')'); } } else { $expect = $sub_tests; $this->assertEqual($expect, phutil_format_relative_time_detailed($duration), 'phutil_format_relative_time_detailed(' . $duration . ')'); } } }
private function buildRepositoryUpdateInterval(PhabricatorRepository $repository) { $smart_wait = $repository->loadUpdateInterval(); $doc_href = PhabricatorEnv::getDoclink('Diffusion User Guide: Repository Updates'); return array(phutil_format_relative_time_detailed($smart_wait), " · ", phutil_tag('a', array('href' => $doc_href, 'target' => '_blank'), pht('Learn More'))); }
private function buildRepositoryStatus(PhabricatorRepository $repository, array $messages) { $viewer = $this->getViewer(); $is_cluster = $repository->getAlmanacServicePHID(); $view = new PHUIStatusListView(); if ($repository->isTracked()) { $view->addItem(id(new PHUIStatusItemView())->setIcon(PHUIStatusItemView::ICON_ACCEPT, 'green')->setTarget(pht('Repository Active'))); } else { $view->addItem(id(new PHUIStatusItemView())->setIcon(PHUIStatusItemView::ICON_WARNING, 'bluegrey')->setTarget(pht('Repository Inactive'))->setNote(pht('Activate this repository to begin or resume import.'))); return $view; } $binaries = array(); $svnlook_check = false; switch ($repository->getVersionControlSystem()) { case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: $binaries[] = 'git'; break; case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: $binaries[] = 'svn'; break; case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL: $binaries[] = 'hg'; break; } if ($repository->isHosted()) { $proto_https = PhabricatorRepositoryURI::BUILTIN_PROTOCOL_HTTPS; $proto_http = PhabricatorRepositoryURI::BUILTIN_PROTOCOL_HTTP; $can_http = $repository->canServeProtocol($proto_http, false) || $repository->canServeProtocol($proto_https, false); if ($can_http) { switch ($repository->getVersionControlSystem()) { case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: $binaries[] = 'git-http-backend'; break; case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: $binaries[] = 'svnserve'; $binaries[] = 'svnadmin'; $binaries[] = 'svnlook'; $svnlook_check = true; break; case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL: $binaries[] = 'hg'; break; } } $proto_ssh = PhabricatorRepositoryURI::BUILTIN_PROTOCOL_SSH; $can_ssh = $repository->canServeProtocol($proto_ssh, false); if ($can_ssh) { switch ($repository->getVersionControlSystem()) { case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: $binaries[] = 'git-receive-pack'; $binaries[] = 'git-upload-pack'; break; case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: $binaries[] = 'svnserve'; $binaries[] = 'svnadmin'; $binaries[] = 'svnlook'; $svnlook_check = true; break; case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL: $binaries[] = 'hg'; break; } } } $binaries = array_unique($binaries); if (!$is_cluster) { // We're only checking for binaries if we aren't running with a cluster // configuration. In theory, we could check for binaries on the // repository host machine, but we'd need to make this more complicated // to do that. foreach ($binaries as $binary) { $where = Filesystem::resolveBinary($binary); if (!$where) { $view->addItem(id(new PHUIStatusItemView())->setIcon(PHUIStatusItemView::ICON_WARNING, 'red')->setTarget(pht('Missing Binary %s', phutil_tag('tt', array(), $binary)))->setNote(pht("Unable to find this binary in the webserver's PATH. You may " . "need to configure %s.", $this->getEnvConfigLink()))); } else { $view->addItem(id(new PHUIStatusItemView())->setIcon(PHUIStatusItemView::ICON_ACCEPT, 'green')->setTarget(pht('Found Binary %s', phutil_tag('tt', array(), $binary)))->setNote(phutil_tag('tt', array(), $where))); } } // This gets checked generically above. However, for svn commit hooks, we // need this to be in environment.append-paths because subversion strips // PATH. if ($svnlook_check) { $where = Filesystem::resolveBinary('svnlook'); if ($where) { $path = substr($where, 0, strlen($where) - strlen('svnlook')); $dirs = PhabricatorEnv::getEnvConfig('environment.append-paths'); $in_path = false; foreach ($dirs as $dir) { if (Filesystem::isDescendant($path, $dir)) { $in_path = true; break; } } if (!$in_path) { $view->addItem(id(new PHUIStatusItemView())->setIcon(PHUIStatusItemView::ICON_WARNING, 'red')->setTarget(pht('Missing Binary %s', phutil_tag('tt', array(), $binary)))->setNote(pht('Unable to find this binary in `%s`. ' . 'You need to configure %s and include %s.', 'environment.append-paths', $this->getEnvConfigLink(), $path))); } } } } $doc_href = PhabricatorEnv::getDocLink('Managing Daemons with phd'); $daemon_instructions = pht('Use %s to start daemons. See %s.', phutil_tag('tt', array(), 'bin/phd start'), phutil_tag('a', array('href' => $doc_href), pht('Managing Daemons with phd'))); $pull_daemon = id(new PhabricatorDaemonLogQuery())->setViewer(PhabricatorUser::getOmnipotentUser())->withStatus(PhabricatorDaemonLogQuery::STATUS_ALIVE)->withDaemonClasses(array('PhabricatorRepositoryPullLocalDaemon'))->setLimit(1)->execute(); if ($pull_daemon) { // TODO: In a cluster environment, we need a daemon on this repository's // host, specifically, and we aren't checking for that right now. This // is a reasonable proxy for things being more-or-less correctly set up, // though. $view->addItem(id(new PHUIStatusItemView())->setIcon(PHUIStatusItemView::ICON_ACCEPT, 'green')->setTarget(pht('Pull Daemon Running'))); } else { $view->addItem(id(new PHUIStatusItemView())->setIcon(PHUIStatusItemView::ICON_WARNING, 'red')->setTarget(pht('Pull Daemon Not Running'))->setNote($daemon_instructions)); } $task_daemon = id(new PhabricatorDaemonLogQuery())->setViewer(PhabricatorUser::getOmnipotentUser())->withStatus(PhabricatorDaemonLogQuery::STATUS_ALIVE)->withDaemonClasses(array('PhabricatorTaskmasterDaemon'))->setLimit(1)->execute(); if ($task_daemon) { $view->addItem(id(new PHUIStatusItemView())->setIcon(PHUIStatusItemView::ICON_ACCEPT, 'green')->setTarget(pht('Task Daemon Running'))); } else { $view->addItem(id(new PHUIStatusItemView())->setIcon(PHUIStatusItemView::ICON_WARNING, 'red')->setTarget(pht('Task Daemon Not Running'))->setNote($daemon_instructions)); } if ($is_cluster) { // Just omit this status check for now in cluster environments. We // could make a service call and pull it from the repository host // eventually. } else { if ($repository->usesLocalWorkingCopy()) { $local_parent = dirname($repository->getLocalPath()); if (Filesystem::pathExists($local_parent)) { $view->addItem(id(new PHUIStatusItemView())->setIcon(PHUIStatusItemView::ICON_ACCEPT, 'green')->setTarget(pht('Storage Directory OK'))->setNote(phutil_tag('tt', array(), $local_parent))); } else { $view->addItem(id(new PHUIStatusItemView())->setIcon(PHUIStatusItemView::ICON_WARNING, 'red')->setTarget(pht('No Storage Directory'))->setNote(pht('Storage directory %s does not exist, or is not readable by ' . 'the webserver. Create this directory or make it readable.', phutil_tag('tt', array(), $local_parent)))); return $view; } $local_path = $repository->getLocalPath(); $message = idx($messages, PhabricatorRepositoryStatusMessage::TYPE_INIT); if ($message) { switch ($message->getStatusCode()) { case PhabricatorRepositoryStatusMessage::CODE_ERROR: $view->addItem(id(new PHUIStatusItemView())->setIcon(PHUIStatusItemView::ICON_WARNING, 'red')->setTarget(pht('Initialization Error'))->setNote($message->getParameter('message'))); return $view; case PhabricatorRepositoryStatusMessage::CODE_OKAY: if (Filesystem::pathExists($local_path)) { $view->addItem(id(new PHUIStatusItemView())->setIcon(PHUIStatusItemView::ICON_ACCEPT, 'green')->setTarget(pht('Working Copy OK'))->setNote(phutil_tag('tt', array(), $local_path))); } else { $view->addItem(id(new PHUIStatusItemView())->setIcon(PHUIStatusItemView::ICON_WARNING, 'red')->setTarget(pht('Working Copy Error'))->setNote(pht('Working copy %s has been deleted, or is not ' . 'readable by the webserver. Make this directory ' . 'readable. If it has been deleted, the daemons should ' . 'restore it automatically.', phutil_tag('tt', array(), $local_path)))); return $view; } break; case PhabricatorRepositoryStatusMessage::CODE_WORKING: $view->addItem(id(new PHUIStatusItemView())->setIcon(PHUIStatusItemView::ICON_CLOCK, 'green')->setTarget(pht('Initializing Working Copy'))->setNote(pht('Daemons are initializing the working copy.'))); return $view; default: $view->addItem(id(new PHUIStatusItemView())->setIcon(PHUIStatusItemView::ICON_WARNING, 'red')->setTarget(pht('Unknown Init Status'))->setNote($message->getStatusCode())); return $view; } } else { $view->addItem(id(new PHUIStatusItemView())->setIcon(PHUIStatusItemView::ICON_CLOCK, 'orange')->setTarget(pht('No Working Copy Yet'))->setNote(pht('Waiting for daemons to build a working copy.'))); return $view; } } } $message = idx($messages, PhabricatorRepositoryStatusMessage::TYPE_FETCH); if ($message) { switch ($message->getStatusCode()) { case PhabricatorRepositoryStatusMessage::CODE_ERROR: $message = $message->getParameter('message'); $suggestion = null; if (preg_match('/Permission denied \\(publickey\\)./', $message)) { $suggestion = pht('Public Key Error: This error usually indicates that the ' . 'keypair you have configured does not have permission to ' . 'access the repository.'); } $view->addItem(id(new PHUIStatusItemView())->setIcon(PHUIStatusItemView::ICON_WARNING, 'red')->setTarget(pht('Update Error'))->setNote($suggestion)); return $view; case PhabricatorRepositoryStatusMessage::CODE_OKAY: $ago = PhabricatorTime::getNow() - $message->getEpoch(); $view->addItem(id(new PHUIStatusItemView())->setIcon(PHUIStatusItemView::ICON_ACCEPT, 'green')->setTarget(pht('Updates OK'))->setNote(pht('Last updated %s (%s ago).', phabricator_datetime($message->getEpoch(), $viewer), phutil_format_relative_time_detailed($ago)))); break; } } else { $view->addItem(id(new PHUIStatusItemView())->setIcon(PHUIStatusItemView::ICON_CLOCK, 'orange')->setTarget(pht('Waiting For Update'))->setNote(pht('Waiting for daemons to read updates.'))); } if ($repository->isImporting()) { $ratio = $repository->loadImportProgress(); $percentage = sprintf('%.2f%%', 100 * $ratio); $view->addItem(id(new PHUIStatusItemView())->setIcon(PHUIStatusItemView::ICON_CLOCK, 'green')->setTarget(pht('Importing'))->setNote(pht('%s Complete', $percentage))); } else { $view->addItem(id(new PHUIStatusItemView())->setIcon(PHUIStatusItemView::ICON_ACCEPT, 'green')->setTarget(pht('Fully Imported'))); } if (idx($messages, PhabricatorRepositoryStatusMessage::TYPE_NEEDS_UPDATE)) { $view->addItem(id(new PHUIStatusItemView())->setIcon(PHUIStatusItemView::ICON_UP, 'indigo')->setTarget(pht('Prioritized'))->setNote(pht('This repository will be updated soon!'))); } return $view; }
public function renderPropertyViewValue(array $handles) { $absolute = phabricator_datetime($this->getObject()->getDateCreated(), $this->getViewer()); $relative = phutil_format_relative_time_detailed(time() - $this->getObject()->getDateCreated(), $levels = 2); return hsprintf('%s (%s)', $absolute, $relative); }
public function processRequest() { $request = $this->getRequest(); $viewer = $request->getUser(); $id = $this->id; $generation = $request->getInt('g'); $build = id(new HarbormasterBuildQuery())->setViewer($viewer)->withIDs(array($id))->executeOne(); if (!$build) { return new Aphront404Response(); } require_celerity_resource('harbormaster-css'); $title = pht('Build %d', $id); $header = id(new PHUIHeaderView())->setHeader($title)->setUser($viewer)->setPolicyObject($build); if ($build->isRestarting()) { $header->setStatus('fa-exclamation-triangle', 'red', pht('Restarting')); } else { if ($build->isStopping()) { $header->setStatus('fa-exclamation-triangle', 'red', pht('Pausing')); } else { if ($build->isResuming()) { $header->setStatus('fa-exclamation-triangle', 'red', pht('Resuming')); } } } $box = id(new PHUIObjectBoxView())->setHeader($header); $actions = $this->buildActionList($build); $this->buildPropertyLists($box, $build, $actions); $crumbs = $this->buildApplicationCrumbs(); $crumbs->addTextCrumb($build->getBuildable()->getMonogram(), '/' . $build->getBuildable()->getMonogram()); $crumbs->addTextCrumb($title); if ($generation === null || $generation > $build->getBuildGeneration() || $generation < 0) { $generation = $build->getBuildGeneration(); } $build_targets = id(new HarbormasterBuildTargetQuery())->setViewer($viewer)->needBuildSteps(true)->withBuildPHIDs(array($build->getPHID()))->withBuildGenerations(array($generation))->execute(); if ($build_targets) { $messages = id(new HarbormasterBuildMessageQuery())->setViewer($viewer)->withBuildTargetPHIDs(mpull($build_targets, 'getPHID'))->execute(); $messages = mgroup($messages, 'getBuildTargetPHID'); } else { $messages = array(); } $targets = array(); foreach ($build_targets as $build_target) { $header = id(new PHUIHeaderView())->setHeader($build_target->getName())->setUser($viewer); $target_box = id(new PHUIObjectBoxView())->setHeader($header); $properties = new PHUIPropertyListView(); $status_view = new PHUIStatusListView(); $item = new PHUIStatusItemView(); $status = $build_target->getTargetStatus(); $status_name = HarbormasterBuildTarget::getBuildTargetStatusName($status); $icon = HarbormasterBuildTarget::getBuildTargetStatusIcon($status); $color = HarbormasterBuildTarget::getBuildTargetStatusColor($status); $item->setTarget($status_name); $item->setIcon($icon, $color); $status_view->addItem($item); $properties->addProperty(pht('Name'), $build_target->getName()); if ($build_target->getDateStarted() !== null) { $properties->addProperty(pht('Started'), phabricator_datetime($build_target->getDateStarted(), $viewer)); if ($build_target->isComplete()) { $properties->addProperty(pht('Completed'), phabricator_datetime($build_target->getDateCompleted(), $viewer)); $properties->addProperty(pht('Duration'), phutil_format_relative_time_detailed($build_target->getDateCompleted() - $build_target->getDateStarted())); } else { $properties->addProperty(pht('Elapsed'), phutil_format_relative_time_detailed(time() - $build_target->getDateStarted())); } } $properties->addProperty(pht('Status'), $status_view); $target_box->addPropertyList($properties, pht('Overview')); $step = $build_target->getBuildStep(); if ($step) { $description = $step->getDescription(); if ($description) { $rendered = PhabricatorMarkupEngine::renderOneObject(id(new PhabricatorMarkupOneOff())->setContent($description)->setPreserveLinebreaks(true), 'default', $viewer); $properties->addSectionHeader(pht('Description')); $properties->addTextContent($rendered); } } else { $target_box->setFormErrors(array(pht('This build step has since been deleted on the build plan. ' . 'Some information may be omitted.'))); } $details = $build_target->getDetails(); if ($details) { $properties = new PHUIPropertyListView(); foreach ($details as $key => $value) { $properties->addProperty($key, $value); } $target_box->addPropertyList($properties, pht('Configuration')); } $variables = $build_target->getVariables(); if ($variables) { $properties = new PHUIPropertyListView(); foreach ($variables as $key => $value) { $properties->addProperty($key, $value); } $target_box->addPropertyList($properties, pht('Variables')); } $artifacts = $this->buildArtifacts($build_target); if ($artifacts) { $properties = new PHUIPropertyListView(); $properties->addRawContent($artifacts); $target_box->addPropertyList($properties, pht('Artifacts')); } $build_messages = idx($messages, $build_target->getPHID(), array()); if ($build_messages) { $properties = new PHUIPropertyListView(); $properties->addRawContent($this->buildMessages($build_messages)); $target_box->addPropertyList($properties, pht('Messages')); } $properties = new PHUIPropertyListView(); $properties->addProperty('Build Target ID', $build_target->getID()); $target_box->addPropertyList($properties, pht('Metadata')); $targets[] = $target_box; $targets[] = $this->buildLog($build, $build_target); } $xactions = id(new HarbormasterBuildTransactionQuery())->setViewer($viewer)->withObjectPHIDs(array($build->getPHID()))->execute(); $timeline = id(new PhabricatorApplicationTransactionView())->setUser($viewer)->setObjectPHID($build->getPHID())->setTransactions($xactions); return $this->buildApplicationPage(array($crumbs, $box, $targets, $timeline), array('title' => $title)); }
public function handleRequest(AphrontRequest $request) { $request = $this->getRequest(); $viewer = $request->getUser(); $id = $request->getURIData('id'); $generation = $request->getInt('g'); $build = id(new HarbormasterBuildQuery())->setViewer($viewer)->withIDs(array($id))->executeOne(); if (!$build) { return new Aphront404Response(); } require_celerity_resource('harbormaster-css'); $title = pht('Build %d', $id); $header = id(new PHUIHeaderView())->setHeader($title)->setUser($viewer)->setPolicyObject($build); if ($build->isRestarting()) { $header->setStatus('fa-exclamation-triangle', 'red', pht('Restarting')); } else { if ($build->isPausing()) { $header->setStatus('fa-exclamation-triangle', 'red', pht('Pausing')); } else { if ($build->isResuming()) { $header->setStatus('fa-exclamation-triangle', 'red', pht('Resuming')); } else { if ($build->isAborting()) { $header->setStatus('fa-exclamation-triangle', 'red', pht('Aborting')); } } } } $box = id(new PHUIObjectBoxView())->setHeader($header); $actions = $this->buildActionList($build); $this->buildPropertyLists($box, $build, $actions); $crumbs = $this->buildApplicationCrumbs(); $this->addBuildableCrumb($crumbs, $build->getBuildable()); $crumbs->addTextCrumb($title); if ($generation === null || $generation > $build->getBuildGeneration() || $generation < 0) { $generation = $build->getBuildGeneration(); } $build_targets = id(new HarbormasterBuildTargetQuery())->setViewer($viewer)->needBuildSteps(true)->withBuildPHIDs(array($build->getPHID()))->withBuildGenerations(array($generation))->execute(); if ($build_targets) { $messages = id(new HarbormasterBuildMessageQuery())->setViewer($viewer)->withBuildTargetPHIDs(mpull($build_targets, 'getPHID'))->execute(); $messages = mgroup($messages, 'getBuildTargetPHID'); } else { $messages = array(); } if ($build_targets) { $artifacts = id(new HarbormasterBuildArtifactQuery())->setViewer($viewer)->withBuildTargetPHIDs(mpull($build_targets, 'getPHID'))->execute(); $artifacts = msort($artifacts, 'getArtifactKey'); $artifacts = mgroup($artifacts, 'getBuildTargetPHID'); } else { $artifacts = array(); } $targets = array(); foreach ($build_targets as $build_target) { $header = id(new PHUIHeaderView())->setHeader($build_target->getName())->setUser($viewer); $target_box = id(new PHUIObjectBoxView())->setHeader($header); $properties = new PHUIPropertyListView(); $target_artifacts = idx($artifacts, $build_target->getPHID(), array()); $links = array(); $type_uri = HarbormasterURIArtifact::ARTIFACTCONST; foreach ($target_artifacts as $artifact) { if ($artifact->getArtifactType() == $type_uri) { $impl = $artifact->getArtifactImplementation(); if ($impl->isExternalLink()) { $links[] = $impl->renderLink(); } } } if ($links) { $links = phutil_implode_html(phutil_tag('br'), $links); $properties->addProperty(pht('External Link'), $links); } $status_view = new PHUIStatusListView(); $item = new PHUIStatusItemView(); $status = $build_target->getTargetStatus(); $status_name = HarbormasterBuildTarget::getBuildTargetStatusName($status); $icon = HarbormasterBuildTarget::getBuildTargetStatusIcon($status); $color = HarbormasterBuildTarget::getBuildTargetStatusColor($status); $item->setTarget($status_name); $item->setIcon($icon, $color); $status_view->addItem($item); $when = array(); $started = $build_target->getDateStarted(); $now = PhabricatorTime::getNow(); if ($started) { $ended = $build_target->getDateCompleted(); if ($ended) { $when[] = pht('Completed at %s', phabricator_datetime($started, $viewer)); $duration = $ended - $started; if ($duration) { $when[] = pht('Built for %s', phutil_format_relative_time_detailed($duration)); } else { $when[] = pht('Built instantly'); } } else { $when[] = pht('Started at %s', phabricator_datetime($started, $viewer)); $duration = $now - $started; if ($duration) { $when[] = pht('Running for %s', phutil_format_relative_time_detailed($duration)); } } } else { $created = $build_target->getDateCreated(); $when[] = pht('Queued at %s', phabricator_datetime($started, $viewer)); $duration = $now - $created; if ($duration) { $when[] = pht('Waiting for %s', phutil_format_relative_time_detailed($duration)); } } $properties->addProperty(pht('When'), phutil_implode_html(" · ", $when)); $properties->addProperty(pht('Status'), $status_view); $target_box->addPropertyList($properties, pht('Overview')); $step = $build_target->getBuildStep(); if ($step) { $description = $step->getDescription(); if ($description) { $description = new PHUIRemarkupView($viewer, $description); $properties->addSectionHeader(pht('Description'), PHUIPropertyListView::ICON_SUMMARY); $properties->addTextContent($description); } } else { $target_box->setFormErrors(array(pht('This build step has since been deleted on the build plan. ' . 'Some information may be omitted.'))); } $details = $build_target->getDetails(); $properties = new PHUIPropertyListView(); foreach ($details as $key => $value) { $properties->addProperty($key, $value); } $target_box->addPropertyList($properties, pht('Configuration')); $variables = $build_target->getVariables(); $properties = new PHUIPropertyListView(); $properties->addRawContent($this->buildProperties($variables)); $target_box->addPropertyList($properties, pht('Variables')); $artifacts_tab = $this->buildArtifacts($build_target, $target_artifacts); $properties = new PHUIPropertyListView(); $properties->addRawContent($artifacts_tab); $target_box->addPropertyList($properties, pht('Artifacts')); $build_messages = idx($messages, $build_target->getPHID(), array()); $properties = new PHUIPropertyListView(); $properties->addRawContent($this->buildMessages($build_messages)); $target_box->addPropertyList($properties, pht('Messages')); $properties = new PHUIPropertyListView(); $properties->addProperty(pht('Build Target ID'), $build_target->getID()); $properties->addProperty(pht('Build Target PHID'), $build_target->getPHID()); $target_box->addPropertyList($properties, pht('Metadata')); $targets[] = $target_box; $targets[] = $this->buildLog($build, $build_target); } $timeline = $this->buildTransactionTimeline($build, new HarbormasterBuildTransactionQuery()); $timeline->setShouldTerminate(true); return $this->buildApplicationPage(array($crumbs, $box, $targets, $timeline), array('title' => $title)); }
protected function renderResultList(array $usertimes, PhabricatorSavedQuery $query, array $handles) { assert_instances_of($usertimes, 'PhrequentUserTime'); $viewer = $this->requireViewer(); $view = id(new PHUIObjectItemListView())->setUser($viewer); foreach ($usertimes as $usertime) { $item = new PHUIObjectItemView(); if ($usertime->getObjectPHID() === null) { $item->setHeader($usertime->getNote()); } else { $obj = $handles[$usertime->getObjectPHID()]; $item->setHeader($obj->getLinkName()); $item->setHref($obj->getURI()); } $item->setObject($usertime); $item->addByline(pht('Tracked: %s', $handles[$usertime->getUserPHID()]->renderLink())); $started_date = phabricator_date($usertime->getDateStarted(), $viewer); $item->addIcon('none', $started_date); $block = new PhrequentTimeBlock(array($usertime)); $time_spent = $block->getTimeSpentOnObject($usertime->getObjectPHID(), PhabricatorTime::getNow()); $time_spent = $time_spent == 0 ? 'none' : phutil_format_relative_time_detailed($time_spent); if ($usertime->getDateEnded() !== null) { $item->addAttribute(pht('Tracked %s', $time_spent)); $item->addAttribute(pht('Ended on %s', phabricator_datetime($usertime->getDateEnded(), $viewer))); } else { $item->addAttribute(pht('Tracked %s so far', $time_spent)); if ($usertime->getObjectPHID() !== null && $usertime->getUserPHID() === $viewer->getPHID()) { $item->addAction(id(new PHUIListItemView())->setIcon('fa-stop')->addSigil('phrequent-stop-tracking')->setWorkflow(true)->setRenderNameAsTooltip(true)->setName(pht('Stop'))->setHref('/phrequent/track/stop/' . $usertime->getObjectPHID() . '/')); } $item->setBarColor('green'); } $view->addItem($item); } return $view; }