Page MenuHomestyx hydra

No OneTemporary

This file is larger than 256 KB, so syntax highlighting was skipped.
diff --git a/src/applications/almanac/controller/AlmanacBindingViewController.php b/src/applications/almanac/controller/AlmanacBindingViewController.php
index ead3e1b4c1..d1ed2178d2 100644
--- a/src/applications/almanac/controller/AlmanacBindingViewController.php
+++ b/src/applications/almanac/controller/AlmanacBindingViewController.php
@@ -1,159 +1,159 @@
<?php
final class AlmanacBindingViewController
extends AlmanacServiceController {
public function shouldAllowPublic() {
return true;
}
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$id = $request->getURIData('id');
$binding = id(new AlmanacBindingQuery())
->setViewer($viewer)
->withIDs(array($id))
->needProperties(true)
->executeOne();
if (!$binding) {
return new Aphront404Response();
}
$service = $binding->getService();
$service_uri = $service->getURI();
$title = pht('Binding %s', $binding->getID());
$properties = $this->buildPropertyList($binding);
$details = $this->buildPropertySection($binding);
$curtain = $this->buildCurtain($binding);
$header = id(new PHUIHeaderView())
->setUser($viewer)
->setHeader($title)
->setPolicyObject($binding)
->setHeaderIcon('fa-object-group');
if ($binding->getIsDisabled()) {
$header->setStatus('fa-ban', 'red', pht('Disabled'));
}
$issue = null;
if ($binding->getService()->isClusterService()) {
$issue = $this->addClusterMessage(
pht('The service for this binding is a cluster service.'),
pht(
'The service for this binding is a cluster service. You do not '.
'have permission to manage cluster services, so this binding can '.
'not be edited.'));
}
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb($service->getName(), $service_uri);
$crumbs->addTextCrumb($title);
$crumbs->setBorder(true);
$timeline = $this->buildTransactionTimeline(
$binding,
new AlmanacBindingTransactionQuery());
$timeline->setShouldTerminate(true);
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setCurtain($curtain)
->setMainColumn(array(
$issue,
$this->buildAlmanacPropertiesTable($binding),
$timeline,
))
- ->addPropertySection(pht('DETAILS'), $details);
+ ->addPropertySection(pht('Details'), $details);
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->appendChild(
array(
$view,
));
}
private function buildPropertySection(AlmanacBinding $binding) {
$viewer = $this->getViewer();
$properties = id(new PHUIPropertyListView())
->setUser($viewer);
$properties->addProperty(
pht('Service'),
$viewer->renderHandle($binding->getServicePHID()));
$properties->addProperty(
pht('Device'),
$viewer->renderHandle($binding->getDevicePHID()));
$properties->addProperty(
pht('Network'),
$viewer->renderHandle($binding->getInterface()->getNetworkPHID()));
$properties->addProperty(
pht('Interface'),
$binding->getInterface()->renderDisplayAddress());
return $properties;
}
private function buildPropertyList(AlmanacBinding $binding) {
$viewer = $this->getViewer();
$properties = id(new PHUIPropertyListView())
->setUser($viewer)
->setObject($binding);
$properties->invokeWillRenderEvent();
return $properties;
}
private function buildCurtain(AlmanacBinding $binding) {
$viewer = $this->getViewer();
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$binding,
PhabricatorPolicyCapability::CAN_EDIT);
$id = $binding->getID();
$edit_uri = $this->getApplicationURI("binding/edit/{$id}/");
$disable_uri = $this->getApplicationURI("binding/disable/{$id}/");
$curtain = $this->newCurtainView($binding);
$curtain->addAction(
id(new PhabricatorActionView())
->setIcon('fa-pencil')
->setName(pht('Edit Binding'))
->setHref($edit_uri)
->setWorkflow(!$can_edit)
->setDisabled(!$can_edit));
if ($binding->getIsDisabled()) {
$disable_icon = 'fa-check';
$disable_text = pht('Enable Binding');
} else {
$disable_icon = 'fa-ban';
$disable_text = pht('Disable Binding');
}
$curtain->addAction(
id(new PhabricatorActionView())
->setIcon($disable_icon)
->setName($disable_text)
->setHref($disable_uri)
->setWorkflow(true)
->setDisabled(!$can_edit));
return $curtain;
}
}
diff --git a/src/applications/almanac/controller/AlmanacController.php b/src/applications/almanac/controller/AlmanacController.php
index c76fdf11b5..c23e99591d 100644
--- a/src/applications/almanac/controller/AlmanacController.php
+++ b/src/applications/almanac/controller/AlmanacController.php
@@ -1,214 +1,214 @@
<?php
abstract class AlmanacController
extends PhabricatorController {
protected function buildAlmanacPropertiesTable(
AlmanacPropertyInterface $object) {
$viewer = $this->getViewer();
$properties = $object->getAlmanacProperties();
$this->requireResource('almanac-css');
Javelin::initBehavior('phabricator-tooltips', array());
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$object,
PhabricatorPolicyCapability::CAN_EDIT);
$properties = $object->getAlmanacProperties();
$icon_builtin = id(new PHUIIconView())
->setIcon('fa-circle')
->addSigil('has-tooltip')
->setMetadata(
array(
'tip' => pht('Builtin Property'),
'align' => 'E',
));
$icon_custom = id(new PHUIIconView())
->setIcon('fa-circle-o grey')
->addSigil('has-tooltip')
->setMetadata(
array(
'tip' => pht('Custom Property'),
'align' => 'E',
));
$builtins = $object->getAlmanacPropertyFieldSpecifications();
$defaults = mpull($builtins, null, 'getValueForTransaction');
// Sort fields so builtin fields appear first, then fields are ordered
// alphabetically.
$properties = msort($properties, 'getFieldName');
$head = array();
$tail = array();
foreach ($properties as $property) {
$key = $property->getFieldName();
if (isset($builtins[$key])) {
$head[$key] = $property;
} else {
$tail[$key] = $property;
}
}
$properties = $head + $tail;
$delete_base = $this->getApplicationURI('property/delete/');
$edit_base = $this->getApplicationURI('property/update/');
$rows = array();
foreach ($properties as $key => $property) {
$value = $property->getFieldValue();
$is_builtin = isset($builtins[$key]);
$delete_uri = id(new PhutilURI($delete_base))
->setQueryParams(
array(
'key' => $key,
'objectPHID' => $object->getPHID(),
));
$edit_uri = id(new PhutilURI($edit_base))
->setQueryParams(
array(
'key' => $key,
'objectPHID' => $object->getPHID(),
));
$delete = javelin_tag(
'a',
array(
'class' => ($can_edit
? 'button grey small'
: 'button grey small disabled'),
'sigil' => 'workflow',
'href' => $delete_uri,
),
$is_builtin ? pht('Reset') : pht('Delete'));
$default = idx($defaults, $key);
$is_default = ($default !== null && $default === $value);
$display_value = PhabricatorConfigJSON::prettyPrintJSON($value);
if ($is_default) {
$display_value = phutil_tag(
'span',
array(
'class' => 'almanac-default-property-value',
),
$display_value);
}
$display_key = $key;
if ($can_edit) {
$display_key = javelin_tag(
'a',
array(
'href' => $edit_uri,
'sigil' => 'workflow',
),
$display_key);
}
$rows[] = array(
($is_builtin ? $icon_builtin : $icon_custom),
$display_key,
$display_value,
$delete,
);
}
$table = id(new AphrontTableView($rows))
->setNoDataString(pht('No properties.'))
->setHeaders(
array(
null,
pht('Name'),
pht('Value'),
null,
))
->setColumnClasses(
array(
null,
null,
'wide',
'action',
));
$phid = $object->getPHID();
$add_uri = id(new PhutilURI($edit_base))
->setQueryParam('objectPHID', $object->getPHID());
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$object,
PhabricatorPolicyCapability::CAN_EDIT);
$add_button = id(new PHUIButtonView())
->setTag('a')
->setHref($add_uri)
->setWorkflow(true)
->setDisabled(!$can_edit)
->setText(pht('Add Property'))
->setIcon('fa-plus');
$header = id(new PHUIHeaderView())
- ->setHeader(pht('PROPERTIES'))
+ ->setHeader(pht('Properties'))
->addActionLink($add_button);
return id(new PHUIObjectBoxView())
->setHeader($header)
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->setTable($table);
}
protected function addClusterMessage(
$positive,
$negative) {
$can_manage = $this->hasApplicationCapability(
AlmanacManageClusterServicesCapability::CAPABILITY);
$doc_link = phutil_tag(
'a',
array(
'href' => PhabricatorEnv::getDoclink(
'User Guide: Phabricator Clusters'),
'target' => '_blank',
),
pht('Learn More'));
if ($can_manage) {
$severity = PHUIInfoView::SEVERITY_NOTICE;
$message = $positive;
} else {
$severity = PHUIInfoView::SEVERITY_WARNING;
$message = $negative;
}
$icon = id(new PHUIIconView())
->setIcon('fa-sitemap');
return id(new PHUIInfoView())
->setSeverity($severity)
->setErrors(
array(
array($icon, ' ', $message, ' ', $doc_link),
));
}
protected function getPropertyDeleteURI($object) {
return null;
}
protected function getPropertyUpdateURI($object) {
return null;
}
}
diff --git a/src/applications/almanac/controller/AlmanacServiceViewController.php b/src/applications/almanac/controller/AlmanacServiceViewController.php
index 1036dc9e78..f4057ca32f 100644
--- a/src/applications/almanac/controller/AlmanacServiceViewController.php
+++ b/src/applications/almanac/controller/AlmanacServiceViewController.php
@@ -1,151 +1,151 @@
<?php
final class AlmanacServiceViewController
extends AlmanacServiceController {
public function shouldAllowPublic() {
return true;
}
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$name = $request->getURIData('name');
$service = id(new AlmanacServiceQuery())
->setViewer($viewer)
->withNames(array($name))
->needProperties(true)
->executeOne();
if (!$service) {
return new Aphront404Response();
}
$title = pht('Service %s', $service->getName());
$curtain = $this->buildCurtain($service);
$details = $this->buildPropertySection($service);
$header = id(new PHUIHeaderView())
->setUser($viewer)
->setHeader($service->getName())
->setPolicyObject($service)
->setHeaderIcon('fa-plug');
$issue = null;
if ($service->isClusterService()) {
$issue = $this->addClusterMessage(
pht('This is a cluster service.'),
pht(
'This service is a cluster service. You do not have permission to '.
'edit cluster services, so you can not edit this service.'));
}
$bindings = $this->buildBindingList($service);
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb($service->getName());
$crumbs->setBorder(true);
$timeline = $this->buildTransactionTimeline(
$service,
new AlmanacServiceTransactionQuery());
$timeline->setShouldTerminate(true);
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setCurtain($curtain)
->setMainColumn(array(
$issue,
$details,
$bindings,
$this->buildAlmanacPropertiesTable($service),
$timeline,
));
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->appendChild($view);
}
private function buildPropertySection(
AlmanacService $service) {
$viewer = $this->getViewer();
$properties = id(new PHUIPropertyListView())
->setUser($viewer);
$properties->addProperty(
pht('Service Type'),
$service->getServiceImplementation()->getServiceTypeShortName());
return id(new PHUIObjectBoxView())
- ->setHeaderText(pht('DETAILS'))
+ ->setHeaderText(pht('Details'))
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->appendChild($properties);
}
private function buildCurtain(AlmanacService $service) {
$viewer = $this->getViewer();
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$service,
PhabricatorPolicyCapability::CAN_EDIT);
$id = $service->getID();
$edit_uri = $this->getApplicationURI("service/edit/{$id}/");
$curtain = $this->newCurtainView($service);
$curtain->addAction(
id(new PhabricatorActionView())
->setIcon('fa-pencil')
->setName(pht('Edit Service'))
->setHref($edit_uri)
->setWorkflow(!$can_edit)
->setDisabled(!$can_edit));
return $curtain;
}
private function buildBindingList(AlmanacService $service) {
$viewer = $this->getViewer();
$id = $service->getID();
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$service,
PhabricatorPolicyCapability::CAN_EDIT);
$bindings = id(new AlmanacBindingQuery())
->setViewer($viewer)
->withServicePHIDs(array($service->getPHID()))
->execute();
$table = id(new AlmanacBindingTableView())
->setNoDataString(
pht('This service has not been bound to any device interfaces yet.'))
->setUser($viewer)
->setBindings($bindings)
->setHideServiceColumn(true);
$header = id(new PHUIHeaderView())
->setHeader(pht('SERVICE BINDINGS'))
->addActionLink(
id(new PHUIButtonView())
->setTag('a')
->setHref($this->getApplicationURI("binding/edit/?serviceID={$id}"))
->setWorkflow(!$can_edit)
->setDisabled(!$can_edit)
->setText(pht('Add Binding'))
->setIcon('fa-plus'));
return id(new PHUIObjectBoxView())
->setHeader($header)
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->setTable($table);
}
}
diff --git a/src/applications/badges/controller/PhabricatorBadgesViewController.php b/src/applications/badges/controller/PhabricatorBadgesViewController.php
index 4539440588..05f6286020 100644
--- a/src/applications/badges/controller/PhabricatorBadgesViewController.php
+++ b/src/applications/badges/controller/PhabricatorBadgesViewController.php
@@ -1,159 +1,159 @@
<?php
final class PhabricatorBadgesViewController
extends PhabricatorBadgesController {
public function shouldAllowPublic() {
return true;
}
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$id = $request->getURIData('id');
$badge = id(new PhabricatorBadgesQuery())
->setViewer($viewer)
->withIDs(array($id))
->needRecipients(true)
->executeOne();
if (!$badge) {
return new Aphront404Response();
}
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb($badge->getName());
$crumbs->setBorder(true);
$title = $badge->getName();
if ($badge->isArchived()) {
$status_icon = 'fa-ban';
$status_color = 'dark';
} else {
$status_icon = 'fa-check';
$status_color = 'bluegrey';
}
$status_name = idx(
PhabricatorBadgesBadge::getStatusNameMap(),
$badge->getStatus());
$header = id(new PHUIHeaderView())
->setHeader($badge->getName())
->setUser($viewer)
->setPolicyObject($badge)
->setStatus($status_icon, $status_color, $status_name)
->setHeaderIcon('fa-trophy');
$curtain = $this->buildCurtain($badge);
$details = $this->buildDetailsView($badge);
$timeline = $this->buildTransactionTimeline(
$badge,
new PhabricatorBadgesTransactionQuery());
$awards = $badge->getAwards();
$recipient_phids = mpull($awards, 'getRecipientPHID');
$recipient_phids = array_reverse($recipient_phids);
$handles = $this->loadViewerHandles($recipient_phids);
$recipient_list = id(new PhabricatorBadgesRecipientsListView())
->setBadge($badge)
->setHandles($handles)
->setUser($viewer);
$comment_view = id(new PhabricatorBadgesEditEngine())
->setViewer($viewer)
->buildEditEngineCommentView($badge);
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setCurtain($curtain)
->setMainColumn(array(
$recipient_list,
$timeline,
$comment_view,
))
- ->addPropertySection(pht('DESCRIPTION'), $details);
+ ->addPropertySection(pht('Description'), $details);
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->setPageObjectPHIDs(array($badge->getPHID()))
->appendChild($view);
}
private function buildDetailsView(
PhabricatorBadgesBadge $badge) {
$viewer = $this->getViewer();
$view = id(new PHUIPropertyListView())
->setUser($viewer);
$description = $badge->getDescription();
if (strlen($description)) {
$view->addTextContent(
new PHUIRemarkupView($viewer, $description));
}
$badge = id(new PHUIBadgeView())
->setIcon($badge->getIcon())
->setHeader($badge->getName())
->setSubhead($badge->getFlavor())
->setQuality($badge->getQuality());
$view->addTextContent($badge);
return $view;
}
private function buildCurtain(PhabricatorBadgesBadge $badge) {
$viewer = $this->getViewer();
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$badge,
PhabricatorPolicyCapability::CAN_EDIT);
$id = $badge->getID();
$edit_uri = $this->getApplicationURI("/edit/{$id}/");
$archive_uri = $this->getApplicationURI("/archive/{$id}/");
$award_uri = $this->getApplicationURI("/recipients/{$id}/");
$curtain = $this->newCurtainView($badge);
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('Edit Badge'))
->setIcon('fa-pencil')
->setDisabled(!$can_edit)
->setHref($edit_uri));
if ($badge->isArchived()) {
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('Activate Badge'))
->setIcon('fa-check')
->setDisabled(!$can_edit)
->setWorkflow($can_edit)
->setHref($archive_uri));
} else {
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('Archive Badge'))
->setIcon('fa-ban')
->setDisabled(!$can_edit)
->setWorkflow($can_edit)
->setHref($archive_uri));
}
$curtain->addAction(
id(new PhabricatorActionView())
->setName('Add Recipients')
->setIcon('fa-users')
->setDisabled(!$can_edit)
->setWorkflow(true)
->setHref($award_uri));
return $curtain;
}
}
diff --git a/src/applications/calendar/controller/PhabricatorCalendarEventViewController.php b/src/applications/calendar/controller/PhabricatorCalendarEventViewController.php
index 5880db1210..7c3b34bc09 100644
--- a/src/applications/calendar/controller/PhabricatorCalendarEventViewController.php
+++ b/src/applications/calendar/controller/PhabricatorCalendarEventViewController.php
@@ -1,388 +1,388 @@
<?php
final class PhabricatorCalendarEventViewController
extends PhabricatorCalendarController {
public function shouldAllowPublic() {
return true;
}
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$id = $request->getURIData('id');
$sequence = $request->getURIData('sequence');
$timeline = null;
$event = id(new PhabricatorCalendarEventQuery())
->setViewer($viewer)
->withIDs(array($id))
->executeOne();
if (!$event) {
return new Aphront404Response();
}
if ($sequence) {
$result = $this->getEventAtIndexForGhostPHID(
$viewer,
$event->getPHID(),
$sequence);
if ($result) {
$parent_event = $event;
$event = $result;
$event->attachParentEvent($parent_event);
return id(new AphrontRedirectResponse())
->setURI('/E'.$result->getID());
} else if ($sequence && $event->getIsRecurring()) {
$parent_event = $event;
$event = $event->generateNthGhost($sequence, $viewer);
$event->attachParentEvent($parent_event);
} else if ($sequence) {
return new Aphront404Response();
}
$title = $event->getMonogram().' ('.$sequence.')';
$page_title = $title.' '.$event->getName();
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb($title, '/'.$event->getMonogram().'/'.$sequence);
} else {
$title = 'E'.$event->getID();
$page_title = $title.' '.$event->getName();
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb($title);
$crumbs->setBorder(true);
}
if (!$event->getIsGhostEvent()) {
$timeline = $this->buildTransactionTimeline(
$event,
new PhabricatorCalendarEventTransactionQuery());
}
$header = $this->buildHeaderView($event);
$curtain = $this->buildCurtain($event);
$details = $this->buildPropertySection($event);
$description = $this->buildDescriptionView($event);
$is_serious = PhabricatorEnv::getEnvConfig('phabricator.serious-business');
$add_comment_header = $is_serious
? pht('Add Comment')
: pht('Add To Plate');
$draft = PhabricatorDraft::newFromUserAndKey($viewer, $event->getPHID());
if ($sequence) {
$comment_uri = $this->getApplicationURI(
'/event/comment/'.$event->getID().'/'.$sequence.'/');
} else {
$comment_uri = $this->getApplicationURI(
'/event/comment/'.$event->getID().'/');
}
$add_comment_form = id(new PhabricatorApplicationTransactionCommentView())
->setUser($viewer)
->setObjectPHID($event->getPHID())
->setDraft($draft)
->setHeaderText($add_comment_header)
->setAction($comment_uri)
->setSubmitButtonName(pht('Add Comment'));
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setMainColumn(array(
$timeline,
$add_comment_form,
))
->setCurtain($curtain)
- ->addPropertySection(pht('DETAILS'), $details)
- ->addPropertySection(pht('DESCRIPTION'), $description);
+ ->addPropertySection(pht('Details'), $details)
+ ->addPropertySection(pht('Description'), $description);
return $this->newPage()
->setTitle($page_title)
->setCrumbs($crumbs)
->setPageObjectPHIDs(array($event->getPHID()))
->appendChild(
array(
$view,
));
}
private function buildHeaderView(
PhabricatorCalendarEvent $event) {
$viewer = $this->getViewer();
$id = $event->getID();
$is_cancelled = $event->getIsCancelled();
$icon = $is_cancelled ? ('fa-ban') : ('fa-check');
$color = $is_cancelled ? ('red') : ('bluegrey');
$status = $is_cancelled ? pht('Cancelled') : pht('Active');
$invite_status = $event->getUserInviteStatus($viewer->getPHID());
$status_invited = PhabricatorCalendarEventInvitee::STATUS_INVITED;
$is_invite_pending = ($invite_status == $status_invited);
$header = id(new PHUIHeaderView())
->setUser($viewer)
->setHeader($event->getName())
->setStatus($icon, $color, $status)
->setPolicyObject($event)
->setHeaderIcon('fa-calendar');
if ($is_invite_pending) {
$decline_button = id(new PHUIButtonView())
->setTag('a')
->setIcon('fa-times grey')
->setHref($this->getApplicationURI("/event/decline/{$id}/"))
->setWorkflow(true)
->setText(pht('Decline'));
$accept_button = id(new PHUIButtonView())
->setTag('a')
->setIcon('fa-check green')
->setHref($this->getApplicationURI("/event/accept/{$id}/"))
->setWorkflow(true)
->setText(pht('Accept'));
$header->addActionLink($decline_button)
->addActionLink($accept_button);
}
return $header;
}
private function buildCurtain(PhabricatorCalendarEvent $event) {
$viewer = $this->getRequest()->getUser();
$id = $event->getID();
$is_cancelled = $event->getIsCancelled();
$is_attending = $event->getIsUserAttending($viewer->getPHID());
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$event,
PhabricatorPolicyCapability::CAN_EDIT);
$edit_label = false;
$edit_uri = false;
if ($event->getIsGhostEvent()) {
$index = $event->getSequenceIndex();
$edit_label = pht('Edit This Instance');
$edit_uri = "event/edit/{$id}/{$index}/";
} else if ($event->getIsRecurrenceException()) {
$edit_label = pht('Edit This Instance');
$edit_uri = "event/edit/{$id}/";
} else {
$edit_label = pht('Edit');
$edit_uri = "event/edit/{$id}/";
}
$curtain = $this->newCurtainView($event);
if ($edit_label && $edit_uri) {
$curtain->addAction(
id(new PhabricatorActionView())
->setName($edit_label)
->setIcon('fa-pencil')
->setHref($this->getApplicationURI($edit_uri))
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit));
}
if ($is_attending) {
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('Decline Event'))
->setIcon('fa-user-times')
->setHref($this->getApplicationURI("event/join/{$id}/"))
->setWorkflow(true));
} else {
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('Join Event'))
->setIcon('fa-user-plus')
->setHref($this->getApplicationURI("event/join/{$id}/"))
->setWorkflow(true));
}
$cancel_uri = $this->getApplicationURI("event/cancel/{$id}/");
if ($event->getIsGhostEvent()) {
$index = $event->getSequenceIndex();
$can_reinstate = $event->getIsParentCancelled();
$cancel_label = pht('Cancel This Instance');
$reinstate_label = pht('Reinstate This Instance');
$cancel_disabled = (!$can_edit || $can_reinstate);
$cancel_uri = $this->getApplicationURI("event/cancel/{$id}/{$index}/");
} else if ($event->getIsRecurrenceException()) {
$can_reinstate = $event->getIsParentCancelled();
$cancel_label = pht('Cancel This Instance');
$reinstate_label = pht('Reinstate This Instance');
$cancel_disabled = (!$can_edit || $can_reinstate);
} else if ($event->getIsRecurrenceParent()) {
$cancel_label = pht('Cancel Recurrence');
$reinstate_label = pht('Reinstate Recurrence');
$cancel_disabled = !$can_edit;
} else {
$cancel_label = pht('Cancel Event');
$reinstate_label = pht('Reinstate Event');
$cancel_disabled = !$can_edit;
}
if ($is_cancelled) {
$curtain->addAction(
id(new PhabricatorActionView())
->setName($reinstate_label)
->setIcon('fa-plus')
->setHref($cancel_uri)
->setDisabled($cancel_disabled)
->setWorkflow(true));
} else {
$curtain->addAction(
id(new PhabricatorActionView())
->setName($cancel_label)
->setIcon('fa-times')
->setHref($cancel_uri)
->setDisabled($cancel_disabled)
->setWorkflow(true));
}
return $curtain;
}
private function buildPropertySection(
PhabricatorCalendarEvent $event) {
$viewer = $this->getViewer();
$properties = id(new PHUIPropertyListView())
->setUser($viewer);
if ($event->getIsAllDay()) {
$date_start = phabricator_date($event->getDateFrom(), $viewer);
$date_end = phabricator_date($event->getDateTo(), $viewer);
if ($date_start == $date_end) {
$properties->addProperty(
pht('Time'),
phabricator_date($event->getDateFrom(), $viewer));
} else {
$properties->addProperty(
pht('Starts'),
phabricator_date($event->getDateFrom(), $viewer));
$properties->addProperty(
pht('Ends'),
phabricator_date($event->getDateTo(), $viewer));
}
} else {
$properties->addProperty(
pht('Starts'),
phabricator_datetime($event->getDateFrom(), $viewer));
$properties->addProperty(
pht('Ends'),
phabricator_datetime($event->getDateTo(), $viewer));
}
if ($event->getIsRecurring()) {
$properties->addProperty(
pht('Recurs'),
ucwords(idx($event->getRecurrenceFrequency(), 'rule')));
if ($event->getRecurrenceEndDate()) {
$properties->addProperty(
pht('Recurrence Ends'),
phabricator_datetime($event->getRecurrenceEndDate(), $viewer));
}
if ($event->getInstanceOfEventPHID()) {
$properties->addProperty(
pht('Recurrence of Event'),
pht('%s of %s',
$event->getSequenceIndex(),
$viewer->renderHandle($event->getInstanceOfEventPHID())->render()));
}
}
$properties->addProperty(
pht('Host'),
$viewer->renderHandle($event->getUserPHID()));
$invitees = $event->getInvitees();
foreach ($invitees as $key => $invitee) {
if ($invitee->isUninvited()) {
unset($invitees[$key]);
}
}
if ($invitees) {
$invitee_list = new PHUIStatusListView();
$icon_invited = PHUIStatusItemView::ICON_OPEN;
$icon_attending = PHUIStatusItemView::ICON_ACCEPT;
$icon_declined = PHUIStatusItemView::ICON_REJECT;
$status_invited = PhabricatorCalendarEventInvitee::STATUS_INVITED;
$status_attending = PhabricatorCalendarEventInvitee::STATUS_ATTENDING;
$status_declined = PhabricatorCalendarEventInvitee::STATUS_DECLINED;
$icon_map = array(
$status_invited => $icon_invited,
$status_attending => $icon_attending,
$status_declined => $icon_declined,
);
$icon_color_map = array(
$status_invited => null,
$status_attending => 'green',
$status_declined => 'red',
);
foreach ($invitees as $invitee) {
$item = new PHUIStatusItemView();
$invitee_phid = $invitee->getInviteePHID();
$status = $invitee->getStatus();
$target = $viewer->renderHandle($invitee_phid);
$icon = $icon_map[$status];
$icon_color = $icon_color_map[$status];
$item->setIcon($icon, $icon_color)
->setTarget($target);
$invitee_list->addItem($item);
}
} else {
$invitee_list = phutil_tag(
'em',
array(),
pht('None'));
}
$properties->addProperty(
pht('Invitees'),
$invitee_list);
$properties->invokeWillRenderEvent();
$properties->addProperty(
pht('Icon'),
id(new PhabricatorCalendarIconSet())
->getIconLabel($event->getIcon()));
return $properties;
}
private function buildDescriptionView(
PhabricatorCalendarEvent $event) {
$viewer = $this->getViewer();
$properties = id(new PHUIPropertyListView())
->setUser($viewer);
if (strlen($event->getDescription())) {
$description = new PHUIRemarkupView($viewer, $event->getDescription());
$properties->addTextContent($description);
return $properties;
}
return null;
}
}
diff --git a/src/applications/daemon/controller/PhabricatorDaemonBulkJobViewController.php b/src/applications/daemon/controller/PhabricatorDaemonBulkJobViewController.php
index f78bfdd7df..f794024591 100644
--- a/src/applications/daemon/controller/PhabricatorDaemonBulkJobViewController.php
+++ b/src/applications/daemon/controller/PhabricatorDaemonBulkJobViewController.php
@@ -1,94 +1,94 @@
<?php
final class PhabricatorDaemonBulkJobViewController
extends PhabricatorDaemonController {
public function shouldAllowPublic() {
return true;
}
public function handleRequest(AphrontRequest $request) {
$viewer = $this->getViewer();
$job = id(new PhabricatorWorkerBulkJobQuery())
->setViewer($viewer)
->withIDs(array($request->getURIData('id')))
->executeOne();
if (!$job) {
return new Aphront404Response();
}
$title = pht('Bulk Job %d', $job->getID());
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb(pht('Bulk Jobs'), '/daemon/bulk/');
$crumbs->addTextCrumb($title);
$crumbs->setBorder(true);
$properties = $this->renderProperties($job);
$curtain = $this->buildCurtainView($job);
$box = id(new PHUIObjectBoxView())
- ->setHeaderText(pht('DETAILS'))
+ ->setHeaderText(pht('Details'))
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->addPropertyList($properties);
$timeline = $this->buildTransactionTimeline(
$job,
new PhabricatorWorkerBulkJobTransactionQuery());
$timeline->setShouldTerminate(true);
$header = id(new PHUIHeaderView())
->setHeader($title)
->setHeaderIcon('fa-hourglass');
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setCurtain($curtain)
->setMainColumn(array(
$box,
$timeline,
));
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->appendChild($view);
}
private function renderProperties(PhabricatorWorkerBulkJob $job) {
$viewer = $this->getViewer();
$view = id(new PHUIPropertyListView())
->setUser($viewer)
->setObject($job);
$view->addProperty(
pht('Author'),
$viewer->renderHandle($job->getAuthorPHID()));
$view->addProperty(pht('Status'), $job->getStatusName());
return $view;
}
private function buildCurtainView(PhabricatorWorkerBulkJob $job) {
$viewer = $this->getViewer();
$curtain = $this->newCurtainView($job);
if ($job->isConfirming()) {
$continue_uri = $job->getMonitorURI();
} else {
$continue_uri = $job->getDoneURI();
}
$curtain->addAction(
id(new PhabricatorActionView())
->setHref($continue_uri)
->setIcon('fa-arrow-circle-o-right')
->setName(pht('Continue')));
return $curtain;
}
}
diff --git a/src/applications/dashboard/controller/PhabricatorDashboardManageController.php b/src/applications/dashboard/controller/PhabricatorDashboardManageController.php
index 475716d18d..4e31b2d2ae 100644
--- a/src/applications/dashboard/controller/PhabricatorDashboardManageController.php
+++ b/src/applications/dashboard/controller/PhabricatorDashboardManageController.php
@@ -1,206 +1,206 @@
<?php
final class PhabricatorDashboardManageController
extends PhabricatorDashboardController {
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$id = $request->getURIData('id');
$dashboard_uri = $this->getApplicationURI('view/'.$id.'/');
// TODO: This UI should drop a lot of capabilities if the user can't
// edit the dashboard, but we should still let them in for "Install" and
// "View History".
$dashboard = id(new PhabricatorDashboardQuery())
->setViewer($viewer)
->withIDs(array($id))
->needPanels(true)
->executeOne();
if (!$dashboard) {
return new Aphront404Response();
}
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$dashboard,
PhabricatorPolicyCapability::CAN_EDIT);
$title = $dashboard->getName();
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb(
pht('Dashboard %d', $dashboard->getID()),
$dashboard_uri);
$crumbs->addTextCrumb(pht('Manage'));
$crumbs->setBorder(true);
$header = $this->buildHeaderView($dashboard);
$curtain = $this->buildCurtainview($dashboard);
$properties = $this->buildPropertyView($dashboard);
$timeline = $this->buildTransactionTimeline(
$dashboard,
new PhabricatorDashboardTransactionQuery());
$info_view = null;
if (!$can_edit) {
$no_edit = pht(
'You do not have permission to edit this dashboard. If you want to '.
'make changes, make a copy first.');
$info_view = id(new PHUIInfoView())
->setSeverity(PHUIInfoView::SEVERITY_NOTICE)
->setErrors(array($no_edit));
}
$rendered_dashboard = id(new PhabricatorDashboardRenderingEngine())
->setViewer($viewer)
->setDashboard($dashboard)
->setArrangeMode($can_edit)
->renderDashboard();
$dashboard_box = id(new PHUIBoxView())
->addClass('dashboard-preview-box')
->appendChild($rendered_dashboard);
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setCurtain($curtain)
->setMainColumn(array(
$info_view,
$properties,
$timeline,
))
->setFooter($dashboard_box);
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->appendChild($view);
}
private function buildHeaderView(PhabricatorDashboard $dashboard) {
$viewer = $this->getViewer();
$id = $dashboard->getID();
if ($dashboard->isArchived()) {
$status_icon = 'fa-ban';
$status_color = 'dark';
} else {
$status_icon = 'fa-check';
$status_color = 'bluegrey';
}
$status_name = idx(
PhabricatorDashboard::getStatusNameMap(),
$dashboard->getStatus());
$button = id(new PHUIButtonView())
->setTag('a')
->setText(pht('View Dashboard'))
->setIcon('fa-columns')
->setHref($this->getApplicationURI("view/{$id}/"));
return id(new PHUIHeaderView())
->setUser($viewer)
->setHeader($dashboard->getName())
->setPolicyObject($dashboard)
->setStatus($status_icon, $status_color, $status_name)
->setHeaderIcon('fa-dashboard')
->addActionLink($button);
}
private function buildCurtainView(PhabricatorDashboard $dashboard) {
$viewer = $this->getViewer();
$id = $dashboard->getID();
$curtain = $this->newCurtainView($dashboard);
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$dashboard,
PhabricatorPolicyCapability::CAN_EDIT);
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('Edit Dashboard'))
->setIcon('fa-pencil')
->setHref($this->getApplicationURI("edit/{$id}/"))
->setDisabled(!$can_edit));
if ($dashboard->isArchived()) {
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('Activate Dashboard'))
->setIcon('fa-check')
->setHref($this->getApplicationURI("archive/{$id}/"))
->setDisabled(!$can_edit)
->setWorkflow($can_edit));
} else {
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('Archive Dashboard'))
->setIcon('fa-ban')
->setHref($this->getApplicationURI("archive/{$id}/"))
->setDisabled(!$can_edit)
->setWorkflow($can_edit));
}
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('Copy Dashboard'))
->setIcon('fa-files-o')
->setHref($this->getApplicationURI("copy/{$id}/"))
->setWorkflow(true));
$installed_dashboard = id(new PhabricatorDashboardInstall())
->loadOneWhere(
'objectPHID = %s AND applicationClass = %s',
$viewer->getPHID(),
'PhabricatorHomeApplication');
if ($installed_dashboard &&
$installed_dashboard->getDashboardPHID() == $dashboard->getPHID()) {
$title_install = pht('Uninstall Dashboard');
$href_install = "uninstall/{$id}/";
} else {
$title_install = pht('Install Dashboard');
$href_install = "install/{$id}/";
}
$curtain->addAction(
id(new PhabricatorActionView())
->setName($title_install)
->setIcon('fa-wrench')
->setHref($this->getApplicationURI($href_install))
->setWorkflow(true));
return $curtain;
}
private function buildPropertyView(PhabricatorDashboard $dashboard) {
$viewer = $this->getViewer();
$properties = id(new PHUIPropertyListView())
->setUser($viewer);
$descriptions = PhabricatorPolicyQuery::renderPolicyDescriptions(
$viewer,
$dashboard);
$properties->addProperty(
pht('Editable By'),
$descriptions[PhabricatorPolicyCapability::CAN_EDIT]);
$properties->addProperty(
pht('Panels'),
$viewer->renderHandleList($dashboard->getPanelPHIDs()));
return id(new PHUIObjectBoxView())
- ->setHeaderText(pht('DETAILS'))
+ ->setHeaderText(pht('Details'))
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->addPropertyList($properties);
}
}
diff --git a/src/applications/dashboard/controller/PhabricatorDashboardPanelViewController.php b/src/applications/dashboard/controller/PhabricatorDashboardPanelViewController.php
index b16f60abd7..9fd9e1840d 100644
--- a/src/applications/dashboard/controller/PhabricatorDashboardPanelViewController.php
+++ b/src/applications/dashboard/controller/PhabricatorDashboardPanelViewController.php
@@ -1,173 +1,173 @@
<?php
final class PhabricatorDashboardPanelViewController
extends PhabricatorDashboardController {
public function shouldAllowPublic() {
return true;
}
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$id = $request->getURIData('id');
$panel = id(new PhabricatorDashboardPanelQuery())
->setViewer($viewer)
->withIDs(array($id))
->executeOne();
if (!$panel) {
return new Aphront404Response();
}
$title = $panel->getMonogram().' '.$panel->getName();
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb(
pht('Panels'),
$this->getApplicationURI('panel/'));
$crumbs->addTextCrumb($panel->getMonogram());
$crumbs->setBorder(true);
$header = $this->buildHeaderView($panel);
$curtain = $this->buildCurtainView($panel);
$properties = $this->buildPropertyView($panel);
$timeline = $this->buildTransactionTimeline(
$panel,
new PhabricatorDashboardPanelTransactionQuery());
$rendered_panel = id(new PhabricatorDashboardPanelRenderingEngine())
->setViewer($viewer)
->setPanel($panel)
->setParentPanelPHIDs(array())
->renderPanel();
$preview = id(new PHUIBoxView())
->addClass('dashboard-preview-box')
->appendChild($rendered_panel);
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setCurtain($curtain)
->setMainColumn(array(
$properties,
$timeline,
))
->setFooter($rendered_panel);
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->appendChild($view);
}
private function buildHeaderView(PhabricatorDashboardPanel $panel) {
$viewer = $this->getViewer();
$id = $panel->getID();
$button = id(new PHUIButtonView())
->setTag('a')
->setText(pht('View Panel'))
->setIcon('fa-columns')
->setHref($this->getApplicationURI("panel/render/{$id}/"));
$header = id(new PHUIHeaderView())
->setUser($viewer)
->setHeader($panel->getName())
->setPolicyObject($panel)
->setHeaderIcon('fa-columns')
->addActionLink($button);
if (!$panel->getIsArchived()) {
$header->setStatus('fa-check', 'bluegrey', pht('Active'));
} else {
$header->setStatus('fa-ban', 'red', pht('Archived'));
}
return $header;
}
private function buildCurtainView(PhabricatorDashboardPanel $panel) {
$viewer = $this->getViewer();
$id = $panel->getID();
$curtain = $this->newCurtainView($panel);
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$panel,
PhabricatorPolicyCapability::CAN_EDIT);
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('Edit Panel'))
->setIcon('fa-pencil')
->setHref($this->getApplicationURI("panel/edit/{$id}/"))
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit));
if (!$panel->getIsArchived()) {
$archive_text = pht('Archive Panel');
$archive_icon = 'fa-ban';
} else {
$archive_text = pht('Activate Panel');
$archive_icon = 'fa-check';
}
$curtain->addAction(
id(new PhabricatorActionView())
->setName($archive_text)
->setIcon($archive_icon)
->setHref($this->getApplicationURI("panel/archive/{$id}/"))
->setDisabled(!$can_edit)
->setWorkflow(true));
return $curtain;
}
private function buildPropertyView(PhabricatorDashboardPanel $panel) {
$viewer = $this->getViewer();
$properties = id(new PHUIPropertyListView())
->setUser($viewer);
$descriptions = PhabricatorPolicyQuery::renderPolicyDescriptions(
$viewer,
$panel);
$panel_type = $panel->getImplementation();
if ($panel_type) {
$type_name = $panel_type->getPanelTypeName();
} else {
$type_name = phutil_tag(
'em',
array(),
nonempty($panel->getPanelType(), pht('null')));
}
$properties->addProperty(
pht('Panel Type'),
$type_name);
$properties->addProperty(
pht('Editable By'),
$descriptions[PhabricatorPolicyCapability::CAN_EDIT]);
$dashboard_phids = PhabricatorEdgeQuery::loadDestinationPHIDs(
$panel->getPHID(),
PhabricatorDashboardPanelHasDashboardEdgeType::EDGECONST);
$does_not_appear = pht(
'This panel does not appear on any dashboards.');
$properties->addProperty(
pht('Appears On'),
$dashboard_phids
? $viewer->renderHandleList($dashboard_phids)
: phutil_tag('em', array(), $does_not_appear));
return id(new PHUIObjectBoxView())
- ->setHeaderText(pht('DETAILS'))
+ ->setHeaderText(pht('Details'))
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->addPropertyList($properties);
}
}
diff --git a/src/applications/differential/controller/DifferentialRevisionViewController.php b/src/applications/differential/controller/DifferentialRevisionViewController.php
index 61fffd9b2a..78bcd667b2 100644
--- a/src/applications/differential/controller/DifferentialRevisionViewController.php
+++ b/src/applications/differential/controller/DifferentialRevisionViewController.php
@@ -1,1173 +1,1173 @@
<?php
final class DifferentialRevisionViewController extends DifferentialController {
private $revisionID;
public function shouldAllowPublic() {
return true;
}
public function handleRequest(AphrontRequest $request) {
$viewer = $this->getViewer();
$this->revisionID = $request->getURIData('id');
$viewer_is_anonymous = !$viewer->isLoggedIn();
$revision = id(new DifferentialRevisionQuery())
->withIDs(array($this->revisionID))
->setViewer($request->getUser())
->needRelationships(true)
->needReviewerStatus(true)
->needReviewerAuthority(true)
->executeOne();
if (!$revision) {
return new Aphront404Response();
}
$diffs = id(new DifferentialDiffQuery())
->setViewer($request->getUser())
->withRevisionIDs(array($this->revisionID))
->execute();
$diffs = array_reverse($diffs, $preserve_keys = true);
if (!$diffs) {
throw new Exception(
pht('This revision has no diffs. Something has gone quite wrong.'));
}
$revision->attachActiveDiff(last($diffs));
$diff_vs = $request->getInt('vs');
$target_id = $request->getInt('id');
$target = idx($diffs, $target_id, end($diffs));
$target_manual = $target;
if (!$target_id) {
foreach ($diffs as $diff) {
if ($diff->getCreationMethod() != 'commit') {
$target_manual = $diff;
}
}
}
if (empty($diffs[$diff_vs])) {
$diff_vs = null;
}
$repository = null;
$repository_phid = $target->getRepositoryPHID();
if ($repository_phid) {
if ($repository_phid == $revision->getRepositoryPHID()) {
$repository = $revision->getRepository();
} else {
$repository = id(new PhabricatorRepositoryQuery())
->setViewer($viewer)
->withPHIDs(array($repository_phid))
->executeOne();
}
}
list($changesets, $vs_map, $vs_changesets, $rendering_references) =
$this->loadChangesetsAndVsMap(
$target,
idx($diffs, $diff_vs),
$repository);
if ($request->getExists('download')) {
return $this->buildRawDiffResponse(
$revision,
$changesets,
$vs_changesets,
$vs_map,
$repository);
}
$map = $vs_map;
if (!$map) {
$map = array_fill_keys(array_keys($changesets), 0);
}
$old_ids = array();
$new_ids = array();
foreach ($map as $id => $vs) {
if ($vs <= 0) {
$old_ids[] = $id;
$new_ids[] = $id;
} else {
$new_ids[] = $id;
$new_ids[] = $vs;
}
}
$this->loadDiffProperties($diffs);
$props = $target_manual->getDiffProperties();
$object_phids = array_merge(
$revision->getReviewers(),
$revision->getCCPHIDs(),
$revision->loadCommitPHIDs(),
array(
$revision->getAuthorPHID(),
$viewer->getPHID(),
));
foreach ($revision->getAttached() as $type => $phids) {
foreach ($phids as $phid => $info) {
$object_phids[] = $phid;
}
}
$field_list = PhabricatorCustomField::getObjectFields(
$revision,
PhabricatorCustomField::ROLE_VIEW);
$field_list->setViewer($viewer);
$field_list->readFieldsFromStorage($revision);
$warning_handle_map = array();
foreach ($field_list->getFields() as $key => $field) {
$req = $field->getRequiredHandlePHIDsForRevisionHeaderWarnings();
foreach ($req as $phid) {
$warning_handle_map[$key][] = $phid;
$object_phids[] = $phid;
}
}
$handles = $this->loadViewerHandles($object_phids);
$request_uri = $request->getRequestURI();
$limit = 100;
$large = $request->getStr('large');
if (count($changesets) > $limit && !$large) {
$count = count($changesets);
$warning = new PHUIInfoView();
$warning->setTitle(pht('Very Large Diff'));
$warning->setSeverity(PHUIInfoView::SEVERITY_WARNING);
$warning->appendChild(hsprintf(
'%s <strong>%s</strong>',
pht(
'This diff is very large and affects %s files. '.
'You may load each file individually or ',
new PhutilNumber($count)),
phutil_tag(
'a',
array(
'class' => 'button grey',
'href' => $request_uri
->alter('large', 'true')
->setFragment('toc'),
),
pht('Show All Files Inline'))));
$warning = $warning->render();
$old = array_select_keys($changesets, $old_ids);
$new = array_select_keys($changesets, $new_ids);
$query = id(new DifferentialInlineCommentQuery())
->setViewer($viewer)
->needHidden(true)
->withRevisionPHIDs(array($revision->getPHID()));
$inlines = $query->execute();
$inlines = $query->adjustInlinesForChangesets(
$inlines,
$old,
$new,
$revision);
$visible_changesets = array();
foreach ($inlines as $inline) {
$changeset_id = $inline->getChangesetID();
if (isset($changesets[$changeset_id])) {
$visible_changesets[$changeset_id] = $changesets[$changeset_id];
}
}
} else {
$warning = null;
$visible_changesets = $changesets;
}
$commit_hashes = mpull($diffs, 'getSourceControlBaseRevision');
$local_commits = idx($props, 'local:commits', array());
foreach ($local_commits as $local_commit) {
$commit_hashes[] = idx($local_commit, 'tree');
$commit_hashes[] = idx($local_commit, 'local');
}
$commit_hashes = array_unique(array_filter($commit_hashes));
if ($commit_hashes) {
$commits_for_links = id(new DiffusionCommitQuery())
->setViewer($viewer)
->withIdentifiers($commit_hashes)
->execute();
$commits_for_links = mpull(
$commits_for_links,
null,
'getCommitIdentifier');
} else {
$commits_for_links = array();
}
$header = $this->buildHeader($revision);
$subheader = $this->buildSubheaderView($revision);
$details = $this->buildDetails($revision, $field_list);
$curtain = $this->buildCurtain($revision);
$whitespace = $request->getStr(
'whitespace',
DifferentialChangesetParser::WHITESPACE_IGNORE_MOST);
$repository = $revision->getRepository();
if ($repository) {
$symbol_indexes = $this->buildSymbolIndexes(
$repository,
$visible_changesets);
} else {
$symbol_indexes = array();
}
$revision_warnings = $this->buildRevisionWarnings(
$revision,
$field_list,
$warning_handle_map,
$handles);
$info_view = null;
if ($revision_warnings) {
$info_view = id(new PHUIInfoView())
->setSeverity(PHUIInfoView::SEVERITY_WARNING)
->setErrors($revision_warnings);
}
$detail_diffs = array_select_keys(
$diffs,
array($diff_vs, $target->getID()));
$detail_diffs = mpull($detail_diffs, null, 'getPHID');
$this->loadHarbormasterData($detail_diffs);
$diff_detail_box = $this->buildDiffDetailView(
$detail_diffs,
$revision,
$field_list);
$unit_box = $this->buildUnitMessagesView(
$target,
$revision);
$comment_view = $this->buildTransactions(
$revision,
$diff_vs ? $diffs[$diff_vs] : $target,
$target,
$old_ids,
$new_ids);
if (!$viewer_is_anonymous) {
$comment_view->setQuoteRef('D'.$revision->getID());
$comment_view->setQuoteTargetID('comment-content');
}
$changeset_view = id(new DifferentialChangesetListView())
->setChangesets($changesets)
->setVisibleChangesets($visible_changesets)
->setStandaloneURI('/differential/changeset/')
->setRawFileURIs(
'/differential/changeset/?view=old',
'/differential/changeset/?view=new')
->setUser($viewer)
->setDiff($target)
->setRenderingReferences($rendering_references)
->setVsMap($vs_map)
->setWhitespace($whitespace)
->setSymbolIndexes($symbol_indexes)
->setTitle(pht('Diff %s', $target->getID()))
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY);
if ($repository) {
$changeset_view->setRepository($repository);
}
if (!$viewer_is_anonymous) {
$changeset_view->setInlineCommentControllerURI(
'/differential/comment/inline/edit/'.$revision->getID().'/');
}
$diff_history = id(new DifferentialRevisionUpdateHistoryView())
->setUser($viewer)
->setDiffs($diffs)
->setSelectedVersusDiffID($diff_vs)
->setSelectedDiffID($target->getID())
->setSelectedWhitespace($whitespace)
->setCommitsForLinks($commits_for_links);
$local_view = id(new DifferentialLocalCommitsView())
->setUser($viewer)
->setLocalCommits(idx($props, 'local:commits'))
->setCommitsForLinks($commits_for_links);
if ($repository) {
$other_revisions = $this->loadOtherRevisions(
$changesets,
$target,
$repository);
} else {
$other_revisions = array();
}
$other_view = null;
if ($other_revisions) {
$other_view = $this->renderOtherRevisions($other_revisions);
}
$toc_view = $this->buildTableOfContents(
$changesets,
$visible_changesets,
$target->loadCoverageMap($viewer));
$comment_form = null;
if (!$viewer_is_anonymous) {
$comment_form = $this->buildCommentForm($revision, $field_list);
}
$signatures = DifferentialRequiredSignaturesField::loadForRevision(
$revision);
$missing_signatures = false;
foreach ($signatures as $phid => $signed) {
if (!$signed) {
$missing_signatures = true;
}
}
$footer = array();
$signature_message = null;
if ($missing_signatures) {
$signature_message = id(new PHUIInfoView())
->setTitle(pht('Content Hidden'))
->appendChild(
pht(
'The content of this revision is hidden until the author has '.
'signed all of the required legal agreements.'));
} else {
$footer[] =
array(
$diff_history,
$warning,
$local_view,
$toc_view,
$other_view,
$changeset_view,
);
}
if ($comment_form) {
$footer[] = $comment_form;
} else {
// TODO: For now, just use this to get "Login to Comment".
$footer[] = id(new PhabricatorApplicationTransactionCommentView())
->setUser($viewer)
->setRequestURI($request->getRequestURI());
}
$object_id = 'D'.$revision->getID();
$operations_box = $this->buildOperationsBox($revision);
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb($object_id, '/'.$object_id);
$crumbs->setBorder(true);
$prefs = $viewer->loadPreferences();
$pref_filetree = PhabricatorUserPreferences::PREFERENCE_DIFF_FILETREE;
$nav = null;
if ($prefs->getPreference($pref_filetree)) {
$collapsed = $prefs->getPreference(
PhabricatorUserPreferences::PREFERENCE_NAV_COLLAPSED,
false);
$nav = id(new DifferentialChangesetFileTreeSideNavBuilder())
->setTitle('D'.$revision->getID())
->setBaseURI(new PhutilURI('/D'.$revision->getID()))
->setCollapsed((bool)$collapsed)
->build($changesets);
}
// Haunt Mode
$pane_id = celerity_generate_unique_node_id();
Javelin::initBehavior(
'differential-keyboard-navigation',
array(
'haunt' => $pane_id,
));
Javelin::initBehavior('differential-user-select');
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setSubheader($subheader)
->setCurtain($curtain)
->setID($pane_id)
->setMainColumn(array(
$operations_box,
$info_view,
$details,
$diff_detail_box,
$unit_box,
$comment_view,
$signature_message,
))
->setFooter($footer);
$page = $this->newPage()
->setTitle($object_id.' '.$revision->getTitle())
->setCrumbs($crumbs)
->setPageObjectPHIDs(array($revision->getPHID()))
->appendChild($view);
if ($nav) {
$page->setNavigation($nav);
}
return $page;
}
private function buildHeader(DifferentialRevision $revision) {
$view = id(new PHUIHeaderView())
->setHeader($revision->getTitle($revision))
->setUser($this->getViewer())
->setPolicyObject($revision)
->setHeaderIcon('fa-cog');
$status = $revision->getStatus();
$status_name =
DifferentialRevisionStatus::renderFullDescription($status);
$view->addProperty(PHUIHeaderView::PROPERTY_STATUS, $status_name);
return $view;
}
private function buildSubheaderView(DifferentialRevision $revision) {
$viewer = $this->getViewer();
$author_phid = $revision->getAuthorPHID();
$author = $viewer->renderHandle($author_phid)->render();
$date = phabricator_datetime($revision->getDateCreated(), $viewer);
$author = phutil_tag('strong', array(), $author);
$handles = $viewer->loadHandles(array($author_phid));
$image_uri = $handles[$author_phid]->getImageURI();
$image_href = $handles[$author_phid]->getURI();
$content = pht('Authored by %s on %s.', $author, $date);
return id(new PHUIHeadThingView())
->setImage($image_uri)
->setImageHref($image_href)
->setContent($content);
}
private function buildDetails(
DifferentialRevision $revision,
$custom_fields) {
$viewer = $this->getViewer();
$properties = id(new PHUIPropertyListView())
->setUser($viewer);
if ($custom_fields) {
$custom_fields->appendFieldsToPropertyList(
$revision,
$viewer,
$properties);
}
$header = id(new PHUIHeaderView())
- ->setHeader(pht('DETAILS'));
+ ->setHeader(pht('Details'));
return id(new PHUIObjectBoxView())
->setHeader($header)
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->appendChild($properties);
}
private function buildCurtain(DifferentialRevision $revision) {
$viewer = $this->getViewer();
$revision_id = $revision->getID();
$revision_phid = $revision->getPHID();
$curtain = $this->newCurtainView($revision);
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$revision,
PhabricatorPolicyCapability::CAN_EDIT);
$curtain->addAction(
id(new PhabricatorActionView())
->setIcon('fa-pencil')
->setHref("/differential/revision/edit/{$revision_id}/")
->setName(pht('Edit Revision'))
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit));
$curtain->addAction(
id(new PhabricatorActionView())
->setIcon('fa-upload')
->setHref("/differential/revision/update/{$revision_id}/")
->setName(pht('Update Diff'))
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit));
$this->requireResource('phabricator-object-selector-css');
$this->requireResource('javelin-behavior-phabricator-object-selector');
$curtain->addAction(
id(new PhabricatorActionView())
->setIcon('fa-link')
->setName(pht('Edit Dependencies'))
->setHref("/search/attach/{$revision_phid}/DREV/dependencies/")
->setWorkflow(true)
->setDisabled(!$can_edit));
$maniphest = 'PhabricatorManiphestApplication';
if (PhabricatorApplication::isClassInstalled($maniphest)) {
$curtain->addAction(
id(new PhabricatorActionView())
->setIcon('fa-anchor')
->setName(pht('Edit Maniphest Tasks'))
->setHref("/search/attach/{$revision_phid}/TASK/")
->setWorkflow(true)
->setDisabled(!$can_edit));
}
$request_uri = $this->getRequest()->getRequestURI();
$curtain->addAction(
id(new PhabricatorActionView())
->setIcon('fa-download')
->setName(pht('Download Raw Diff'))
->setHref($request_uri->alter('download', 'true')));
return $curtain;
}
private function buildCommentForm(
DifferentialRevision $revision,
$field_list) {
$viewer = $this->getViewer();
$draft = id(new PhabricatorDraft())->loadOneWhere(
'authorPHID = %s AND draftKey = %s',
$viewer->getPHID(),
'differential-comment-'.$revision->getID());
$reviewers = array();
$ccs = array();
if ($draft) {
$reviewers = idx($draft->getMetadata(), 'reviewers', array());
$ccs = idx($draft->getMetadata(), 'ccs', array());
if ($reviewers || $ccs) {
$handles = $this->loadViewerHandles(array_merge($reviewers, $ccs));
$reviewers = array_select_keys($handles, $reviewers);
$ccs = array_select_keys($handles, $ccs);
}
}
$comment_form = id(new DifferentialAddCommentView())
->setRevision($revision);
$review_warnings = array();
foreach ($field_list->getFields() as $field) {
$review_warnings[] = $field->getWarningsForDetailView();
}
$review_warnings = array_mergev($review_warnings);
if ($review_warnings) {
$review_warnings_panel = id(new PHUIInfoView())
->setSeverity(PHUIInfoView::SEVERITY_WARNING)
->setErrors($review_warnings);
$comment_form->setInfoView($review_warnings_panel);
}
$action_uri = $this->getApplicationURI(
'comment/save/'.$revision->getID().'/');
$comment_form->setActions($this->getRevisionCommentActions($revision))
->setActionURI($action_uri)
->setUser($viewer)
->setDraft($draft)
->setReviewers(mpull($reviewers, 'getFullName', 'getPHID'))
->setCCs(mpull($ccs, 'getFullName', 'getPHID'));
// TODO: This just makes the "Z" key work. Generalize this and remove
// it at some point.
$comment_form = phutil_tag(
'div',
array(
'class' => 'differential-add-comment-panel',
),
$comment_form);
return $comment_form;
}
private function getRevisionCommentActions(DifferentialRevision $revision) {
$actions = array(
DifferentialAction::ACTION_COMMENT => true,
);
$viewer = $this->getViewer();
$viewer_phid = $viewer->getPHID();
$viewer_is_owner = ($viewer_phid == $revision->getAuthorPHID());
$viewer_is_reviewer = in_array($viewer_phid, $revision->getReviewers());
$status = $revision->getStatus();
$viewer_has_accepted = false;
$viewer_has_rejected = false;
$status_accepted = DifferentialReviewerStatus::STATUS_ACCEPTED;
$status_rejected = DifferentialReviewerStatus::STATUS_REJECTED;
foreach ($revision->getReviewerStatus() as $reviewer) {
if ($reviewer->getReviewerPHID() == $viewer_phid) {
if ($reviewer->getStatus() == $status_accepted) {
$viewer_has_accepted = true;
}
if ($reviewer->getStatus() == $status_rejected) {
$viewer_has_rejected = true;
}
break;
}
}
$allow_self_accept = PhabricatorEnv::getEnvConfig(
'differential.allow-self-accept');
$always_allow_abandon = PhabricatorEnv::getEnvConfig(
'differential.always-allow-abandon');
$always_allow_close = PhabricatorEnv::getEnvConfig(
'differential.always-allow-close');
$allow_reopen = PhabricatorEnv::getEnvConfig(
'differential.allow-reopen');
if ($viewer_is_owner) {
switch ($status) {
case ArcanistDifferentialRevisionStatus::NEEDS_REVIEW:
$actions[DifferentialAction::ACTION_ACCEPT] = $allow_self_accept;
$actions[DifferentialAction::ACTION_ABANDON] = true;
$actions[DifferentialAction::ACTION_RETHINK] = true;
break;
case ArcanistDifferentialRevisionStatus::NEEDS_REVISION:
case ArcanistDifferentialRevisionStatus::CHANGES_PLANNED:
$actions[DifferentialAction::ACTION_ACCEPT] = $allow_self_accept;
$actions[DifferentialAction::ACTION_ABANDON] = true;
$actions[DifferentialAction::ACTION_REQUEST] = true;
break;
case ArcanistDifferentialRevisionStatus::ACCEPTED:
$actions[DifferentialAction::ACTION_ABANDON] = true;
$actions[DifferentialAction::ACTION_REQUEST] = true;
$actions[DifferentialAction::ACTION_RETHINK] = true;
$actions[DifferentialAction::ACTION_CLOSE] = true;
break;
case ArcanistDifferentialRevisionStatus::CLOSED:
break;
case ArcanistDifferentialRevisionStatus::ABANDONED:
$actions[DifferentialAction::ACTION_RECLAIM] = true;
break;
}
} else {
switch ($status) {
case ArcanistDifferentialRevisionStatus::NEEDS_REVIEW:
$actions[DifferentialAction::ACTION_ABANDON] = $always_allow_abandon;
$actions[DifferentialAction::ACTION_ACCEPT] = true;
$actions[DifferentialAction::ACTION_REJECT] = true;
$actions[DifferentialAction::ACTION_RESIGN] = $viewer_is_reviewer;
break;
case ArcanistDifferentialRevisionStatus::NEEDS_REVISION:
case ArcanistDifferentialRevisionStatus::CHANGES_PLANNED:
$actions[DifferentialAction::ACTION_ABANDON] = $always_allow_abandon;
$actions[DifferentialAction::ACTION_ACCEPT] = true;
$actions[DifferentialAction::ACTION_REJECT] = !$viewer_has_rejected;
$actions[DifferentialAction::ACTION_RESIGN] = $viewer_is_reviewer;
break;
case ArcanistDifferentialRevisionStatus::ACCEPTED:
$actions[DifferentialAction::ACTION_ABANDON] = $always_allow_abandon;
$actions[DifferentialAction::ACTION_ACCEPT] = !$viewer_has_accepted;
$actions[DifferentialAction::ACTION_REJECT] = true;
$actions[DifferentialAction::ACTION_RESIGN] = $viewer_is_reviewer;
break;
case ArcanistDifferentialRevisionStatus::CLOSED:
case ArcanistDifferentialRevisionStatus::ABANDONED:
break;
}
if ($status != ArcanistDifferentialRevisionStatus::CLOSED) {
$actions[DifferentialAction::ACTION_CLAIM] = true;
$actions[DifferentialAction::ACTION_CLOSE] = $always_allow_close;
}
}
$actions[DifferentialAction::ACTION_ADDREVIEWERS] = true;
$actions[DifferentialAction::ACTION_ADDCCS] = true;
$actions[DifferentialAction::ACTION_REOPEN] = $allow_reopen &&
($status == ArcanistDifferentialRevisionStatus::CLOSED);
$actions = array_keys(array_filter($actions));
$actions_dict = array();
foreach ($actions as $action) {
$actions_dict[$action] = DifferentialAction::getActionVerb($action);
}
return $actions_dict;
}
private function loadChangesetsAndVsMap(
DifferentialDiff $target,
DifferentialDiff $diff_vs = null,
PhabricatorRepository $repository = null) {
$load_diffs = array($target);
if ($diff_vs) {
$load_diffs[] = $diff_vs;
}
$raw_changesets = id(new DifferentialChangesetQuery())
->setViewer($this->getRequest()->getUser())
->withDiffs($load_diffs)
->execute();
$changeset_groups = mgroup($raw_changesets, 'getDiffID');
$changesets = idx($changeset_groups, $target->getID(), array());
$changesets = mpull($changesets, null, 'getID');
$refs = array();
$vs_map = array();
$vs_changesets = array();
if ($diff_vs) {
$vs_id = $diff_vs->getID();
$vs_changesets_path_map = array();
foreach (idx($changeset_groups, $vs_id, array()) as $changeset) {
$path = $changeset->getAbsoluteRepositoryPath($repository, $diff_vs);
$vs_changesets_path_map[$path] = $changeset;
$vs_changesets[$changeset->getID()] = $changeset;
}
foreach ($changesets as $key => $changeset) {
$path = $changeset->getAbsoluteRepositoryPath($repository, $target);
if (isset($vs_changesets_path_map[$path])) {
$vs_map[$changeset->getID()] =
$vs_changesets_path_map[$path]->getID();
$refs[$changeset->getID()] =
$changeset->getID().'/'.$vs_changesets_path_map[$path]->getID();
unset($vs_changesets_path_map[$path]);
} else {
$refs[$changeset->getID()] = $changeset->getID();
}
}
foreach ($vs_changesets_path_map as $path => $changeset) {
$changesets[$changeset->getID()] = $changeset;
$vs_map[$changeset->getID()] = -1;
$refs[$changeset->getID()] = $changeset->getID().'/-1';
}
} else {
foreach ($changesets as $changeset) {
$refs[$changeset->getID()] = $changeset->getID();
}
}
$changesets = msort($changesets, 'getSortKey');
return array($changesets, $vs_map, $vs_changesets, $refs);
}
private function buildSymbolIndexes(
PhabricatorRepository $repository,
array $visible_changesets) {
assert_instances_of($visible_changesets, 'DifferentialChangeset');
$engine = PhabricatorSyntaxHighlighter::newEngine();
$langs = $repository->getSymbolLanguages();
$langs = nonempty($langs, array());
$sources = $repository->getSymbolSources();
$sources = nonempty($sources, array());
$symbol_indexes = array();
if ($langs && $sources) {
$have_symbols = id(new DiffusionSymbolQuery())
->existsSymbolsInRepository($repository->getPHID());
if (!$have_symbols) {
return $symbol_indexes;
}
}
$repository_phids = array_merge(
array($repository->getPHID()),
$sources);
$indexed_langs = array_fill_keys($langs, true);
foreach ($visible_changesets as $key => $changeset) {
$lang = $engine->getLanguageFromFilename($changeset->getFilename());
if (empty($indexed_langs) || isset($indexed_langs[$lang])) {
$symbol_indexes[$key] = array(
'lang' => $lang,
'repositories' => $repository_phids,
);
}
}
return $symbol_indexes;
}
private function loadOtherRevisions(
array $changesets,
DifferentialDiff $target,
PhabricatorRepository $repository) {
assert_instances_of($changesets, 'DifferentialChangeset');
$paths = array();
foreach ($changesets as $changeset) {
$paths[] = $changeset->getAbsoluteRepositoryPath(
$repository,
$target);
}
if (!$paths) {
return array();
}
$path_map = id(new DiffusionPathIDQuery($paths))->loadPathIDs();
if (!$path_map) {
return array();
}
$recent = (PhabricatorTime::getNow() - phutil_units('30 days in seconds'));
$query = id(new DifferentialRevisionQuery())
->setViewer($this->getRequest()->getUser())
->withStatus(DifferentialRevisionQuery::STATUS_OPEN)
->withUpdatedEpochBetween($recent, null)
->setOrder(DifferentialRevisionQuery::ORDER_MODIFIED)
->setLimit(10)
->needFlags(true)
->needDrafts(true)
->needRelationships(true);
foreach ($path_map as $path => $path_id) {
$query->withPath($repository->getID(), $path_id);
}
$results = $query->execute();
// Strip out *this* revision.
foreach ($results as $key => $result) {
if ($result->getID() == $this->revisionID) {
unset($results[$key]);
}
}
return $results;
}
private function renderOtherRevisions(array $revisions) {
assert_instances_of($revisions, 'DifferentialRevision');
$viewer = $this->getViewer();
$header = id(new PHUIHeaderView())
->setHeader(pht('Recent Similar Revisions'));
$view = id(new DifferentialRevisionListView())
->setHeader($header)
->setRevisions($revisions)
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->setUser($viewer);
$phids = $view->getRequiredHandlePHIDs();
$handles = $this->loadViewerHandles($phids);
$view->setHandles($handles);
return $view;
}
/**
* Note this code is somewhat similar to the buildPatch method in
* @{class:DifferentialReviewRequestMail}.
*
* @return @{class:AphrontRedirectResponse}
*/
private function buildRawDiffResponse(
DifferentialRevision $revision,
array $changesets,
array $vs_changesets,
array $vs_map,
PhabricatorRepository $repository = null) {
assert_instances_of($changesets, 'DifferentialChangeset');
assert_instances_of($vs_changesets, 'DifferentialChangeset');
$viewer = $this->getViewer();
id(new DifferentialHunkQuery())
->setViewer($viewer)
->withChangesets($changesets)
->needAttachToChangesets(true)
->execute();
$diff = new DifferentialDiff();
$diff->attachChangesets($changesets);
$raw_changes = $diff->buildChangesList();
$changes = array();
foreach ($raw_changes as $changedict) {
$changes[] = ArcanistDiffChange::newFromDictionary($changedict);
}
$loader = id(new PhabricatorFileBundleLoader())
->setViewer($viewer);
$bundle = ArcanistBundle::newFromChanges($changes);
$bundle->setLoadFileDataCallback(array($loader, 'loadFileData'));
$vcs = $repository ? $repository->getVersionControlSystem() : null;
switch ($vcs) {
case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
$raw_diff = $bundle->toGitPatch();
break;
case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
default:
$raw_diff = $bundle->toUnifiedDiff();
break;
}
$request_uri = $this->getRequest()->getRequestURI();
// this ends up being something like
// D123.diff
// or the verbose
// D123.vs123.id123.whitespaceignore-all.diff
// lame but nice to include these options
$file_name = ltrim($request_uri->getPath(), '/').'.';
foreach ($request_uri->getQueryParams() as $key => $value) {
if ($key == 'download') {
continue;
}
$file_name .= $key.$value.'.';
}
$file_name .= 'diff';
$file = PhabricatorFile::buildFromFileDataOrHash(
$raw_diff,
array(
'name' => $file_name,
'ttl' => (60 * 60 * 24),
'viewPolicy' => PhabricatorPolicies::POLICY_NOONE,
));
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
$file->attachToObject($revision->getPHID());
unset($unguarded);
return $file->getRedirectResponse();
}
private function buildTransactions(
DifferentialRevision $revision,
DifferentialDiff $left_diff,
DifferentialDiff $right_diff,
array $old_ids,
array $new_ids) {
$timeline = $this->buildTransactionTimeline(
$revision,
new DifferentialTransactionQuery(),
$engine = null,
array(
'left' => $left_diff->getID(),
'right' => $right_diff->getID(),
'old' => implode(',', $old_ids),
'new' => implode(',', $new_ids),
));
return $timeline;
}
private function buildRevisionWarnings(
DifferentialRevision $revision,
PhabricatorCustomFieldList $field_list,
array $warning_handle_map,
array $handles) {
$warnings = array();
foreach ($field_list->getFields() as $key => $field) {
$phids = idx($warning_handle_map, $key, array());
$field_handles = array_select_keys($handles, $phids);
$field_warnings = $field->getWarningsForRevisionHeader($field_handles);
foreach ($field_warnings as $warning) {
$warnings[] = $warning;
}
}
return $warnings;
}
private function buildDiffDetailView(
array $diffs,
DifferentialRevision $revision,
PhabricatorCustomFieldList $field_list) {
$viewer = $this->getViewer();
$fields = array();
foreach ($field_list->getFields() as $field) {
if ($field->shouldAppearInDiffPropertyView()) {
$fields[] = $field;
}
}
if (!$fields) {
return null;
}
$property_lists = array();
foreach ($this->getDiffTabLabels($diffs) as $tab) {
list($label, $diff) = $tab;
$property_lists[] = array(
$label,
$this->buildDiffPropertyList($diff, $revision, $fields),
);
}
$box = id(new PHUIObjectBoxView())
->setHeaderText(pht('DIFF DETAIL'))
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->setUser($viewer);
$last_tab = null;
foreach ($property_lists as $key => $property_list) {
list($tab_name, $list_view) = $property_list;
$tab = id(new PHUIListItemView())
->setKey($key)
->setName($tab_name);
$box->addPropertyList($list_view, $tab);
$last_tab = $tab;
}
if ($last_tab) {
$last_tab->setSelected(true);
}
return $box;
}
private function buildDiffPropertyList(
DifferentialDiff $diff,
DifferentialRevision $revision,
array $fields) {
$viewer = $this->getViewer();
$view = id(new PHUIPropertyListView())
->setUser($viewer)
->setObject($diff);
foreach ($fields as $field) {
$label = $field->renderDiffPropertyViewLabel($diff);
$value = $field->renderDiffPropertyViewValue($diff);
if ($value !== null) {
$view->addProperty($label, $value);
}
}
return $view;
}
private function buildOperationsBox(DifferentialRevision $revision) {
$viewer = $this->getViewer();
// Save a query if we can't possibly have pending operations.
$repository = $revision->getRepository();
if (!$repository || !$repository->canPerformAutomation()) {
return null;
}
$operations = id(new DrydockRepositoryOperationQuery())
->setViewer($viewer)
->withObjectPHIDs(array($revision->getPHID()))
->withIsDismissed(false)
->withOperationTypes(
array(
DrydockLandRepositoryOperation::OPCONST,
))
->execute();
if (!$operations) {
return null;
}
$state_fail = DrydockRepositoryOperation::STATE_FAIL;
// We're going to show the oldest operation which hasn't failed, or the
// most recent failure if they're all failures.
$operations = msort($operations, 'getID');
foreach ($operations as $operation) {
if ($operation->getOperationState() != $state_fail) {
break;
}
}
// If we found a completed operation, don't render anything. We don't want
// to show an older error after the thing worked properly.
if ($operation->isDone()) {
return null;
}
$box_view = id(new PHUIObjectBoxView())
->setHeaderText(pht('ACTIVE OPERATIONS'))
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY);
return id(new DrydockRepositoryOperationStatusView())
->setUser($viewer)
->setBoxView($box_view)
->setOperation($operation);
}
private function buildUnitMessagesView(
$diff,
DifferentialRevision $revision) {
$viewer = $this->getViewer();
if (!$diff->getBuildable()) {
return null;
}
if (!$diff->getUnitMessages()) {
return null;
}
$interesting_messages = array();
foreach ($diff->getUnitMessages() as $message) {
switch ($message->getResult()) {
case ArcanistUnitTestResult::RESULT_PASS:
case ArcanistUnitTestResult::RESULT_SKIP:
break;
default:
$interesting_messages[] = $message;
break;
}
}
if (!$interesting_messages) {
return null;
}
$excuse = null;
if ($diff->hasDiffProperty('arc:unit-excuse')) {
$excuse = $diff->getProperty('arc:unit-excuse');
}
return id(new HarbormasterUnitSummaryView())
->setUser($viewer)
->setExcuse($excuse)
->setBuildable($diff->getBuildable())
->setUnitMessages($diff->getUnitMessages())
->setLimit(5)
->setShowViewAll(true);
}
}
diff --git a/src/applications/diffusion/controller/DiffusionBrowseController.php b/src/applications/diffusion/controller/DiffusionBrowseController.php
index a79613cfa9..7616922aa8 100644
--- a/src/applications/diffusion/controller/DiffusionBrowseController.php
+++ b/src/applications/diffusion/controller/DiffusionBrowseController.php
@@ -1,2017 +1,2017 @@
<?php
final class DiffusionBrowseController extends DiffusionController {
private $lintCommit;
private $lintMessages;
private $coverage;
public function shouldAllowPublic() {
return true;
}
public function handleRequest(AphrontRequest $request) {
$response = $this->loadDiffusionContext();
if ($response) {
return $response;
}
$drequest = $this->getDiffusionRequest();
// Figure out if we're browsing a directory, a file, or a search result
// list.
$grep = $request->getStr('grep');
$find = $request->getStr('find');
if (strlen($grep) || strlen($find)) {
return $this->browseSearch();
}
$pager = id(new PHUIPagerView())
->readFromRequest($request);
$results = DiffusionBrowseResultSet::newFromConduit(
$this->callConduitWithDiffusionRequest(
'diffusion.browsequery',
array(
'path' => $drequest->getPath(),
'commit' => $drequest->getStableCommit(),
'offset' => $pager->getOffset(),
'limit' => $pager->getPageSize() + 1,
)));
$reason = $results->getReasonForEmptyResultSet();
$is_file = ($reason == DiffusionBrowseResultSet::REASON_IS_FILE);
if ($is_file) {
return $this->browseFile($results);
} else {
$paths = $results->getPaths();
$paths = $pager->sliceResults($paths);
$results->setPaths($paths);
return $this->browseDirectory($results, $pager);
}
}
private function browseSearch() {
$drequest = $this->getDiffusionRequest();
$header = $this->buildHeaderView($drequest);
$search_form = $this->renderSearchForm();
$search_results = $this->renderSearchResults();
$search_form = id(new PHUIObjectBoxView())
->setHeaderText(pht('Search'))
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->appendChild($search_form);
$crumbs = $this->buildCrumbs(
array(
'branch' => true,
'path' => true,
'view' => 'browse',
));
$crumbs->setBorder(true);
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setFooter(array(
$search_form,
$search_results,
));
return $this->newPage()
->setTitle(
array(
nonempty(basename($drequest->getPath()), '/'),
$drequest->getRepository()->getDisplayName(),
))
->setCrumbs($crumbs)
->appendChild($view);
}
private function browseFile() {
$viewer = $this->getViewer();
$request = $this->getRequest();
$drequest = $this->getDiffusionRequest();
$repository = $drequest->getRepository();
$before = $request->getStr('before');
if ($before) {
return $this->buildBeforeResponse($before);
}
$path = $drequest->getPath();
$preferences = $viewer->loadPreferences();
$show_blame = $request->getBool(
'blame',
$preferences->getPreference(
PhabricatorUserPreferences::PREFERENCE_DIFFUSION_BLAME,
false));
$show_color = $request->getBool(
'color',
$preferences->getPreference(
PhabricatorUserPreferences::PREFERENCE_DIFFUSION_COLOR,
true));
$view = $request->getStr('view');
if ($request->isFormPost() && $view != 'raw' && $viewer->isLoggedIn()) {
$preferences->setPreference(
PhabricatorUserPreferences::PREFERENCE_DIFFUSION_BLAME,
$show_blame);
$preferences->setPreference(
PhabricatorUserPreferences::PREFERENCE_DIFFUSION_COLOR,
$show_color);
$preferences->save();
$uri = $request->getRequestURI()
->alter('blame', null)
->alter('color', null);
return id(new AphrontRedirectResponse())->setURI($uri);
}
// We need the blame information if blame is on and we're building plain
// text, or blame is on and this is an Ajax request. If blame is on and
// this is a colorized request, we don't show blame at first (we ajax it
// in afterward) so we don't need to query for it.
$needs_blame = ($show_blame && !$show_color) ||
($show_blame && $request->isAjax());
$params = array(
'commit' => $drequest->getCommit(),
'path' => $drequest->getPath(),
);
$byte_limit = null;
if ($view !== 'raw') {
$byte_limit = PhabricatorFileStorageEngine::getChunkThreshold();
$time_limit = 10;
$params += array(
'timeout' => $time_limit,
'byteLimit' => $byte_limit,
);
}
$response = $this->callConduitWithDiffusionRequest(
'diffusion.filecontentquery',
$params);
$hit_byte_limit = $response['tooHuge'];
$hit_time_limit = $response['tooSlow'];
$file_phid = $response['filePHID'];
if ($hit_byte_limit) {
$corpus = $this->buildErrorCorpus(
pht(
'This file is larger than %s byte(s), and too large to display '.
'in the web UI.',
phutil_format_bytes($byte_limit)));
} else if ($hit_time_limit) {
$corpus = $this->buildErrorCorpus(
pht(
'This file took too long to load from the repository (more than '.
'%s second(s)).',
new PhutilNumber($time_limit)));
} else {
$file = id(new PhabricatorFileQuery())
->setViewer($viewer)
->withPHIDs(array($file_phid))
->executeOne();
if (!$file) {
throw new Exception(pht('Failed to load content file!'));
}
if ($view === 'raw') {
return $file->getRedirectResponse();
}
$data = $file->loadFileData();
$ref = $this->getGitLFSRef($repository, $data);
if ($ref) {
if ($view == 'git-lfs') {
$file = $this->loadGitLFSFile($ref);
// Rename the file locally so we generate a better vanity URI for
// it. In storage, it just has a name like "lfs-13f9a94c0923...",
// since we don't get any hints about possible human-readable names
// at upload time.
$basename = basename($drequest->getPath());
$file->makeEphemeral();
$file->setName($basename);
return $file->getRedirectResponse();
} else {
$corpus = $this->buildGitLFSCorpus($ref);
}
} else if (ArcanistDiffUtils::isHeuristicBinaryFile($data)) {
$file_uri = $file->getBestURI();
if ($file->isViewableImage()) {
$corpus = $this->buildImageCorpus($file_uri);
} else {
$corpus = $this->buildBinaryCorpus($file_uri, $data);
}
} else {
$this->loadLintMessages();
$this->coverage = $drequest->loadCoverage();
// Build the content of the file.
$corpus = $this->buildCorpus(
$show_blame,
$show_color,
$data,
$needs_blame,
$drequest,
$path,
$data);
}
}
if ($request->isAjax()) {
return id(new AphrontAjaxResponse())->setContent($corpus);
}
require_celerity_resource('diffusion-source-css');
// Render the page.
$view = $this->buildCurtain($drequest);
$curtain = $this->enrichCurtain(
$view,
$drequest,
$show_blame,
$show_color);
$properties = $this->buildPropertyView($drequest);
$header = $this->buildHeaderView($drequest);
$header->setHeaderIcon('fa-file-code-o');
$content = array();
$follow = $request->getStr('follow');
if ($follow) {
$notice = new PHUIInfoView();
$notice->setSeverity(PHUIInfoView::SEVERITY_WARNING);
$notice->setTitle(pht('Unable to Continue'));
switch ($follow) {
case 'first':
$notice->appendChild(
pht(
'Unable to continue tracing the history of this file because '.
'this commit is the first commit in the repository.'));
break;
case 'created':
$notice->appendChild(
pht(
'Unable to continue tracing the history of this file because '.
'this commit created the file.'));
break;
}
$content[] = $notice;
}
$renamed = $request->getStr('renamed');
if ($renamed) {
$notice = new PHUIInfoView();
$notice->setSeverity(PHUIInfoView::SEVERITY_NOTICE);
$notice->setTitle(pht('File Renamed'));
$notice->appendChild(
pht(
'File history passes through a rename from "%s" to "%s".',
$drequest->getPath(),
$renamed));
$content[] = $notice;
}
$content[] = $corpus;
$content[] = $this->buildOpenRevisions();
$crumbs = $this->buildCrumbs(
array(
'branch' => true,
'path' => true,
'view' => 'browse',
));
$crumbs->setBorder(true);
$basename = basename($this->getDiffusionRequest()->getPath());
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setCurtain($curtain)
->setMainColumn(array(
$content,
));
if ($properties) {
- $view->addPropertySection(pht('DETAILS'), $properties);
+ $view->addPropertySection(pht('Details'), $properties);
}
$title = array($basename, $repository->getDisplayName());
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->appendChild(
array(
$view,
));
}
public function browseDirectory(
DiffusionBrowseResultSet $results,
PHUIPagerView $pager) {
$request = $this->getRequest();
$drequest = $this->getDiffusionRequest();
$repository = $drequest->getRepository();
$reason = $results->getReasonForEmptyResultSet();
$curtain = $this->buildCurtain($drequest);
$details = $this->buildPropertyView($drequest);
$header = $this->buildHeaderView($drequest);
$header->setHeaderIcon('fa-folder-open');
$search_form = $this->renderSearchForm();
$empty_result = null;
$browse_panel = null;
if (!$results->isValidResults()) {
$empty_result = new DiffusionEmptyResultView();
$empty_result->setDiffusionRequest($drequest);
$empty_result->setDiffusionBrowseResultSet($results);
$empty_result->setView($request->getStr('view'));
} else {
$phids = array();
foreach ($results->getPaths() as $result) {
$data = $result->getLastCommitData();
if ($data) {
if ($data->getCommitDetail('authorPHID')) {
$phids[$data->getCommitDetail('authorPHID')] = true;
}
}
}
$phids = array_keys($phids);
$handles = $this->loadViewerHandles($phids);
$browse_table = id(new DiffusionBrowseTableView())
->setDiffusionRequest($drequest)
->setHandles($handles)
->setPaths($results->getPaths())
->setUser($request->getUser());
$browse_header = id(new PHUIHeaderView())
->setHeader(nonempty(basename($drequest->getPath()), '/'))
->setHeaderIcon('fa-folder-open');
$browse_panel = id(new PHUIObjectBoxView())
->setHeader($browse_header)
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->setTable($browse_table);
$browse_panel->setShowHide(
array(pht('Show Search')),
pht('Hide Search'),
$search_form,
'#');
}
$open_revisions = $this->buildOpenRevisions();
$readme = $this->renderDirectoryReadme($results);
$crumbs = $this->buildCrumbs(
array(
'branch' => true,
'path' => true,
'view' => 'browse',
));
$pager_box = $this->renderTablePagerBox($pager);
$crumbs->setBorder(true);
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setCurtain($curtain)
->setMainColumn(array(
$empty_result,
$browse_panel,
))
->setFooter(array(
$open_revisions,
$readme,
$pager_box,
));
if ($details) {
- $view->addPropertySection(pht('DETAILS'), $details);
+ $view->addPropertySection(pht('Details'), $details);
}
return $this->newPage()
->setTitle(array(
nonempty(basename($drequest->getPath()), '/'),
$repository->getDisplayName(),
))
->setCrumbs($crumbs)
->appendChild(
array(
$view,
));
}
private function renderSearchResults() {
$request = $this->getRequest();
$drequest = $this->getDiffusionRequest();
$repository = $drequest->getRepository();
$results = array();
$pager = id(new PHUIPagerView())
->readFromRequest($request);
$search_mode = null;
switch ($repository->getVersionControlSystem()) {
case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
$results = array();
break;
default:
if (strlen($this->getRequest()->getStr('grep'))) {
$search_mode = 'grep';
$query_string = $request->getStr('grep');
$results = $this->callConduitWithDiffusionRequest(
'diffusion.searchquery',
array(
'grep' => $query_string,
'commit' => $drequest->getStableCommit(),
'path' => $drequest->getPath(),
'limit' => $pager->getPageSize() + 1,
'offset' => $pager->getOffset(),
));
} else { // Filename search.
$search_mode = 'find';
$query_string = $request->getStr('find');
$results = $this->callConduitWithDiffusionRequest(
'diffusion.querypaths',
array(
'pattern' => $query_string,
'commit' => $drequest->getStableCommit(),
'path' => $drequest->getPath(),
'limit' => $pager->getPageSize() + 1,
'offset' => $pager->getOffset(),
));
}
break;
}
$results = $pager->sliceResults($results);
if ($search_mode == 'grep') {
$table = $this->renderGrepResults($results, $query_string);
$header = pht(
'File content matching "%s" under "%s"',
$query_string,
nonempty($drequest->getPath(), '/'));
} else {
$table = $this->renderFindResults($results);
$header = pht(
'Paths matching "%s" under "%s"',
$query_string,
nonempty($drequest->getPath(), '/'));
}
$box = id(new PHUIObjectBoxView())
->setHeaderText($header)
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->setTable($table);
$pager_box = $this->renderTablePagerBox($pager);
return array($box, $pager_box);
}
private function renderGrepResults(array $results, $pattern) {
$drequest = $this->getDiffusionRequest();
require_celerity_resource('phabricator-search-results-css');
$rows = array();
foreach ($results as $result) {
list($path, $line, $string) = $result;
$href = $drequest->generateURI(array(
'action' => 'browse',
'path' => $path,
'line' => $line,
));
$matches = null;
$count = @preg_match_all(
'('.$pattern.')u',
$string,
$matches,
PREG_OFFSET_CAPTURE);
if (!$count) {
$output = ltrim($string);
} else {
$output = array();
$cursor = 0;
$length = strlen($string);
foreach ($matches[0] as $match) {
$offset = $match[1];
if ($cursor != $offset) {
$output[] = array(
'text' => substr($string, $cursor, $offset),
'highlight' => false,
);
}
$output[] = array(
'text' => $match[0],
'highlight' => true,
);
$cursor = $offset + strlen($match[0]);
}
if ($cursor != $length) {
$output[] = array(
'text' => substr($string, $cursor),
'highlight' => false,
);
}
if ($output) {
$output[0]['text'] = ltrim($output[0]['text']);
}
foreach ($output as $key => $segment) {
if ($segment['highlight']) {
$output[$key] = phutil_tag('strong', array(), $segment['text']);
} else {
$output[$key] = $segment['text'];
}
}
}
$string = phutil_tag(
'pre',
array('class' => 'PhabricatorMonospaced phui-source-fragment'),
$output);
$path = Filesystem::readablePath($path, $drequest->getPath());
$rows[] = array(
phutil_tag('a', array('href' => $href), $path),
$line,
$string,
);
}
$table = id(new AphrontTableView($rows))
->setClassName('remarkup-code')
->setHeaders(array(pht('Path'), pht('Line'), pht('String')))
->setColumnClasses(array('', 'n', 'wide'))
->setNoDataString(
pht(
'The pattern you searched for was not found in the content of any '.
'files.'));
return $table;
}
private function renderFindResults(array $results) {
$drequest = $this->getDiffusionRequest();
$rows = array();
foreach ($results as $result) {
$href = $drequest->generateURI(array(
'action' => 'browse',
'path' => $result,
));
$readable = Filesystem::readablePath($result, $drequest->getPath());
$rows[] = array(
phutil_tag('a', array('href' => $href), $readable),
);
}
$table = id(new AphrontTableView($rows))
->setHeaders(array(pht('Path')))
->setColumnClasses(array('wide'))
->setNoDataString(
pht(
'The pattern you searched for did not match the names of any '.
'files.'));
return $table;
}
private function loadLintMessages() {
$drequest = $this->getDiffusionRequest();
$branch = $drequest->loadBranch();
if (!$branch || !$branch->getLintCommit()) {
return;
}
$this->lintCommit = $branch->getLintCommit();
$conn = id(new PhabricatorRepository())->establishConnection('r');
$where = '';
if ($drequest->getLint()) {
$where = qsprintf(
$conn,
'AND code = %s',
$drequest->getLint());
}
$this->lintMessages = queryfx_all(
$conn,
'SELECT * FROM %T WHERE branchID = %d %Q AND path = %s',
PhabricatorRepository::TABLE_LINTMESSAGE,
$branch->getID(),
$where,
'/'.$drequest->getPath());
}
private function buildCorpus(
$show_blame,
$show_color,
$file_corpus,
$needs_blame,
DiffusionRequest $drequest,
$path,
$data) {
$viewer = $this->getViewer();
$blame_timeout = 15;
$blame_failed = false;
$highlight_limit = DifferentialChangesetParser::HIGHLIGHT_BYTE_LIMIT;
$blame_limit = DifferentialChangesetParser::HIGHLIGHT_BYTE_LIMIT;
$can_highlight = (strlen($file_corpus) <= $highlight_limit);
$can_blame = (strlen($file_corpus) <= $blame_limit);
if ($needs_blame && $can_blame) {
$blame = $this->loadBlame($path, $drequest->getCommit(), $blame_timeout);
list($blame_list, $blame_commits) = $blame;
if ($blame_list === null) {
$blame_failed = true;
$blame_list = array();
}
} else {
$blame_list = array();
$blame_commits = array();
}
if (!$show_color) {
$corpus = $this->renderPlaintextCorpus(
$file_corpus,
$blame_list,
$blame_commits,
$show_blame);
} else {
if ($can_highlight) {
require_celerity_resource('syntax-highlighting-css');
$highlighted = PhabricatorSyntaxHighlighter::highlightWithFilename(
$path,
$file_corpus);
$lines = phutil_split_lines($highlighted);
} else {
$lines = phutil_split_lines($file_corpus);
}
$rows = $this->buildDisplayRows(
$lines,
$blame_list,
$blame_commits,
$show_blame,
$show_color);
$corpus_table = javelin_tag(
'table',
array(
'class' => 'diffusion-source remarkup-code PhabricatorMonospaced',
'sigil' => 'phabricator-source',
),
$rows);
if ($this->getRequest()->isAjax()) {
return $corpus_table;
}
$id = celerity_generate_unique_node_id();
$repo = $drequest->getRepository();
$symbol_repos = nonempty($repo->getSymbolSources(), array());
$symbol_repos[] = $repo->getPHID();
$lang = last(explode('.', $drequest->getPath()));
$repo_languages = $repo->getSymbolLanguages();
$repo_languages = nonempty($repo_languages, array());
$repo_languages = array_fill_keys($repo_languages, true);
$needs_symbols = true;
if ($repo_languages && $symbol_repos) {
$have_symbols = id(new DiffusionSymbolQuery())
->existsSymbolsInRepository($repo->getPHID());
if (!$have_symbols) {
$needs_symbols = false;
}
}
if ($needs_symbols && $repo_languages) {
$needs_symbols = isset($repo_languages[$lang]);
}
if ($needs_symbols) {
Javelin::initBehavior(
'repository-crossreference',
array(
'container' => $id,
'lang' => $lang,
'repositories' => $symbol_repos,
));
}
$corpus = phutil_tag(
'div',
array(
'id' => $id,
),
$corpus_table);
Javelin::initBehavior('load-blame', array('id' => $id));
}
$edit = $this->renderEditButton();
$file = $this->renderFileButton();
$header = id(new PHUIHeaderView())
->setHeader(basename($this->getDiffusionRequest()->getPath()))
->setHeaderIcon('fa-file-code-o')
->addActionLink($edit)
->addActionLink($file);
$corpus = id(new PHUIObjectBoxView())
->setHeader($header)
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->appendChild($corpus)
->setCollapsed(true);
$messages = array();
if (!$can_highlight) {
$messages[] = pht(
'This file is larger than %s, so syntax highlighting is disabled '.
'by default.',
phutil_format_bytes($highlight_limit));
}
if ($show_blame && !$can_blame) {
$messages[] = pht(
'This file is larger than %s, so blame is disabled.',
phutil_format_bytes($blame_limit));
}
if ($blame_failed) {
$messages[] = pht(
'Failed to load blame information for this file in %s second(s).',
new PhutilNumber($blame_timeout));
}
if ($messages) {
$corpus->setInfoView(
id(new PHUIInfoView())
->setSeverity(PHUIInfoView::SEVERITY_WARNING)
->setErrors($messages));
}
return $corpus;
}
private function enrichCurtain(
PHUICurtainView $curtain,
DiffusionRequest $drequest,
$show_blame,
$show_color) {
$viewer = $this->getViewer();
$base_uri = $this->getRequest()->getRequestURI();
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('Show Last Change'))
->setHref(
$drequest->generateURI(
array(
'action' => 'change',
)))
->setIcon('fa-backward'));
if ($show_blame) {
$blame_text = pht('Disable Blame');
$blame_icon = 'fa-exclamation-circle lightgreytext';
$blame_value = 0;
} else {
$blame_text = pht('Enable Blame');
$blame_icon = 'fa-exclamation-circle';
$blame_value = 1;
}
$curtain->addAction(
id(new PhabricatorActionView())
->setName($blame_text)
->setHref($base_uri->alter('blame', $blame_value))
->setIcon($blame_icon)
->setUser($viewer)
->setRenderAsForm($viewer->isLoggedIn()));
if ($show_color) {
$highlight_text = pht('Disable Highlighting');
$highlight_icon = 'fa-star-o grey';
$highlight_value = 0;
} else {
$highlight_text = pht('Enable Highlighting');
$highlight_icon = 'fa-star';
$highlight_value = 1;
}
$curtain->addAction(
id(new PhabricatorActionView())
->setName($highlight_text)
->setHref($base_uri->alter('color', $highlight_value))
->setIcon($highlight_icon)
->setUser($viewer)
->setRenderAsForm($viewer->isLoggedIn()));
$href = null;
if ($this->getRequest()->getStr('lint') !== null) {
$lint_text = pht('Hide %d Lint Message(s)', count($this->lintMessages));
$href = $base_uri->alter('lint', null);
} else if ($this->lintCommit === null) {
$lint_text = pht('Lint not Available');
} else {
$lint_text = pht(
'Show %d Lint Message(s)',
count($this->lintMessages));
$href = $this->getDiffusionRequest()->generateURI(array(
'action' => 'browse',
'commit' => $this->lintCommit,
))->alter('lint', '');
}
$curtain->addAction(
id(new PhabricatorActionView())
->setName($lint_text)
->setHref($href)
->setIcon('fa-exclamation-triangle')
->setDisabled(!$href));
$repository = $drequest->getRepository();
$owners = 'PhabricatorOwnersApplication';
if (PhabricatorApplication::isClassInstalled($owners)) {
$package_query = id(new PhabricatorOwnersPackageQuery())
->setViewer($viewer)
->withStatuses(array(PhabricatorOwnersPackage::STATUS_ACTIVE))
->withControl(
$repository->getPHID(),
array(
$drequest->getPath(),
));
$package_query->execute();
$packages = $package_query->getControllingPackagesForPath(
$repository->getPHID(),
$drequest->getPath());
if ($packages) {
$ownership = id(new PHUIStatusListView())
->setUser($viewer);
foreach ($packages as $package) {
$icon = 'fa-list-alt';
$color = 'grey';
$item = id(new PHUIStatusItemView())
->setIcon($icon, $color)
->setTarget($viewer->renderHandle($package->getPHID()));
$ownership->addItem($item);
}
} else {
$ownership = phutil_tag('em', array(), pht('None'));
}
$curtain->newPanel()
->setHeaderText(pht('Owners'))
->appendChild($ownership);
}
return $curtain;
}
private function renderEditButton() {
$request = $this->getRequest();
$user = $request->getUser();
$drequest = $this->getDiffusionRequest();
$repository = $drequest->getRepository();
$path = $drequest->getPath();
$line = nonempty((int)$drequest->getLine(), 1);
$editor_link = $user->loadEditorLink($path, $line, $repository);
$template = $user->loadEditorLink($path, '%l', $repository);
$button = id(new PHUIButtonView())
->setTag('a')
->setText(pht('Open in Editor'))
->setHref($editor_link)
->setIcon('fa-pencil')
->setID('editor_link')
->setMetadata(array('link_template' => $template))
->setDisabled(!$editor_link);
return $button;
}
private function renderFileButton($file_uri = null, $label = null) {
$base_uri = $this->getRequest()->getRequestURI();
if ($file_uri) {
$text = pht('Download Raw File');
$href = $file_uri;
$icon = 'fa-download';
} else {
$text = pht('View Raw File');
$href = $base_uri->alter('view', 'raw');
$icon = 'fa-file-text';
}
if ($label !== null) {
$text = $label;
}
$button = id(new PHUIButtonView())
->setTag('a')
->setText($text)
->setHref($href)
->setIcon($icon);
return $button;
}
private function renderGitLFSButton() {
$viewer = $this->getViewer();
$uri = $this->getRequest()->getRequestURI();
$href = $uri->alter('view', 'git-lfs');
$text = pht('Download from Git LFS');
$icon = 'fa-download';
return id(new PHUIButtonView())
->setTag('a')
->setText($text)
->setHref($href)
->setIcon($icon);
}
private function buildDisplayRows(
array $lines,
array $blame_list,
array $blame_commits,
$show_blame,
$show_color) {
$request = $this->getRequest();
$viewer = $this->getViewer();
$drequest = $this->getDiffusionRequest();
$repository = $drequest->getRepository();
$revision_map = array();
$revisions = array();
if ($blame_commits) {
$commit_map = mpull($blame_commits, 'getCommitIdentifier', 'getPHID');
$revision_ids = id(new DifferentialRevision())
->loadIDsByCommitPHIDs(array_keys($commit_map));
if ($revision_ids) {
$revisions = id(new DifferentialRevisionQuery())
->setViewer($viewer)
->withIDs($revision_ids)
->execute();
$revisions = mpull($revisions, null, 'getID');
}
foreach ($revision_ids as $commit_phid => $revision_id) {
$revision_map[$commit_map[$commit_phid]] = $revision_id;
}
}
$phids = array();
foreach ($blame_commits as $commit) {
$author_phid = $commit->getAuthorPHID();
if ($author_phid === null) {
continue;
}
$phids[$author_phid] = $author_phid;
}
foreach ($revisions as $revision) {
$author_phid = $revision->getAuthorPHID();
if ($author_phid === null) {
continue;
}
$phids[$author_phid] = $author_phid;
}
$handles = $viewer->loadHandles($phids);
$colors = array();
if ($blame_commits) {
$epochs = array();
foreach ($blame_commits as $identifier => $commit) {
$epochs[$identifier] = $commit->getEpoch();
}
$epoch_list = array_filter($epochs);
$epoch_list = array_unique($epoch_list);
$epoch_list = array_values($epoch_list);
$epoch_min = min($epoch_list);
$epoch_max = max($epoch_list);
$epoch_range = ($epoch_max - $epoch_min) + 1;
foreach ($blame_commits as $identifier => $commit) {
$epoch = $epochs[$identifier];
if (!$epoch) {
$color = '#ffffdd'; // Warning color, missing data.
} else {
$color_ratio = ($epoch - $epoch_min) / $epoch_range;
$color_value = 0xE6 * (1.0 - $color_ratio);
$color = sprintf(
'#%02x%02x%02x',
$color_value,
0xF6,
$color_value);
}
$colors[$identifier] = $color;
}
}
$display = array();
$last_identifier = null;
$last_color = null;
foreach ($lines as $line_index => $line) {
$color = '#f6f6f6';
$duplicate = false;
if (isset($blame_list[$line_index])) {
$identifier = $blame_list[$line_index];
if (isset($colors[$identifier])) {
$color = $colors[$identifier];
}
if ($identifier === $last_identifier) {
$duplicate = true;
} else {
$last_identifier = $identifier;
}
}
$display[$line_index] = array(
'data' => $line,
'target' => false,
'highlighted' => false,
'color' => $color,
'duplicate' => $duplicate,
);
}
$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,
);
}
}
// Mark the first highlighted line as the target line.
if ($line_arr) {
$target_line = $line_arr[0]['min'];
if (isset($display[$target_line - 1])) {
$display[$target_line - 1]['target'] = true;
}
}
// Mark all other highlighted lines as highlighted.
foreach ($line_arr as $range) {
for ($ii = $range['min']; $ii <= $range['max']; $ii++) {
if (isset($display[$ii - 1])) {
$display[$ii - 1]['highlighted'] = true;
}
}
}
$engine = null;
$inlines = array();
if ($this->getRequest()->getStr('lint') !== null && $this->lintMessages) {
$engine = new PhabricatorMarkupEngine();
$engine->setViewer($viewer);
foreach ($this->lintMessages as $message) {
$inline = id(new PhabricatorAuditInlineComment())
->setSyntheticAuthor(
ArcanistLintSeverity::getStringForSeverity($message['severity']).
' '.$message['code'].' ('.$message['name'].')')
->setLineNumber($message['line'])
->setContent($message['description']);
$inlines[$message['line']][] = $inline;
$engine->addObject(
$inline,
PhabricatorInlineCommentInterface::MARKUP_FIELD_BODY);
}
$engine->process();
require_celerity_resource('differential-changeset-view-css');
}
$rows = $this->renderInlines(
idx($inlines, 0, array()),
$show_blame,
(bool)$this->coverage,
$engine);
// NOTE: We're doing this manually because rendering is otherwise
// dominated by URI generation for very large files.
$line_base = (string)$drequest->generateURI(
array(
'action' => 'browse',
'stable' => true,
));
require_celerity_resource('aphront-tooltip-css');
Javelin::initBehavior('phabricator-oncopy');
Javelin::initBehavior('phabricator-tooltips');
Javelin::initBehavior('phabricator-line-linker');
// Render these once, since they tend to get repeated many times in large
// blame outputs.
$commit_links = $this->renderCommitLinks($blame_commits, $handles);
$revision_links = $this->renderRevisionLinks($revisions, $handles);
$skip_text = pht('Skip Past This Commit');
foreach ($display as $line_index => $line) {
$row = array();
$line_number = $line_index + 1;
$line_href = $line_base.'$'.$line_number;
if (isset($blame_list[$line_index])) {
$identifier = $blame_list[$line_index];
} else {
$identifier = null;
}
$revision_link = null;
$commit_link = null;
$before_link = null;
$style = 'background: '.$line['color'].';';
if ($identifier && !$line['duplicate']) {
if (isset($commit_links[$identifier])) {
$commit_link = $commit_links[$identifier];
}
if (isset($revision_map[$identifier])) {
$revision_id = $revision_map[$identifier];
if (isset($revision_links[$revision_id])) {
$revision_link = $revision_links[$revision_id];
}
}
$skip_href = $line_href.'?before='.$identifier.'&view=blame';
$before_link = javelin_tag(
'a',
array(
'href' => $skip_href,
'sigil' => 'has-tooltip',
'meta' => array(
'tip' => $skip_text,
'align' => 'E',
'size' => 300,
),
),
"\xC2\xAB");
}
if ($show_blame) {
$row[] = phutil_tag(
'th',
array(
'class' => 'diffusion-blame-link',
),
$before_link);
$object_links = array();
$object_links[] = $commit_link;
if ($revision_link) {
$object_links[] = phutil_tag('span', array(), '/');
$object_links[] = $revision_link;
}
$row[] = phutil_tag(
'th',
array(
'class' => 'diffusion-rev-link',
),
$object_links);
}
$line_link = phutil_tag(
'a',
array(
'href' => $line_href,
'style' => $style,
),
$line_number);
$row[] = javelin_tag(
'th',
array(
'class' => 'diffusion-line-link',
'sigil' => 'phabricator-source-line',
'style' => $style,
),
$line_link);
if ($line['target']) {
Javelin::initBehavior(
'diffusion-jump-to',
array(
'target' => 'scroll_target',
));
$anchor_text = phutil_tag(
'a',
array(
'id' => 'scroll_target',
),
'');
} else {
$anchor_text = null;
}
$row[] = phutil_tag(
'td',
array(
),
array(
$anchor_text,
// NOTE: See phabricator-oncopy behavior.
"\xE2\x80\x8B",
// TODO: [HTML] Not ideal.
phutil_safe_html(str_replace("\t", ' ', $line['data'])),
));
if ($this->coverage) {
require_celerity_resource('differential-changeset-view-css');
$cov_index = $line_index;
if (isset($this->coverage[$cov_index])) {
$cov_class = $this->coverage[$cov_index];
} else {
$cov_class = 'N';
}
$row[] = phutil_tag(
'td',
array(
'class' => 'cov cov-'.$cov_class,
),
'');
}
$rows[] = phutil_tag(
'tr',
array(
'class' => ($line['highlighted'] ?
'phabricator-source-highlight' :
null),
),
$row);
$cur_inlines = $this->renderInlines(
idx($inlines, $line_number, array()),
$show_blame,
$this->coverage,
$engine);
foreach ($cur_inlines as $cur_inline) {
$rows[] = $cur_inline;
}
}
return $rows;
}
private function renderInlines(
array $inlines,
$show_blame,
$has_coverage,
$engine) {
$rows = array();
foreach ($inlines as $inline) {
// TODO: This should use modern scaffolding code.
$inline_view = id(new PHUIDiffInlineCommentDetailView())
->setUser($this->getViewer())
->setMarkupEngine($engine)
->setInlineComment($inline)
->render();
$row = array_fill(0, ($show_blame ? 3 : 1), phutil_tag('th'));
$row[] = phutil_tag('td', array(), $inline_view);
if ($has_coverage) {
$row[] = phutil_tag(
'td',
array(
'class' => 'cov cov-I',
));
}
$rows[] = phutil_tag('tr', array('class' => 'inline'), $row);
}
return $rows;
}
private function buildImageCorpus($file_uri) {
$properties = new PHUIPropertyListView();
$properties->addImageContent(
phutil_tag(
'img',
array(
'src' => $file_uri,
)));
$file = $this->renderFileButton($file_uri);
$header = id(new PHUIHeaderView())
->setHeader(basename($this->getDiffusionRequest()->getPath()))
->addActionLink($file)
->setHeaderIcon('fa-file-image-o');
return id(new PHUIObjectBoxView())
->setHeader($header)
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->addPropertyList($properties);
}
private function buildBinaryCorpus($file_uri, $data) {
$size = new PhutilNumber(strlen($data));
$text = pht('This is a binary file. It is %s byte(s) in length.', $size);
$text = id(new PHUIBoxView())
->addPadding(PHUI::PADDING_LARGE)
->appendChild($text);
$file = $this->renderFileButton($file_uri);
$header = id(new PHUIHeaderView())
- ->setHeader(pht('DETAILS'))
+ ->setHeader(pht('Details'))
->addActionLink($file);
$box = id(new PHUIObjectBoxView())
->setHeader($header)
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->appendChild($text);
return $box;
}
private function buildErrorCorpus($message) {
$text = id(new PHUIBoxView())
->addPadding(PHUI::PADDING_LARGE)
->appendChild($message);
$header = id(new PHUIHeaderView())
- ->setHeader(pht('DETAILS'));
+ ->setHeader(pht('Details'));
$box = id(new PHUIObjectBoxView())
->setHeader($header)
->appendChild($text);
return $box;
}
private function buildBeforeResponse($before) {
$request = $this->getRequest();
$drequest = $this->getDiffusionRequest();
// NOTE: We need to get the grandparent so we can capture filename changes
// in the parent.
$parent = $this->loadParentCommitOf($before);
$old_filename = null;
$was_created = false;
if ($parent) {
$grandparent = $this->loadParentCommitOf($parent);
if ($grandparent) {
$rename_query = new DiffusionRenameHistoryQuery();
$rename_query->setRequest($drequest);
$rename_query->setOldCommit($grandparent);
$rename_query->setViewer($request->getUser());
$old_filename = $rename_query->loadOldFilename();
$was_created = $rename_query->getWasCreated();
}
}
$follow = null;
if ($was_created) {
// If the file was created in history, that means older commits won't
// have it. Since we know it existed at 'before', it must have been
// created then; jump there.
$target_commit = $before;
$follow = 'created';
} else if ($parent) {
// If we found a parent, jump to it. This is the normal case.
$target_commit = $parent;
} else {
// If there's no parent, this was probably created in the initial commit?
// And the "was_created" check will fail because we can't identify the
// grandparent. Keep the user at 'before'.
$target_commit = $before;
$follow = 'first';
}
$path = $drequest->getPath();
$renamed = null;
if ($old_filename !== null &&
$old_filename !== '/'.$path) {
$renamed = $path;
$path = $old_filename;
}
$line = null;
// If there's a follow error, drop the line so the user sees the message.
if (!$follow) {
$line = $this->getBeforeLineNumber($target_commit);
}
$before_uri = $drequest->generateURI(
array(
'action' => 'browse',
'commit' => $target_commit,
'line' => $line,
'path' => $path,
));
$before_uri->setQueryParams($request->getRequestURI()->getQueryParams());
$before_uri = $before_uri->alter('before', null);
$before_uri = $before_uri->alter('renamed', $renamed);
$before_uri = $before_uri->alter('follow', $follow);
return id(new AphrontRedirectResponse())->setURI($before_uri);
}
private function getBeforeLineNumber($target_commit) {
$drequest = $this->getDiffusionRequest();
$line = $drequest->getLine();
if (!$line) {
return null;
}
$raw_diff = $this->callConduitWithDiffusionRequest(
'diffusion.rawdiffquery',
array(
'commit' => $drequest->getCommit(),
'path' => $drequest->getPath(),
'againstCommit' => $target_commit,
));
$old_line = 0;
$new_line = 0;
foreach (explode("\n", $raw_diff) as $text) {
if ($text[0] == '-' || $text[0] == ' ') {
$old_line++;
}
if ($text[0] == '+' || $text[0] == ' ') {
$new_line++;
}
if ($new_line == $line) {
return $old_line;
}
}
// We didn't find the target line.
return $line;
}
private function loadParentCommitOf($commit) {
$drequest = $this->getDiffusionRequest();
$user = $this->getRequest()->getUser();
$before_req = DiffusionRequest::newFromDictionary(
array(
'user' => $user,
'repository' => $drequest->getRepository(),
'commit' => $commit,
));
$parents = DiffusionQuery::callConduitWithDiffusionRequest(
$user,
$before_req,
'diffusion.commitparentsquery',
array(
'commit' => $commit,
));
return head($parents);
}
private function renderRevisionTooltip(
DifferentialRevision $revision,
$handles) {
$viewer = $this->getRequest()->getUser();
$date = phabricator_date($revision->getDateModified(), $viewer);
$id = $revision->getID();
$title = $revision->getTitle();
$header = "D{$id} {$title}";
$author = $handles[$revision->getAuthorPHID()]->getName();
return "{$header}\n{$date} \xC2\xB7 {$author}";
}
private function renderCommitTooltip(
PhabricatorRepositoryCommit $commit,
$author) {
$viewer = $this->getRequest()->getUser();
$date = phabricator_date($commit->getEpoch(), $viewer);
$summary = trim($commit->getSummary());
return "{$summary}\n{$date} \xC2\xB7 {$author}";
}
protected function renderSearchForm() {
$drequest = $this->getDiffusionRequest();
$forms = array();
$form = id(new AphrontFormView())
->setUser($this->getViewer())
->setMethod('GET');
switch ($drequest->getRepository()->getVersionControlSystem()) {
case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
$forms[] = id(clone $form)
->appendChild(pht('Search is not available in Subversion.'));
break;
default:
$forms[] = id(clone $form)
->appendChild(
id(new AphrontFormTextWithSubmitControl())
->setLabel(pht('File Name'))
->setSubmitLabel(pht('Search File Names'))
->setName('find')
->setValue($this->getRequest()->getStr('find')));
$forms[] = id(clone $form)
->appendChild(
id(new AphrontFormTextWithSubmitControl())
->setLabel(pht('Pattern'))
->setSubmitLabel(pht('Grep File Content'))
->setName('grep')
->setValue($this->getRequest()->getStr('grep')));
break;
}
require_celerity_resource('diffusion-icons-css');
$form_box = phutil_tag_div('diffusion-search-boxen', $forms);
return $form_box;
}
protected function markupText($text) {
$engine = PhabricatorMarkupEngine::newDiffusionMarkupEngine();
$engine->setConfig('viewer', $this->getRequest()->getUser());
$text = $engine->markupText($text);
$text = phutil_tag(
'div',
array(
'class' => 'phabricator-remarkup',
),
$text);
return $text;
}
protected function buildHeaderView(DiffusionRequest $drequest) {
$viewer = $this->getViewer();
$tag = $this->renderCommitHashTag($drequest);
$header = id(new PHUIHeaderView())
->setUser($viewer)
->setHeader($this->renderPathLinks($drequest, $mode = 'browse'))
->addTag($tag);
return $header;
}
protected function buildCurtain(DiffusionRequest $drequest) {
$viewer = $this->getViewer();
$curtain = $this->newCurtainView($drequest);
$history_uri = $drequest->generateURI(
array(
'action' => 'history',
));
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('View History'))
->setHref($history_uri)
->setIcon('fa-list'));
$behind_head = $drequest->getSymbolicCommit();
$head_uri = $drequest->generateURI(
array(
'commit' => '',
'action' => 'browse',
));
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('Jump to HEAD'))
->setHref($head_uri)
->setIcon('fa-home')
->setDisabled(!$behind_head));
return $curtain;
}
protected function buildPropertyView(
DiffusionRequest $drequest) {
$viewer = $this->getViewer();
$view = id(new PHUIPropertyListView())
->setUser($viewer);
if ($drequest->getSymbolicType() == 'tag') {
$symbolic = $drequest->getSymbolicCommit();
$view->addProperty(pht('Tag'), $symbolic);
$tags = $this->callConduitWithDiffusionRequest(
'diffusion.tagsquery',
array(
'names' => array($symbolic),
'needMessages' => true,
));
$tags = DiffusionRepositoryTag::newFromConduit($tags);
$tags = mpull($tags, null, 'getName');
$tag = idx($tags, $symbolic);
if ($tag && strlen($tag->getMessage())) {
$view->addSectionHeader(
pht('Tag Content'), 'fa-tag');
$view->addTextContent($this->markupText($tag->getMessage()));
}
}
if ($view->hasAnyProperties()) {
return $view;
}
return null;
}
private function buildOpenRevisions() {
$viewer = $this->getViewer();
$drequest = $this->getDiffusionRequest();
$repository = $drequest->getRepository();
$path = $drequest->getPath();
$path_map = id(new DiffusionPathIDQuery(array($path)))->loadPathIDs();
$path_id = idx($path_map, $path);
if (!$path_id) {
return null;
}
$recent = (PhabricatorTime::getNow() - phutil_units('30 days in seconds'));
$revisions = id(new DifferentialRevisionQuery())
->setViewer($viewer)
->withPath($repository->getID(), $path_id)
->withStatus(DifferentialRevisionQuery::STATUS_OPEN)
->withUpdatedEpochBetween($recent, null)
->setOrder(DifferentialRevisionQuery::ORDER_MODIFIED)
->setLimit(10)
->needRelationships(true)
->needFlags(true)
->needDrafts(true)
->execute();
if (!$revisions) {
return null;
}
$header = id(new PHUIHeaderView())
->setHeader(pht('Open Revisions'))
->setSubheader(
pht('Recently updated open revisions affecting this file.'));
$view = id(new DifferentialRevisionListView())
->setHeader($header)
->setRevisions($revisions)
->setUser($viewer);
$phids = $view->getRequiredHandlePHIDs();
$handles = $this->loadViewerHandles($phids);
$view->setHandles($handles);
return $view;
}
private function loadBlame($path, $commit, $timeout) {
$blame = $this->callConduitWithDiffusionRequest(
'diffusion.blame',
array(
'commit' => $commit,
'paths' => array($path),
'timeout' => $timeout,
));
$identifiers = idx($blame, $path, null);
if ($identifiers) {
$viewer = $this->getViewer();
$drequest = $this->getDiffusionRequest();
$repository = $drequest->getRepository();
$commits = id(new DiffusionCommitQuery())
->setViewer($viewer)
->withRepository($repository)
->withIdentifiers($identifiers)
// TODO: We only fetch this to improve author display behavior, but
// shouldn't really need to?
->needCommitData(true)
->execute();
$commits = mpull($commits, null, 'getCommitIdentifier');
} else {
$commits = array();
}
return array($identifiers, $commits);
}
private function renderCommitLinks(array $commits, $handles) {
$links = array();
foreach ($commits as $identifier => $commit) {
$tooltip = $this->renderCommitTooltip(
$commit,
$commit->renderAuthorShortName($handles));
$commit_link = javelin_tag(
'a',
array(
'href' => $commit->getURI(),
'sigil' => 'has-tooltip',
'meta' => array(
'tip' => $tooltip,
'align' => 'E',
'size' => 600,
),
),
$commit->getLocalName());
$links[$identifier] = $commit_link;
}
return $links;
}
private function renderRevisionLinks(array $revisions, $handles) {
$links = array();
foreach ($revisions as $revision) {
$revision_id = $revision->getID();
$tooltip = $this->renderRevisionTooltip($revision, $handles);
$revision_link = javelin_tag(
'a',
array(
'href' => '/'.$revision->getMonogram(),
'sigil' => 'has-tooltip',
'meta' => array(
'tip' => $tooltip,
'align' => 'E',
'size' => 600,
),
),
$revision->getMonogram());
$links[$revision_id] = $revision_link;
}
return $links;
}
private function renderPlaintextCorpus(
$file_corpus,
array $blame_list,
array $blame_commits,
$show_blame) {
$viewer = $this->getViewer();
if (!$show_blame) {
$corpus = $file_corpus;
} else {
$author_phids = array();
foreach ($blame_commits as $commit) {
$author_phid = $commit->getAuthorPHID();
if ($author_phid === null) {
continue;
}
$author_phids[$author_phid] = $author_phid;
}
if ($author_phids) {
$handles = $viewer->loadHandles($author_phids);
} else {
$handles = array();
}
$authors = array();
$names = array();
foreach ($blame_commits as $identifier => $commit) {
$author = $commit->renderAuthorShortName($handles);
$name = $commit->getLocalName();
$authors[$identifier] = $author;
$names[$identifier] = $name;
}
$lines = phutil_split_lines($file_corpus);
$rows = array();
foreach ($lines as $line_number => $line) {
$commit_name = null;
$author = null;
if (isset($blame_list[$line_number])) {
$identifier = $blame_list[$line_number];
if (isset($names[$identifier])) {
$commit_name = $names[$identifier];
}
if (isset($authors[$identifier])) {
$author = $authors[$identifier];
}
}
$rows[] = sprintf(
'%-10s %-20s %s',
$commit_name,
$author,
$line);
}
$corpus = implode('', $rows);
}
return phutil_tag(
'textarea',
array(
'style' => 'border: none; width: 100%; height: 80em; '.
'font-family: monospace',
),
$corpus);
}
private function getGitLFSRef(PhabricatorRepository $repository, $data) {
if (!$repository->canUseGitLFS()) {
return null;
}
$lfs_pattern = '(^version https://git-lfs\\.github\\.com/spec/v1[\r\n])';
if (!preg_match($lfs_pattern, $data)) {
return null;
}
$matches = null;
if (!preg_match('(^oid sha256:(.*)$)m', $data, $matches)) {
return null;
}
$hash = $matches[1];
$hash = trim($hash);
return id(new PhabricatorRepositoryGitLFSRefQuery())
->setViewer($this->getViewer())
->withRepositoryPHIDs(array($repository->getPHID()))
->withObjectHashes(array($hash))
->executeOne();
}
private function buildGitLFSCorpus(PhabricatorRepositoryGitLFSRef $ref) {
// TODO: We should probably test if we can load the file PHID here and
// show the user an error if we can't, rather than making them click
// through to hit an error.
$header = id(new PHUIHeaderView())
->setHeader(basename($this->getDiffusionRequest()->getPath()))
->setHeaderIcon('fa-archive');
$severity = PHUIInfoView::SEVERITY_NOTICE;
$messages = array();
$messages[] = pht(
'This %s file is stored in Git Large File Storage.',
phutil_format_bytes($ref->getByteSize()));
try {
$file = $this->loadGitLFSFile($ref);
$data = $this->renderGitLFSButton();
$header->addActionLink($data);
} catch (Exception $ex) {
$severity = PHUIInfoView::SEVERITY_ERROR;
$messages[] = pht('The data for this file could not be loaded.');
}
$raw = $this->renderFileButton(null, pht('View Raw LFS Pointer'));
$header->addActionLink($raw);
$corpus = id(new PHUIObjectBoxView())
->setHeader($header)
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->setCollapsed(true);
if ($messages) {
$corpus->setInfoView(
id(new PHUIInfoView())
->setSeverity($severity)
->setErrors($messages));
}
return $corpus;
}
private function loadGitLFSFile(PhabricatorRepositoryGitLFSRef $ref) {
$viewer = $this->getViewer();
$file = id(new PhabricatorFileQuery())
->setViewer($viewer)
->withPHIDs(array($ref->getFilePHID()))
->executeOne();
if (!$file) {
throw new Exception(
pht(
'Failed to load file object for Git LFS ref "%s"!',
$ref->getObjectHash()));
}
return $file;
}
}
diff --git a/src/applications/diffusion/controller/DiffusionCommitController.php b/src/applications/diffusion/controller/DiffusionCommitController.php
index 69c40b044f..e2f0d51f4a 100644
--- a/src/applications/diffusion/controller/DiffusionCommitController.php
+++ b/src/applications/diffusion/controller/DiffusionCommitController.php
@@ -1,1286 +1,1286 @@
<?php
final class DiffusionCommitController extends DiffusionController {
const CHANGES_LIMIT = 100;
private $auditAuthorityPHIDs;
private $highlightedAudits;
private $commitParents;
private $commitRefs;
private $commitMerges;
private $commitErrors;
private $commitExists;
public function shouldAllowPublic() {
return true;
}
public function handleRequest(AphrontRequest $request) {
$response = $this->loadDiffusionContext();
if ($response) {
return $response;
}
$drequest = $this->getDiffusionRequest();
$viewer = $request->getUser();
if ($request->getStr('diff')) {
return $this->buildRawDiffResponse($drequest);
}
$repository = $drequest->getRepository();
$commit = id(new DiffusionCommitQuery())
->setViewer($viewer)
->withRepository($repository)
->withIdentifiers(array($drequest->getCommit()))
->needCommitData(true)
->needAuditRequests(true)
->executeOne();
$crumbs = $this->buildCrumbs(array(
'commit' => true,
));
$crumbs->setBorder(true);
if (!$commit) {
if (!$this->getCommitExists()) {
return new Aphront404Response();
}
$error = id(new PHUIInfoView())
->setTitle(pht('Commit Still Parsing'))
->appendChild(
pht(
'Failed to load the commit because the commit has not been '.
'parsed yet.'));
$title = pht('Commit Still Parsing');
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->appendChild($error);
}
$audit_requests = $commit->getAudits();
$this->auditAuthorityPHIDs =
PhabricatorAuditCommentEditor::loadAuditPHIDsForUser($viewer);
$commit_data = $commit->getCommitData();
$is_foreign = $commit_data->getCommitDetail('foreign-svn-stub');
$error_panel = null;
if ($is_foreign) {
$subpath = $commit_data->getCommitDetail('svn-subpath');
$error_panel = new PHUIInfoView();
$error_panel->setTitle(pht('Commit Not Tracked'));
$error_panel->setSeverity(PHUIInfoView::SEVERITY_WARNING);
$error_panel->appendChild(
pht(
"This Diffusion repository is configured to track only one ".
"subdirectory of the entire Subversion repository, and this commit ".
"didn't affect the tracked subdirectory ('%s'), so no ".
"information is available.",
$subpath));
} else {
$engine = PhabricatorMarkupEngine::newDifferentialMarkupEngine();
$engine->setConfig('viewer', $viewer);
$commit_tag = $this->renderCommitHashTag($drequest);
$header = id(new PHUIHeaderView())
->setHeader(nonempty($commit->getSummary(), pht('Commit Detail')))
->setHeaderIcon('fa-code-fork')
->addTag($commit_tag);
if ($commit->getAuditStatus()) {
$icon = PhabricatorAuditCommitStatusConstants::getStatusIcon(
$commit->getAuditStatus());
$color = PhabricatorAuditCommitStatusConstants::getStatusColor(
$commit->getAuditStatus());
$status = PhabricatorAuditCommitStatusConstants::getStatusName(
$commit->getAuditStatus());
$header->setStatus($icon, $color, $status);
}
$curtain = $this->buildCurtain($commit, $repository);
$subheader = $this->buildSubheaderView($commit, $commit_data);
$details = $this->buildPropertyListView(
$commit,
$commit_data,
$audit_requests);
$message = $commit_data->getCommitMessage();
$revision = $commit->getCommitIdentifier();
$message = $this->linkBugtraq($message);
$message = $engine->markupText($message);
$detail_list = new PHUIPropertyListView();
$detail_list->addTextContent(
phutil_tag(
'div',
array(
'class' => 'diffusion-commit-message phabricator-remarkup',
),
$message));
if ($this->getCommitErrors()) {
$error_panel = id(new PHUIInfoView())
->appendChild($this->getCommitErrors())
->setSeverity(PHUIInfoView::SEVERITY_WARNING);
}
}
$timeline = $this->buildComments($commit);
$hard_limit = 1000;
if ($commit->isImported()) {
$change_query = DiffusionPathChangeQuery::newFromDiffusionRequest(
$drequest);
$change_query->setLimit($hard_limit + 1);
$changes = $change_query->loadChanges();
} else {
$changes = array();
}
$was_limited = (count($changes) > $hard_limit);
if ($was_limited) {
$changes = array_slice($changes, 0, $hard_limit);
}
$merge_table = $this->buildMergesTable($commit);
$highlighted_audits = $commit->getAuthorityAudits(
$viewer,
$this->auditAuthorityPHIDs);
$count = count($changes);
$bad_commit = null;
if ($count == 0) {
$bad_commit = queryfx_one(
id(new PhabricatorRepository())->establishConnection('r'),
'SELECT * FROM %T WHERE fullCommitName = %s',
PhabricatorRepository::TABLE_BADCOMMIT,
$commit->getMonogram());
}
$show_changesets = false;
$info_panel = null;
$change_list = null;
$change_table = null;
if ($bad_commit) {
$info_panel = $this->renderStatusMessage(
pht('Bad Commit'),
$bad_commit['description']);
} else if ($is_foreign) {
// Don't render anything else.
} else if (!$commit->isImported()) {
$info_panel = $this->renderStatusMessage(
pht('Still Importing...'),
pht(
'This commit is still importing. Changes will be visible once '.
'the import finishes.'));
} else if (!count($changes)) {
$info_panel = $this->renderStatusMessage(
pht('Empty Commit'),
pht(
'This commit is empty and does not affect any paths.'));
} else if ($was_limited) {
$info_panel = $this->renderStatusMessage(
pht('Enormous Commit'),
pht(
'This commit is enormous, and affects more than %d files. '.
'Changes are not shown.',
$hard_limit));
} else if (!$this->getCommitExists()) {
$info_panel = $this->renderStatusMessage(
pht('Commit No Longer Exists'),
pht('This commit no longer exists in the repository.'));
} else {
$show_changesets = true;
// The user has clicked "Show All Changes", and we should show all the
// changes inline even if there are more than the soft limit.
$show_all_details = $request->getBool('show_all');
$change_header = id(new PHUIHeaderView())
->setHeader(pht('Changes (%s)', new PhutilNumber($count)));
$warning_view = null;
if ($count > self::CHANGES_LIMIT && !$show_all_details) {
$button = id(new PHUIButtonView())
->setText(pht('Show All Changes'))
->setHref('?show_all=true')
->setTag('a')
->setIcon('fa-files-o');
$warning_view = id(new PHUIInfoView())
->setSeverity(PHUIInfoView::SEVERITY_WARNING)
->setTitle(pht('Very Large Commit'))
->appendChild(
pht('This commit is very large. Load each file individually.'));
$change_header->addActionLink($button);
}
$changesets = DiffusionPathChange::convertToDifferentialChangesets(
$viewer,
$changes);
// TODO: This table and panel shouldn't really be separate, but we need
// to clean up the "Load All Files" interaction first.
$change_table = $this->buildTableOfContents(
$changesets,
$change_header,
$warning_view);
$vcs = $repository->getVersionControlSystem();
switch ($vcs) {
case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
$vcs_supports_directory_changes = true;
break;
case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
$vcs_supports_directory_changes = false;
break;
default:
throw new Exception(pht('Unknown VCS.'));
}
$references = array();
foreach ($changesets as $key => $changeset) {
$file_type = $changeset->getFileType();
if ($file_type == DifferentialChangeType::FILE_DIRECTORY) {
if (!$vcs_supports_directory_changes) {
unset($changesets[$key]);
continue;
}
}
$references[$key] = $drequest->generateURI(
array(
'action' => 'rendering-ref',
'path' => $changeset->getFilename(),
));
}
// TODO: Some parts of the views still rely on properties of the
// DifferentialChangeset. Make the objects ephemeral to make sure we don't
// accidentally save them, and then set their ID to the appropriate ID for
// this application (the path IDs).
$path_ids = array_flip(mpull($changes, 'getPath'));
foreach ($changesets as $changeset) {
$changeset->makeEphemeral();
$changeset->setID($path_ids[$changeset->getFilename()]);
}
if ($count <= self::CHANGES_LIMIT || $show_all_details) {
$visible_changesets = $changesets;
} else {
$visible_changesets = array();
$inlines = PhabricatorAuditInlineComment::loadDraftAndPublishedComments(
$viewer,
$commit->getPHID());
$path_ids = mpull($inlines, null, 'getPathID');
foreach ($changesets as $key => $changeset) {
if (array_key_exists($changeset->getID(), $path_ids)) {
$visible_changesets[$key] = $changeset;
}
}
}
$change_list_title = $commit->getDisplayName();
$change_list = new DifferentialChangesetListView();
$change_list->setTitle($change_list_title);
$change_list->setChangesets($changesets);
$change_list->setVisibleChangesets($visible_changesets);
$change_list->setRenderingReferences($references);
$change_list->setRenderURI($repository->getPathURI('diff/'));
$change_list->setRepository($repository);
$change_list->setUser($viewer);
$change_list->setBackground(PHUIObjectBoxView::BLUE_PROPERTY);
// TODO: Try to setBranch() to something reasonable here?
$change_list->setStandaloneURI(
$repository->getPathURI('diff/'));
$change_list->setRawFileURIs(
// TODO: Implement this, somewhat tricky if there's an octopus merge
// or whatever?
null,
$repository->getPathURI('diff/?view=r'));
$change_list->setInlineCommentControllerURI(
'/diffusion/inline/edit/'.phutil_escape_uri($commit->getPHID()).'/');
}
$add_comment = $this->renderAddCommentPanel($commit, $audit_requests);
$prefs = $viewer->loadPreferences();
$pref_filetree = PhabricatorUserPreferences::PREFERENCE_DIFF_FILETREE;
$pref_collapse = PhabricatorUserPreferences::PREFERENCE_NAV_COLLAPSED;
$show_filetree = $prefs->getPreference($pref_filetree);
$collapsed = $prefs->getPreference($pref_collapse);
$nav = null;
if ($show_changesets && $show_filetree) {
$nav = id(new DifferentialChangesetFileTreeSideNavBuilder())
->setTitle($commit->getDisplayName())
->setBaseURI(new PhutilURI($commit->getURI()))
->build($changesets)
->setCrumbs($crumbs)
->setCollapsed((bool)$collapsed);
}
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setSubheader($subheader)
->setMainColumn(array(
$error_panel,
$timeline,
$merge_table,
$info_panel,
))
->setFooter(array(
$change_table,
$change_list,
$add_comment,
))
- ->addPropertySection(pht('DESCRIPTION'), $detail_list)
- ->addPropertySection(pht('DETAILS'), $details)
+ ->addPropertySection(pht('Description'), $detail_list)
+ ->addPropertySection(pht('Details'), $details)
->setCurtain($curtain);
$page = $this->newPage()
->setTitle($commit->getDisplayName())
->setCrumbs($crumbs)
->setPageObjectPHIDS(array($commit->getPHID()))
->appendChild(
array(
$view,
));
if ($nav) {
$page->setNavigation($nav);
}
return $page;
}
private function buildPropertyListView(
PhabricatorRepositoryCommit $commit,
PhabricatorRepositoryCommitData $data,
array $audit_requests) {
$viewer = $this->getViewer();
$commit_phid = $commit->getPHID();
$drequest = $this->getDiffusionRequest();
$repository = $drequest->getRepository();
$view = id(new PHUIPropertyListView())
->setUser($this->getRequest()->getUser());
$edge_query = id(new PhabricatorEdgeQuery())
->withSourcePHIDs(array($commit_phid))
->withEdgeTypes(array(
DiffusionCommitHasTaskEdgeType::EDGECONST,
DiffusionCommitHasRevisionEdgeType::EDGECONST,
DiffusionCommitRevertsCommitEdgeType::EDGECONST,
DiffusionCommitRevertedByCommitEdgeType::EDGECONST,
));
$edges = $edge_query->execute();
$task_phids = array_keys(
$edges[$commit_phid][DiffusionCommitHasTaskEdgeType::EDGECONST]);
$revision_phid = key(
$edges[$commit_phid][DiffusionCommitHasRevisionEdgeType::EDGECONST]);
$reverts_phids = array_keys(
$edges[$commit_phid][DiffusionCommitRevertsCommitEdgeType::EDGECONST]);
$reverted_by_phids = array_keys(
$edges[$commit_phid][DiffusionCommitRevertedByCommitEdgeType::EDGECONST]);
$phids = $edge_query->getDestinationPHIDs(array($commit_phid));
if ($data->getCommitDetail('authorPHID')) {
$phids[] = $data->getCommitDetail('authorPHID');
}
if ($data->getCommitDetail('reviewerPHID')) {
$phids[] = $data->getCommitDetail('reviewerPHID');
}
if ($data->getCommitDetail('committerPHID')) {
$phids[] = $data->getCommitDetail('committerPHID');
}
// NOTE: We should never normally have more than a single push log, but
// it can occur naturally if a commit is pushed, then the branch it was
// on is deleted, then the commit is pushed again (or through other similar
// chains of events). This should be rare, but does not indicate a bug
// or data issue.
// NOTE: We never query push logs in SVN because the commiter is always
// the pusher and the commit time is always the push time; the push log
// is redundant and we save a query by skipping it.
$push_logs = array();
if ($repository->isHosted() && !$repository->isSVN()) {
$push_logs = id(new PhabricatorRepositoryPushLogQuery())
->setViewer($viewer)
->withRepositoryPHIDs(array($repository->getPHID()))
->withNewRefs(array($commit->getCommitIdentifier()))
->withRefTypes(array(PhabricatorRepositoryPushLog::REFTYPE_COMMIT))
->execute();
foreach ($push_logs as $log) {
$phids[] = $log->getPusherPHID();
}
}
$handles = array();
if ($phids) {
$handles = $this->loadViewerHandles($phids);
}
$props = array();
if ($audit_requests) {
$user_requests = array();
$other_requests = array();
foreach ($audit_requests as $audit_request) {
if ($audit_request->isUser()) {
$user_requests[] = $audit_request;
} else {
$other_requests[] = $audit_request;
}
}
if ($user_requests) {
$view->addProperty(
pht('Auditors'),
$this->renderAuditStatusView($user_requests));
}
if ($other_requests) {
$view->addProperty(
pht('Project/Package Auditors'),
$this->renderAuditStatusView($other_requests));
}
}
$author_phid = $data->getCommitDetail('authorPHID');
$author_name = $data->getAuthorName();
$author_epoch = $data->getCommitDetail('authorEpoch');
$committed_info = id(new PHUIStatusItemView())
->setNote(phabricator_datetime($commit->getEpoch(), $viewer));
$committer_phid = $data->getCommitDetail('committerPHID');
$committer_name = $data->getCommitDetail('committer');
if ($committer_phid) {
$committed_info->setTarget($handles[$committer_phid]->renderLink());
} else if (strlen($committer_name)) {
$committed_info->setTarget($committer_name);
} else if ($author_phid) {
$committed_info->setTarget($handles[$author_phid]->renderLink());
} else if (strlen($author_name)) {
$committed_info->setTarget($author_name);
}
$committed_list = new PHUIStatusListView();
$committed_list->addItem($committed_info);
$view->addProperty(
pht('Committed'),
$committed_list);
if ($push_logs) {
$pushed_list = new PHUIStatusListView();
foreach ($push_logs as $push_log) {
$pushed_item = id(new PHUIStatusItemView())
->setTarget($handles[$push_log->getPusherPHID()]->renderLink())
->setNote(phabricator_datetime($push_log->getEpoch(), $viewer));
$pushed_list->addItem($pushed_item);
}
$view->addProperty(
pht('Pushed'),
$pushed_list);
}
$reviewer_phid = $data->getCommitDetail('reviewerPHID');
if ($reviewer_phid) {
$view->addProperty(
pht('Reviewer'),
$handles[$reviewer_phid]->renderLink());
}
if ($revision_phid) {
$view->addProperty(
pht('Differential Revision'),
$handles[$revision_phid]->renderLink());
}
$parents = $this->getCommitParents();
if ($parents) {
$view->addProperty(
pht('Parents'),
$viewer->renderHandleList(mpull($parents, 'getPHID')));
}
if ($this->getCommitExists()) {
$view->addProperty(
pht('Branches'),
phutil_tag(
'span',
array(
'id' => 'commit-branches',
),
pht('Unknown')));
$view->addProperty(
pht('Tags'),
phutil_tag(
'span',
array(
'id' => 'commit-tags',
),
pht('Unknown')));
$identifier = $commit->getCommitIdentifier();
$root = $repository->getPathURI("commit/{$identifier}");
Javelin::initBehavior(
'diffusion-commit-branches',
array(
$root.'/branches/' => 'commit-branches',
$root.'/tags/' => 'commit-tags',
));
}
$refs = $this->getCommitRefs();
if ($refs) {
$ref_links = array();
foreach ($refs as $ref_data) {
$ref_links[] = phutil_tag(
'a',
array(
'href' => $ref_data['href'],
),
$ref_data['ref']);
}
$view->addProperty(
pht('References'),
phutil_implode_html(', ', $ref_links));
}
if ($reverts_phids) {
$view->addProperty(
pht('Reverts'),
$viewer->renderHandleList($reverts_phids));
}
if ($reverted_by_phids) {
$view->addProperty(
pht('Reverted By'),
$viewer->renderHandleList($reverted_by_phids));
}
if ($task_phids) {
$task_list = array();
foreach ($task_phids as $phid) {
$task_list[] = $handles[$phid]->renderLink();
}
$task_list = phutil_implode_html(phutil_tag('br'), $task_list);
$view->addProperty(
pht('Tasks'),
$task_list);
}
return $view;
}
private function buildSubheaderView(
PhabricatorRepositoryCommit $commit,
PhabricatorRepositoryCommitData $data) {
$viewer = $this->getViewer();
$drequest = $this->getDiffusionRequest();
$repository = $drequest->getRepository();
if ($repository->isSVN()) {
return null;
}
$author_phid = $data->getCommitDetail('authorPHID');
$author_name = $data->getAuthorName();
$author_epoch = $data->getCommitDetail('authorEpoch');
$date = null;
if ($author_epoch !== null) {
$date = phabricator_datetime($author_epoch, $viewer);
}
if ($author_phid) {
$handles = $viewer->loadHandles(array($author_phid));
$image_uri = $handles[$author_phid]->getImageURI();
$image_href = $handles[$author_phid]->getURI();
$author = $handles[$author_phid]->renderLink();
} else if (strlen($author_name)) {
$author = $author_name;
$image_uri = null;
$image_href = null;
} else {
return null;
}
$author = phutil_tag('strong', array(), $author);
if ($date) {
$content = pht('Authored by %s on %s.', $author, $date);
} else {
$content = pht('Authored by %s.', $author);
}
return id(new PHUIHeadThingView())
->setImage($image_uri)
->setImageHref($image_href)
->setContent($content);
}
private function buildComments(PhabricatorRepositoryCommit $commit) {
$timeline = $this->buildTransactionTimeline(
$commit,
new PhabricatorAuditTransactionQuery());
$commit->willRenderTimeline($timeline, $this->getRequest());
return $timeline;
}
private function renderAddCommentPanel(
PhabricatorRepositoryCommit $commit,
array $audit_requests) {
assert_instances_of($audit_requests, 'PhabricatorRepositoryAuditRequest');
$request = $this->getRequest();
$viewer = $request->getUser();
if (!$viewer->isLoggedIn()) {
return id(new PhabricatorApplicationTransactionCommentView())
->setUser($viewer)
->setRequestURI($request->getRequestURI());
}
$is_serious = PhabricatorEnv::getEnvConfig('phabricator.serious-business');
$pane_id = celerity_generate_unique_node_id();
Javelin::initBehavior(
'differential-keyboard-navigation',
array(
'haunt' => $pane_id,
));
$draft = id(new PhabricatorDraft())->loadOneWhere(
'authorPHID = %s AND draftKey = %s',
$viewer->getPHID(),
'diffusion-audit-'.$commit->getID());
if ($draft) {
$draft = $draft->getDraft();
} else {
$draft = null;
}
$actions = $this->getAuditActions($commit, $audit_requests);
$mailable_source = new PhabricatorMetaMTAMailableDatasource();
$auditor_source = new DiffusionAuditorDatasource();
$form = id(new AphrontFormView())
->setUser($viewer)
->setAction('/audit/addcomment/')
->addHiddenInput('commit', $commit->getPHID())
->appendChild(
id(new AphrontFormSelectControl())
->setLabel(pht('Action'))
->setName('action')
->setID('audit-action')
->setOptions($actions))
->appendControl(
id(new AphrontFormTokenizerControl())
->setLabel(pht('Add Auditors'))
->setName('auditors')
->setControlID('add-auditors')
->setControlStyle('display: none')
->setID('add-auditors-tokenizer')
->setDisableBehavior(true)
->setDatasource($auditor_source))
->appendControl(
id(new AphrontFormTokenizerControl())
->setLabel(pht('Add CCs'))
->setName('ccs')
->setControlID('add-ccs')
->setControlStyle('display: none')
->setID('add-ccs-tokenizer')
->setDisableBehavior(true)
->setDatasource($mailable_source))
->appendChild(
id(new PhabricatorRemarkupControl())
->setLabel(pht('Comments'))
->setName('content')
->setValue($draft)
->setID('audit-content')
->setUser($viewer))
->appendChild(
id(new AphrontFormSubmitControl())
->setValue(pht('Submit')));
$header = new PHUIHeaderView();
$header->setHeader(
$is_serious ? pht('Audit Commit') : pht('Creative Accounting'));
Javelin::initBehavior(
'differential-add-reviewers-and-ccs',
array(
'dynamic' => array(
'add-auditors-tokenizer' => array(
'actions' => array('add_auditors' => 1),
'src' => $auditor_source->getDatasourceURI(),
'row' => 'add-auditors',
'placeholder' => $auditor_source->getPlaceholderText(),
),
'add-ccs-tokenizer' => array(
'actions' => array('add_ccs' => 1),
'src' => $mailable_source->getDatasourceURI(),
'row' => 'add-ccs',
'placeholder' => $mailable_source->getPlaceholderText(),
),
),
'select' => 'audit-action',
));
Javelin::initBehavior('differential-feedback-preview', array(
'uri' => '/audit/preview/'.$commit->getID().'/',
'preview' => 'audit-preview',
'content' => 'audit-content',
'action' => 'audit-action',
'previewTokenizers' => array(
'auditors' => 'add-auditors-tokenizer',
'ccs' => 'add-ccs-tokenizer',
),
'inline' => 'inline-comment-preview',
'inlineuri' => '/diffusion/inline/preview/'.$commit->getPHID().'/',
));
$loading = phutil_tag_div(
'aphront-panel-preview-loading-text',
pht('Loading preview...'));
$preview_panel = phutil_tag_div(
'aphront-panel-preview aphront-panel-flush',
array(
phutil_tag('div', array('id' => 'audit-preview'), $loading),
phutil_tag('div', array('id' => 'inline-comment-preview')),
));
// TODO: This is pretty awkward, unify the CSS between Diffusion and
// Differential better.
require_celerity_resource('differential-core-view-css');
$anchor = id(new PhabricatorAnchorView())
->setAnchorName('comment')
->setNavigationMarker(true)
->render();
$comment_box = id(new PHUIObjectBoxView())
->setHeader($header)
->appendChild($form);
return phutil_tag(
'div',
array(
'id' => $pane_id,
),
phutil_tag_div(
'differential-add-comment-panel',
array($anchor, $comment_box, $preview_panel)));
}
/**
* Return a map of available audit actions for rendering into a <select />.
* This shows the user valid actions, and does not show nonsense/invalid
* actions (like closing an already-closed commit, or resigning from a commit
* you have no association with).
*/
private function getAuditActions(
PhabricatorRepositoryCommit $commit,
array $audit_requests) {
assert_instances_of($audit_requests, 'PhabricatorRepositoryAuditRequest');
$viewer = $this->getViewer();
$user_is_author = ($commit->getAuthorPHID() == $viewer->getPHID());
$user_request = null;
foreach ($audit_requests as $audit_request) {
if ($audit_request->getAuditorPHID() == $viewer->getPHID()) {
$user_request = $audit_request;
break;
}
}
$actions = array();
$actions[PhabricatorAuditActionConstants::COMMENT] = true;
// We allow you to accept your own commits. A use case here is that you
// notice an issue with your own commit and "Raise Concern" as an indicator
// to other auditors that you're on top of the issue, then later resolve it
// and "Accept". You can not accept on behalf of projects or packages,
// however.
$actions[PhabricatorAuditActionConstants::ACCEPT] = true;
$actions[PhabricatorAuditActionConstants::CONCERN] = true;
// To resign, a user must have authority on some request and not be the
// commit's author.
if (!$user_is_author) {
$may_resign = false;
$authority_map = array_fill_keys($this->auditAuthorityPHIDs, true);
foreach ($audit_requests as $request) {
if (empty($authority_map[$request->getAuditorPHID()])) {
continue;
}
$may_resign = true;
break;
}
// If the user has already resigned, don't show "Resign...".
$status_resigned = PhabricatorAuditStatusConstants::RESIGNED;
if ($user_request) {
if ($user_request->getAuditStatus() == $status_resigned) {
$may_resign = false;
}
}
if ($may_resign) {
$actions[PhabricatorAuditActionConstants::RESIGN] = true;
}
}
$status_concern = PhabricatorAuditCommitStatusConstants::CONCERN_RAISED;
$concern_raised = ($commit->getAuditStatus() == $status_concern);
$can_close_option = PhabricatorEnv::getEnvConfig(
'audit.can-author-close-audit');
if ($can_close_option && $user_is_author && $concern_raised) {
$actions[PhabricatorAuditActionConstants::CLOSE] = true;
}
$actions[PhabricatorAuditActionConstants::ADD_AUDITORS] = true;
$actions[PhabricatorAuditActionConstants::ADD_CCS] = true;
foreach ($actions as $constant => $ignored) {
$actions[$constant] =
PhabricatorAuditActionConstants::getActionName($constant);
}
return $actions;
}
private function buildMergesTable(PhabricatorRepositoryCommit $commit) {
$viewer = $this->getViewer();
$drequest = $this->getDiffusionRequest();
$repository = $drequest->getRepository();
$merges = $this->getCommitMerges();
if (!$merges) {
return null;
}
$limit = $this->getMergeDisplayLimit();
$caption = null;
if (count($merges) > $limit) {
$merges = array_slice($merges, 0, $limit);
$caption = new PHUIInfoView();
$caption->setSeverity(PHUIInfoView::SEVERITY_NOTICE);
$caption->appendChild(
pht(
'This commit merges a very large number of changes. '.
'Only the first %s are shown.',
new PhutilNumber($limit)));
}
$history_table = id(new DiffusionHistoryTableView())
->setUser($viewer)
->setDiffusionRequest($drequest)
->setHistory($merges);
$history_table->loadRevisions();
$panel = id(new PHUIObjectBoxView())
->setHeaderText(pht('Merged Changes'))
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->setTable($history_table);
if ($caption) {
$panel->setInfoView($caption);
}
return $panel;
}
private function buildCurtain(
PhabricatorRepositoryCommit $commit,
PhabricatorRepository $repository) {
$request = $this->getRequest();
$viewer = $this->getViewer();
$curtain = $this->newCurtainView($commit);
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$commit,
PhabricatorPolicyCapability::CAN_EDIT);
$identifier = $commit->getCommitIdentifier();
$uri = $repository->getPathURI("commit/{$identifier}/edit/");
$action = id(new PhabricatorActionView())
->setName(pht('Edit Commit'))
->setHref($uri)
->setIcon('fa-pencil')
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit);
$curtain->addAction($action);
require_celerity_resource('phabricator-object-selector-css');
require_celerity_resource('javelin-behavior-phabricator-object-selector');
$maniphest = 'PhabricatorManiphestApplication';
if (PhabricatorApplication::isClassInstalled($maniphest)) {
$action = id(new PhabricatorActionView())
->setName(pht('Edit Maniphest Tasks'))
->setIcon('fa-anchor')
->setHref('/search/attach/'.$commit->getPHID().'/TASK/edge/')
->setWorkflow(true)
->setDisabled(!$can_edit);
$curtain->addAction($action);
}
$action = id(new PhabricatorActionView())
->setName(pht('Download Raw Diff'))
->setHref($request->getRequestURI()->alter('diff', true))
->setIcon('fa-download');
$curtain->addAction($action);
return $curtain;
}
private function buildRawDiffResponse(DiffusionRequest $drequest) {
$raw_diff = $this->callConduitWithDiffusionRequest(
'diffusion.rawdiffquery',
array(
'commit' => $drequest->getCommit(),
'path' => $drequest->getPath(),
));
$file = PhabricatorFile::buildFromFileDataOrHash(
$raw_diff,
array(
'name' => $drequest->getCommit().'.diff',
'ttl' => (60 * 60 * 24),
'viewPolicy' => PhabricatorPolicies::POLICY_NOONE,
));
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
$file->attachToObject($drequest->getRepository()->getPHID());
unset($unguarded);
return $file->getRedirectResponse();
}
private function renderAuditStatusView(array $audit_requests) {
assert_instances_of($audit_requests, 'PhabricatorRepositoryAuditRequest');
$viewer = $this->getViewer();
$authority_map = array_fill_keys($this->auditAuthorityPHIDs, true);
$view = new PHUIStatusListView();
foreach ($audit_requests as $request) {
$code = $request->getAuditStatus();
$item = new PHUIStatusItemView();
$item->setIcon(
PhabricatorAuditStatusConstants::getStatusIcon($code),
PhabricatorAuditStatusConstants::getStatusColor($code),
PhabricatorAuditStatusConstants::getStatusName($code));
$note = array();
foreach ($request->getAuditReasons() as $reason) {
$note[] = phutil_tag('div', array(), $reason);
}
$item->setNote($note);
$auditor_phid = $request->getAuditorPHID();
$target = $viewer->renderHandle($auditor_phid);
$item->setTarget($target);
if (isset($authority_map[$auditor_phid])) {
$item->setHighlighted(true);
}
$view->addItem($item);
}
return $view;
}
private function linkBugtraq($corpus) {
$url = PhabricatorEnv::getEnvConfig('bugtraq.url');
if (!strlen($url)) {
return $corpus;
}
$regexes = PhabricatorEnv::getEnvConfig('bugtraq.logregex');
if (!$regexes) {
return $corpus;
}
$parser = id(new PhutilBugtraqParser())
->setBugtraqPattern("[[ {$url} | %BUGID% ]]")
->setBugtraqCaptureExpression(array_shift($regexes));
$select = array_shift($regexes);
if ($select) {
$parser->setBugtraqSelectExpression($select);
}
return $parser->processCorpus($corpus);
}
private function buildTableOfContents(
array $changesets,
$header,
$info_view) {
$drequest = $this->getDiffusionRequest();
$viewer = $this->getViewer();
$toc_view = id(new PHUIDiffTableOfContentsListView())
->setUser($viewer)
->setHeader($header)
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY);
if ($info_view) {
$toc_view->setInfoView($info_view);
}
// TODO: This is hacky, we just want access to the linkX() methods on
// DiffusionView.
$diffusion_view = id(new DiffusionEmptyResultView())
->setDiffusionRequest($drequest);
$have_owners = PhabricatorApplication::isClassInstalledForViewer(
'PhabricatorOwnersApplication',
$viewer);
if (!$changesets) {
$have_owners = false;
}
if ($have_owners) {
if ($viewer->getPHID()) {
$packages = id(new PhabricatorOwnersPackageQuery())
->setViewer($viewer)
->withStatuses(array(PhabricatorOwnersPackage::STATUS_ACTIVE))
->withAuthorityPHIDs(array($viewer->getPHID()))
->execute();
$toc_view->setAuthorityPackages($packages);
}
$repository = $drequest->getRepository();
$repository_phid = $repository->getPHID();
$control_query = id(new PhabricatorOwnersPackageQuery())
->setViewer($viewer)
->withStatuses(array(PhabricatorOwnersPackage::STATUS_ACTIVE))
->withControl($repository_phid, mpull($changesets, 'getFilename'));
$control_query->execute();
}
foreach ($changesets as $changeset_id => $changeset) {
$path = $changeset->getFilename();
$anchor = substr(md5($path), 0, 8);
$history_link = $diffusion_view->linkHistory($path);
$browse_link = $diffusion_view->linkBrowse(
$path,
array(
'type' => $changeset->getFileType(),
));
$item = id(new PHUIDiffTableOfContentsItemView())
->setChangeset($changeset)
->setAnchor($anchor)
->setContext(
array(
$history_link,
' ',
$browse_link,
));
if ($have_owners) {
$packages = $control_query->getControllingPackagesForPath(
$repository_phid,
$changeset->getFilename());
$item->setPackages($packages);
}
$toc_view->addItem($item);
}
return $toc_view;
}
private function loadCommitState() {
$viewer = $this->getViewer();
$drequest = $this->getDiffusionRequest();
$repository = $drequest->getRepository();
$commit = $drequest->getCommit();
// TODO: We could use futures here and resolve these calls in parallel.
$exceptions = array();
try {
$parent_refs = $this->callConduitWithDiffusionRequest(
'diffusion.commitparentsquery',
array(
'commit' => $commit,
));
if ($parent_refs) {
$parents = id(new DiffusionCommitQuery())
->setViewer($viewer)
->withRepository($repository)
->withIdentifiers($parent_refs)
->execute();
} else {
$parents = array();
}
$this->commitParents = $parents;
} catch (Exception $ex) {
$this->commitParents = false;
$exceptions[] = $ex;
}
$merge_limit = $this->getMergeDisplayLimit();
try {
if ($repository->isSVN()) {
$this->commitMerges = array();
} else {
$merges = $this->callConduitWithDiffusionRequest(
'diffusion.mergedcommitsquery',
array(
'commit' => $commit,
'limit' => $merge_limit + 1,
));
$this->commitMerges = DiffusionPathChange::newFromConduit($merges);
}
} catch (Exception $ex) {
$this->commitMerges = false;
$exceptions[] = $ex;
}
try {
if ($repository->isGit()) {
$refs = $this->callConduitWithDiffusionRequest(
'diffusion.refsquery',
array(
'commit' => $commit,
));
} else {
$refs = array();
}
$this->commitRefs = $refs;
} catch (Exception $ex) {
$this->commitRefs = false;
$exceptions[] = $ex;
}
if ($exceptions) {
$exists = $this->callConduitWithDiffusionRequest(
'diffusion.existsquery',
array(
'commit' => $commit,
));
if ($exists) {
$this->commitExists = true;
foreach ($exceptions as $exception) {
$this->commitErrors[] = $exception->getMessage();
}
} else {
$this->commitExists = false;
$this->commitErrors[] = pht(
'This commit no longer exists in the repository. It may have '.
'been part of a branch which was deleted.');
}
} else {
$this->commitExists = true;
$this->commitErrors = array();
}
}
private function getMergeDisplayLimit() {
return 50;
}
private function getCommitExists() {
if ($this->commitExists === null) {
$this->loadCommitState();
}
return $this->commitExists;
}
private function getCommitParents() {
if ($this->commitParents === null) {
$this->loadCommitState();
}
return $this->commitParents;
}
private function getCommitRefs() {
if ($this->commitRefs === null) {
$this->loadCommitState();
}
return $this->commitRefs;
}
private function getCommitMerges() {
if ($this->commitMerges === null) {
$this->loadCommitState();
}
return $this->commitMerges;
}
private function getCommitErrors() {
if ($this->commitErrors === null) {
$this->loadCommitState();
}
return $this->commitErrors;
}
}
diff --git a/src/applications/diffusion/controller/DiffusionRepositoryController.php b/src/applications/diffusion/controller/DiffusionRepositoryController.php
index 792bcc4249..b9e8fa40c6 100644
--- a/src/applications/diffusion/controller/DiffusionRepositoryController.php
+++ b/src/applications/diffusion/controller/DiffusionRepositoryController.php
@@ -1,780 +1,780 @@
<?php
final class DiffusionRepositoryController extends DiffusionController {
public function shouldAllowPublic() {
return true;
}
public function handleRequest(AphrontRequest $request) {
$response = $this->loadDiffusionContext();
if ($response) {
return $response;
}
$viewer = $this->getViewer();
$drequest = $this->getDiffusionRequest();
$repository = $drequest->getRepository();
$crumbs = $this->buildCrumbs();
$crumbs->setBorder(true);
$header = $this->buildHeaderView($repository);
$curtain = $this->buildCurtain($repository);
$property_table = $this->buildPropertiesTable($repository);
$description = $this->buildDescriptionView($repository);
$locate_file = $this->buildLocateFile();
// Before we do any work, make sure we're looking at a some content: we're
// on a valid branch, and the repository is not empty.
$page_has_content = false;
$empty_title = null;
$empty_message = null;
// If this VCS supports branches, check that the selected branch actually
// exists.
if ($drequest->supportsBranches()) {
// NOTE: Mercurial may have multiple branch heads with the same name.
$ref_cursors = id(new PhabricatorRepositoryRefCursorQuery())
->setViewer($viewer)
->withRepositoryPHIDs(array($repository->getPHID()))
->withRefTypes(array(PhabricatorRepositoryRefCursor::TYPE_BRANCH))
->withRefNames(array($drequest->getBranch()))
->execute();
if ($ref_cursors) {
// This is a valid branch, so we necessarily have some content.
$page_has_content = true;
} else {
$empty_title = pht('No Such Branch');
$empty_message = pht(
'There is no branch named "%s" in this repository.',
$drequest->getBranch());
}
}
// If we didn't find any branches, check if there are any commits at all.
// This can tailor the message for empty repositories.
if (!$page_has_content) {
$any_commit = id(new DiffusionCommitQuery())
->setViewer($viewer)
->withRepository($repository)
->setLimit(1)
->execute();
if ($any_commit) {
if (!$drequest->supportsBranches()) {
$page_has_content = true;
}
} else {
$empty_title = pht('Empty Repository');
$empty_message = pht('This repository does not have any commits yet.');
}
}
if ($page_has_content) {
$content = $this->buildNormalContent($drequest);
} else {
$content = id(new PHUIInfoView())
->setTitle($empty_title)
->setSeverity(PHUIInfoView::SEVERITY_WARNING)
->setErrors(array($empty_message));
}
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setCurtain($curtain)
->setMainColumn(array(
$property_table,
$description,
$locate_file,
))
->setFooter($content);
return $this->newPage()
->setTitle(
array(
$repository->getName(),
$repository->getDisplayName(),
))
->setCrumbs($crumbs)
->appendChild(array(
$view,
));
}
private function buildNormalContent(DiffusionRequest $drequest) {
$request = $this->getRequest();
$repository = $drequest->getRepository();
$phids = array();
$content = array();
try {
$history_results = $this->callConduitWithDiffusionRequest(
'diffusion.historyquery',
array(
'commit' => $drequest->getCommit(),
'path' => $drequest->getPath(),
'offset' => 0,
'limit' => 15,
));
$history = DiffusionPathChange::newFromConduit(
$history_results['pathChanges']);
foreach ($history as $item) {
$data = $item->getCommitData();
if ($data) {
if ($data->getCommitDetail('authorPHID')) {
$phids[$data->getCommitDetail('authorPHID')] = true;
}
if ($data->getCommitDetail('committerPHID')) {
$phids[$data->getCommitDetail('committerPHID')] = true;
}
}
}
$history_exception = null;
} catch (Exception $ex) {
$history_results = null;
$history = null;
$history_exception = $ex;
}
$browse_pager = id(new PHUIPagerView())
->readFromRequest($request);
try {
$browse_results = DiffusionBrowseResultSet::newFromConduit(
$this->callConduitWithDiffusionRequest(
'diffusion.browsequery',
array(
'path' => $drequest->getPath(),
'commit' => $drequest->getCommit(),
'limit' => $browse_pager->getPageSize() + 1,
)));
$browse_paths = $browse_results->getPaths();
$browse_paths = $browse_pager->sliceResults($browse_paths);
foreach ($browse_paths as $item) {
$data = $item->getLastCommitData();
if ($data) {
if ($data->getCommitDetail('authorPHID')) {
$phids[$data->getCommitDetail('authorPHID')] = true;
}
if ($data->getCommitDetail('committerPHID')) {
$phids[$data->getCommitDetail('committerPHID')] = true;
}
}
}
$browse_exception = null;
} catch (Exception $ex) {
$browse_results = null;
$browse_paths = null;
$browse_exception = $ex;
}
$phids = array_keys($phids);
$handles = $this->loadViewerHandles($phids);
if ($browse_results) {
$readme = $this->renderDirectoryReadme($browse_results);
} else {
$readme = null;
}
$content[] = $this->buildBrowseTable(
$browse_results,
$browse_paths,
$browse_exception,
$handles,
$browse_pager);
$content[] = $this->buildHistoryTable(
$history_results,
$history,
$history_exception);
try {
$content[] = $this->buildTagListTable($drequest);
} catch (Exception $ex) {
if (!$repository->isImporting()) {
$content[] = $this->renderStatusMessage(
pht('Unable to Load Tags'),
$ex->getMessage());
}
}
try {
$content[] = $this->buildBranchListTable($drequest);
} catch (Exception $ex) {
if (!$repository->isImporting()) {
$content[] = $this->renderStatusMessage(
pht('Unable to Load Branches'),
$ex->getMessage());
}
}
if ($readme) {
$content[] = $readme;
}
return $content;
}
private function buildHeaderView(PhabricatorRepository $repository) {
$viewer = $this->getViewer();
$header = id(new PHUIHeaderView())
->setHeader($repository->getName())
->setUser($viewer)
->setPolicyObject($repository)
->setHeaderIcon('fa-code');
if (!$repository->isTracked()) {
$header->setStatus('fa-ban', 'dark', pht('Inactive'));
} else if ($repository->isImporting()) {
$ratio = $repository->loadImportProgress();
$percentage = sprintf('%.2f%%', 100 * $ratio);
$header->setStatus(
'fa-clock-o',
'indigo',
pht('Importing (%s)...', $percentage));
} else {
$header->setStatus('fa-check', 'bluegrey', pht('Active'));
}
return $header;
}
private function buildCurtain(PhabricatorRepository $repository) {
$viewer = $this->getViewer();
$edit_uri = $repository->getPathURI('edit/');
$curtain = $this->newCurtainView($repository);
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$repository,
PhabricatorPolicyCapability::CAN_EDIT);
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('Edit Repository'))
->setIcon('fa-pencil')
->setHref($edit_uri)
->setWorkflow(!$can_edit)
->setDisabled(!$can_edit));
if ($repository->isHosted()) {
$push_uri = $this->getApplicationURI(
'pushlog/?repositories='.$repository->getMonogram());
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('View Push Logs'))
->setIcon('fa-list-alt')
->setHref($push_uri));
}
return $curtain;
}
private function buildDescriptionView(PhabricatorRepository $repository) {
$viewer = $this->getViewer();
$view = id(new PHUIPropertyListView())
->setUser($viewer);
$description = $repository->getDetail('description');
if (strlen($description)) {
$description = new PHUIRemarkupView($viewer, $description);
$view->addTextContent($description);
return id(new PHUIObjectBoxView())
- ->setHeaderText(pht('DESCRIPTION'))
+ ->setHeaderText(pht('Description'))
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->appendChild($view);
}
return null;
}
private function buildPropertiesTable(PhabricatorRepository $repository) {
$viewer = $this->getViewer();
$view = id(new PHUIPropertyListView())
->setUser($viewer);
if ($repository->isHosted()) {
$ssh_uri = $repository->getSSHCloneURIObject();
if ($ssh_uri) {
$clone_uri = $this->renderCloneCommand(
$repository,
$ssh_uri,
$repository->getServeOverSSH(),
'/settings/panel/ssh/');
$view->addProperty(
$repository->isSVN()
? pht('Checkout (SSH)')
: pht('Clone (SSH)'),
$clone_uri);
}
$http_uri = $repository->getHTTPCloneURIObject();
if ($http_uri) {
$clone_uri = $this->renderCloneCommand(
$repository,
$http_uri,
$repository->getServeOverHTTP(),
PhabricatorEnv::getEnvConfig('diffusion.allow-http-auth')
? '/settings/panel/vcspassword/'
: null);
$view->addProperty(
$repository->isSVN()
? pht('Checkout (HTTP)')
: pht('Clone (HTTP)'),
$clone_uri);
}
} else {
switch ($repository->getVersionControlSystem()) {
case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
$view->addProperty(
pht('Clone'),
$this->renderCloneCommand(
$repository,
$repository->getPublicCloneURI()));
break;
case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
$view->addProperty(
pht('Checkout'),
$this->renderCloneCommand(
$repository,
$repository->getPublicCloneURI()));
break;
}
}
$box = id(new PHUIObjectBoxView())
- ->setHeaderText(pht('DETAILS'))
+ ->setHeaderText(pht('Details'))
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->appendChild($view);
$info = null;
$drequest = $this->getDiffusionRequest();
// Try to load alternatives. This may fail for repositories which have not
// cloned yet. If it does, just ignore it and continue.
try {
$alternatives = $drequest->getRefAlternatives();
} catch (ConduitClientException $ex) {
$alternatives = array();
}
if ($alternatives) {
$message = array(
pht(
'The ref "%s" is ambiguous in this repository.',
$drequest->getBranch()),
' ',
phutil_tag(
'a',
array(
'href' => $drequest->generateURI(
array(
'action' => 'refs',
)),
),
pht('View Alternatives')),
);
$messages = array($message);
$info = id(new PHUIInfoView())
->setSeverity(PHUIInfoView::SEVERITY_WARNING)
->setErrors(array($message));
$box->setInfoView($info);
}
return $box;
}
private function buildBranchListTable(DiffusionRequest $drequest) {
$viewer = $this->getViewer();
if ($drequest->getBranch() === null) {
return null;
}
$limit = 15;
$branches = $this->callConduitWithDiffusionRequest(
'diffusion.branchquery',
array(
'closed' => false,
'limit' => $limit + 1,
));
if (!$branches) {
return null;
}
$more_branches = (count($branches) > $limit);
$branches = array_slice($branches, 0, $limit);
$branches = DiffusionRepositoryRef::loadAllFromDictionaries($branches);
$commits = id(new DiffusionCommitQuery())
->setViewer($viewer)
->withIdentifiers(mpull($branches, 'getCommitIdentifier'))
->withRepository($drequest->getRepository())
->execute();
$table = id(new DiffusionBranchTableView())
->setUser($viewer)
->setDiffusionRequest($drequest)
->setBranches($branches)
->setCommits($commits);
$panel = id(new PHUIObjectBoxView())
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY);
$header = new PHUIHeaderView();
$header->setHeader(pht('Branches'));
if ($more_branches) {
$header->setSubHeader(pht('Showing %d branches.', $limit));
}
$button = new PHUIButtonView();
$button->setText(pht('Show All'));
$button->setTag('a');
$button->setIcon('fa-code-fork');
$button->setHref($drequest->generateURI(
array(
'action' => 'branches',
)));
$header->addActionLink($button);
$panel->setHeader($header);
$panel->setTable($table);
return $panel;
}
private function buildTagListTable(DiffusionRequest $drequest) {
$viewer = $this->getViewer();
$repository = $drequest->getRepository();
switch ($repository->getVersionControlSystem()) {
case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
// no tags in SVN
return null;
}
$tag_limit = 15;
$tags = array();
$tags = DiffusionRepositoryTag::newFromConduit(
$this->callConduitWithDiffusionRequest(
'diffusion.tagsquery',
array(
// On the home page, we want to find tags on any branch.
'commit' => null,
'limit' => $tag_limit + 1,
)));
if (!$tags) {
return null;
}
$more_tags = (count($tags) > $tag_limit);
$tags = array_slice($tags, 0, $tag_limit);
$commits = id(new DiffusionCommitQuery())
->setViewer($viewer)
->withIdentifiers(mpull($tags, 'getCommitIdentifier'))
->withRepository($repository)
->needCommitData(true)
->execute();
$view = id(new DiffusionTagListView())
->setUser($viewer)
->setDiffusionRequest($drequest)
->setTags($tags)
->setCommits($commits);
$phids = $view->getRequiredHandlePHIDs();
$handles = $this->loadViewerHandles($phids);
$view->setHandles($handles);
$panel = new PHUIObjectBoxView();
$header = new PHUIHeaderView();
$header->setHeader(pht('Tags'));
if ($more_tags) {
$header->setSubHeader(
pht('Showing the %d most recent tags.', $tag_limit));
}
$button = new PHUIButtonView();
$button->setText(pht('Show All Tags'));
$button->setTag('a');
$button->setIcon('fa-tag');
$button->setHref($drequest->generateURI(
array(
'action' => 'tags',
)));
$header->addActionLink($button);
$panel->setHeader($header);
$panel->setTable($view);
$panel->setBackground(PHUIObjectBoxView::BLUE_PROPERTY);
return $panel;
}
private function buildHistoryTable(
$history_results,
$history,
$history_exception) {
$request = $this->getRequest();
$viewer = $request->getUser();
$drequest = $this->getDiffusionRequest();
$repository = $drequest->getRepository();
if ($history_exception) {
if ($repository->isImporting()) {
return $this->renderStatusMessage(
pht('Still Importing...'),
pht(
'This repository is still importing. History is not yet '.
'available.'));
} else {
return $this->renderStatusMessage(
pht('Unable to Retrieve History'),
$history_exception->getMessage());
}
}
$history_table = id(new DiffusionHistoryTableView())
->setUser($viewer)
->setDiffusionRequest($drequest)
->setHistory($history);
// TODO: Super sketchy.
$history_table->loadRevisions();
if ($history_results) {
$history_table->setParents($history_results['parents']);
}
$history_table->setIsHead(true);
$icon = id(new PHUIIconView())
->setIcon('fa-list-alt');
$button = id(new PHUIButtonView())
->setText(pht('View History'))
->setHref($drequest->generateURI(
array(
'action' => 'history',
)))
->setTag('a')
->setIcon($icon);
$panel = id(new PHUIObjectBoxView())
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY);
$header = id(new PHUIHeaderView())
->setHeader(pht('Recent Commits'))
->addActionLink($button);
$panel->setHeader($header);
$panel->setTable($history_table);
return $panel;
}
private function buildLocateFile() {
$request = $this->getRequest();
$viewer = $request->getUser();
$drequest = $this->getDiffusionRequest();
$repository = $drequest->getRepository();
$locate_panel = null;
if ($repository->canUsePathTree()) {
Javelin::initBehavior(
'diffusion-locate-file',
array(
'controlID' => 'locate-control',
'inputID' => 'locate-input',
'browseBaseURI' => (string)$drequest->generateURI(
array(
'action' => 'browse',
)),
'uri' => (string)$drequest->generateURI(
array(
'action' => 'pathtree',
)),
));
$form = id(new AphrontFormView())
->setUser($viewer)
->appendChild(
id(new AphrontFormTypeaheadControl())
->setHardpointID('locate-control')
->setID('locate-input')
->setLabel(pht('Locate File')));
$form_box = id(new PHUIBoxView())
->appendChild($form->buildLayoutView());
$locate_panel = id(new PHUIObjectBoxView())
->setHeaderText(pht('Locate File'))
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->appendChild($form_box);
}
return $locate_panel;
}
private function buildBrowseTable(
$browse_results,
$browse_paths,
$browse_exception,
array $handles,
PHUIPagerView $pager) {
require_celerity_resource('diffusion-icons-css');
$request = $this->getRequest();
$viewer = $request->getUser();
$drequest = $this->getDiffusionRequest();
$repository = $drequest->getRepository();
if ($browse_exception) {
if ($repository->isImporting()) {
// The history table renders a useful message.
return null;
} else {
return $this->renderStatusMessage(
pht('Unable to Retrieve Paths'),
$browse_exception->getMessage());
}
}
$browse_table = id(new DiffusionBrowseTableView())
->setUser($viewer)
->setDiffusionRequest($drequest)
->setHandles($handles);
if ($browse_paths) {
$browse_table->setPaths($browse_paths);
} else {
$browse_table->setPaths(array());
}
$browse_uri = $drequest->generateURI(array('action' => 'browse'));
$browse_panel = id(new PHUIObjectBoxView())
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY);
$header = id(new PHUIHeaderView())
->setHeader($repository->getName());
$icon = id(new PHUIIconView())
->setIcon('fa-folder-open');
$button = new PHUIButtonView();
$button->setText(pht('Browse Repository'));
$button->setTag('a');
$button->setIcon($icon);
$button->setHref($browse_uri);
$header->addActionLink($button);
$browse_panel->setHeader($header);
$browse_panel->setTable($browse_table);
$pager->setURI($browse_uri, 'offset');
if ($pager->willShowPagingControls()) {
$pager_box = $this->renderTablePagerBox($pager);
} else {
$pager_box = null;
}
return array(
$browse_panel,
$pager_box,
);
}
private function renderCloneCommand(
PhabricatorRepository $repository,
$uri,
$serve_mode = null,
$manage_uri = null) {
require_celerity_resource('diffusion-icons-css');
Javelin::initBehavior('select-on-click');
switch ($repository->getVersionControlSystem()) {
case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
$command = csprintf(
'git clone %R',
$uri);
break;
case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
$command = csprintf(
'hg clone %R',
$uri);
break;
case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
if ($repository->isHosted()) {
$command = csprintf(
'svn checkout %R %R',
$uri,
$repository->getCloneName());
} else {
$command = csprintf(
'svn checkout %R',
$uri);
}
break;
}
$input = javelin_tag(
'input',
array(
'type' => 'text',
'value' => (string)$command,
'class' => 'diffusion-clone-uri',
'sigil' => 'select-on-click',
'readonly' => 'true',
));
$extras = array();
if ($serve_mode) {
if ($serve_mode === PhabricatorRepository::SERVE_READONLY) {
$extras[] = pht('(Read Only)');
}
}
if ($manage_uri) {
if ($this->getRequest()->getUser()->isLoggedIn()) {
$extras[] = phutil_tag(
'a',
array(
'href' => $manage_uri,
),
pht('Manage Credentials'));
}
}
if ($extras) {
$extras = phutil_implode_html(' ', $extras);
$extras = phutil_tag(
'div',
array(
'class' => 'diffusion-clone-extras',
),
$extras);
}
return array($input, $extras);
}
}
diff --git a/src/applications/fund/controller/FundInitiativeViewController.php b/src/applications/fund/controller/FundInitiativeViewController.php
index 18b123bf3a..4960e7aa35 100644
--- a/src/applications/fund/controller/FundInitiativeViewController.php
+++ b/src/applications/fund/controller/FundInitiativeViewController.php
@@ -1,190 +1,190 @@
<?php
final class FundInitiativeViewController
extends FundController {
public function shouldAllowPublic() {
return true;
}
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$id = $request->getURIData('id');
$initiative = id(new FundInitiativeQuery())
->setViewer($viewer)
->withIDs(array($id))
->executeOne();
if (!$initiative) {
return new Aphront404Response();
}
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb($initiative->getMonogram());
$crumbs->setBorder(true);
$title = pht(
'%s %s',
$initiative->getMonogram(),
$initiative->getName());
if ($initiative->isClosed()) {
$status_icon = 'fa-times';
$status_color = 'bluegrey';
} else {
$status_icon = 'fa-check';
$status_color = 'bluegrey';
}
$status_name = idx(
FundInitiative::getStatusNameMap(),
$initiative->getStatus());
$header = id(new PHUIHeaderView())
->setHeader($initiative->getName())
->setUser($viewer)
->setPolicyObject($initiative)
->setStatus($status_icon, $status_color, $status_name)
->setHeaderIcon('fa-heart');
$curtain = $this->buildCurtain($initiative);
$details = $this->buildPropertySectionView($initiative);
$timeline = $this->buildTransactionTimeline(
$initiative,
new FundInitiativeTransactionQuery());
$add_comment = $this->buildCommentForm($initiative);
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setCurtain($curtain)
->setMainColumn(array(
$timeline,
$add_comment,
))
- ->addPropertySection(pht('DETAILS'), $details);
+ ->addPropertySection(pht('Details'), $details);
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->setPageObjectPHIDs(array($initiative->getPHID()))
->appendChild($view);
}
private function buildPropertySectionView(FundInitiative $initiative) {
$viewer = $this->getRequest()->getUser();
$view = id(new PHUIPropertyListView())
->setUser($viewer);
$owner_phid = $initiative->getOwnerPHID();
$merchant_phid = $initiative->getMerchantPHID();
$view->addProperty(
pht('Owner'),
$viewer->renderHandle($owner_phid));
$view->addProperty(
pht('Payable to Merchant'),
$viewer->renderHandle($merchant_phid));
$view->addProperty(
pht('Total Funding'),
$initiative->getTotalAsCurrency()->formatForDisplay());
$description = $initiative->getDescription();
if (strlen($description)) {
$description = new PHUIRemarkupView($viewer, $description);
$view->addSectionHeader(
pht('Description'), PHUIPropertyListView::ICON_SUMMARY);
$view->addTextContent($description);
}
$risks = $initiative->getRisks();
if (strlen($risks)) {
$risks = new PHUIRemarkupView($viewer, $risks);
$view->addSectionHeader(
pht('Risks/Challenges'), 'fa-ambulance');
$view->addTextContent($risks);
}
return $view;
}
private function buildCurtain(FundInitiative $initiative) {
$viewer = $this->getViewer();
$id = $initiative->getID();
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$initiative,
PhabricatorPolicyCapability::CAN_EDIT);
$curtain = $this->newCurtainView($initiative);
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('Edit Initiative'))
->setIcon('fa-pencil')
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit)
->setHref($this->getApplicationURI("/edit/{$id}/")));
if ($initiative->isClosed()) {
$close_name = pht('Reopen Initiative');
$close_icon = 'fa-check';
} else {
$close_name = pht('Close Initiative');
$close_icon = 'fa-times';
}
$curtain->addAction(
id(new PhabricatorActionView())
->setName($close_name)
->setIcon($close_icon)
->setDisabled(!$can_edit)
->setWorkflow(true)
->setHref($this->getApplicationURI("/close/{$id}/")));
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('Back Initiative'))
->setIcon('fa-money')
->setDisabled($initiative->isClosed())
->setWorkflow(true)
->setHref($this->getApplicationURI("/back/{$id}/")));
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('View Backers'))
->setIcon('fa-bank')
->setHref($this->getApplicationURI("/backers/{$id}/")));
return $curtain;
}
private function buildCommentForm(FundInitiative $initiative) {
$viewer = $this->getViewer();
$is_serious = PhabricatorEnv::getEnvConfig('phabricator.serious-business');
$add_comment_header = $is_serious
? pht('Add Comment')
: pht('Add Liquidity');
$draft = PhabricatorDraft::newFromUserAndKey(
$viewer, $initiative->getPHID());
return id(new PhabricatorApplicationTransactionCommentView())
->setUser($viewer)
->setObjectPHID($initiative->getPHID())
->setDraft($draft)
->setHeaderText($add_comment_header)
->setAction(
$this->getApplicationURI('/comment/'.$initiative->getID().'/'))
->setSubmitButtonName(pht('Add Comment'));
}
}
diff --git a/src/applications/harbormaster/controller/HarbormasterBuildViewController.php b/src/applications/harbormaster/controller/HarbormasterBuildViewController.php
index 320e94d642..6b0e7c1e0d 100644
--- a/src/applications/harbormaster/controller/HarbormasterBuildViewController.php
+++ b/src/applications/harbormaster/controller/HarbormasterBuildViewController.php
@@ -1,643 +1,643 @@
<?php
final class HarbormasterBuildViewController
extends HarbormasterController {
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);
$page_header = id(new PHUIHeaderView())
->setHeader($title)
->setUser($viewer)
->setPolicyObject($build)
->setHeaderIcon('fa-cubes');
if ($build->isRestarting()) {
$page_header->setStatus(
'fa-exclamation-triangle', 'red', pht('Restarting'));
} else if ($build->isPausing()) {
$page_header->setStatus(
'fa-exclamation-triangle', 'red', pht('Pausing'));
} else if ($build->isResuming()) {
$page_header->setStatus(
'fa-exclamation-triangle', 'red', pht('Resuming'));
} else if ($build->isAborting()) {
$page_header->setStatus(
'fa-exclamation-triangle', 'red', pht('Aborting'));
}
$curtain = $this->buildCurtainView($build);
$properties = $this->buildPropertyList($build);
$crumbs = $this->buildApplicationCrumbs();
$this->addBuildableCrumb($crumbs, $build->getBuildable());
$crumbs->addTextCrumb($title);
$crumbs->setBorder(true);
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)
->setHeaderIcon('fa-bullseye');
$target_box = id(new PHUIObjectBoxView())
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->setHeader($header);
$property_list = 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);
$property_list->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));
}
}
$property_list->addProperty(
pht('When'),
phutil_implode_html(" \xC2\xB7 ", $when));
$property_list->addProperty(pht('Status'), $status_view);
$target_box->addPropertyList($property_list, pht('Overview'));
$step = $build_target->getBuildStep();
if ($step) {
$description = $step->getDescription();
if ($description) {
$description = new PHUIRemarkupView($viewer, $description);
$property_list->addSectionHeader(
pht('Description'), PHUIPropertyListView::ICON_SUMMARY);
$property_list->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();
$property_list = new PHUIPropertyListView();
foreach ($details as $key => $value) {
$property_list->addProperty($key, $value);
}
$target_box->addPropertyList($property_list, pht('Configuration'));
$variables = $build_target->getVariables();
$property_list = new PHUIPropertyListView();
$property_list->addRawContent($this->buildProperties($variables));
$target_box->addPropertyList($property_list, pht('Variables'));
$artifacts_tab = $this->buildArtifacts($build_target, $target_artifacts);
$property_list = new PHUIPropertyListView();
$property_list->addRawContent($artifacts_tab);
$target_box->addPropertyList($property_list, pht('Artifacts'));
$build_messages = idx($messages, $build_target->getPHID(), array());
$property_list = new PHUIPropertyListView();
$property_list->addRawContent($this->buildMessages($build_messages));
$target_box->addPropertyList($property_list, pht('Messages'));
$property_list = new PHUIPropertyListView();
$property_list->addProperty(
pht('Build Target ID'),
$build_target->getID());
$property_list->addProperty(
pht('Build Target PHID'),
$build_target->getPHID());
$target_box->addPropertyList($property_list, pht('Metadata'));
$targets[] = $target_box;
$targets[] = $this->buildLog($build, $build_target);
}
$timeline = $this->buildTransactionTimeline(
$build,
new HarbormasterBuildTransactionQuery());
$timeline->setShouldTerminate(true);
$view = id(new PHUITwoColumnView())
->setHeader($page_header)
->setCurtain($curtain)
->setMainColumn(array(
$properties,
$targets,
$timeline,
));
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->appendChild($view);
}
private function buildArtifacts(
HarbormasterBuildTarget $build_target,
array $artifacts) {
$viewer = $this->getViewer();
$rows = array();
foreach ($artifacts as $artifact) {
$impl = $artifact->getArtifactImplementation();
if ($impl) {
$summary = $impl->renderArtifactSummary($viewer);
$type_name = $impl->getArtifactTypeName();
} else {
$summary = pht('<Unknown Artifact Type>');
$type_name = $artifact->getType();
}
$rows[] = array(
$artifact->getArtifactKey(),
$type_name,
$summary,
);
}
$table = id(new AphrontTableView($rows))
->setNoDataString(pht('This target has no associated artifacts.'))
->setHeaders(
array(
pht('Key'),
pht('Type'),
pht('Summary'),
))
->setColumnClasses(
array(
'pri',
'',
'wide',
));
return $table;
}
private function buildLog(
HarbormasterBuild $build,
HarbormasterBuildTarget $build_target) {
$request = $this->getRequest();
$viewer = $request->getUser();
$limit = $request->getInt('l', 25);
$logs = id(new HarbormasterBuildLogQuery())
->setViewer($viewer)
->withBuildTargetPHIDs(array($build_target->getPHID()))
->execute();
$empty_logs = array();
$log_boxes = array();
foreach ($logs as $log) {
$start = 1;
$lines = preg_split("/\r\n|\r|\n/", $log->getLogText());
if ($limit !== 0) {
$start = count($lines) - $limit;
if ($start >= 1) {
$lines = array_slice($lines, -$limit, $limit);
} else {
$start = 1;
}
}
$id = null;
$is_empty = false;
if (count($lines) === 1 && trim($lines[0]) === '') {
// Prevent Harbormaster from showing empty build logs.
$id = celerity_generate_unique_node_id();
$empty_logs[] = $id;
$is_empty = true;
}
$log_view = new ShellLogView();
$log_view->setLines($lines);
$log_view->setStart($start);
$header = id(new PHUIHeaderView())
->setHeader(pht(
'Build Log %d (%s - %s)',
$log->getID(),
$log->getLogSource(),
$log->getLogType()))
->setSubheader($this->createLogHeader($build, $log))
->setUser($viewer);
$log_box = id(new PHUIObjectBoxView())
->setHeader($header)
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->setForm($log_view);
if ($is_empty) {
$log_box = phutil_tag(
'div',
array(
'style' => 'display: none',
'id' => $id,
),
$log_box);
}
$log_boxes[] = $log_box;
}
if ($empty_logs) {
$hide_id = celerity_generate_unique_node_id();
Javelin::initBehavior('phabricator-reveal-content');
$expand = phutil_tag(
'div',
array(
'id' => $hide_id,
'class' => 'harbormaster-empty-logs-are-hidden',
),
array(
pht(
'%s empty logs are hidden.',
phutil_count($empty_logs)),
' ',
javelin_tag(
'a',
array(
'href' => '#',
'sigil' => 'reveal-content',
'meta' => array(
'showIDs' => $empty_logs,
'hideIDs' => array($hide_id),
),
),
pht('Show all logs.')),
));
array_unshift($log_boxes, $expand);
}
return $log_boxes;
}
private function createLogHeader($build, $log) {
$request = $this->getRequest();
$limit = $request->getInt('l', 25);
$lines_25 = $this->getApplicationURI('/build/'.$build->getID().'/?l=25');
$lines_50 = $this->getApplicationURI('/build/'.$build->getID().'/?l=50');
$lines_100 =
$this->getApplicationURI('/build/'.$build->getID().'/?l=100');
$lines_0 = $this->getApplicationURI('/build/'.$build->getID().'/?l=0');
$link_25 = phutil_tag('a', array('href' => $lines_25), pht('25'));
$link_50 = phutil_tag('a', array('href' => $lines_50), pht('50'));
$link_100 = phutil_tag('a', array('href' => $lines_100), pht('100'));
$link_0 = phutil_tag('a', array('href' => $lines_0), pht('Unlimited'));
if ($limit === 25) {
$link_25 = phutil_tag('strong', array(), $link_25);
} else if ($limit === 50) {
$link_50 = phutil_tag('strong', array(), $link_50);
} else if ($limit === 100) {
$link_100 = phutil_tag('strong', array(), $link_100);
} else if ($limit === 0) {
$link_0 = phutil_tag('strong', array(), $link_0);
}
return phutil_tag(
'span',
array(),
array(
$link_25,
' - ',
$link_50,
' - ',
$link_100,
' - ',
$link_0,
' Lines',
));
}
private function buildCurtainView(HarbormasterBuild $build) {
$viewer = $this->getViewer();
$id = $build->getID();
$curtain = $this->newCurtainView($build);
$can_restart =
$build->canRestartBuild() &&
$build->canIssueCommand(
$viewer,
HarbormasterBuildCommand::COMMAND_RESTART);
$can_pause =
$build->canPauseBuild() &&
$build->canIssueCommand(
$viewer,
HarbormasterBuildCommand::COMMAND_PAUSE);
$can_resume =
$build->canResumeBuild() &&
$build->canIssueCommand(
$viewer,
HarbormasterBuildCommand::COMMAND_RESUME);
$can_abort =
$build->canAbortBuild() &&
$build->canIssueCommand(
$viewer,
HarbormasterBuildCommand::COMMAND_ABORT);
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('Restart Build'))
->setIcon('fa-repeat')
->setHref($this->getApplicationURI('/build/restart/'.$id.'/'))
->setDisabled(!$can_restart)
->setWorkflow(true));
if ($build->canResumeBuild()) {
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('Resume Build'))
->setIcon('fa-play')
->setHref($this->getApplicationURI('/build/resume/'.$id.'/'))
->setDisabled(!$can_resume)
->setWorkflow(true));
} else {
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('Pause Build'))
->setIcon('fa-pause')
->setHref($this->getApplicationURI('/build/pause/'.$id.'/'))
->setDisabled(!$can_pause)
->setWorkflow(true));
}
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('Abort Build'))
->setIcon('fa-exclamation-triangle')
->setHref($this->getApplicationURI('/build/abort/'.$id.'/'))
->setDisabled(!$can_abort)
->setWorkflow(true));
return $curtain;
}
private function buildPropertyList(HarbormasterBuild $build) {
$viewer = $this->getViewer();
$properties = id(new PHUIPropertyListView())
->setUser($viewer);
$handles = id(new PhabricatorHandleQuery())
->setViewer($viewer)
->withPHIDs(array(
$build->getBuildablePHID(),
$build->getBuildPlanPHID(),
))
->execute();
$properties->addProperty(
pht('Buildable'),
$handles[$build->getBuildablePHID()]->renderLink());
$properties->addProperty(
pht('Build Plan'),
$handles[$build->getBuildPlanPHID()]->renderLink());
$properties->addProperty(
pht('Restarts'),
$build->getBuildGeneration());
$properties->addProperty(
pht('Status'),
$this->getStatus($build));
return id(new PHUIObjectBoxView())
- ->setHeaderText(pht('PROPERTIES'))
+ ->setHeaderText(pht('Properties'))
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->appendChild($properties);
}
private function getStatus(HarbormasterBuild $build) {
$status_view = new PHUIStatusListView();
$item = new PHUIStatusItemView();
if ($build->isPausing()) {
$status_name = pht('Pausing');
$icon = PHUIStatusItemView::ICON_RIGHT;
$color = 'dark';
} else {
$status = $build->getBuildStatus();
$status_name =
HarbormasterBuild::getBuildStatusName($status);
$icon = HarbormasterBuild::getBuildStatusIcon($status);
$color = HarbormasterBuild::getBuildStatusColor($status);
}
$item->setTarget($status_name);
$item->setIcon($icon, $color);
$status_view->addItem($item);
return $status_view;
}
private function buildMessages(array $messages) {
$viewer = $this->getRequest()->getUser();
if ($messages) {
$handles = id(new PhabricatorHandleQuery())
->setViewer($viewer)
->withPHIDs(mpull($messages, 'getAuthorPHID'))
->execute();
} else {
$handles = array();
}
$rows = array();
foreach ($messages as $message) {
$rows[] = array(
$message->getID(),
$handles[$message->getAuthorPHID()]->renderLink(),
$message->getType(),
$message->getIsConsumed() ? pht('Consumed') : null,
phabricator_datetime($message->getDateCreated(), $viewer),
);
}
$table = new AphrontTableView($rows);
$table->setNoDataString(pht('No messages for this build target.'));
$table->setHeaders(
array(
pht('ID'),
pht('From'),
pht('Type'),
pht('Consumed'),
pht('Received'),
));
$table->setColumnClasses(
array(
'',
'',
'wide',
'',
'date',
));
return $table;
}
private function buildProperties(array $properties) {
ksort($properties);
$rows = array();
foreach ($properties as $key => $value) {
$rows[] = array(
$key,
$value,
);
}
$table = id(new AphrontTableView($rows))
->setHeaders(
array(
pht('Key'),
pht('Value'),
))
->setColumnClasses(
array(
'pri right',
'wide',
));
return $table;
}
}
diff --git a/src/applications/harbormaster/controller/HarbormasterBuildableViewController.php b/src/applications/harbormaster/controller/HarbormasterBuildableViewController.php
index fe124367df..390ff2ec3d 100644
--- a/src/applications/harbormaster/controller/HarbormasterBuildableViewController.php
+++ b/src/applications/harbormaster/controller/HarbormasterBuildableViewController.php
@@ -1,352 +1,352 @@
<?php
final class HarbormasterBuildableViewController
extends HarbormasterController {
public function handleRequest(AphrontRequest $request) {
$viewer = $this->getViewer();
$buildable = id(new HarbormasterBuildableQuery())
->setViewer($viewer)
->withIDs(array($request->getURIData('id')))
->executeOne();
if (!$buildable) {
return new Aphront404Response();
}
$id = $buildable->getID();
// Pull builds and build targets.
$builds = id(new HarbormasterBuildQuery())
->setViewer($viewer)
->withBuildablePHIDs(array($buildable->getPHID()))
->needBuildTargets(true)
->execute();
list($lint, $unit) = $this->renderLintAndUnit($buildable, $builds);
$buildable->attachBuilds($builds);
$object = $buildable->getBuildableObject();
$build_list = $this->buildBuildList($buildable);
$title = pht('Buildable %d', $id);
$header = id(new PHUIHeaderView())
->setHeader($title)
->setUser($viewer)
->setPolicyObject($buildable)
->setHeaderIcon('fa-recycle');
$timeline = $this->buildTransactionTimeline(
$buildable,
new HarbormasterBuildableTransactionQuery());
$timeline->setShouldTerminate(true);
$curtain = $this->buildCurtainView($buildable);
$properties = $this->buildPropertyList($buildable);
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb($buildable->getMonogram());
$crumbs->setBorder(true);
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setCurtain($curtain)
->setMainColumn(array(
$properties,
$lint,
$unit,
$build_list,
$timeline,
));
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->appendChild($view);
}
private function buildCurtainView(HarbormasterBuildable $buildable) {
$viewer = $this->getViewer();
$id = $buildable->getID();
$curtain = $this->newCurtainView($buildable);
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$buildable,
PhabricatorPolicyCapability::CAN_EDIT);
$can_restart = false;
$can_resume = false;
$can_pause = false;
$can_abort = false;
$command_restart = HarbormasterBuildCommand::COMMAND_RESTART;
$command_resume = HarbormasterBuildCommand::COMMAND_RESUME;
$command_pause = HarbormasterBuildCommand::COMMAND_PAUSE;
$command_abort = HarbormasterBuildCommand::COMMAND_ABORT;
foreach ($buildable->getBuilds() as $build) {
if ($build->canRestartBuild()) {
if ($build->canIssueCommand($viewer, $command_restart)) {
$can_restart = true;
}
}
if ($build->canResumeBuild()) {
if ($build->canIssueCommand($viewer, $command_resume)) {
$can_resume = true;
}
}
if ($build->canPauseBuild()) {
if ($build->canIssueCommand($viewer, $command_pause)) {
$can_pause = true;
}
}
if ($build->canAbortBuild()) {
if ($build->canIssueCommand($viewer, $command_abort)) {
$can_abort = true;
}
}
}
$restart_uri = "buildable/{$id}/restart/";
$pause_uri = "buildable/{$id}/pause/";
$resume_uri = "buildable/{$id}/resume/";
$abort_uri = "buildable/{$id}/abort/";
$curtain->addAction(
id(new PhabricatorActionView())
->setIcon('fa-repeat')
->setName(pht('Restart All Builds'))
->setHref($this->getApplicationURI($restart_uri))
->setWorkflow(true)
->setDisabled(!$can_restart || !$can_edit));
$curtain->addAction(
id(new PhabricatorActionView())
->setIcon('fa-pause')
->setName(pht('Pause All Builds'))
->setHref($this->getApplicationURI($pause_uri))
->setWorkflow(true)
->setDisabled(!$can_pause || !$can_edit));
$curtain->addAction(
id(new PhabricatorActionView())
->setIcon('fa-play')
->setName(pht('Resume All Builds'))
->setHref($this->getApplicationURI($resume_uri))
->setWorkflow(true)
->setDisabled(!$can_resume || !$can_edit));
$curtain->addAction(
id(new PhabricatorActionView())
->setIcon('fa-exclamation-triangle')
->setName(pht('Abort All Builds'))
->setHref($this->getApplicationURI($abort_uri))
->setWorkflow(true)
->setDisabled(!$can_abort || !$can_edit));
return $curtain;
}
private function buildPropertyList(HarbormasterBuildable $buildable) {
$viewer = $this->getViewer();
$properties = id(new PHUIPropertyListView())
->setUser($viewer);
$container_phid = $buildable->getContainerPHID();
$buildable_phid = $buildable->getBuildablePHID();
if ($container_phid) {
$properties->addProperty(
pht('Container'),
$viewer->renderHandle($container_phid));
}
$properties->addProperty(
pht('Buildable'),
$viewer->renderHandle($buildable_phid));
$properties->addProperty(
pht('Origin'),
$buildable->getIsManualBuildable()
? pht('Manual Buildable')
: pht('Automatic Buildable'));
return id(new PHUIObjectBoxView())
- ->setHeaderText(pht('PROPERTIES'))
+ ->setHeaderText(pht('Properties'))
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->appendChild($properties);
}
private function buildBuildList(HarbormasterBuildable $buildable) {
$viewer = $this->getRequest()->getUser();
$build_list = id(new PHUIObjectItemListView())
->setUser($viewer);
foreach ($buildable->getBuilds() as $build) {
$view_uri = $this->getApplicationURI('/build/'.$build->getID().'/');
$item = id(new PHUIObjectItemView())
->setObjectName(pht('Build %d', $build->getID()))
->setHeader($build->getName())
->setHref($view_uri);
$status = $build->getBuildStatus();
$item->setStatusIcon(
'fa-dot-circle-o '.HarbormasterBuild::getBuildStatusColor($status),
HarbormasterBuild::getBuildStatusName($status));
$item->addAttribute(HarbormasterBuild::getBuildStatusName($status));
if ($build->isRestarting()) {
$item->addIcon('fa-repeat', pht('Restarting'));
} else if ($build->isPausing()) {
$item->addIcon('fa-pause', pht('Pausing'));
} else if ($build->isResuming()) {
$item->addIcon('fa-play', pht('Resuming'));
}
$build_id = $build->getID();
$restart_uri = "build/restart/{$build_id}/buildable/";
$resume_uri = "build/resume/{$build_id}/buildable/";
$pause_uri = "build/pause/{$build_id}/buildable/";
$abort_uri = "build/abort/{$build_id}/buildable/";
$item->addAction(
id(new PHUIListItemView())
->setIcon('fa-repeat')
->setName(pht('Restart'))
->setHref($this->getApplicationURI($restart_uri))
->setWorkflow(true)
->setDisabled(!$build->canRestartBuild()));
if ($build->canResumeBuild()) {
$item->addAction(
id(new PHUIListItemView())
->setIcon('fa-play')
->setName(pht('Resume'))
->setHref($this->getApplicationURI($resume_uri))
->setWorkflow(true));
} else {
$item->addAction(
id(new PHUIListItemView())
->setIcon('fa-pause')
->setName(pht('Pause'))
->setHref($this->getApplicationURI($pause_uri))
->setWorkflow(true)
->setDisabled(!$build->canPauseBuild()));
}
$targets = $build->getBuildTargets();
if ($targets) {
$target_list = id(new PHUIStatusListView());
foreach ($targets as $target) {
$status = $target->getTargetStatus();
$icon = HarbormasterBuildTarget::getBuildTargetStatusIcon($status);
$color = HarbormasterBuildTarget::getBuildTargetStatusColor($status);
$status_name =
HarbormasterBuildTarget::getBuildTargetStatusName($status);
$name = $target->getName();
$target_list->addItem(
id(new PHUIStatusItemView())
->setIcon($icon, $color, $status_name)
->setTarget(pht('Target %d', $target->getID()))
->setNote($name));
}
$target_box = id(new PHUIBoxView())
->addPadding(PHUI::PADDING_SMALL)
->appendChild($target_list);
$item->appendChild($target_box);
}
$build_list->addItem($item);
}
$build_list->setFlush(true);
$box = id(new PHUIObjectBoxView())
->setHeaderText(pht('Builds'))
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->appendChild($build_list);
return $box;
}
private function renderLintAndUnit(
HarbormasterBuildable $buildable,
array $builds) {
$viewer = $this->getViewer();
$targets = array();
foreach ($builds as $build) {
foreach ($build->getBuildTargets() as $target) {
$targets[] = $target;
}
}
if (!$targets) {
return;
}
$target_phids = mpull($targets, 'getPHID');
$lint_data = id(new HarbormasterBuildLintMessage())->loadAllWhere(
'buildTargetPHID IN (%Ls)',
$target_phids);
$unit_data = id(new HarbormasterBuildUnitMessage())->loadAllWhere(
'buildTargetPHID IN (%Ls)',
$target_phids);
if ($lint_data) {
$lint_table = id(new HarbormasterLintPropertyView())
->setUser($viewer)
->setLimit(10)
->setLintMessages($lint_data);
$lint_href = $this->getApplicationURI('lint/'.$buildable->getID().'/');
$lint_header = id(new PHUIHeaderView())
->setHeader(pht('Lint Messages'))
->addActionLink(
id(new PHUIButtonView())
->setTag('a')
->setHref($lint_href)
->setIcon('fa-list-ul')
->setText('View All'));
$lint = id(new PHUIObjectBoxView())
->setHeader($lint_header)
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->setTable($lint_table);
} else {
$lint = null;
}
if ($unit_data) {
$unit = id(new HarbormasterUnitSummaryView())
->setBuildable($buildable)
->setUnitMessages($unit_data)
->setShowViewAll(true)
->setLimit(5);
} else {
$unit = null;
}
return array($lint, $unit);
}
}
diff --git a/src/applications/harbormaster/controller/HarbormasterStepViewController.php b/src/applications/harbormaster/controller/HarbormasterStepViewController.php
index 07be537fa9..a404b48e88 100644
--- a/src/applications/harbormaster/controller/HarbormasterStepViewController.php
+++ b/src/applications/harbormaster/controller/HarbormasterStepViewController.php
@@ -1,144 +1,144 @@
<?php
final class HarbormasterStepViewController
extends HarbormasterPlanController {
public function handleRequest(AphrontRequest $request) {
$viewer = $this->getViewer();
$id = $request->getURIData('id');
$step = id(new HarbormasterBuildStepQuery())
->setViewer($viewer)
->withIDs(array($id))
->executeOne();
if (!$step) {
return new Aphront404Response();
}
$plan = $step->getBuildPlan();
$plan_id = $plan->getID();
$plan_uri = $this->getApplicationURI("plan/{$plan_id}/");
$field_list = PhabricatorCustomField::getObjectFields(
$step,
PhabricatorCustomField::ROLE_VIEW);
$field_list
->setViewer($viewer)
->readFieldsFromStorage($step);
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb(pht('Plan %d', $plan_id), $plan_uri);
$crumbs->addTextCrumb(pht('Step %d', $id));
$crumbs->setBorder(true);
$header = id(new PHUIHeaderView())
->setHeader(pht('Build Step %d: %s', $id, $step->getName()))
->setHeaderIcon('fa-chevron-circle-right');
$properties = $this->buildPropertyList($step, $field_list);
$curtain = $this->buildCurtainView($step);
$timeline = $this->buildTransactionTimeline(
$step,
new HarbormasterBuildStepTransactionQuery());
$timeline->setShouldTerminate(true);
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setCurtain($curtain)
->setMainColumn(array(
$properties,
$timeline,
));
return $this->newPage()
->setTitle(pht('Step %d', $id))
->setCrumbs($crumbs)
->appendChild($view);
}
private function buildPropertyList(
HarbormasterBuildStep $step,
PhabricatorCustomFieldList $field_list) {
$viewer = $this->getViewer();
$view = id(new PHUIPropertyListView())
->setUser($viewer);
try {
$implementation = $step->getStepImplementation();
} catch (Exception $ex) {
$implementation = null;
}
if ($implementation) {
$type = $implementation->getName();
} else {
$type = phutil_tag(
'em',
array(),
pht(
'Invalid Implementation ("%s")!',
$step->getClassName()));
}
$view->addProperty(pht('Step Type'), $type);
$view->addProperty(
pht('Created'),
phabricator_datetime($step->getDateCreated(), $viewer));
$field_list->appendFieldsToPropertyList(
$step,
$viewer,
$view);
$description = $step->getDescription();
if (strlen($description)) {
$view->addSectionHeader(
pht('Description'),
PHUIPropertyListView::ICON_SUMMARY);
$view->addTextContent(
new PHUIRemarkupView($viewer, $description));
}
return id(new PHUIObjectBoxView())
- ->setHeaderText(pht('PROPERTIES'))
+ ->setHeaderText(pht('Properties'))
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->appendChild($view);
}
private function buildCurtainView(HarbormasterBuildStep $step) {
$viewer = $this->getViewer();
$id = $step->getID();
$curtain = $this->newCurtainView($step);
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$step,
PhabricatorPolicyCapability::CAN_EDIT);
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('Edit Step'))
->setHref($this->getApplicationURI("step/edit/{$id}/"))
->setWorkflow(!$can_edit)
->setDisabled(!$can_edit)
->setIcon('fa-pencil'));
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('Delete Step'))
->setHref($this->getApplicationURI("step/delete/{$id}/"))
->setWorkflow(true)
->setDisabled(!$can_edit)
->setIcon('fa-times'));
return $curtain;
}
}
diff --git a/src/applications/herald/controller/HeraldRuleViewController.php b/src/applications/herald/controller/HeraldRuleViewController.php
index f5e058d3f3..818eb7560f 100644
--- a/src/applications/herald/controller/HeraldRuleViewController.php
+++ b/src/applications/herald/controller/HeraldRuleViewController.php
@@ -1,158 +1,158 @@
<?php
final class HeraldRuleViewController extends HeraldController {
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$id = $request->getURIData('id');
$rule = id(new HeraldRuleQuery())
->setViewer($viewer)
->withIDs(array($id))
->needConditionsAndActions(true)
->executeOne();
if (!$rule) {
return new Aphront404Response();
}
$header = id(new PHUIHeaderView())
->setUser($viewer)
->setHeader($rule->getName())
->setPolicyObject($rule)
->setHeaderIcon('fa-bullhorn');
if ($rule->getIsDisabled()) {
$header->setStatus(
'fa-ban',
'red',
pht('Archived'));
} else {
$header->setStatus(
'fa-check',
'bluegrey',
pht('Active'));
}
$curtain = $this->buildCurtain($rule);
$details = $this->buildPropertySectionView($rule);
$description = $this->buildDescriptionView($rule);
$id = $rule->getID();
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb("H{$id}");
$crumbs->setBorder(true);
$timeline = $this->buildTransactionTimeline(
$rule,
new HeraldTransactionQuery());
$timeline->setShouldTerminate(true);
$title = $rule->getName();
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setCurtain($curtain)
->setMainColumn($timeline)
- ->addPropertySection(pht('DETAILS'), $details)
- ->addPropertySection(pht('DESCRIPTION'), $description);
+ ->addPropertySection(pht('Details'), $details)
+ ->addPropertySection(pht('Description'), $description);
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->appendChild($view);
}
private function buildCurtain(HeraldRule $rule) {
$viewer = $this->getViewer();
$id = $rule->getID();
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$rule,
PhabricatorPolicyCapability::CAN_EDIT);
$curtain = $this->newCurtainView($rule);
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('Edit Rule'))
->setHref($this->getApplicationURI("edit/{$id}/"))
->setIcon('fa-pencil')
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit));
if ($rule->getIsDisabled()) {
$disable_uri = "disable/{$id}/enable/";
$disable_icon = 'fa-check';
$disable_name = pht('Activate Rule');
} else {
$disable_uri = "disable/{$id}/disable/";
$disable_icon = 'fa-ban';
$disable_name = pht('Archive Rule');
}
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('Disable Rule'))
->setHref($this->getApplicationURI($disable_uri))
->setIcon($disable_icon)
->setName($disable_name)
->setDisabled(!$can_edit)
->setWorkflow(true));
return $curtain;
}
private function buildPropertySectionView(
HeraldRule $rule) {
$viewer = $this->getRequest()->getUser();
$view = id(new PHUIPropertyListView())
->setUser($viewer);
$view->addProperty(
pht('Rule Type'),
idx(HeraldRuleTypeConfig::getRuleTypeMap(), $rule->getRuleType()));
if ($rule->isPersonalRule()) {
$view->addProperty(
pht('Author'),
$viewer->renderHandle($rule->getAuthorPHID()));
}
$adapter = HeraldAdapter::getAdapterForContentType($rule->getContentType());
if ($adapter) {
$view->addProperty(
pht('Applies To'),
idx(
HeraldAdapter::getEnabledAdapterMap($viewer),
$rule->getContentType()));
if ($rule->isObjectRule()) {
$view->addProperty(
pht('Trigger Object'),
$viewer->renderHandle($rule->getTriggerObjectPHID()));
}
}
return $view;
}
private function buildDescriptionView(HeraldRule $rule) {
$viewer = $this->getRequest()->getUser();
$view = id(new PHUIPropertyListView())
->setUser($viewer);
$adapter = HeraldAdapter::getAdapterForContentType($rule->getContentType());
if ($adapter) {
$handles = $viewer->loadHandles(HeraldAdapter::getHandlePHIDs($rule));
$rule_text = $adapter->renderRuleAsText($rule, $handles, $viewer);
$view->addTextContent($rule_text);
return $view;
}
return null;
}
}
diff --git a/src/applications/legalpad/controller/LegalpadDocumentManageController.php b/src/applications/legalpad/controller/LegalpadDocumentManageController.php
index 134dada128..1a39f2c143 100644
--- a/src/applications/legalpad/controller/LegalpadDocumentManageController.php
+++ b/src/applications/legalpad/controller/LegalpadDocumentManageController.php
@@ -1,211 +1,211 @@
<?php
final class LegalpadDocumentManageController extends LegalpadController {
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$id = $request->getURIData('id');
// NOTE: We require CAN_EDIT to view this page.
$document = id(new LegalpadDocumentQuery())
->setViewer($viewer)
->withIDs(array($id))
->needDocumentBodies(true)
->needContributors(true)
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
if (!$document) {
return new Aphront404Response();
}
$subscribers = PhabricatorSubscribersQuery::loadSubscribersForPHID(
$document->getPHID());
$document_body = $document->getDocumentBody();
$engine = id(new PhabricatorMarkupEngine())
->setViewer($viewer);
$engine->addObject(
$document_body,
LegalpadDocumentBody::MARKUP_FIELD_TEXT);
$timeline = $this->buildTransactionTimeline(
$document,
new LegalpadTransactionQuery(),
$engine);
$title = $document_body->getTitle();
$header = id(new PHUIHeaderView())
->setHeader($title)
->setUser($viewer)
->setPolicyObject($document)
->setHeaderIcon('fa-gavel');
$curtain = $this->buildCurtainView($document);
$properties = $this->buildPropertyView($document, $engine);
$document_view = $this->buildDocumentView($document, $engine);
$comment_form_id = celerity_generate_unique_node_id();
$add_comment = $this->buildAddCommentView($document, $comment_form_id);
$crumbs = $this->buildApplicationCrumbs($this->buildSideNav());
$crumbs->addTextCrumb(
$document->getMonogram(),
'/'.$document->getMonogram());
$crumbs->addTextCrumb(pht('Manage'));
$crumbs->setBorder(true);
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setCurtain($curtain)
->setMainColumn(array(
$properties,
$document_view,
$timeline,
$add_comment,
));
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->setPageObjectPHIDs(array($document->getPHID()))
->appendChild($view);
}
private function buildDocumentView(
LegalpadDocument $document,
PhabricatorMarkupEngine $engine) {
$viewer = $this->getViewer();
$view = id(new PHUIPropertyListView())
->setUser($viewer);
$document_body = $document->getDocumentBody();
$document_text = $engine->getOutput(
$document_body, LegalpadDocumentBody::MARKUP_FIELD_TEXT);
$preamble_box = null;
if (strlen($document->getPreamble())) {
$preamble_text = new PHUIRemarkupView($viewer, $document->getPreamble());
$view->addTextContent($preamble_text);
$view->addSectionHeader('');
$view->addTextContent($document_text);
} else {
$view->addTextContent($document_text);
}
return id(new PHUIObjectBoxView())
->setHeaderText(pht('DOCUMENT'))
->addPropertyList($view)
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY);
}
private function buildCurtainView(LegalpadDocument $document) {
$viewer = $this->getViewer();
$curtain = $this->newCurtainView($document);
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$document,
PhabricatorPolicyCapability::CAN_EDIT);
$doc_id = $document->getID();
$curtain->addAction(
id(new PhabricatorActionView())
->setIcon('fa-pencil-square')
->setName(pht('View/Sign Document'))
->setHref('/'.$document->getMonogram()));
$curtain->addAction(
id(new PhabricatorActionView())
->setIcon('fa-pencil')
->setName(pht('Edit Document'))
->setHref($this->getApplicationURI('/edit/'.$doc_id.'/'))
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit));
$curtain->addAction(
id(new PhabricatorActionView())
->setIcon('fa-terminal')
->setName(pht('View Signatures'))
->setHref($this->getApplicationURI('/signatures/'.$doc_id.'/')));
return $curtain;
}
private function buildPropertyView(
LegalpadDocument $document,
PhabricatorMarkupEngine $engine) {
$viewer = $this->getViewer();
$properties = id(new PHUIPropertyListView())
->setUser($viewer);
$properties->addProperty(
pht('Signature Type'),
$document->getSignatureTypeName());
$properties->addProperty(
pht('Last Updated'),
phabricator_datetime($document->getDateModified(), $viewer));
$properties->addProperty(
pht('Updated By'),
$viewer->renderHandle($document->getDocumentBody()->getCreatorPHID()));
$properties->addProperty(
pht('Versions'),
$document->getVersions());
if ($document->getContributors()) {
$properties->addProperty(
pht('Contributors'),
$viewer
->renderHandleList($document->getContributors())
->setAsInline(true));
}
return id(new PHUIObjectBoxView())
- ->setHeaderText(pht('PROPERTIES'))
+ ->setHeaderText(pht('Properties'))
->addPropertyList($properties)
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY);
}
private function buildAddCommentView(
LegalpadDocument $document,
$comment_form_id) {
$viewer = $this->getViewer();
$draft = PhabricatorDraft::newFromUserAndKey($viewer, $document->getPHID());
$is_serious = PhabricatorEnv::getEnvConfig('phabricator.serious-business');
$title = $is_serious
? pht('Add Comment')
: pht('Debate Legislation');
$form = id(new PhabricatorApplicationTransactionCommentView())
->setUser($viewer)
->setObjectPHID($document->getPHID())
->setFormID($comment_form_id)
->setHeaderText($title)
->setDraft($draft)
->setSubmitButtonName(pht('Add Comment'))
->setAction($this->getApplicationURI('/comment/'.$document->getID().'/'))
->setRequestURI($this->getRequest()->getRequestURI());
return $form;
}
}
diff --git a/src/applications/macro/controller/PhabricatorMacroViewController.php b/src/applications/macro/controller/PhabricatorMacroViewController.php
index 8c12e5528a..386029c7ce 100644
--- a/src/applications/macro/controller/PhabricatorMacroViewController.php
+++ b/src/applications/macro/controller/PhabricatorMacroViewController.php
@@ -1,202 +1,202 @@
<?php
final class PhabricatorMacroViewController
extends PhabricatorMacroController {
public function shouldAllowPublic() {
return true;
}
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$id = $request->getURIData('id');
$macro = id(new PhabricatorMacroQuery())
->setViewer($viewer)
->withIDs(array($id))
->needFiles(true)
->executeOne();
if (!$macro) {
return new Aphront404Response();
}
$title_short = pht('Macro "%s"', $macro->getName());
$title_long = pht('Image Macro "%s"', $macro->getName());
$curtain = $this->buildCurtain($macro);
$subheader = $this->buildSubheaderView($macro);
$file = $this->buildFileView($macro);
$details = $this->buildPropertySectionView($macro);
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb($macro->getName());
$crumbs->setBorder(true);
$timeline = $this->buildTransactionTimeline(
$macro,
new PhabricatorMacroTransactionQuery());
$header = id(new PHUIHeaderView())
->setUser($viewer)
->setPolicyObject($macro)
->setHeader($macro->getName())
->setHeaderIcon('fa-file-image-o');
if (!$macro->getIsDisabled()) {
$header->setStatus('fa-check', 'bluegrey', pht('Active'));
} else {
$header->setStatus('fa-ban', 'red', pht('Archived'));
}
$is_serious = PhabricatorEnv::getEnvConfig('phabricator.serious-business');
$comment_header = $is_serious
? pht('Add Comment')
: pht('Grovel in Awe');
$draft = PhabricatorDraft::newFromUserAndKey($viewer, $macro->getPHID());
$add_comment_form = id(new PhabricatorApplicationTransactionCommentView())
->setUser($viewer)
->setObjectPHID($macro->getPHID())
->setDraft($draft)
->setHeaderText($comment_header)
->setAction($this->getApplicationURI('/comment/'.$macro->getID().'/'))
->setSubmitButtonName(pht('Add Comment'));
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setSubheader($subheader)
->setCurtain($curtain)
->setMainColumn(array(
$timeline,
$add_comment_form,
))
- ->addPropertySection(pht('MACRO'), $file)
- ->addPropertySection(pht('DETAILS'), $details);
+ ->addPropertySection(pht('Macro'), $file)
+ ->addPropertySection(pht('Details'), $details);
return $this->newPage()
->setTitle($title_short)
->setCrumbs($crumbs)
->setPageObjectPHIDs(array($macro->getPHID()))
->appendChild($view);
}
private function buildCurtain(
PhabricatorFileImageMacro $macro) {
$can_manage = $this->hasApplicationCapability(
PhabricatorMacroManageCapability::CAPABILITY);
$curtain = $this->newCurtainView($macro);
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('Edit Macro'))
->setHref($this->getApplicationURI('/edit/'.$macro->getID().'/'))
->setDisabled(!$can_manage)
->setWorkflow(!$can_manage)
->setIcon('fa-pencil'));
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('Edit Audio'))
->setHref($this->getApplicationURI('/audio/'.$macro->getID().'/'))
->setDisabled(!$can_manage)
->setWorkflow(!$can_manage)
->setIcon('fa-music'));
if ($macro->getIsDisabled()) {
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('Activate Macro'))
->setHref($this->getApplicationURI('/disable/'.$macro->getID().'/'))
->setWorkflow(true)
->setDisabled(!$can_manage)
->setIcon('fa-check'));
} else {
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('Archive Macro'))
->setHref($this->getApplicationURI('/disable/'.$macro->getID().'/'))
->setWorkflow(true)
->setDisabled(!$can_manage)
->setIcon('fa-ban'));
}
return $curtain;
}
private function buildSubheaderView(
PhabricatorFileImageMacro $macro) {
$viewer = $this->getViewer();
$author_phid = $macro->getAuthorPHID();
$author = $viewer->renderHandle($author_phid)->render();
$date = phabricator_datetime($macro->getDateCreated(), $viewer);
$author = phutil_tag('strong', array(), $author);
$handles = $viewer->loadHandles(array($author_phid));
$image_uri = $handles[$author_phid]->getImageURI();
$image_href = $handles[$author_phid]->getURI();
$content = pht('Masterfully imagined by %s on %s.', $author, $date);
return id(new PHUIHeadThingView())
->setImage($image_uri)
->setImageHref($image_href)
->setContent($content);
}
private function buildPropertySectionView(
PhabricatorFileImageMacro $macro) {
$viewer = $this->getViewer();
$view = id(new PHUIPropertyListView())
->setUser($viewer);
switch ($macro->getAudioBehavior()) {
case PhabricatorFileImageMacro::AUDIO_BEHAVIOR_ONCE:
$view->addProperty(pht('Audio Behavior'), pht('Play Once'));
break;
case PhabricatorFileImageMacro::AUDIO_BEHAVIOR_LOOP:
$view->addProperty(pht('Audio Behavior'), pht('Loop'));
break;
}
$audio_phid = $macro->getAudioPHID();
if ($audio_phid) {
$view->addProperty(
pht('Audio'),
$viewer->renderHandle($audio_phid));
}
if ($view->hasAnyProperties()) {
return $view;
}
return null;
}
private function buildFileView(
PhabricatorFileImageMacro $macro) {
$viewer = $this->getViewer();
$view = id(new PHUIPropertyListView())
->setUser($viewer);
$file = $macro->getFile();
if ($file) {
$view->addImageContent(
phutil_tag(
'img',
array(
'src' => $file->getViewURI(),
'class' => 'phabricator-image-macro-hero',
)));
return $view;
}
return null;
}
}
diff --git a/src/applications/maniphest/controller/ManiphestTaskDetailController.php b/src/applications/maniphest/controller/ManiphestTaskDetailController.php
index 285f919ce8..1007672e23 100644
--- a/src/applications/maniphest/controller/ManiphestTaskDetailController.php
+++ b/src/applications/maniphest/controller/ManiphestTaskDetailController.php
@@ -1,364 +1,364 @@
<?php
final class ManiphestTaskDetailController extends ManiphestController {
public function shouldAllowPublic() {
return true;
}
public function handleRequest(AphrontRequest $request) {
$viewer = $this->getViewer();
$id = $request->getURIData('id');
$task = id(new ManiphestTaskQuery())
->setViewer($viewer)
->withIDs(array($id))
->needSubscriberPHIDs(true)
->executeOne();
if (!$task) {
return new Aphront404Response();
}
$field_list = PhabricatorCustomField::getObjectFields(
$task,
PhabricatorCustomField::ROLE_VIEW);
$field_list
->setViewer($viewer)
->readFieldsFromStorage($task);
$edit_engine = id(new ManiphestEditEngine())
->setViewer($viewer)
->setTargetObject($task);
$e_commit = ManiphestTaskHasCommitEdgeType::EDGECONST;
$e_dep_on = ManiphestTaskDependsOnTaskEdgeType::EDGECONST;
$e_dep_by = ManiphestTaskDependedOnByTaskEdgeType::EDGECONST;
$e_rev = ManiphestTaskHasRevisionEdgeType::EDGECONST;
$e_mock = ManiphestTaskHasMockEdgeType::EDGECONST;
$phid = $task->getPHID();
$query = id(new PhabricatorEdgeQuery())
->withSourcePHIDs(array($phid))
->withEdgeTypes(
array(
$e_commit,
$e_dep_on,
$e_dep_by,
$e_rev,
$e_mock,
));
$edges = idx($query->execute(), $phid);
$phids = array_fill_keys($query->getDestinationPHIDs(), true);
if ($task->getOwnerPHID()) {
$phids[$task->getOwnerPHID()] = true;
}
$phids[$task->getAuthorPHID()] = true;
$phids = array_keys($phids);
$handles = $viewer->loadHandles($phids);
$timeline = $this->buildTransactionTimeline(
$task,
new ManiphestTransactionQuery());
$monogram = $task->getMonogram();
$crumbs = $this->buildApplicationCrumbs()
->addTextCrumb($monogram)
->setBorder(true);
$header = $this->buildHeaderView($task);
$details = $this->buildPropertyView($task, $field_list, $edges, $handles);
$description = $this->buildDescriptionView($task);
$curtain = $this->buildCurtain($task, $edit_engine);
$title = pht('%s %s', $monogram, $task->getTitle());
$comment_view = $edit_engine
->buildEditEngineCommentView($task);
$timeline->setQuoteRef($monogram);
$comment_view->setTransactionTimeline($timeline);
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setCurtain($curtain)
->setMainColumn(array(
$timeline,
$comment_view,
))
- ->addPropertySection(pht('DESCRIPTION'), $description)
- ->addPropertySection(pht('DETAILS'), $details);
+ ->addPropertySection(pht('Description'), $description)
+ ->addPropertySection(pht('Details'), $details);
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->setPageObjectPHIDs(
array(
$task->getPHID(),
))
->appendChild(
array(
$view,
));
}
private function buildHeaderView(ManiphestTask $task) {
$view = id(new PHUIHeaderView())
->setHeader($task->getTitle())
->setUser($this->getRequest()->getUser())
->setPolicyObject($task);
$priority_name = ManiphestTaskPriority::getTaskPriorityName(
$task->getPriority());
$priority_color = ManiphestTaskPriority::getTaskPriorityColor(
$task->getPriority());
$status = $task->getStatus();
$status_name = ManiphestTaskStatus::renderFullDescription(
$status, $priority_name, $priority_color);
$view->addProperty(PHUIHeaderView::PROPERTY_STATUS, $status_name);
$view->setHeaderIcon(ManiphestTaskStatus::getStatusIcon(
$task->getStatus()).' '.$priority_color);
if (ManiphestTaskPoints::getIsEnabled()) {
$points = $task->getPoints();
if ($points !== null) {
$points_name = pht('%s %s',
$task->getPoints(),
ManiphestTaskPoints::getPointsLabel());
$tag = id(new PHUITagView())
->setName($points_name)
->setShade('blue')
->setType(PHUITagView::TYPE_SHADE);
$view->addTag($tag);
}
}
return $view;
}
private function buildCurtain(
ManiphestTask $task,
PhabricatorEditEngine $edit_engine) {
$viewer = $this->getViewer();
$id = $task->getID();
$phid = $task->getPHID();
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$task,
PhabricatorPolicyCapability::CAN_EDIT);
$curtain = $this->newCurtainView($task);
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('Edit Task'))
->setIcon('fa-pencil')
->setHref($this->getApplicationURI("/task/edit/{$id}/"))
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit));
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('Merge Duplicates In'))
->setHref("/search/attach/{$phid}/TASK/merge/")
->setWorkflow(true)
->setIcon('fa-compress')
->setDisabled(!$can_edit)
->setWorkflow(true));
$edit_config = $edit_engine->loadDefaultEditConfiguration();
$can_create = (bool)$edit_config;
$can_reassign = $edit_engine->hasEditAccessToTransaction(
ManiphestTransaction::TYPE_OWNER);
if ($can_create) {
$form_key = $edit_config->getIdentifier();
$edit_uri = id(new PhutilURI("/task/edit/form/{$form_key}/"))
->setQueryParam('parent', $id)
->setQueryParam('template', $id)
->setQueryParam('status', ManiphestTaskStatus::getDefaultStatus());
$edit_uri = $this->getApplicationURI($edit_uri);
} else {
// TODO: This will usually give us a somewhat-reasonable error page, but
// could be a bit cleaner.
$edit_uri = "/task/edit/{$id}/";
$edit_uri = $this->getApplicationURI($edit_uri);
}
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('Create Subtask'))
->setHref($edit_uri)
->setIcon('fa-level-down')
->setDisabled(!$can_create)
->setWorkflow(!$can_create));
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('Edit Blocking Tasks'))
->setHref("/search/attach/{$phid}/TASK/blocks/")
->setWorkflow(true)
->setIcon('fa-link')
->setDisabled(!$can_edit)
->setWorkflow(true));
$owner_phid = $task->getOwnerPHID();
$author_phid = $task->getAuthorPHID();
$handles = $viewer->loadHandles(array($owner_phid, $author_phid));
if ($owner_phid) {
$image_uri = $handles[$owner_phid]->getImageURI();
$image_href = $handles[$owner_phid]->getURI();
$owner = $viewer->renderHandle($owner_phid)->render();
$content = phutil_tag('strong', array(), $owner);
$assigned_to = id(new PHUIHeadThingView())
->setImage($image_uri)
->setImageHref($image_href)
->setContent($content);
} else {
$assigned_to = phutil_tag('em', array(), pht('None'));
}
$curtain->newPanel()
->setHeaderText(pht('Assigned To'))
->appendChild($assigned_to);
$author_uri = $handles[$author_phid]->getImageURI();
$author_href = $handles[$author_phid]->getURI();
$author = $viewer->renderHandle($author_phid)->render();
$content = phutil_tag('strong', array(), $author);
$date = phabricator_date($task->getDateCreated(), $viewer);
$content = pht('%s, %s', $content, $date);
$authored_by = id(new PHUIHeadThingView())
->setImage($author_uri)
->setImageHref($author_href)
->setContent($content);
$curtain->newPanel()
->setHeaderText(pht('Authored By'))
->appendChild($authored_by);
return $curtain;
}
private function buildPropertyView(
ManiphestTask $task,
PhabricatorCustomFieldList $field_list,
array $edges,
$handles) {
$viewer = $this->getRequest()->getUser();
$view = id(new PHUIPropertyListView())
->setUser($viewer);
$source = $task->getOriginalEmailSource();
if ($source) {
$subject = '[T'.$task->getID().'] '.$task->getTitle();
$view->addProperty(
pht('From Email'),
phutil_tag(
'a',
array(
'href' => 'mailto:'.$source.'?subject='.$subject,
),
$source));
}
$edge_types = array(
ManiphestTaskDependedOnByTaskEdgeType::EDGECONST
=> pht('Blocks'),
ManiphestTaskDependsOnTaskEdgeType::EDGECONST
=> pht('Blocked By'),
ManiphestTaskHasRevisionEdgeType::EDGECONST
=> pht('Differential Revisions'),
ManiphestTaskHasMockEdgeType::EDGECONST
=> pht('Pholio Mocks'),
);
$revisions_commits = array();
$commit_phids = array_keys(
$edges[ManiphestTaskHasCommitEdgeType::EDGECONST]);
if ($commit_phids) {
$commit_drev = DiffusionCommitHasRevisionEdgeType::EDGECONST;
$drev_edges = id(new PhabricatorEdgeQuery())
->withSourcePHIDs($commit_phids)
->withEdgeTypes(array($commit_drev))
->execute();
foreach ($commit_phids as $phid) {
$revisions_commits[$phid] = $handles->renderHandle($phid)
->setShowHovercard(true);
$revision_phid = key($drev_edges[$phid][$commit_drev]);
$revision_handle = $handles->getHandleIfExists($revision_phid);
if ($revision_handle) {
$task_drev = ManiphestTaskHasRevisionEdgeType::EDGECONST;
unset($edges[$task_drev][$revision_phid]);
$revisions_commits[$phid] = hsprintf(
'%s / %s',
$revision_handle->renderHovercardLink($revision_handle->getName()),
$revisions_commits[$phid]);
}
}
}
foreach ($edge_types as $edge_type => $edge_name) {
if ($edges[$edge_type]) {
$edge_handles = $viewer->loadHandles(array_keys($edges[$edge_type]));
$view->addProperty(
$edge_name,
$edge_handles->renderList());
}
}
if ($revisions_commits) {
$view->addProperty(
pht('Commits'),
phutil_implode_html(phutil_tag('br'), $revisions_commits));
}
$field_list->appendFieldsToPropertyList(
$task,
$viewer,
$view);
if ($view->hasAnyProperties()) {
return $view;
}
return null;
}
private function buildDescriptionView(ManiphestTask $task) {
$viewer = $this->getViewer();
$section = null;
$description = $task->getDescription();
if (strlen($description)) {
$section = new PHUIPropertyListView();
$section->addTextContent(
phutil_tag(
'div',
array(
'class' => 'phabricator-remarkup',
),
id(new PHUIRemarkupView($viewer, $description))
->setContextObject($task)));
}
return $section;
}
}
diff --git a/src/applications/meta/controller/PhabricatorApplicationDetailViewController.php b/src/applications/meta/controller/PhabricatorApplicationDetailViewController.php
index f86f9eb4d1..54068e1e71 100644
--- a/src/applications/meta/controller/PhabricatorApplicationDetailViewController.php
+++ b/src/applications/meta/controller/PhabricatorApplicationDetailViewController.php
@@ -1,210 +1,210 @@
<?php
final class PhabricatorApplicationDetailViewController
extends PhabricatorApplicationsController {
public function shouldAllowPublic() {
return true;
}
public function handleRequest(AphrontRequest $request) {
$viewer = $this->getViewer();
$application = $request->getURIData('application');
$selected = id(new PhabricatorApplicationQuery())
->setViewer($viewer)
->withClasses(array($application))
->executeOne();
if (!$selected) {
return new Aphront404Response();
}
$title = $selected->getName();
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb($selected->getName());
$crumbs->setBorder(true);
$header = id(new PHUIHeaderView())
->setHeader($title)
->setUser($viewer)
->setPolicyObject($selected)
->setHeaderIcon($selected->getIcon());
if ($selected->isInstalled()) {
$header->setStatus('fa-check', 'bluegrey', pht('Installed'));
} else {
$header->setStatus('fa-ban', 'dark', pht('Uninstalled'));
}
$curtain = $this->buildCurtain($selected);
$details = $this->buildPropertySectionView($selected);
$policies = $this->buildPolicyView($selected);
$configs =
PhabricatorApplicationConfigurationPanel::loadAllPanelsForApplication(
$selected);
$panels = array();
foreach ($configs as $config) {
$config->setViewer($viewer);
$config->setApplication($selected);
$panel = $config->buildConfigurationPagePanel();
$panel->setBackground(PHUIObjectBoxView::BLUE_PROPERTY);
$panels[] = $panel;
}
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setCurtain($curtain)
->setMainColumn(array(
$policies,
$panels,
))
- ->addPropertySection(pht('DETAILS'), $details);
+ ->addPropertySection(pht('Details'), $details);
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->appendChild(
array(
$view,
));
}
private function buildPropertySectionView(
PhabricatorApplication $application) {
$viewer = $this->getViewer();
$properties = id(new PHUIPropertyListView());
$properties->addProperty(
pht('Description'),
$application->getShortDescription());
if ($application->getFlavorText()) {
$properties->addProperty(
null,
phutil_tag('em', array(), $application->getFlavorText()));
}
if ($application->isPrototype()) {
$proto_href = PhabricatorEnv::getDoclink(
'User Guide: Prototype Applications');
$learn_more = phutil_tag(
'a',
array(
'href' => $proto_href,
'target' => '_blank',
),
pht('Learn More'));
$properties->addProperty(
pht('Prototype'),
pht(
'This application is a prototype. %s',
$learn_more));
}
$overview = $application->getOverview();
if (strlen($overview)) {
$overview = new PHUIRemarkupView($viewer, $overview);
$properties->addSectionHeader(
pht('Overview'), PHUIPropertyListView::ICON_SUMMARY);
$properties->addTextContent($overview);
}
return $properties;
}
private function buildPolicyView(
PhabricatorApplication $application) {
$viewer = $this->getViewer();
$properties = id(new PHUIPropertyListView());
$header = id(new PHUIHeaderView())
->setHeader(pht('POLICIES'))
->setHeaderIcon('fa-lock');
$descriptions = PhabricatorPolicyQuery::renderPolicyDescriptions(
$viewer,
$application);
foreach ($application->getCapabilities() as $capability) {
$properties->addProperty(
$application->getCapabilityLabel($capability),
idx($descriptions, $capability));
}
return id(new PHUIObjectBoxView())
->setHeader($header)
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->appendChild($properties);
}
private function buildCurtain(PhabricatorApplication $application) {
$viewer = $this->getViewer();
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$application,
PhabricatorPolicyCapability::CAN_EDIT);
$key = get_class($application);
$edit_uri = $this->getApplicationURI("edit/{$key}/");
$install_uri = $this->getApplicationURI("{$key}/install/");
$uninstall_uri = $this->getApplicationURI("{$key}/uninstall/");
$curtain = $this->newCurtainView($application);
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('Edit Policies'))
->setIcon('fa-pencil')
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit)
->setHref($edit_uri));
if ($application->canUninstall()) {
if ($application->isInstalled()) {
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('Uninstall'))
->setIcon('fa-times')
->setDisabled(!$can_edit)
->setWorkflow(true)
->setHref($uninstall_uri));
} else {
$action = id(new PhabricatorActionView())
->setName(pht('Install'))
->setIcon('fa-plus')
->setDisabled(!$can_edit)
->setWorkflow(true)
->setHref($install_uri);
$prototypes_enabled = PhabricatorEnv::getEnvConfig(
'phabricator.show-prototypes');
if ($application->isPrototype() && !$prototypes_enabled) {
$action->setDisabled(true);
}
$curtain->addAction($action);
}
} else {
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('Uninstall'))
->setIcon('fa-times')
->setWorkflow(true)
->setDisabled(true)
->setHref($uninstall_uri));
}
return $curtain;
}
}
diff --git a/src/applications/nuance/controller/NuanceItemManageController.php b/src/applications/nuance/controller/NuanceItemManageController.php
index c86d2cd985..2b6b4c89b8 100644
--- a/src/applications/nuance/controller/NuanceItemManageController.php
+++ b/src/applications/nuance/controller/NuanceItemManageController.php
@@ -1,109 +1,109 @@
<?php
final class NuanceItemManageController extends NuanceController {
public function handleRequest(AphrontRequest $request) {
$viewer = $this->getViewer();
$id = $request->getURIData('id');
$item = id(new NuanceItemQuery())
->setViewer($viewer)
->withIDs(array($id))
->executeOne();
if (!$item) {
return new Aphront404Response();
}
$title = pht('Item %d', $item->getID());
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb(
pht('Items'),
$this->getApplicationURI('item/'));
$crumbs->addTextCrumb(
$title,
$item->getURI());
$crumbs->addTextCrumb(pht('Manage'));
$crumbs->setBorder(true);
$properties = $this->buildPropertyView($item);
$curtain = $this->buildCurtain($item);
$header = id(new PHUIHeaderView())
->setHeader($title);
$timeline = $this->buildTransactionTimeline(
$item,
new NuanceItemTransactionQuery());
$timeline->setShouldTerminate(true);
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setCurtain($curtain)
- ->addPropertySection(pht('DETAILS'), $properties)
+ ->addPropertySection(pht('Details'), $properties)
->setMainColumn($timeline);
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->appendChild($view);
}
private function buildPropertyView(NuanceItem $item) {
$viewer = $this->getViewer();
$properties = id(new PHUIPropertyListView())
->setUser($viewer);
$properties->addProperty(
pht('Date Created'),
phabricator_datetime($item->getDateCreated(), $viewer));
$requestor_phid = $item->getRequestorPHID();
if ($requestor_phid) {
$requestor_view = $viewer->renderHandle($requestor_phid);
} else {
$requestor_view = phutil_tag('em', array(), pht('None'));
}
$properties->addProperty(pht('Requestor'), $requestor_view);
$properties->addProperty(
pht('Source'),
$viewer->renderHandle($item->getSourcePHID()));
$queue_phid = $item->getQueuePHID();
if ($queue_phid) {
$queue_view = $viewer->renderHandle($queue_phid);
} else {
$queue_view = phutil_tag('em', array(), pht('None'));
}
$properties->addProperty(pht('Queue'), $queue_view);
$source = $item->getSource();
$definition = $source->getDefinition();
$definition->renderItemEditProperties(
$viewer,
$item,
$properties);
return $properties;
}
private function buildCurtain(NuanceItem $item) {
$viewer = $this->getViewer();
$id = $item->getID();
$curtain = $this->newCurtainView($item);
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('View Item'))
->setIcon('fa-eye')
->setHref($item->getURI()));
return $curtain;
}
}
diff --git a/src/applications/nuance/controller/NuanceSourceViewController.php b/src/applications/nuance/controller/NuanceSourceViewController.php
index af602bfd7e..93facde3c1 100644
--- a/src/applications/nuance/controller/NuanceSourceViewController.php
+++ b/src/applications/nuance/controller/NuanceSourceViewController.php
@@ -1,116 +1,116 @@
<?php
final class NuanceSourceViewController
extends NuanceSourceController {
public function handleRequest(AphrontRequest $request) {
$viewer = $this->getViewer();
$source = id(new NuanceSourceQuery())
->setViewer($viewer)
->withIDs(array($request->getURIData('id')))
->executeOne();
if (!$source) {
return new Aphront404Response();
}
$source_id = $source->getID();
$header = $this->buildHeaderView($source);
$curtain = $this->buildCurtain($source);
$properties = $this->buildPropertyView($source);
$title = $source->getName();
$routing_list = id(new PHUIPropertyListView())
->addProperty(
pht('Default Queue'),
$viewer->renderHandle($source->getDefaultQueuePHID()));
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb(pht('Sources'), $this->getApplicationURI('source/'));
$crumbs->addTextCrumb($title);
$crumbs->setBorder(true);
$timeline = $this->buildTransactionTimeline(
$source,
new NuanceSourceTransactionQuery());
$timeline->setShouldTerminate(true);
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setCurtain($curtain)
- ->addPropertySection(pht('DETAILS'), $properties)
- ->addPropertySection(pht('ROUTING'), $routing_list)
+ ->addPropertySection(pht('Details'), $properties)
+ ->addPropertySection(pht('Routing'), $routing_list)
->setMainColumn($timeline);
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->appendChild($view);
}
private function buildHeaderView(NuanceSource $source) {
$viewer = $this->getViewer();
$header = id(new PHUIHeaderView())
->setUser($viewer)
->setHeader($source->getName())
->setPolicyObject($source);
return $header;
}
private function buildCurtain(NuanceSource $source) {
$viewer = $this->getViewer();
$id = $source->getID();
$actions = id(new PhabricatorActionListView())
->setUser($viewer);
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$source,
PhabricatorPolicyCapability::CAN_EDIT);
$curtain = $this->newCurtainView($source);
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('Edit Source'))
->setIcon('fa-pencil')
->setHref($this->getApplicationURI("source/edit/{$id}/"))
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit));
$request = $this->getRequest();
$definition = $source->getDefinition();
$definition
->setViewer($viewer)
->setSource($source);
$source_actions = $definition->getSourceViewActions($request);
foreach ($source_actions as $source_action) {
$curtain->addAction($source_action);
}
return $curtain;
}
private function buildPropertyView(
NuanceSource $source) {
$viewer = $this->getViewer();
$properties = id(new PHUIPropertyListView())
->setViewer($viewer);
$definition = $source->getDefinition();
$properties->addProperty(
pht('Source Type'),
$definition->getName());
return $properties;
}
}
diff --git a/src/applications/passphrase/controller/PassphraseCredentialViewController.php b/src/applications/passphrase/controller/PassphraseCredentialViewController.php
index db31964773..1bbea88ec6 100644
--- a/src/applications/passphrase/controller/PassphraseCredentialViewController.php
+++ b/src/applications/passphrase/controller/PassphraseCredentialViewController.php
@@ -1,227 +1,227 @@
<?php
final class PassphraseCredentialViewController extends PassphraseController {
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$id = $request->getURIData('id');
$credential = id(new PassphraseCredentialQuery())
->setViewer($viewer)
->withIDs(array($id))
->executeOne();
if (!$credential) {
return new Aphront404Response();
}
$type = $credential->getImplementation();
$timeline = $this->buildTransactionTimeline(
$credential,
new PassphraseCredentialTransactionQuery());
$timeline->setShouldTerminate(true);
$title = pht('%s %s', $credential->getMonogram(), $credential->getName());
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb($credential->getMonogram());
$crumbs->setBorder(true);
$header = $this->buildHeaderView($credential);
$curtain = $this->buildCurtain($credential, $type);
$subheader = $this->buildSubheaderView($credential);
$content = $this->buildPropertySectionView($credential, $type);
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setSubheader($subheader)
->setCurtain($curtain)
->setMainColumn($timeline)
- ->addPropertySection(pht('PROPERTIES'), $content);
+ ->addPropertySection(pht('Properties'), $content);
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->appendChild($view);
}
private function buildHeaderView(PassphraseCredential $credential) {
$viewer = $this->getRequest()->getUser();
$header = id(new PHUIHeaderView())
->setUser($viewer)
->setHeader($credential->getName())
->setPolicyObject($credential)
->setHeaderIcon('fa-user-secret');
if ($credential->getIsDestroyed()) {
$header->setStatus('fa-ban', 'red', pht('Destroyed'));
}
return $header;
}
private function buildSubheaderView(
PassphraseCredential $credential) {
$viewer = $this->getViewer();
$author = $viewer->renderHandle($credential->getAuthorPHID())->render();
$date = phabricator_datetime($credential->getDateCreated(), $viewer);
$author = phutil_tag('strong', array(), $author);
$person = id(new PhabricatorPeopleQuery())
->setViewer($viewer)
->withPHIDs(array($credential->getAuthorPHID()))
->needProfileImage(true)
->executeOne();
if (!$person) {
return null;
}
$image_uri = $person->getProfileImageURI();
$image_href = '/p/'.$credential->getUsername();
$content = pht('Created by %s on %s.', $author, $date);
return id(new PHUIHeadThingView())
->setImage($image_uri)
->setImageHref($image_href)
->setContent($content);
}
private function buildCurtain(
PassphraseCredential $credential,
PassphraseCredentialType $type) {
$viewer = $this->getViewer();
$id = $credential->getID();
$is_locked = $credential->getIsLocked();
if ($is_locked) {
$credential_lock_text = pht('Locked Permanently');
$credential_lock_icon = 'fa-lock';
} else {
$credential_lock_text = pht('Lock Permanently');
$credential_lock_icon = 'fa-unlock';
}
$allow_conduit = $credential->getAllowConduit();
if ($allow_conduit) {
$credential_conduit_text = pht('Prevent Conduit Access');
$credential_conduit_icon = 'fa-ban';
} else {
$credential_conduit_text = pht('Allow Conduit Access');
$credential_conduit_icon = 'fa-wrench';
}
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$credential,
PhabricatorPolicyCapability::CAN_EDIT);
$curtain = $this->newCurtainView($credential);
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('Edit Credential'))
->setIcon('fa-pencil')
->setHref($this->getApplicationURI("edit/{$id}/"))
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit));
if (!$credential->getIsDestroyed()) {
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('Destroy Credential'))
->setIcon('fa-times')
->setHref($this->getApplicationURI("destroy/{$id}/"))
->setDisabled(!$can_edit)
->setWorkflow(true));
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('Show Secret'))
->setIcon('fa-eye')
->setHref($this->getApplicationURI("reveal/{$id}/"))
->setDisabled(!$can_edit || $is_locked)
->setWorkflow(true));
if ($type->hasPublicKey()) {
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('Show Public Key'))
->setIcon('fa-download')
->setHref($this->getApplicationURI("public/{$id}/"))
->setDisabled(!$can_edit)
->setWorkflow(true));
}
$curtain->addAction(
id(new PhabricatorActionView())
->setName($credential_conduit_text)
->setIcon($credential_conduit_icon)
->setHref($this->getApplicationURI("conduit/{$id}/"))
->setDisabled(!$can_edit)
->setWorkflow(true));
$curtain->addAction(
id(new PhabricatorActionView())
->setName($credential_lock_text)
->setIcon($credential_lock_icon)
->setHref($this->getApplicationURI("lock/{$id}/"))
->setDisabled(!$can_edit || $is_locked)
->setWorkflow(true));
}
return $curtain;
}
private function buildPropertySectionView(
PassphraseCredential $credential,
PassphraseCredentialType $type) {
$viewer = $this->getRequest()->getUser();
$properties = id(new PHUIPropertyListView())
->setUser($viewer);
$properties->addProperty(
pht('Credential Type'),
$type->getCredentialTypeName());
$descriptions = PhabricatorPolicyQuery::renderPolicyDescriptions(
$viewer,
$credential);
$properties->addProperty(
pht('Editable By'),
$descriptions[PhabricatorPolicyCapability::CAN_EDIT]);
if ($type->shouldRequireUsername()) {
$properties->addProperty(
pht('Username'),
$credential->getUsername());
}
$used_by_phids = PhabricatorEdgeQuery::loadDestinationPHIDs(
$credential->getPHID(),
PhabricatorCredentialsUsedByObjectEdgeType::EDGECONST);
if ($used_by_phids) {
$properties->addProperty(
pht('Used By'),
$viewer->renderHandleList($used_by_phids));
}
$description = $credential->getDescription();
if (strlen($description)) {
$properties->addSectionHeader(
pht('Description'),
PHUIPropertyListView::ICON_SUMMARY);
$properties->addTextContent(
new PHUIRemarkupView($viewer, $description));
}
return $properties;
}
}
diff --git a/src/applications/people/controller/PhabricatorPeopleProfileManageController.php b/src/applications/people/controller/PhabricatorPeopleProfileManageController.php
index 51cf79ecff..08b438eaa2 100644
--- a/src/applications/people/controller/PhabricatorPeopleProfileManageController.php
+++ b/src/applications/people/controller/PhabricatorPeopleProfileManageController.php
@@ -1,195 +1,195 @@
<?php
final class PhabricatorPeopleProfileManageController
extends PhabricatorPeopleProfileController {
public function shouldAllowPublic() {
return true;
}
public function handleRequest(AphrontRequest $request) {
$viewer = $this->getViewer();
$id = $request->getURIData('id');
$user = id(new PhabricatorPeopleQuery())
->setViewer($viewer)
->withIDs(array($id))
->needProfile(true)
->needProfileImage(true)
->needAvailability(true)
->executeOne();
if (!$user) {
return new Aphront404Response();
}
$this->setUser($user);
$profile = $user->loadUserProfile();
$picture = $user->getProfileImageURI();
$profile_icon = PhabricatorPeopleIconSet::getIconIcon($profile->getIcon());
$profile_icon = id(new PHUIIconView())
->setIcon($profile_icon);
$profile_title = $profile->getDisplayTitle();
$header = id(new PHUIHeaderView())
->setHeader($user->getFullName())
->setSubheader(array($profile_icon, $profile_title))
->setImage($picture)
->setProfileHeader(true);
$curtain = $this->buildCurtain($user);
$properties = $this->buildPropertyView($user);
$name = $user->getUsername();
$nav = $this->getProfileMenu();
$nav->selectFilter(PhabricatorPeopleProfilePanelEngine::PANEL_MANAGE);
$timeline = $this->buildTransactionTimeline(
$user,
new PhabricatorPeopleTransactionQuery());
$timeline->setShouldTerminate(true);
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb(pht('Manage'));
$crumbs->setBorder(true);
$manage = id(new PHUITwoColumnView())
->setHeader($header)
->setCurtain($curtain)
- ->addPropertySection(pht('DETAILS'), $properties)
+ ->addPropertySection(pht('Details'), $properties)
->setMainColumn(
array(
$timeline,
));
return $this->newPage()
->setTitle(
array(
pht('Manage User'),
$user->getUsername(),
))
->setNavigation($nav)
->setCrumbs($crumbs)
->appendChild(
array(
$manage,
));
}
private function buildPropertyView(PhabricatorUser $user) {
$viewer = $this->getRequest()->getUser();
$view = id(new PHUIPropertyListView())
->setUser($viewer)
->setObject($user);
$field_list = PhabricatorCustomField::getObjectFields(
$user,
PhabricatorCustomField::ROLE_VIEW);
$field_list->appendFieldsToPropertyList($user, $viewer, $view);
return $view;
}
private function buildCurtain(PhabricatorUser $user) {
$viewer = $this->getViewer();
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$user,
PhabricatorPolicyCapability::CAN_EDIT);
$curtain = $this->newCurtainView($user);
$curtain->addAction(
id(new PhabricatorActionView())
->setIcon('fa-pencil')
->setName(pht('Edit Profile'))
->setHref($this->getApplicationURI('editprofile/'.$user->getID().'/'))
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit));
$curtain->addAction(
id(new PhabricatorActionView())
->setIcon('fa-picture-o')
->setName(pht('Edit Profile Picture'))
->setHref($this->getApplicationURI('picture/'.$user->getID().'/'))
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit));
$curtain->addAction(
id(new PhabricatorActionView())
->setIcon('fa-wrench')
->setName(pht('Edit Settings'))
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit)
->setHref('/settings/'.$user->getID().'/'));
if ($user->getIsAdmin()) {
$empower_icon = 'fa-arrow-circle-o-down';
$empower_name = pht('Remove Administrator');
} else {
$empower_icon = 'fa-arrow-circle-o-up';
$empower_name = pht('Make Administrator');
}
$is_admin = $viewer->getIsAdmin();
$is_self = ($user->getPHID() === $viewer->getPHID());
$can_admin = ($is_admin && !$is_self);
$curtain->addAction(
id(new PhabricatorActionView())
->setIcon($empower_icon)
->setName($empower_name)
->setDisabled(!$can_admin)
->setWorkflow(true)
->setHref($this->getApplicationURI('empower/'.$user->getID().'/')));
$curtain->addAction(
id(new PhabricatorActionView())
->setIcon('fa-tag')
->setName(pht('Change Username'))
->setDisabled(!$is_admin)
->setWorkflow(true)
->setHref($this->getApplicationURI('rename/'.$user->getID().'/')));
if ($user->getIsDisabled()) {
$disable_icon = 'fa-check-circle-o';
$disable_name = pht('Enable User');
} else {
$disable_icon = 'fa-ban';
$disable_name = pht('Disable User');
}
$curtain->addAction(
id(new PhabricatorActionView())
->setIcon($disable_icon)
->setName($disable_name)
->setDisabled(!$can_admin)
->setWorkflow(true)
->setHref($this->getApplicationURI('disable/'.$user->getID().'/')));
$curtain->addAction(
id(new PhabricatorActionView())
->setIcon('fa-times')
->setName(pht('Delete User'))
->setDisabled(!$can_admin)
->setWorkflow(true)
->setHref($this->getApplicationURI('delete/'.$user->getID().'/')));
$can_welcome = ($is_admin && $user->canEstablishWebSessions());
$curtain->addAction(
id(new PhabricatorActionView())
->setIcon('fa-envelope')
->setName(pht('Send Welcome Email'))
->setWorkflow(true)
->setDisabled(!$can_welcome)
->setHref($this->getApplicationURI('welcome/'.$user->getID().'/')));
return $curtain;
}
}
diff --git a/src/applications/phlux/controller/PhluxViewController.php b/src/applications/phlux/controller/PhluxViewController.php
index b3019443be..6ad0fe22cd 100644
--- a/src/applications/phlux/controller/PhluxViewController.php
+++ b/src/applications/phlux/controller/PhluxViewController.php
@@ -1,83 +1,83 @@
<?php
final class PhluxViewController extends PhluxController {
public function handleRequest(AphrontRequest $request) {
$viewer = $this->getViewer();
$key = $request->getURIData('key');
$var = id(new PhluxVariableQuery())
->setViewer($viewer)
->withKeys(array($key))
->executeOne();
if (!$var) {
return new Aphront404Response();
}
$title = $var->getVariableKey();
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb($title, $request->getRequestURI());
$crumbs->setBorder(true);
$curtain = $this->buildCurtainView($var);
$header = id(new PHUIHeaderView())
->setHeader($title)
->setUser($viewer)
->setPolicyObject($var)
->setHeaderIcon('fa-copy');
$display_value = json_encode($var->getVariableValue());
$properties = id(new PHUIPropertyListView())
->setUser($viewer)
->addProperty(pht('Value'), $display_value);
$timeline = $this->buildTransactionTimeline(
$var,
new PhluxTransactionQuery());
$timeline->setShouldTerminate(true);
$object_box = id(new PHUIObjectBoxView())
- ->setHeaderText(pht('DETAILS'))
+ ->setHeaderText(pht('Details'))
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->addPropertyList($properties);
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setCurtain($curtain)
->setMainColumn(array(
$object_box,
$timeline,
));
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->appendChild($view);
}
private function buildCurtainView(PhluxVariable $var) {
$viewer = $this->getViewer();
$curtain = $this->newCurtainView($var);
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$var,
PhabricatorPolicyCapability::CAN_EDIT);
$curtain->addAction(
id(new PhabricatorActionView())
->setIcon('fa-pencil')
->setName(pht('Edit Variable'))
->setHref($this->getApplicationURI('/edit/'.$var->getVariableKey().'/'))
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit));
return $curtain;
}
}
diff --git a/src/applications/phortune/controller/PhortuneMerchantViewController.php b/src/applications/phortune/controller/PhortuneMerchantViewController.php
index 59d9273eaa..a0e1100004 100644
--- a/src/applications/phortune/controller/PhortuneMerchantViewController.php
+++ b/src/applications/phortune/controller/PhortuneMerchantViewController.php
@@ -1,320 +1,320 @@
<?php
final class PhortuneMerchantViewController
extends PhortuneMerchantController {
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$id = $request->getURIData('id');
$merchant = id(new PhortuneMerchantQuery())
->setViewer($viewer)
->withIDs(array($id))
->executeOne();
if (!$merchant) {
return new Aphront404Response();
}
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb($merchant->getName());
$crumbs->setBorder(true);
$title = pht(
'Merchant %d %s',
$merchant->getID(),
$merchant->getName());
$header = id(new PHUIHeaderView())
->setHeader($merchant->getName())
->setUser($viewer)
->setPolicyObject($merchant)
->setHeaderIcon('fa-bank');
$providers = id(new PhortunePaymentProviderConfigQuery())
->setViewer($viewer)
->withMerchantPHIDs(array($merchant->getPHID()))
->execute();
$details = $this->buildDetailsView($merchant, $providers);
$description = $this->buildDescriptionView($merchant);
$curtain = $this->buildCurtainView($merchant);
$provider_list = $this->buildProviderList(
$merchant,
$providers);
$timeline = $this->buildTransactionTimeline(
$merchant,
new PhortuneMerchantTransactionQuery());
$timeline->setShouldTerminate(true);
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setCurtain($curtain)
->setMainColumn(array(
$details,
$description,
$provider_list,
$timeline,
));
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->appendChild($view);
}
private function buildDetailsView(
PhortuneMerchant $merchant,
array $providers) {
$viewer = $this->getRequest()->getUser();
$view = id(new PHUIPropertyListView())
->setUser($viewer)
->setObject($merchant);
$status_view = new PHUIStatusListView();
$have_any = false;
$any_test = false;
foreach ($providers as $provider_config) {
$provider = $provider_config->buildProvider();
if ($provider->isEnabled()) {
$have_any = true;
}
if (!$provider->isAcceptingLivePayments()) {
$any_test = true;
}
}
if ($have_any) {
$status_view->addItem(
id(new PHUIStatusItemView())
->setIcon(PHUIStatusItemView::ICON_ACCEPT, 'green')
->setTarget(pht('Accepts Payments'))
->setNote(pht('This merchant can accept payments.')));
if ($any_test) {
$status_view->addItem(
id(new PHUIStatusItemView())
->setIcon(PHUIStatusItemView::ICON_WARNING, 'yellow')
->setTarget(pht('Test Mode'))
->setNote(pht('This merchant is accepting test payments.')));
} else {
$status_view->addItem(
id(new PHUIStatusItemView())
->setIcon(PHUIStatusItemView::ICON_ACCEPT, 'green')
->setTarget(pht('Live Mode'))
->setNote(pht('This merchant is accepting live payments.')));
}
} else if ($providers) {
$status_view->addItem(
id(new PHUIStatusItemView())
->setIcon(PHUIStatusItemView::ICON_REJECT, 'red')
->setTarget(pht('No Enabled Providers'))
->setNote(
pht(
'All of the payment providers for this merchant are '.
'disabled.')));
} else {
$status_view->addItem(
id(new PHUIStatusItemView())
->setIcon(PHUIStatusItemView::ICON_WARNING, 'yellow')
->setTarget(pht('No Providers'))
->setNote(
pht(
'This merchant does not have any payment providers configured '.
'yet, so it can not accept payments. Add a provider.')));
}
$view->addProperty(pht('Status'), $status_view);
return id(new PHUIObjectBoxView())
- ->setHeaderText(pht('DETAILS'))
+ ->setHeaderText(pht('Details'))
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->appendChild($view);
}
private function buildDescriptionView(PhortuneMerchant $merchant) {
$viewer = $this->getViewer();
$view = id(new PHUIPropertyListView())
->setUser($viewer);
$description = $merchant->getDescription();
if (strlen($description)) {
$description = new PHUIRemarkupView($viewer, $description);
$view->addTextContent($description);
return id(new PHUIObjectBoxView())
- ->setHeaderText(pht('DESCRIPTION'))
+ ->setHeaderText(pht('Description'))
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->appendChild($view);
}
return null;
}
private function buildCurtainView(PhortuneMerchant $merchant) {
$viewer = $this->getRequest()->getUser();
$id = $merchant->getID();
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$merchant,
PhabricatorPolicyCapability::CAN_EDIT);
$curtain = $this->newCurtainView($merchant);
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('Edit Merchant'))
->setIcon('fa-pencil')
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit)
->setHref($this->getApplicationURI("merchant/edit/{$id}/")));
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('View Orders'))
->setIcon('fa-shopping-cart')
->setHref($this->getApplicationURI("merchant/orders/{$id}/"))
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit));
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('View Subscriptions'))
->setIcon('fa-moon-o')
->setHref($this->getApplicationURI("merchant/{$id}/subscription/"))
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit));
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('New Invoice'))
->setIcon('fa-fax')
->setHref($this->getApplicationURI("merchant/{$id}/invoice/new/"))
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit));
$member_phids = $merchant->getMemberPHIDs();
$handles = $viewer->loadHandles($member_phids);
$member_list = id(new PHUIObjectItemListView())
->setSimple(true);
foreach ($member_phids as $member_phid) {
$image_uri = $handles[$member_phid]->getImageURI();
$image_href = $handles[$member_phid]->getURI();
$person = $handles[$member_phid];
$member = id(new PHUIObjectItemView())
->setImageURI($image_uri)
->setHref($image_href)
->setHeader($person->getFullName());
$member_list->addItem($member);
}
$curtain->newPanel()
->setHeaderText(pht('Members'))
->appendChild($member_list);
return $curtain;
}
private function buildProviderList(
PhortuneMerchant $merchant,
array $providers) {
$viewer = $this->getRequest()->getUser();
$id = $merchant->getID();
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$merchant,
PhabricatorPolicyCapability::CAN_EDIT);
$provider_list = id(new PHUIObjectItemListView())
->setFlush(true)
->setNoDataString(pht('This merchant has no payment providers.'));
foreach ($providers as $provider_config) {
$provider = $provider_config->buildProvider();
$provider_id = $provider_config->getID();
$item = id(new PHUIObjectItemView())
->setHeader($provider->getName());
if ($provider->isEnabled()) {
if ($provider->isAcceptingLivePayments()) {
$item->setStatusIcon('fa-check green');
} else {
$item->setStatusIcon('fa-warning yellow');
$item->addIcon('fa-exclamation-triangle', pht('Test Mode'));
}
$item->addAttribute($provider->getConfigureProvidesDescription());
} else {
// Don't show disabled providers to users who can't manage the merchant
// account.
if (!$can_edit) {
continue;
}
$item->setDisabled(true);
$item->addAttribute(
phutil_tag('em', array(), pht('This payment provider is disabled.')));
}
if ($can_edit) {
$edit_uri = $this->getApplicationURI(
"/provider/edit/{$provider_id}/");
$disable_uri = $this->getApplicationURI(
"/provider/disable/{$provider_id}/");
if ($provider->isEnabled()) {
$disable_icon = 'fa-times';
$disable_name = pht('Disable');
} else {
$disable_icon = 'fa-check';
$disable_name = pht('Enable');
}
$item->addAction(
id(new PHUIListItemView())
->setIcon($disable_icon)
->setHref($disable_uri)
->setName($disable_name)
->setWorkflow(true));
$item->addAction(
id(new PHUIListItemView())
->setIcon('fa-pencil')
->setHref($edit_uri)
->setName(pht('Edit')));
}
$provider_list->addItem($item);
}
$add_action = id(new PHUIButtonView())
->setTag('a')
->setHref($this->getApplicationURI('provider/edit/?merchantID='.$id))
->setText(pht('Add Payment Provider'))
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit)
->setIcon('fa-plus');
$header = id(new PHUIHeaderView())
->setHeader(pht('Payment Providers'))
->addActionLink($add_action);
return id(new PHUIObjectBoxView())
->setHeader($header)
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->setObjectList($provider_list);
}
}
diff --git a/src/applications/phortune/controller/PhortuneProductViewController.php b/src/applications/phortune/controller/PhortuneProductViewController.php
index 0bf022e373..a434a94534 100644
--- a/src/applications/phortune/controller/PhortuneProductViewController.php
+++ b/src/applications/phortune/controller/PhortuneProductViewController.php
@@ -1,58 +1,58 @@
<?php
final class PhortuneProductViewController extends PhortuneController {
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$id = $request->getURIData('id');
$product = id(new PhortuneProductQuery())
->setViewer($viewer)
->withIDs(array($id))
->executeOne();
if (!$product) {
return new Aphront404Response();
}
$title = pht('Product: %s', $product->getProductName());
$header = id(new PHUIHeaderView())
->setHeader($product->getProductName())
->setHeaderIcon('fa-gift');
$edit_uri = $this->getApplicationURI('product/edit/'.$product->getID().'/');
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb(
pht('Products'),
$this->getApplicationURI('product/'));
$crumbs->addTextCrumb(
pht('#%d', $product->getID()),
$request->getRequestURI());
$crumbs->setBorder(true);
$properties = id(new PHUIPropertyListView())
->setUser($viewer)
->addProperty(
pht('Price'),
$product->getPriceAsCurrency()->formatForDisplay());
$object_box = id(new PHUIObjectBoxView())
- ->setHeaderText(pht('DETAILS'))
+ ->setHeaderText(pht('Details'))
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->addPropertyList($properties);
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setFooter(array(
$object_box,
));
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->appendChild($view);
}
}
diff --git a/src/applications/phortune/controller/PhortuneSubscriptionViewController.php b/src/applications/phortune/controller/PhortuneSubscriptionViewController.php
index 0e1bc55b62..0aea396136 100644
--- a/src/applications/phortune/controller/PhortuneSubscriptionViewController.php
+++ b/src/applications/phortune/controller/PhortuneSubscriptionViewController.php
@@ -1,209 +1,209 @@
<?php
final class PhortuneSubscriptionViewController extends PhortuneController {
public function handleRequest(AphrontRequest $request) {
$viewer = $this->getViewer();
$authority = $this->loadMerchantAuthority();
$subscription_query = id(new PhortuneSubscriptionQuery())
->setViewer($viewer)
->withIDs(array($request->getURIData('id')))
->needTriggers(true);
if ($authority) {
$subscription_query->withMerchantPHIDs(array($authority->getPHID()));
}
$subscription = $subscription_query->executeOne();
if (!$subscription) {
return new Aphront404Response();
}
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$subscription,
PhabricatorPolicyCapability::CAN_EDIT);
$merchant = $subscription->getMerchant();
$account = $subscription->getAccount();
$account_id = $account->getID();
$subscription_id = $subscription->getID();
$title = $subscription->getSubscriptionFullName();
$header = id(new PHUIHeaderView())
->setHeader($title)
->setHeaderIcon('fa-calendar-o');
$curtain = $this->newCurtainView($subscription);
$edit_uri = $subscription->getEditURI();
$curtain->addAction(
id(new PhabricatorActionView())
->setIcon('fa-pencil')
->setName(pht('Edit Subscription'))
->setHref($edit_uri)
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit));
$crumbs = $this->buildApplicationCrumbs();
if ($authority) {
$this->addMerchantCrumb($crumbs, $merchant);
} else {
$this->addAccountCrumb($crumbs, $account);
}
$crumbs->addTextCrumb($subscription->getSubscriptionCrumbName());
$crumbs->setBorder(true);
$properties = id(new PHUIPropertyListView())
->setUser($viewer);
$next_invoice = $subscription->getTrigger()->getNextEventPrediction();
$properties->addProperty(
pht('Next Invoice'),
phabricator_datetime($next_invoice, $viewer));
$default_method = $subscription->getDefaultPaymentMethodPHID();
if ($default_method) {
$handles = $this->loadViewerHandles(array($default_method));
$autopay_method = $handles[$default_method]->renderLink();
} else {
$autopay_method = phutil_tag(
'em',
array(),
pht('No Autopay Method Configured'));
}
$properties->addProperty(
pht('Autopay With'),
$autopay_method);
$details = id(new PHUIObjectBoxView())
- ->setHeaderText(pht('DETAILS'))
+ ->setHeaderText(pht('Details'))
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->addPropertyList($properties);
$due_box = $this->buildDueInvoices($subscription, $authority);
$invoice_box = $this->buildPastInvoices($subscription, $authority);
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setCurtain($curtain)
->setMainColumn(array(
$details,
$due_box,
$invoice_box,
));
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->appendChild($view);
}
private function buildDueInvoices(
PhortuneSubscription $subscription,
$authority) {
$viewer = $this->getViewer();
$invoices = id(new PhortuneCartQuery())
->setViewer($viewer)
->withSubscriptionPHIDs(array($subscription->getPHID()))
->needPurchases(true)
->withInvoices(true)
->execute();
$phids = array();
foreach ($invoices as $invoice) {
$phids[] = $invoice->getPHID();
$phids[] = $invoice->getMerchantPHID();
foreach ($invoice->getPurchases() as $purchase) {
$phids[] = $purchase->getPHID();
}
}
$handles = $this->loadViewerHandles($phids);
$invoice_table = id(new PhortuneOrderTableView())
->setUser($viewer)
->setCarts($invoices)
->setIsInvoices(true)
->setIsMerchantView((bool)$authority)
->setHandles($handles);
$invoice_header = id(new PHUIHeaderView())
->setHeader(pht('Invoices Due'));
return id(new PHUIObjectBoxView())
->setHeader($invoice_header)
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->appendChild($invoice_table);
}
private function buildPastInvoices(
PhortuneSubscription $subscription,
$authority) {
$viewer = $this->getViewer();
$invoices = id(new PhortuneCartQuery())
->setViewer($viewer)
->withSubscriptionPHIDs(array($subscription->getPHID()))
->needPurchases(true)
->withStatuses(
array(
PhortuneCart::STATUS_PURCHASING,
PhortuneCart::STATUS_CHARGED,
PhortuneCart::STATUS_HOLD,
PhortuneCart::STATUS_REVIEW,
PhortuneCart::STATUS_PURCHASED,
))
->setLimit(50)
->execute();
$phids = array();
foreach ($invoices as $invoice) {
$phids[] = $invoice->getPHID();
foreach ($invoice->getPurchases() as $purchase) {
$phids[] = $purchase->getPHID();
}
}
$handles = $this->loadViewerHandles($phids);
$invoice_table = id(new PhortuneOrderTableView())
->setUser($viewer)
->setCarts($invoices)
->setHandles($handles);
$account = $subscription->getAccount();
$merchant = $subscription->getMerchant();
$account_id = $account->getID();
$merchant_id = $merchant->getID();
$subscription_id = $subscription->getID();
if ($authority) {
$invoices_uri = $this->getApplicationURI(
"merchant/{$merchant_id}/subscription/order/{$subscription_id}/");
} else {
$invoices_uri = $this->getApplicationURI(
"{$account_id}/subscription/order/{$subscription_id}/");
}
$invoice_header = id(new PHUIHeaderView())
->setHeader(pht('Past Invoices'))
->addActionLink(
id(new PHUIButtonView())
->setTag('a')
->setIcon('fa-list')
->setHref($invoices_uri)
->setText(pht('View All Invoices')));
return id(new PHUIObjectBoxView())
->setHeader($invoice_header)
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->appendChild($invoice_table);
}
}
diff --git a/src/applications/phurl/controller/PhabricatorPhurlURLViewController.php b/src/applications/phurl/controller/PhabricatorPhurlURLViewController.php
index 946e35c854..4703adade5 100644
--- a/src/applications/phurl/controller/PhabricatorPhurlURLViewController.php
+++ b/src/applications/phurl/controller/PhabricatorPhurlURLViewController.php
@@ -1,153 +1,153 @@
<?php
final class PhabricatorPhurlURLViewController
extends PhabricatorPhurlController {
public function shouldAllowPublic() {
return true;
}
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$id = $request->getURIData('id');
$timeline = null;
$url = id(new PhabricatorPhurlURLQuery())
->setViewer($viewer)
->withIDs(array($id))
->executeOne();
if (!$url) {
return new Aphront404Response();
}
$title = $url->getMonogram();
$page_title = $title.' '.$url->getName();
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb($title);
$crumbs->setBorder(true);
$timeline = $this->buildTransactionTimeline(
$url,
new PhabricatorPhurlURLTransactionQuery());
$header = $this->buildHeaderView($url);
$curtain = $this->buildCurtain($url);
$details = $this->buildPropertySectionView($url);
$url_error = id(new PHUIInfoView())
->setErrors(array(pht('This URL is invalid due to a bad protocol.')))
->setIsHidden($url->isValid());
$is_serious = PhabricatorEnv::getEnvConfig('phabricator.serious-business');
$add_comment_header = $is_serious
? pht('Add Comment')
: pht('More Cowbell');
$draft = PhabricatorDraft::newFromUserAndKey($viewer, $url->getPHID());
$comment_uri = $this->getApplicationURI(
'/url/comment/'.$url->getID().'/');
$add_comment_form = id(new PhabricatorApplicationTransactionCommentView())
->setUser($viewer)
->setObjectPHID($url->getPHID())
->setDraft($draft)
->setHeaderText($add_comment_header)
->setAction($comment_uri)
->setSubmitButtonName(pht('Add Comment'));
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setCurtain($curtain)
->setMainColumn(array(
$url_error,
$details,
$timeline,
$add_comment_form,
));
return $this->newPage()
->setTitle($page_title)
->setCrumbs($crumbs)
->setPageObjectPHIDs(array($url->getPHID()))
->appendChild(
array(
$view,
));
}
private function buildHeaderView(PhabricatorPhurlURL $url) {
$viewer = $this->getViewer();
$icon = 'fa-check';
$color = 'bluegrey';
$status = pht('Active');
$id = $url->getID();
$visit = id(new PHUIButtonView())
->setTag('a')
->setText(pht('Visit URL'))
->setIcon('fa-external-link')
->setHref("u/{$id}")
->setDisabled(!$url->isValid());
$header = id(new PHUIHeaderView())
->setUser($viewer)
->setHeader($url->getDisplayName())
->setStatus($icon, $color, $status)
->setPolicyObject($url)
->setHeaderIcon('fa-compress')
->addActionLink($visit);
return $header;
}
private function buildCurtain(PhabricatorPhurlURL $url) {
$viewer = $this->getViewer();
$id = $url->getID();
$curtain = $this->newCurtainView($url);
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$url,
PhabricatorPolicyCapability::CAN_EDIT);
$curtain
->addAction(
id(new PhabricatorActionView())
->setName(pht('Edit'))
->setIcon('fa-pencil')
->setHref($this->getApplicationURI("url/edit/{$id}/"))
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit));
return $curtain;
}
private function buildPropertySectionView(PhabricatorPhurlURL $url) {
$viewer = $this->getViewer();
$properties = id(new PHUIPropertyListView())
->setUser($viewer);
$properties->addProperty(
pht('Original URL'),
$url->getLongURL());
$properties->addProperty(
pht('Alias'),
$url->getAlias());
$description = $url->getDescription();
if (strlen($description)) {
$description = new PHUIRemarkupView($viewer, $description);
$properties->addSectionHeader(pht('Description'));
$properties->addTextContent($description);
}
return id(new PHUIObjectBoxView())
- ->setHeaderText(pht('DETAILS'))
+ ->setHeaderText(pht('Details'))
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->appendChild($properties);
}
}
diff --git a/src/applications/ponder/controller/PonderQuestionViewController.php b/src/applications/ponder/controller/PonderQuestionViewController.php
index a1105c6c8e..529c396328 100644
--- a/src/applications/ponder/controller/PonderQuestionViewController.php
+++ b/src/applications/ponder/controller/PonderQuestionViewController.php
@@ -1,276 +1,276 @@
<?php
final class PonderQuestionViewController extends PonderController {
public function shouldAllowPublic() {
return true;
}
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$id = $request->getURIData('id');
$question = id(new PonderQuestionQuery())
->setViewer($viewer)
->withIDs(array($id))
->needAnswers(true)
->needProjectPHIDs(true)
->executeOne();
if (!$question) {
return new Aphront404Response();
}
$answers = $this->buildAnswers($question);
$answer_add_panel = id(new PonderAddAnswerView())
->setQuestion($question)
->setUser($viewer)
->setActionURI('/ponder/answer/add/');
$header = new PHUIHeaderView();
$header->setHeader($question->getTitle());
$header->setUser($viewer);
$header->setPolicyObject($question);
$header->setHeaderIcon('fa-university');
if ($question->getStatus() == PonderQuestionStatus::STATUS_OPEN) {
$header->setStatus('fa-square-o', 'bluegrey', pht('Open'));
} else {
$text = PonderQuestionStatus::getQuestionStatusFullName(
$question->getStatus());
$icon = PonderQuestionStatus::getQuestionStatusIcon(
$question->getStatus());
$header->setStatus($icon, 'dark', $text);
}
$curtain = $this->buildCurtain($question);
$details = $this->buildPropertySectionView($question);
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$question,
PhabricatorPolicyCapability::CAN_EDIT);
$content_id = celerity_generate_unique_node_id();
$timeline = $this->buildTransactionTimeline(
$question,
id(new PonderQuestionTransactionQuery())
->withTransactionTypes(array(PhabricatorTransactions::TYPE_COMMENT)));
$xactions = $timeline->getTransactions();
$add_comment = id(new PhabricatorApplicationTransactionCommentView())
->setUser($viewer)
->setObjectPHID($question->getPHID())
->setShowPreview(false)
->setAction($this->getApplicationURI("/question/comment/{$id}/"))
->setSubmitButtonName(pht('Comment'));
$add_comment = phutil_tag_div(
'ponder-question-add-comment-view', $add_comment);
$comment_view = phutil_tag(
'div',
array(
'id' => $content_id,
'style' => 'display: none;',
),
array(
$timeline,
$add_comment,
));
$footer = id(new PonderFooterView())
->setContentID($content_id)
->setCount(count($xactions));
$crumbs = $this->buildApplicationCrumbs($this->buildSideNavView());
$crumbs->addTextCrumb('Q'.$id, '/Q'.$id);
$crumbs->setBorder(true);
$subheader = $this->buildSubheaderView($question);
$answer_wiki = null;
if ($question->getAnswerWiki()) {
$wiki = new PHUIRemarkupView($viewer, $question->getAnswerWiki());
$answer_wiki = id(new PHUIObjectBoxView())
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->setHeaderText(pht('ANSWER SUMMARY'))
->appendChild($wiki)
->addClass('ponder-answer-wiki');
}
require_celerity_resource('ponder-view-css');
$ponder_content = phutil_tag(
'div',
array(
'class' => 'ponder-question-content',
),
array(
$answer_wiki,
$footer,
$comment_view,
$answers,
$answer_add_panel,
));
$ponder_view = id(new PHUITwoColumnView())
->setHeader($header)
->setSubheader($subheader)
->setCurtain($curtain)
->setMainColumn($ponder_content)
- ->addPropertySection(pht('DETAILS'), $details)
+ ->addPropertySection(pht('Details'), $details)
->addClass('ponder-question-view');
$page_objects = array_merge(
array($question->getPHID()),
mpull($question->getAnswers(), 'getPHID'));
return $this->newPage()
->setTitle('Q'.$question->getID().' '.$question->getTitle())
->setCrumbs($crumbs)
->setPageObjectPHIDs($page_objects)
->appendChild($ponder_view);
}
private function buildCurtain(PonderQuestion $question) {
$viewer = $this->getViewer();
$id = $question->getID();
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$question,
PhabricatorPolicyCapability::CAN_EDIT);
$curtain = $this->newCurtainView($question);
if ($question->getStatus() == PonderQuestionStatus::STATUS_OPEN) {
$name = pht('Close Question');
$icon = 'fa-check-square-o';
} else {
$name = pht('Reopen Question');
$icon = 'fa-square-o';
}
$curtain->addAction(
id(new PhabricatorActionView())
->setIcon('fa-pencil')
->setName(pht('Edit Question'))
->setHref($this->getApplicationURI("/question/edit/{$id}/"))
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit));
$curtain->addAction(
id(new PhabricatorActionView())
->setName($name)
->setIcon($icon)
->setWorkflow(true)
->setDisabled(!$can_edit)
->setHref($this->getApplicationURI("/question/status/{$id}/")));
$curtain->addAction(
id(new PhabricatorActionView())
->setIcon('fa-list')
->setName(pht('View History'))
->setHref($this->getApplicationURI("/question/history/{$id}/")));
return $curtain;
}
private function buildSubheaderView(
PonderQuestion $question) {
$viewer = $this->getViewer();
$asker = $viewer->renderHandle($question->getAuthorPHID())->render();
$date = phabricator_datetime($question->getDateCreated(), $viewer);
$asker = phutil_tag('strong', array(), $asker);
$author = id(new PhabricatorPeopleQuery())
->setViewer($viewer)
->withPHIDs(array($question->getAuthorPHID()))
->needProfileImage(true)
->executeOne();
$image_uri = $author->getProfileImageURI();
$image_href = '/p/'.$author->getUsername();
$content = pht('Asked by %s on %s.', $asker, $date);
return id(new PHUIHeadThingView())
->setImage($image_uri)
->setImageHref($image_href)
->setContent($content);
}
private function buildPropertySectionView(
PonderQuestion $question) {
$viewer = $this->getViewer();
$question_details = PhabricatorMarkupEngine::renderOneObject(
$question,
$question->getMarkupField(),
$viewer);
if (!$question_details) {
$question_details = phutil_tag(
'em',
array(),
pht('No further details for this question.'));
}
$question_details = phutil_tag_div(
'phabricator-remarkup ml', $question_details);
return $question_details;
}
/**
* This is fairly non-standard; building N timelines at once (N = number of
* answers) is tricky business.
*
* TODO - re-factor this to ajax in one answer panel at a time in a more
* standard fashion. This is necessary to scale this application.
*/
private function buildAnswers(PonderQuestion $question) {
$viewer = $this->getViewer();
$answers = $question->getAnswers();
if ($answers) {
$author_phids = mpull($answers, 'getAuthorPHID');
$handles = $this->loadViewerHandles($author_phids);
$view = array();
foreach ($answers as $answer) {
$id = $answer->getID();
$handle = $handles[$answer->getAuthorPHID()];
$timeline = $this->buildTransactionTimeline(
$answer,
id(new PonderAnswerTransactionQuery())
->withTransactionTypes(array(PhabricatorTransactions::TYPE_COMMENT)));
$xactions = $timeline->getTransactions();
$view[] = id(new PonderAnswerView())
->setUser($viewer)
->setAnswer($answer)
->setTransactions($xactions)
->setTimeline($timeline)
->setHandle($handle);
}
$header = id(new PHUIHeaderView())
->setHeader('Answers');
return id(new PHUIBoxView())
->addClass('ponder-answer-section')
->appendChild($header)
->appendChild($view);
}
return null;
}
}
diff --git a/src/applications/project/controller/PhabricatorProjectManageController.php b/src/applications/project/controller/PhabricatorProjectManageController.php
index d84df87e93..c827f5abab 100644
--- a/src/applications/project/controller/PhabricatorProjectManageController.php
+++ b/src/applications/project/controller/PhabricatorProjectManageController.php
@@ -1,148 +1,148 @@
<?php
final class PhabricatorProjectManageController
extends PhabricatorProjectController {
public function shouldAllowPublic() {
return true;
}
public function handleRequest(AphrontRequest $request) {
$response = $this->loadProject();
if ($response) {
return $response;
}
$viewer = $request->getUser();
$project = $this->getProject();
$id = $project->getID();
$picture = $project->getProfileImageURI();
$header = id(new PHUIHeaderView())
->setHeader(pht('Project History'))
->setUser($viewer)
->setPolicyObject($project)
->setImage($picture);
if ($project->getStatus() == PhabricatorProjectStatus::STATUS_ACTIVE) {
$header->setStatus('fa-check', 'bluegrey', pht('Active'));
} else {
$header->setStatus('fa-ban', 'red', pht('Archived'));
}
$curtain = $this->buildCurtain($project);
$properties = $this->buildPropertyListView($project);
$timeline = $this->buildTransactionTimeline(
$project,
new PhabricatorProjectTransactionQuery());
$timeline->setShouldTerminate(true);
$nav = $this->getProfileMenu();
$nav->selectFilter(PhabricatorProject::PANEL_MANAGE);
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb(pht('Manage'));
$crumbs->setBorder(true);
$manage = id(new PHUITwoColumnView())
->setHeader($header)
->setCurtain($curtain)
- ->addPropertySection(pht('DETAILS'), $properties)
+ ->addPropertySection(pht('Details'), $properties)
->setMainColumn(
array(
$timeline,
));
return $this->newPage()
->setNavigation($nav)
->setCrumbs($crumbs)
->setTitle(
array(
$project->getDisplayName(),
pht('Manage'),
))
->appendChild(
array(
$manage,
));
}
private function buildCurtain(PhabricatorProject $project) {
$viewer = $this->getViewer();
$id = $project->getID();
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$project,
PhabricatorPolicyCapability::CAN_EDIT);
$curtain = $this->newCurtainView($project);
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('Edit Details'))
->setIcon('fa-pencil')
->setHref($this->getApplicationURI("edit/{$id}/"))
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit));
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('Edit Menu'))
->setIcon('fa-th-list')
->setHref($this->getApplicationURI("{$id}/panel/configure/"))
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit));
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('Edit Picture'))
->setIcon('fa-picture-o')
->setHref($this->getApplicationURI("picture/{$id}/"))
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit));
if ($project->isArchived()) {
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('Activate Project'))
->setIcon('fa-check')
->setHref($this->getApplicationURI("archive/{$id}/"))
->setDisabled(!$can_edit)
->setWorkflow(true));
} else {
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('Archive Project'))
->setIcon('fa-ban')
->setHref($this->getApplicationURI("archive/{$id}/"))
->setDisabled(!$can_edit)
->setWorkflow(true));
}
return $curtain;
}
private function buildPropertyListView(
PhabricatorProject $project) {
$viewer = $this->getViewer();
$view = id(new PHUIPropertyListView())
->setUser($viewer);
$view->addProperty(
pht('Looks Like'),
$viewer->renderHandle($project->getPHID())->setAsTag(true));
$field_list = PhabricatorCustomField::getObjectFields(
$project,
PhabricatorCustomField::ROLE_VIEW);
$field_list->appendFieldsToPropertyList($project, $viewer, $view);
return $view;
}
}
diff --git a/src/applications/spaces/controller/PhabricatorSpacesViewController.php b/src/applications/spaces/controller/PhabricatorSpacesViewController.php
index 8319f19a6e..495a0c8dee 100644
--- a/src/applications/spaces/controller/PhabricatorSpacesViewController.php
+++ b/src/applications/spaces/controller/PhabricatorSpacesViewController.php
@@ -1,144 +1,144 @@
<?php
final class PhabricatorSpacesViewController
extends PhabricatorSpacesController {
public function shouldAllowPublic() {
return true;
}
public function handleRequest(AphrontRequest $request) {
$viewer = $this->getViewer();
$space = id(new PhabricatorSpacesNamespaceQuery())
->setViewer($viewer)
->withIDs(array($request->getURIData('id')))
->executeOne();
if (!$space) {
return new Aphront404Response();
}
$curtain = $this->buildCurtain($space);
$property_list = $this->buildPropertyListView($space);
$title = array($space->getMonogram(), $space->getNamespaceName());
$xactions = id(new PhabricatorSpacesNamespaceTransactionQuery())
->setViewer($viewer)
->withObjectPHIDs(array($space->getPHID()))
->execute();
$timeline = $this->buildTransactionTimeline(
$space,
new PhabricatorSpacesNamespaceTransactionQuery());
$timeline->setShouldTerminate(true);
$header = id(new PHUIHeaderView())
->setUser($viewer)
->setHeader($space->getNamespaceName())
->setPolicyObject($space)
->setHeaderIcon('fa-th-large');
if ($space->getIsArchived()) {
$header->setStatus('fa-ban', 'red', pht('Archived'));
} else {
$header->setStatus('fa-check', 'bluegrey', pht('Active'));
}
$box = id(new PHUIObjectBoxView())
- ->setHeaderText(pht('DETAILS'))
+ ->setHeaderText(pht('Details'))
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->addPropertyList($property_list);
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb($space->getMonogram());
$crumbs->setBorder(true);
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setMainColumn(array(
$box,
$timeline,
))
->setCurtain($curtain);
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->appendChild($view);
}
private function buildPropertyListView(PhabricatorSpacesNamespace $space) {
$viewer = $this->getRequest()->getUser();
$list = id(new PHUIPropertyListView())
->setUser($viewer);
$list->addProperty(
pht('Default Space'),
$space->getIsDefaultNamespace()
? pht('Yes')
: pht('No'));
$descriptions = PhabricatorPolicyQuery::renderPolicyDescriptions(
$viewer,
$space);
$list->addProperty(
pht('Editable By'),
$descriptions[PhabricatorPolicyCapability::CAN_EDIT]);
$description = $space->getDescription();
if (strlen($description)) {
$description = new PHUIRemarkupView($viewer, $description);
$list->addSectionHeader(
pht('Description'),
PHUIPropertyListView::ICON_SUMMARY);
$list->addTextContent($description);
}
return $list;
}
private function buildCurtain(PhabricatorSpacesNamespace $space) {
$viewer = $this->getRequest()->getUser();
$curtain = $this->newCurtainView($space);
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$space,
PhabricatorPolicyCapability::CAN_EDIT);
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('Edit Space'))
->setIcon('fa-pencil')
->setHref($this->getApplicationURI('edit/'.$space->getID().'/'))
->setWorkflow(!$can_edit)
->setDisabled(!$can_edit));
$id = $space->getID();
if ($space->getIsArchived()) {
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('Activate Space'))
->setIcon('fa-check')
->setHref($this->getApplicationURI("activate/{$id}/"))
->setDisabled(!$can_edit)
->setWorkflow(true));
} else {
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('Archive Space'))
->setIcon('fa-ban')
->setHref($this->getApplicationURI("archive/{$id}/"))
->setDisabled(!$can_edit)
->setWorkflow(true));
}
return $curtain;
}
}

File Metadata

Mime Type
text/x-diff
Expires
Tue, Apr 7, 9:04 AM (1 d, 6 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1166017
Default Alt Text
(342 KB)

Event Timeline