Page MenuHomestyx hydra

No OneTemporary

diff --git a/src/applications/base/controller/PhabricatorController.php b/src/applications/base/controller/PhabricatorController.php
index 6a76870ba2..42152fcd63 100644
--- a/src/applications/base/controller/PhabricatorController.php
+++ b/src/applications/base/controller/PhabricatorController.php
@@ -1,623 +1,599 @@
<?php
abstract class PhabricatorController extends AphrontController {
private $handles;
public function shouldRequireLogin() {
return true;
}
public function shouldRequireAdmin() {
return false;
}
public function shouldRequireEnabledUser() {
return true;
}
public function shouldAllowPublic() {
return false;
}
public function shouldAllowPartialSessions() {
return false;
}
public function shouldRequireEmailVerification() {
return PhabricatorUserEmail::isEmailVerificationRequired();
}
public function shouldAllowRestrictedParameter($parameter_name) {
return false;
}
public function shouldRequireMultiFactorEnrollment() {
if (!$this->shouldRequireLogin()) {
return false;
}
if (!$this->shouldRequireEnabledUser()) {
return false;
}
if ($this->shouldAllowPartialSessions()) {
return false;
}
$user = $this->getRequest()->getUser();
if (!$user->getIsStandardUser()) {
return false;
}
return PhabricatorEnv::getEnvConfig('security.require-multi-factor-auth');
}
public function shouldAllowLegallyNonCompliantUsers() {
return false;
}
public function willBeginExecution() {
$request = $this->getRequest();
if ($request->getUser()) {
// NOTE: Unit tests can set a user explicitly. Normal requests are not
// permitted to do this.
PhabricatorTestCase::assertExecutingUnitTests();
$user = $request->getUser();
} else {
$user = new PhabricatorUser();
$session_engine = new PhabricatorAuthSessionEngine();
$phsid = $request->getCookie(PhabricatorCookies::COOKIE_SESSION);
if (strlen($phsid)) {
$session_user = $session_engine->loadUserForSession(
PhabricatorAuthSession::TYPE_WEB,
$phsid);
if ($session_user) {
$user = $session_user;
}
} else {
// If the client doesn't have a session token, generate an anonymous
// session. This is used to provide CSRF protection to logged-out users.
$phsid = $session_engine->establishSession(
PhabricatorAuthSession::TYPE_WEB,
null,
$partial = false);
// This may be a resource request, in which case we just don't set
// the cookie.
if ($request->canSetCookies()) {
$request->setCookie(PhabricatorCookies::COOKIE_SESSION, $phsid);
}
}
if (!$user->isLoggedIn()) {
$user->attachAlternateCSRFString(PhabricatorHash::digest($phsid));
}
$request->setUser($user);
}
$locale_code = $user->getTranslation();
if ($locale_code) {
PhabricatorEnv::setLocaleCode($locale_code);
}
$preferences = $user->loadPreferences();
if (PhabricatorEnv::getEnvConfig('darkconsole.enabled')) {
$dark_console = PhabricatorUserPreferences::PREFERENCE_DARK_CONSOLE;
if ($preferences->getPreference($dark_console) ||
PhabricatorEnv::getEnvConfig('darkconsole.always-on')) {
$console = new DarkConsoleCore();
$request->getApplicationConfiguration()->setConsole($console);
}
}
// NOTE: We want to set up the user first so we can render a real page
// here, but fire this before any real logic.
$restricted = array(
'code',
);
foreach ($restricted as $parameter) {
if ($request->getExists($parameter)) {
if (!$this->shouldAllowRestrictedParameter($parameter)) {
throw new Exception(
pht(
'Request includes restricted parameter "%s", but this '.
'controller ("%s") does not whitelist it. Refusing to '.
'serve this request because it might be part of a redirection '.
'attack.',
$parameter,
get_class($this)));
}
}
}
if ($this->shouldRequireEnabledUser()) {
if ($user->isLoggedIn() && !$user->getIsApproved()) {
$controller = new PhabricatorAuthNeedsApprovalController();
return $this->delegateToController($controller);
}
if ($user->getIsDisabled()) {
$controller = new PhabricatorDisabledUserController();
return $this->delegateToController($controller);
}
}
$event = new PhabricatorEvent(
PhabricatorEventType::TYPE_CONTROLLER_CHECKREQUEST,
array(
'request' => $request,
'controller' => $this,
));
$event->setUser($user);
PhutilEventEngine::dispatchEvent($event);
$checker_controller = $event->getValue('controller');
if ($checker_controller != $this) {
return $this->delegateToController($checker_controller);
}
$auth_class = 'PhabricatorAuthApplication';
$auth_application = PhabricatorApplication::getByClass($auth_class);
// Require partial sessions to finish login before doing anything.
if (!$this->shouldAllowPartialSessions()) {
if ($user->hasSession() &&
$user->getSession()->getIsPartial()) {
$login_controller = new PhabricatorAuthFinishController();
$this->setCurrentApplication($auth_application);
return $this->delegateToController($login_controller);
}
}
// Check if the user needs to configure MFA.
$need_mfa = $this->shouldRequireMultiFactorEnrollment();
$have_mfa = $user->getIsEnrolledInMultiFactor();
if ($need_mfa && !$have_mfa) {
// Check if the cache is just out of date. Otherwise, roadblock the user
// and require MFA enrollment.
$user->updateMultiFactorEnrollment();
if (!$user->getIsEnrolledInMultiFactor()) {
$mfa_controller = new PhabricatorAuthNeedsMultiFactorController();
$this->setCurrentApplication($auth_application);
return $this->delegateToController($mfa_controller);
}
}
if ($this->shouldRequireLogin()) {
// This actually means we need either:
// - a valid user, or a public controller; and
// - permission to see the application.
$allow_public = $this->shouldAllowPublic() &&
PhabricatorEnv::getEnvConfig('policy.allow-public');
// If this controller isn't public, and the user isn't logged in, require
// login.
if (!$allow_public && !$user->isLoggedIn()) {
$login_controller = new PhabricatorAuthStartController();
$this->setCurrentApplication($auth_application);
return $this->delegateToController($login_controller);
}
if ($user->isLoggedIn()) {
if ($this->shouldRequireEmailVerification()) {
if (!$user->getIsEmailVerified()) {
$controller = new PhabricatorMustVerifyEmailController();
$this->setCurrentApplication($auth_application);
return $this->delegateToController($controller);
}
}
}
// If the user doesn't have access to the application, don't let them use
// any of its controllers. We query the application in order to generate
// a policy exception if the viewer doesn't have permission.
$application = $this->getCurrentApplication();
if ($application) {
id(new PhabricatorApplicationQuery())
->setViewer($user)
->withPHIDs(array($application->getPHID()))
->executeOne();
}
}
if (!$this->shouldAllowLegallyNonCompliantUsers()) {
$legalpad_class = 'PhabricatorLegalpadApplication';
$legalpad = id(new PhabricatorApplicationQuery())
->setViewer($user)
->withClasses(array($legalpad_class))
->withInstalled(true)
->execute();
$legalpad = head($legalpad);
$doc_query = id(new LegalpadDocumentQuery())
->setViewer($user)
->withSignatureRequired(1)
->needViewerSignatures(true);
if ($user->hasSession() &&
!$user->getSession()->getIsPartial() &&
!$user->getSession()->getSignedLegalpadDocuments() &&
$user->isLoggedIn() &&
$legalpad) {
$sign_docs = $doc_query->execute();
$must_sign_docs = array();
foreach ($sign_docs as $sign_doc) {
if (!$sign_doc->getUserSignature($user->getPHID())) {
$must_sign_docs[] = $sign_doc;
}
}
if ($must_sign_docs) {
$controller = new LegalpadDocumentSignController();
$this->getRequest()->setURIMap(array(
'id' => head($must_sign_docs)->getID(),));
$this->setCurrentApplication($legalpad);
return $this->delegateToController($controller);
} else {
$engine = id(new PhabricatorAuthSessionEngine())
->signLegalpadDocuments($user, $sign_docs);
}
}
}
// NOTE: We do this last so that users get a login page instead of a 403
// if they need to login.
if ($this->shouldRequireAdmin() && !$user->getIsAdmin()) {
return new Aphront403Response();
}
}
public function buildStandardPageView() {
$view = new PhabricatorStandardPageView();
$view->setRequest($this->getRequest());
$view->setController($this);
return $view;
}
public function buildStandardPageResponse($view, array $data) {
$page = $this->buildStandardPageView();
$page->appendChild($view);
return $this->buildPageResponse($page);
}
private function buildPageResponse($page) {
if ($this->getRequest()->isQuicksand()) {
$response = id(new AphrontAjaxResponse())
->setContent($page->renderForQuicksand());
} else {
$response = id(new AphrontWebpageResponse())
->setContent($page->render());
}
return $response;
}
public function getApplicationURI($path = '') {
if (!$this->getCurrentApplication()) {
throw new Exception('No application!');
}
return $this->getCurrentApplication()->getApplicationURI($path);
}
public function buildApplicationPage($view, array $options) {
$page = $this->buildStandardPageView();
$title = PhabricatorEnv::getEnvConfig('phabricator.serious-business') ?
'Phabricator' :
pht('Bacon Ice Cream for Breakfast');
$application = $this->getCurrentApplication();
$page->setTitle(idx($options, 'title', $title));
if ($application) {
$page->setApplicationName($application->getName());
if ($application->getTitleGlyph()) {
$page->setGlyph($application->getTitleGlyph());
}
}
if (!($view instanceof AphrontSideNavFilterView)) {
$nav = new AphrontSideNavFilterView();
$nav->appendChild($view);
$view = $nav;
}
$user = $this->getRequest()->getUser();
$view->setUser($user);
$page->appendChild($view);
$object_phids = idx($options, 'pageObjects', array());
if ($object_phids) {
$page->appendPageObjects($object_phids);
foreach ($object_phids as $object_phid) {
PhabricatorFeedStoryNotification::updateObjectNotificationViews(
$user,
$object_phid);
}
}
if (idx($options, 'device', true)) {
$page->setDeviceReady(true);
}
$page->setShowFooter(idx($options, 'showFooter', true));
$page->setShowChrome(idx($options, 'chrome', true));
$application_menu = $this->buildApplicationMenu();
if ($application_menu) {
$page->setApplicationMenu($application_menu);
}
return $this->buildPageResponse($page);
}
public function didProcessRequest($response) {
// If a bare DialogView is returned, wrap it in a DialogResponse.
if ($response instanceof AphrontDialogView) {
$response = id(new AphrontDialogResponse())->setDialog($response);
}
$request = $this->getRequest();
$response->setRequest($request);
$seen = array();
while ($response instanceof AphrontProxyResponse) {
$hash = spl_object_hash($response);
if (isset($seen[$hash])) {
$seen[] = get_class($response);
throw new Exception(
'Cycle while reducing proxy responses: '.
implode(' -> ', $seen));
}
$seen[$hash] = get_class($response);
$response = $response->reduceProxyResponse();
}
if ($response instanceof AphrontDialogResponse) {
if (!$request->isAjax() && !$request->isQuicksand()) {
$dialog = $response->getDialog();
$title = $dialog->getTitle();
$short = $dialog->getShortTitle();
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb(coalesce($short, $title));
$page_content = array(
$crumbs,
$response->buildResponseString(),
);
$view = id(new PhabricatorStandardPageView())
->setRequest($request)
->setController($this)
->setDeviceReady(true)
->setTitle($title)
->appendChild($page_content);
$response = id(new AphrontWebpageResponse())
->setContent($view->render())
->setHTTPResponseCode($response->getHTTPResponseCode());
} else {
$response->getDialog()->setIsStandalone(true);
return id(new AphrontAjaxResponse())
->setContent(array(
'dialog' => $response->buildResponseString(),
));
}
} else if ($response instanceof AphrontRedirectResponse) {
if ($request->isAjax() || $request->isQuicksand()) {
return id(new AphrontAjaxResponse())
->setContent(
array(
'redirect' => $response->getURI(),
));
}
}
return $response;
}
protected function getHandle($phid) {
if (empty($this->handles[$phid])) {
throw new Exception(
"Attempting to access handle which wasn't loaded: {$phid}");
}
return $this->handles[$phid];
}
protected function loadHandles(array $phids) {
$phids = array_filter($phids);
$this->handles = $this->loadViewerHandles($phids);
return $this;
}
protected function loadViewerHandles(array $phids) {
return id(new PhabricatorHandleQuery())
->setViewer($this->getRequest()->getUser())
->withPHIDs($phids)
->execute();
}
- /**
- * Render a list of links to handles, identified by PHIDs. The handles must
- * already be loaded.
- *
- * @param list<phid> List of PHIDs to render links to.
- * @param string Style, one of "\n" (to put each item on its own line)
- * or "," (to list items inline, separated by commas).
- * @return string Rendered list of handle links.
- */
- protected function renderHandlesForPHIDs(array $phids, $style = "\n") {
- $style_map = array(
- "\n" => phutil_tag('br'),
- ',' => ', ',
- );
-
- if (empty($style_map[$style])) {
- throw new Exception("Unknown handle list style '{$style}'!");
- }
-
- return implode_selected_handle_links($style_map[$style],
- $this->handles,
- array_filter($phids));
- }
-
public function buildApplicationMenu() {
return null;
}
protected function buildApplicationCrumbs() {
$crumbs = array();
$application = $this->getCurrentApplication();
if ($application) {
$icon = $application->getFontIcon();
if (!$icon) {
$icon = 'fa-puzzle';
}
$crumbs[] = id(new PHUICrumbView())
->setHref($this->getApplicationURI())
->setName($application->getName())
->setIcon($icon);
}
$view = new PHUICrumbsView();
foreach ($crumbs as $crumb) {
$view->addCrumb($crumb);
}
return $view;
}
protected function hasApplicationCapability($capability) {
return PhabricatorPolicyFilter::hasCapability(
$this->getRequest()->getUser(),
$this->getCurrentApplication(),
$capability);
}
protected function requireApplicationCapability($capability) {
PhabricatorPolicyFilter::requireCapability(
$this->getRequest()->getUser(),
$this->getCurrentApplication(),
$capability);
}
protected function explainApplicationCapability(
$capability,
$positive_message,
$negative_message) {
$can_act = $this->hasApplicationCapability($capability);
if ($can_act) {
$message = $positive_message;
$icon_name = 'fa-play-circle-o lightgreytext';
} else {
$message = $negative_message;
$icon_name = 'fa-lock';
}
$icon = id(new PHUIIconView())
->setIconFont($icon_name);
require_celerity_resource('policy-css');
$phid = $this->getCurrentApplication()->getPHID();
$explain_uri = "/policy/explain/{$phid}/{$capability}/";
$message = phutil_tag(
'div',
array(
'class' => 'policy-capability-explanation',
),
array(
$icon,
javelin_tag(
'a',
array(
'href' => $explain_uri,
'sigil' => 'workflow',
),
$message),
));
return array($can_act, $message);
}
public function getDefaultResourceSource() {
return 'phabricator';
}
/**
* Create a new @{class:AphrontDialogView} with defaults filled in.
*
* @return AphrontDialogView New dialog.
*/
public function newDialog() {
$submit_uri = new PhutilURI($this->getRequest()->getRequestURI());
$submit_uri = $submit_uri->getPath();
return id(new AphrontDialogView())
->setUser($this->getRequest()->getUser())
->setSubmitURI($submit_uri);
}
protected function buildTransactionTimeline(
PhabricatorApplicationTransactionInterface $object,
PhabricatorApplicationTransactionQuery $query,
PhabricatorMarkupEngine $engine = null,
$render_data = array()) {
$viewer = $this->getRequest()->getUser();
$xaction = $object->getApplicationTransactionTemplate();
$view = $xaction->getApplicationTransactionViewObject();
$pager = id(new AphrontCursorPagerView())
->readFromRequest($this->getRequest())
->setURI(new PhutilURI(
'/transactions/showolder/'.$object->getPHID().'/'));
$xactions = $query
->setViewer($viewer)
->withObjectPHIDs(array($object->getPHID()))
->needComments(true)
->setReversePaging(false)
->executeWithCursorPager($pager);
$xactions = array_reverse($xactions);
if ($engine) {
foreach ($xactions as $xaction) {
if ($xaction->getComment()) {
$engine->addObject(
$xaction->getComment(),
PhabricatorApplicationTransactionComment::MARKUP_FIELD_COMMENT);
}
}
$engine->process();
$view->setMarkupEngine($engine);
}
$timeline = $view
->setUser($viewer)
->setObjectPHID($object->getPHID())
->setTransactions($xactions)
->setPager($pager)
->setRenderData($render_data)
->setQuoteTargetID($this->getRequest()->getStr('quoteTargetID'))
->setQuoteRef($this->getRequest()->getStr('quoteRef'));
$object->willRenderTimeline($timeline, $this->getRequest());
return $timeline;
}
}
diff --git a/src/applications/diffusion/controller/DiffusionCommitController.php b/src/applications/diffusion/controller/DiffusionCommitController.php
index 6de5ac0f82..fa29701f61 100644
--- a/src/applications/diffusion/controller/DiffusionCommitController.php
+++ b/src/applications/diffusion/controller/DiffusionCommitController.php
@@ -1,1084 +1,1082 @@
<?php
final class DiffusionCommitController extends DiffusionController {
const CHANGES_LIMIT = 100;
private $auditAuthorityPHIDs;
private $highlightedAudits;
public function shouldAllowPublic() {
return true;
}
protected function shouldLoadDiffusionRequest() {
return false;
}
protected function processDiffusionRequest(AphrontRequest $request) {
$user = $request->getUser();
// This controller doesn't use blob/path stuff, just pass the dictionary
// in directly instead of using the AphrontRequest parsing mechanism.
$data = $request->getURIMap();
$data['user'] = $user;
$drequest = DiffusionRequest::newFromDictionary($data);
$this->diffusionRequest = $drequest;
if ($request->getStr('diff')) {
return $this->buildRawDiffResponse($drequest);
}
$repository = $drequest->getRepository();
$callsign = $repository->getCallsign();
$content = array();
$commit = id(new DiffusionCommitQuery())
->setViewer($request->getUser())
->withRepository($repository)
->withIdentifiers(array($drequest->getCommit()))
->needCommitData(true)
->needAuditRequests(true)
->executeOne();
$crumbs = $this->buildCrumbs(array(
'commit' => true,
));
if (!$commit) {
$exists = $this->callConduitWithDiffusionRequest(
'diffusion.existsquery',
array('commit' => $drequest->getCommit()));
if (!$exists) {
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.'));
return $this->buildApplicationPage(
array(
$crumbs,
$error,
),
array(
'title' => pht('Commit Still Parsing'),
));
}
$audit_requests = $commit->getAudits();
$this->auditAuthorityPHIDs =
PhabricatorAuditCommentEditor::loadAuditPHIDsForUser($user);
$commit_data = $commit->getCommitData();
$is_foreign = $commit_data->getCommitDetail('foreign-svn-stub');
$changesets = 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));
$content[] = $error_panel;
} else {
$engine = PhabricatorMarkupEngine::newDifferentialMarkupEngine();
$engine->setConfig('viewer', $user);
require_celerity_resource('phabricator-remarkup-css');
$parents = $this->callConduitWithDiffusionRequest(
'diffusion.commitparentsquery',
array('commit' => $drequest->getCommit()));
if ($parents) {
$parents = id(new DiffusionCommitQuery())
->setViewer($user)
->withRepository($repository)
->withIdentifiers($parents)
->execute();
}
$headsup_view = id(new PHUIHeaderView())
->setHeader(nonempty($commit->getSummary(), pht('Commit Detail')));
$headsup_actions = $this->renderHeadsupActionList($commit, $repository);
$commit_properties = $this->loadCommitProperties(
$commit,
$commit_data,
$parents,
$audit_requests);
$property_list = id(new PHUIPropertyListView())
->setHasKeyboardShortcuts(true)
->setUser($user)
->setObject($commit);
foreach ($commit_properties as $key => $value) {
$property_list->addProperty($key, $value);
}
$message = $commit_data->getCommitMessage();
$revision = $commit->getCommitIdentifier();
$message = $this->linkBugtraq($message);
$message = $engine->markupText($message);
$property_list->invokeWillRenderEvent();
$property_list->setActionList($headsup_actions);
$detail_list = new PHUIPropertyListView();
$detail_list->addSectionHeader(
pht('Description'),
PHUIPropertyListView::ICON_SUMMARY);
$detail_list->addTextContent(
phutil_tag(
'div',
array(
'class' => 'diffusion-commit-message phabricator-remarkup',
),
$message));
$object_box = id(new PHUIObjectBoxView())
->setHeader($headsup_view)
->addPropertyList($property_list)
->addPropertyList($detail_list);
$content[] = $object_box;
}
$content[] = $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);
}
$content[] = $this->buildMergesTable($commit);
$highlighted_audits = $commit->getAuthorityAudits(
$user,
$this->auditAuthorityPHIDs);
$owners_paths = array();
if ($highlighted_audits) {
$packages = id(new PhabricatorOwnersPackage())->loadAllWhere(
'phid IN (%Ls)',
mpull($highlighted_audits, 'getAuditorPHID'));
if ($packages) {
$owners_paths = id(new PhabricatorOwnersPath())->loadAllWhere(
'repositoryPHID = %s AND packageID IN (%Ld)',
$repository->getPHID(),
mpull($packages, 'getID'));
}
}
$change_table = new DiffusionCommitChangeTableView();
$change_table->setDiffusionRequest($drequest);
$change_table->setPathChanges($changes);
$change_table->setOwnersPaths($owners_paths);
$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,
'r'.$callsign.$commit->getCommitIdentifier());
}
if ($bad_commit) {
$content[] = $this->renderStatusMessage(
pht('Bad Commit'),
$bad_commit['description']);
} else if ($is_foreign) {
// Don't render anything else.
} else if (!$commit->isImported()) {
$content[] = $this->renderStatusMessage(
pht('Still Importing...'),
pht(
'This commit is still importing. Changes will be visible once '.
'the import finishes.'));
} else if (!count($changes)) {
$content[] = $this->renderStatusMessage(
pht('Empty Commit'),
pht(
'This commit is empty and does not affect any paths.'));
} else if ($was_limited) {
$content[] = $this->renderStatusMessage(
pht('Enormous Commit'),
pht(
'This commit is enormous, and affects more than %d files. '.
'Changes are not shown.',
$hard_limit));
} else {
// 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_panel = new PHUIObjectBoxView();
$header = new PHUIHeaderView();
$header->setHeader('Changes ('.number_format($count).')');
$change_panel->setID('toc');
if ($count > self::CHANGES_LIMIT && !$show_all_details) {
$icon = id(new PHUIIconView())
->setIconFont('fa-files-o');
$button = id(new PHUIButtonView())
->setText(pht('Show All Changes'))
->setHref('?show_all=true')
->setTag('a')
->setIcon($icon);
$warning_view = id(new PHUIInfoView())
->setSeverity(PHUIInfoView::SEVERITY_WARNING)
->setTitle('Very Large Commit')
->appendChild(
pht('This commit is very large. Load each file individually.'));
$change_panel->setInfoView($warning_view);
$header->addActionLink($button);
}
$change_panel->appendChild($change_table);
$change_panel->setHeader($header);
$content[] = $change_panel;
$changesets = DiffusionPathChange::convertToDifferentialChangesets(
$user,
$changes);
$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('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(
$user,
$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 = DiffusionView::nameCommit(
$repository,
$commit->getCommitIdentifier());
$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('/diffusion/'.$callsign.'/diff/');
$change_list->setRepository($repository);
$change_list->setUser($user);
// TODO: Try to setBranch() to something reasonable here?
$change_list->setStandaloneURI(
'/diffusion/'.$callsign.'/diff/');
$change_list->setRawFileURIs(
// TODO: Implement this, somewhat tricky if there's an octopus merge
// or whatever?
null,
'/diffusion/'.$callsign.'/diff/?view=r');
$change_list->setInlineCommentControllerURI(
'/diffusion/inline/edit/'.phutil_escape_uri($commit->getPHID()).'/');
$change_references = array();
foreach ($changesets as $key => $changeset) {
$change_references[$changeset->getID()] = $references[$key];
}
$change_table->setRenderingReferences($change_references);
$content[] = $change_list->render();
}
$content[] = $this->renderAddCommentPanel($commit, $audit_requests);
$commit_id = 'r'.$callsign.$commit->getCommitIdentifier();
$short_name = DiffusionView::nameCommit(
$repository,
$commit->getCommitIdentifier());
$prefs = $user->loadPreferences();
$pref_filetree = PhabricatorUserPreferences::PREFERENCE_DIFF_FILETREE;
$pref_collapse = PhabricatorUserPreferences::PREFERENCE_NAV_COLLAPSED;
$show_filetree = $prefs->getPreference($pref_filetree);
$collapsed = $prefs->getPreference($pref_collapse);
if ($changesets && $show_filetree) {
$nav = id(new DifferentialChangesetFileTreeSideNavBuilder())
->setTitle($short_name)
->setBaseURI(new PhutilURI('/'.$commit_id))
->build($changesets)
->setCrumbs($crumbs)
->setCollapsed((bool)$collapsed)
->appendChild($content);
$content = $nav;
} else {
$content = array($crumbs, $content);
}
return $this->buildApplicationPage(
$content,
array(
'title' => $commit_id,
'pageObjects' => array($commit->getPHID()),
));
}
private function loadCommitProperties(
PhabricatorRepositoryCommit $commit,
PhabricatorRepositoryCommitData $data,
array $parents,
array $audit_requests) {
assert_instances_of($parents, 'PhabricatorRepositoryCommit');
$viewer = $this->getRequest()->getUser();
$commit_phid = $commit->getPHID();
$drequest = $this->getDiffusionRequest();
$repository = $drequest->getRepository();
$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');
}
if ($parents) {
foreach ($parents as $parent) {
$phids[] = $parent->getPHID();
}
}
// 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 ($commit->getAuditStatus()) {
$status = PhabricatorAuditCommitStatusConstants::getStatusName(
$commit->getAuditStatus());
$tag = id(new PHUITagView())
->setType(PHUITagView::TYPE_STATE)
->setName($status);
switch ($commit->getAuditStatus()) {
case PhabricatorAuditCommitStatusConstants::NEEDS_AUDIT:
$tag->setBackgroundColor(PHUITagView::COLOR_ORANGE);
break;
case PhabricatorAuditCommitStatusConstants::CONCERN_RAISED:
$tag->setBackgroundColor(PHUITagView::COLOR_RED);
break;
case PhabricatorAuditCommitStatusConstants::PARTIALLY_AUDITED:
$tag->setBackgroundColor(PHUITagView::COLOR_BLUE);
break;
case PhabricatorAuditCommitStatusConstants::FULLY_AUDITED:
$tag->setBackgroundColor(PHUITagView::COLOR_GREEN);
break;
}
$props['Status'] = $tag;
}
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) {
$props['Auditors'] = $this->renderAuditStatusView(
$user_requests);
}
if ($other_requests) {
$props['Project/Package Auditors'] = $this->renderAuditStatusView(
$other_requests);
}
}
$author_phid = $data->getCommitDetail('authorPHID');
$author_name = $data->getAuthorName();
if (!$repository->isSVN()) {
$authored_info = id(new PHUIStatusItemView());
// TODO: In Git, a distinct authorship date is available. When present,
// we should show it here.
if ($author_phid) {
$authored_info->setTarget($handles[$author_phid]->renderLink());
} else if (strlen($author_name)) {
$authored_info->setTarget($author_name);
}
$props['Authored'] = id(new PHUIStatusListView())
->addItem($authored_info);
}
$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);
}
$props['Committed'] = id(new PHUIStatusListView())
->addItem($committed_info);
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);
}
$props['Pushed'] = $pushed_list;
}
$reviewer_phid = $data->getCommitDetail('reviewerPHID');
if ($reviewer_phid) {
$props['Reviewer'] = $handles[$reviewer_phid]->renderLink();
}
if ($revision_phid) {
$props['Differential Revision'] = $handles[$revision_phid]->renderLink();
}
if ($parents) {
$parent_links = array();
foreach ($parents as $parent) {
$parent_links[] = $handles[$parent->getPHID()]->renderLink();
}
$props['Parents'] = phutil_implode_html(" \xC2\xB7 ", $parent_links);
}
$props['Branches'] = phutil_tag(
'span',
array(
'id' => 'commit-branches',
),
pht('Unknown'));
$props['Tags'] = phutil_tag(
'span',
array(
'id' => 'commit-tags',
),
pht('Unknown'));
$callsign = $repository->getCallsign();
$root = '/diffusion/'.$callsign.'/commit/'.$commit->getCommitIdentifier();
Javelin::initBehavior(
'diffusion-commit-branches',
array(
$root.'/branches/' => 'commit-branches',
$root.'/tags/' => 'commit-tags',
));
$refs = $this->buildRefs($drequest);
if ($refs) {
$props['References'] = $refs;
}
if ($reverts_phids) {
- $this->loadHandles($reverts_phids);
- $props[pht('Reverts')] = $this->renderHandlesForPHIDs($reverts_phids);
+ $props[pht('Reverts')] = $viewer->renderHandleList($reverts_phids);
}
if ($reverted_by_phids) {
- $this->loadHandles($reverted_by_phids);
- $props[pht('Reverted By')] = $this->renderHandlesForPHIDs(
+ $props[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);
$props['Tasks'] = $task_list;
}
return $props;
}
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();
$user = $request->getUser();
if (!$user->isLoggedIn()) {
return id(new PhabricatorApplicationTransactionCommentView())
->setUser($user)
->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',
$user->getPHID(),
'diffusion-audit-'.$commit->getID());
if ($draft) {
$draft = $draft->getDraft();
} else {
$draft = null;
}
$actions = $this->getAuditActions($commit, $audit_requests);
$form = id(new AphrontFormView())
->setUser($user)
->setAction('/audit/addcomment/')
->addHiddenInput('commit', $commit->getPHID())
->appendChild(
id(new AphrontFormSelectControl())
->setLabel(pht('Action'))
->setName('action')
->setID('audit-action')
->setOptions($actions))
->appendChild(
id(new AphrontFormTokenizerControl())
->setLabel(pht('Add Auditors'))
->setName('auditors')
->setControlID('add-auditors')
->setControlStyle('display: none')
->setID('add-auditors-tokenizer')
->setDisableBehavior(true))
->appendChild(
id(new AphrontFormTokenizerControl())
->setLabel(pht('Add CCs'))
->setName('ccs')
->setControlID('add-ccs')
->setControlStyle('display: none')
->setID('add-ccs-tokenizer')
->setDisableBehavior(true))
->appendChild(
id(new PhabricatorRemarkupControl())
->setLabel(pht('Comments'))
->setName('content')
->setValue($draft)
->setID('audit-content')
->setUser($user))
->appendChild(
id(new AphrontFormSubmitControl())
->setValue(pht('Submit')));
$header = new PHUIHeaderView();
$header->setHeader(
$is_serious ? pht('Audit Commit') : pht('Creative Accounting'));
$mailable_source = new PhabricatorMetaMTAMailableDatasource();
$auditor_source = new DiffusionAuditorDatasource();
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');
$user = $this->getRequest()->getUser();
$user_is_author = ($commit->getAuthorPHID() == $user->getPHID());
$user_request = null;
foreach ($audit_requests as $audit_request) {
if ($audit_request->getAuditorPHID() == $user->getPHID()) {
$user_request = $audit_request;
break;
}
}
$actions = array();
$actions[PhabricatorAuditActionConstants::COMMENT] = true;
$actions[PhabricatorAuditActionConstants::ADD_CCS] = true;
$actions[PhabricatorAuditActionConstants::ADD_AUDITORS] = 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;
}
foreach ($actions as $constant => $ignored) {
$actions[$constant] =
PhabricatorAuditActionConstants::getActionName($constant);
}
return $actions;
}
private function buildMergesTable(PhabricatorRepositoryCommit $commit) {
$drequest = $this->getDiffusionRequest();
$repository = $drequest->getRepository();
$vcs = $repository->getVersionControlSystem();
switch ($vcs) {
case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
// These aren't supported under SVN.
return null;
}
$limit = 50;
$merges = $this->callConduitWithDiffusionRequest(
'diffusion.mergedcommitsquery',
array(
'commit' => $drequest->getCommit(),
'limit' => $limit + 1,
));
if (!$merges) {
return null;
}
$merges = DiffusionPathChange::newFromConduit($merges);
$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 = new DiffusionHistoryTableView();
$history_table->setUser($this->getRequest()->getUser());
$history_table->setDiffusionRequest($drequest);
$history_table->setHistory($merges);
$history_table->loadRevisions();
$phids = $history_table->getRequiredHandlePHIDs();
$handles = $this->loadViewerHandles($phids);
$history_table->setHandles($handles);
$panel = new PHUIObjectBoxView();
$panel->setHeaderText(pht('Merged Changes'));
$panel->appendChild($history_table);
if ($caption) {
$panel->setInfoView($caption);
}
return $panel;
}
private function renderHeadsupActionList(
PhabricatorRepositoryCommit $commit,
PhabricatorRepository $repository) {
$request = $this->getRequest();
$user = $request->getUser();
$actions = id(new PhabricatorActionListView())
->setUser($user)
->setObject($commit)
->setObjectURI($request->getRequestURI());
$can_edit = PhabricatorPolicyFilter::hasCapability(
$user,
$commit,
PhabricatorPolicyCapability::CAN_EDIT);
$uri = '/diffusion/'.$repository->getCallsign().'/commit/'.
$commit->getCommitIdentifier().'/edit/';
$action = id(new PhabricatorActionView())
->setName(pht('Edit Commit'))
->setHref($uri)
->setIcon('fa-pencil')
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit);
$actions->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);
$actions->addAction($action);
}
$action = id(new PhabricatorActionView())
->setName(pht('Download Raw Diff'))
->setHref($request->getRequestURI()->alter('diff', true))
->setIcon('fa-download');
$actions->addAction($action);
return $actions;
}
private function buildRefs(DiffusionRequest $request) {
// this is git-only, so save a conduit round trip and just get out of
// here if the repository isn't git
$type_git = PhabricatorRepositoryType::REPOSITORY_TYPE_GIT;
$repository = $request->getRepository();
if ($repository->getVersionControlSystem() != $type_git) {
return null;
}
$results = $this->callConduitWithDiffusionRequest(
'diffusion.refsquery',
array('commit' => $request->getCommit()));
$ref_links = array();
foreach ($results as $ref_data) {
$ref_links[] = phutil_tag('a',
array('href' => $ref_data['href']),
$ref_data['ref']);
}
return phutil_implode_html(', ', $ref_links);
}
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');
$phids = mpull($audit_requests, 'getAuditorPHID');
$this->loadHandles($phids);
$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 = $this->getHandle($auditor_phid)->renderLink();
$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);
}
}
diff --git a/src/applications/diffusion/controller/DiffusionRepositoryController.php b/src/applications/diffusion/controller/DiffusionRepositoryController.php
index 4bd8ba1dc9..9b1936ca0e 100644
--- a/src/applications/diffusion/controller/DiffusionRepositoryController.php
+++ b/src/applications/diffusion/controller/DiffusionRepositoryController.php
@@ -1,712 +1,711 @@
<?php
final class DiffusionRepositoryController extends DiffusionController {
public function shouldAllowPublic() {
return true;
}
protected function processDiffusionRequest(AphrontRequest $request) {
$viewer = $request->getUser();
$drequest = $this->getDiffusionRequest();
$repository = $drequest->getRepository();
$content = array();
$crumbs = $this->buildCrumbs();
$content[] = $crumbs;
$content[] = $this->buildPropertiesTable($drequest->getRepository());
// 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));
}
return $this->buildApplicationPage(
$content,
array(
'title' => $drequest->getRepository()->getName(),
));
}
private function buildNormalContent(DiffusionRequest $drequest) {
$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;
}
try {
$browse_results = DiffusionBrowseResultSet::newFromConduit(
$this->callConduitWithDiffusionRequest(
'diffusion.browsequery',
array(
'path' => $drequest->getPath(),
'commit' => $drequest->getCommit(),
)));
$browse_paths = $browse_results->getPaths();
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);
$readme = null;
if ($browse_results) {
$readme_path = $browse_results->getReadmePath();
if ($readme_path) {
$readme_content = $this->callConduitWithDiffusionRequest(
'diffusion.filecontentquery',
array(
'path' => $readme_path,
'commit' => $drequest->getStableCommit(),
));
if ($readme_content) {
$readme = id(new DiffusionReadmeView())
->setUser($this->getViewer())
->setPath($readme_path)
->setContent($readme_content['corpus']);
}
}
}
$content[] = $this->buildBrowseTable(
$browse_results,
$browse_paths,
$browse_exception,
$handles);
$content[] = $this->buildHistoryTable(
$history_results,
$history,
$history_exception,
$handles);
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 buildPropertiesTable(PhabricatorRepository $repository) {
$user = $this->getRequest()->getUser();
$header = id(new PHUIHeaderView())
->setHeader($repository->getName())
->setUser($user)
->setPolicyObject($repository);
if (!$repository->isTracked()) {
$header->setStatus('fa-ban', 'dark', pht('Inactive'));
} else if ($repository->isImporting()) {
$header->setStatus('fa-clock-o', 'indigo', pht('Importing...'));
} else {
$header->setStatus('fa-check', 'bluegrey', pht('Active'));
}
$actions = $this->buildActionList($repository);
$view = id(new PHUIPropertyListView())
->setUser($user);
$project_phids = PhabricatorEdgeQuery::loadDestinationPHIDs(
$repository->getPHID(),
PhabricatorProjectObjectHasProjectEdgeType::EDGECONST);
if ($project_phids) {
- $this->loadHandles($project_phids);
$view->addProperty(
pht('Projects'),
- $this->renderHandlesForPHIDs($project_phids));
+ $user->renderHandleList($project_phids));
}
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;
}
}
$description = $repository->getDetail('description');
if (strlen($description)) {
$description = PhabricatorMarkupEngine::renderOneObject(
$repository,
'description',
$user);
$view->addSectionHeader(pht('Description'));
$view->addTextContent($description);
}
$view->setActionList($actions);
return id(new PHUIObjectBoxView())
->setHeader($header)
->addPropertyList($view);
}
private function buildBranchListTable(DiffusionRequest $drequest) {
$viewer = $this->getRequest()->getUser();
if ($drequest->getBranch() === null) {
return null;
}
$limit = 15;
$branches = $this->callConduitWithDiffusionRequest(
'diffusion.branchquery',
array(
'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 = new PHUIObjectBoxView();
$header = new PHUIHeaderView();
$header->setHeader(pht('Branches'));
if ($more_branches) {
$header->setSubHeader(pht('Showing %d branches.', $limit));
}
$icon = id(new PHUIIconView())
->setIconFont('fa-code-fork');
$button = new PHUIButtonView();
$button->setText(pht('Show All Branches'));
$button->setTag('a');
$button->setIcon($icon);
$button->setHref($drequest->generateURI(
array(
'action' => 'branches',
)));
$header->addActionLink($button);
$panel->setHeader($header);
$panel->appendChild($table);
return $panel;
}
private function buildTagListTable(DiffusionRequest $drequest) {
$viewer = $this->getRequest()->getUser();
$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));
}
$icon = id(new PHUIIconView())
->setIconFont('fa-tag');
$button = new PHUIButtonView();
$button->setText(pht('Show All Tags'));
$button->setTag('a');
$button->setIcon($icon);
$button->setHref($drequest->generateURI(
array(
'action' => 'tags',
)));
$header->addActionLink($button);
$panel->setHeader($header);
$panel->appendChild($view);
return $panel;
}
private function buildActionList(PhabricatorRepository $repository) {
$viewer = $this->getRequest()->getUser();
$view_uri = $this->getApplicationURI($repository->getCallsign().'/');
$edit_uri = $this->getApplicationURI($repository->getCallsign().'/edit/');
$view = id(new PhabricatorActionListView())
->setUser($viewer)
->setObject($repository)
->setObjectURI($view_uri);
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$repository,
PhabricatorPolicyCapability::CAN_EDIT);
$view->addAction(
id(new PhabricatorActionView())
->setName(pht('Edit Repository'))
->setIcon('fa-pencil')
->setHref($edit_uri)
->setWorkflow(!$can_edit)
->setDisabled(!$can_edit));
if ($repository->isHosted()) {
$callsign = $repository->getCallsign();
$push_uri = $this->getApplicationURI(
'pushlog/?repositories=r'.$callsign);
$view->addAction(
id(new PhabricatorActionView())
->setName(pht('View Push Logs'))
->setIcon('fa-list-alt')
->setHref($push_uri));
}
return $view;
}
private function buildHistoryTable(
$history_results,
$history,
$history_exception,
array $handles) {
$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)
->setHandles($handles)
->setHistory($history);
// TODO: Super sketchy.
$history_table->loadRevisions();
if ($history_results) {
$history_table->setParents($history_results['parents']);
}
$history_table->setIsHead(true);
$callsign = $drequest->getRepository()->getCallsign();
$icon = id(new PHUIIconView())
->setIconFont('fa-list-alt');
$button = id(new PHUIButtonView())
->setText(pht('View Full History'))
->setHref($drequest->generateURI(
array(
'action' => 'history',
)))
->setTag('a')
->setIcon($icon);
$panel = new PHUIObjectBoxView();
$header = id(new PHUIHeaderView())
->setHeader(pht('Recent Commits'))
->addActionLink($button);
$panel->setHeader($header);
$panel->appendChild($history_table);
return $panel;
}
private function buildBrowseTable(
$browse_results,
$browse_paths,
$browse_exception,
array $handles) {
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 = new PHUIObjectBoxView();
$header = id(new PHUIHeaderView())
->setHeader(pht('Repository'));
$icon = id(new PHUIIconView())
->setIconFont('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);
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())
->addClass('diffusion-locate-file-view')
->appendChild($form->buildLayoutView());
$browse_panel->appendChild($form_box);
}
$browse_panel->appendChild($browse_table);
return $browse_panel;
}
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/diffusion/controller/DiffusionRepositoryEditMainController.php b/src/applications/diffusion/controller/DiffusionRepositoryEditMainController.php
index 0b270998f0..d19a7ebeb8 100644
--- a/src/applications/diffusion/controller/DiffusionRepositoryEditMainController.php
+++ b/src/applications/diffusion/controller/DiffusionRepositoryEditMainController.php
@@ -1,1203 +1,1200 @@
<?php
final class DiffusionRepositoryEditMainController
extends DiffusionRepositoryEditController {
protected function processDiffusionRequest(AphrontRequest $request) {
$viewer = $request->getUser();
$drequest = $this->diffusionRequest;
$repository = $drequest->getRepository();
PhabricatorPolicyFilter::requireCapability(
$viewer,
$repository,
PhabricatorPolicyCapability::CAN_EDIT);
$is_svn = false;
$is_git = false;
$is_hg = false;
switch ($repository->getVersionControlSystem()) {
case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
$is_git = true;
break;
case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
$is_svn = true;
break;
case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
$is_hg = true;
break;
}
$has_branches = ($is_git || $is_hg);
$has_local = $repository->usesLocalWorkingCopy();
$crumbs = $this->buildApplicationCrumbs($is_main = true);
$title = pht('Edit %s', $repository->getName());
$header = id(new PHUIHeaderView())
->setHeader($title);
if ($repository->isTracked()) {
$header->setStatus('fa-check', 'bluegrey', pht('Active'));
} else {
$header->setStatus('fa-ban', 'dark', pht('Inactive'));
}
$basic_actions = $this->buildBasicActions($repository);
$basic_properties =
$this->buildBasicProperties($repository, $basic_actions);
$policy_actions = $this->buildPolicyActions($repository);
$policy_properties =
$this->buildPolicyProperties($repository, $policy_actions);
$remote_properties = null;
if (!$repository->isHosted()) {
$remote_properties = $this->buildRemoteProperties(
$repository,
$this->buildRemoteActions($repository));
}
$encoding_actions = $this->buildEncodingActions($repository);
$encoding_properties =
$this->buildEncodingProperties($repository, $encoding_actions);
$hosting_properties = $this->buildHostingProperties(
$repository,
$this->buildHostingActions($repository));
$branches_properties = null;
if ($has_branches) {
$branches_properties = $this->buildBranchesProperties(
$repository,
$this->buildBranchesActions($repository));
}
$subversion_properties = null;
if ($is_svn) {
$subversion_properties = $this->buildSubversionProperties(
$repository,
$this->buildSubversionActions($repository));
}
$storage_properties = null;
if ($has_local) {
$storage_properties = $this->buildStorageProperties(
$repository,
$this->buildStorageActions($repository));
}
$actions_properties = $this->buildActionsProperties(
$repository,
$this->buildActionsActions($repository));
$timeline = $this->buildTransactionTimeline(
$repository,
new PhabricatorRepositoryTransactionQuery());
$timeline->setShouldTerminate(true);
$boxes = array();
$boxes[] = id(new PHUIObjectBoxView())
->setHeader($header)
->addPropertyList($basic_properties);
$boxes[] = id(new PHUIObjectBoxView())
->setHeaderText(pht('Policies'))
->addPropertyList($policy_properties);
$boxes[] = id(new PHUIObjectBoxView())
->setHeaderText(pht('Hosting'))
->addPropertyList($hosting_properties);
if ($repository->canMirror()) {
$mirror_actions = $this->buildMirrorActions($repository);
$mirror_properties = $this->buildMirrorProperties(
$repository,
$mirror_actions);
$mirrors = id(new PhabricatorRepositoryMirrorQuery())
->setViewer($viewer)
->withRepositoryPHIDs(array($repository->getPHID()))
->execute();
$mirror_list = $this->buildMirrorList($repository, $mirrors);
$boxes[] = id(new PhabricatorAnchorView())->setAnchorName('mirrors');
$mirror_info = array();
if (PhabricatorEnv::getEnvConfig('phabricator.silent')) {
$mirror_info[] = pht(
'Phabricator is running in silent mode, so changes will not '.
'be pushed to mirrors.');
}
$boxes[] = id(new PHUIObjectBoxView())
->setFormErrors($mirror_info)
->setHeaderText(pht('Mirrors'))
->addPropertyList($mirror_properties);
$boxes[] = $mirror_list;
}
if ($remote_properties) {
$boxes[] = id(new PHUIObjectBoxView())
->setHeaderText(pht('Remote'))
->addPropertyList($remote_properties);
}
if ($storage_properties) {
$boxes[] = id(new PHUIObjectBoxView())
->setHeaderText(pht('Storage'))
->addPropertyList($storage_properties);
}
$boxes[] = id(new PHUIObjectBoxView())
->setHeaderText(pht('Text Encoding'))
->addPropertyList($encoding_properties);
if ($branches_properties) {
$boxes[] = id(new PHUIObjectBoxView())
->setHeaderText(pht('Branches'))
->addPropertyList($branches_properties);
}
if ($subversion_properties) {
$boxes[] = id(new PHUIObjectBoxView())
->setHeaderText(pht('Subversion'))
->addPropertyList($subversion_properties);
}
$boxes[] = id(new PHUIObjectBoxView())
->setHeaderText(pht('Actions'))
->addPropertyList($actions_properties);
return $this->buildApplicationPage(
array(
$crumbs,
$boxes,
$timeline,
),
array(
'title' => $title,
));
}
private function buildBasicActions(PhabricatorRepository $repository) {
$viewer = $this->getRequest()->getUser();
$view = id(new PhabricatorActionListView())
->setObjectURI($this->getRequest()->getRequestURI())
->setUser($viewer);
$edit = id(new PhabricatorActionView())
->setIcon('fa-pencil')
->setName(pht('Edit Basic Information'))
->setHref($this->getRepositoryControllerURI($repository, 'edit/basic/'));
$view->addAction($edit);
$edit = id(new PhabricatorActionView())
->setIcon('fa-refresh')
->setName(pht('Update Now'))
->setWorkflow(true)
->setHref(
$this->getRepositoryControllerURI($repository, 'edit/update/'));
$view->addAction($edit);
$activate = id(new PhabricatorActionView())
->setHref(
$this->getRepositoryControllerURI($repository, 'edit/activate/'))
->setWorkflow(true);
if ($repository->isTracked()) {
$activate
->setIcon('fa-pause')
->setName(pht('Deactivate Repository'));
} else {
$activate
->setIcon('fa-play')
->setName(pht('Activate Repository'));
}
$view->addAction($activate);
$view->addAction(
id(new PhabricatorActionView())
->setName(pht('Delete Repository'))
->setIcon('fa-times')
->setHref(
$this->getRepositoryControllerURI($repository, 'edit/delete/'))
->setDisabled(true)
->setWorkflow(true));
return $view;
}
private function buildBasicProperties(
PhabricatorRepository $repository,
PhabricatorActionListView $actions) {
$viewer = $this->getRequest()->getUser();
$view = id(new PHUIPropertyListView())
->setUser($viewer)
->setActionList($actions);
$type = PhabricatorRepositoryType::getNameForRepositoryType(
$repository->getVersionControlSystem());
$view->addProperty(pht('Type'), $type);
$view->addProperty(pht('Callsign'), $repository->getCallsign());
$clone_name = $repository->getDetail('clone-name');
if ($repository->isHosted()) {
$view->addProperty(
pht('Clone/Checkout As'),
$clone_name
? $clone_name.'/'
: phutil_tag('em', array(), $repository->getCloneName().'/'));
}
$project_phids = PhabricatorEdgeQuery::loadDestinationPHIDs(
$repository->getPHID(),
PhabricatorProjectObjectHasProjectEdgeType::EDGECONST);
if ($project_phids) {
- $this->loadHandles($project_phids);
- $project_text = $this->renderHandlesForPHIDs($project_phids);
+ $project_text = $viewer->renderHandleList($project_phids);
} else {
$project_text = phutil_tag('em', array(), pht('None'));
}
$view->addProperty(
pht('Projects'),
$project_text);
$view->addProperty(
pht('Status'),
$this->buildRepositoryStatus($repository));
$view->addProperty(
pht('Update Frequency'),
$this->buildRepositoryUpdateInterval($repository));
$description = $repository->getDetail('description');
$view->addSectionHeader(pht('Description'));
if (!strlen($description)) {
$description = phutil_tag('em', array(), pht('No description provided.'));
} else {
$description = PhabricatorMarkupEngine::renderOneObject(
$repository,
'description',
$viewer);
}
$view->addTextContent($description);
return $view;
}
private function buildEncodingActions(PhabricatorRepository $repository) {
$viewer = $this->getRequest()->getUser();
$view = id(new PhabricatorActionListView())
->setObjectURI($this->getRequest()->getRequestURI())
->setUser($viewer);
$edit = id(new PhabricatorActionView())
->setIcon('fa-pencil')
->setName(pht('Edit Text Encoding'))
->setHref(
$this->getRepositoryControllerURI($repository, 'edit/encoding/'));
$view->addAction($edit);
return $view;
}
private function buildEncodingProperties(
PhabricatorRepository $repository,
PhabricatorActionListView $actions) {
$viewer = $this->getRequest()->getUser();
$view = id(new PHUIPropertyListView())
->setUser($viewer)
->setActionList($actions);
$encoding = $repository->getDetail('encoding');
if (!$encoding) {
$encoding = phutil_tag('em', array(), pht('Use Default (UTF-8)'));
}
$view->addProperty(pht('Encoding'), $encoding);
return $view;
}
private function buildPolicyActions(PhabricatorRepository $repository) {
$viewer = $this->getRequest()->getUser();
$view = id(new PhabricatorActionListView())
->setObjectURI($this->getRequest()->getRequestURI())
->setUser($viewer);
$edit = id(new PhabricatorActionView())
->setIcon('fa-pencil')
->setName(pht('Edit Policies'))
->setHref(
$this->getRepositoryControllerURI($repository, 'edit/policy/'));
$view->addAction($edit);
return $view;
}
private function buildPolicyProperties(
PhabricatorRepository $repository,
PhabricatorActionListView $actions) {
$viewer = $this->getRequest()->getUser();
$view = id(new PHUIPropertyListView())
->setUser($viewer)
->setActionList($actions);
$descriptions = PhabricatorPolicyQuery::renderPolicyDescriptions(
$viewer,
$repository);
$view->addProperty(
pht('Visible To'),
$descriptions[PhabricatorPolicyCapability::CAN_VIEW]);
$view->addProperty(
pht('Editable By'),
$descriptions[PhabricatorPolicyCapability::CAN_EDIT]);
$pushable = $repository->isHosted()
? $descriptions[DiffusionPushCapability::CAPABILITY]
: phutil_tag('em', array(), pht('Not a Hosted Repository'));
$view->addProperty(pht('Pushable By'), $pushable);
return $view;
}
private function buildBranchesActions(PhabricatorRepository $repository) {
$viewer = $this->getRequest()->getUser();
$view = id(new PhabricatorActionListView())
->setObjectURI($this->getRequest()->getRequestURI())
->setUser($viewer);
$edit = id(new PhabricatorActionView())
->setIcon('fa-pencil')
->setName(pht('Edit Branches'))
->setHref(
$this->getRepositoryControllerURI($repository, 'edit/branches/'));
$view->addAction($edit);
return $view;
}
private function buildBranchesProperties(
PhabricatorRepository $repository,
PhabricatorActionListView $actions) {
$viewer = $this->getRequest()->getUser();
$view = id(new PHUIPropertyListView())
->setUser($viewer)
->setActionList($actions);
$default_branch = nonempty(
$repository->getHumanReadableDetail('default-branch'),
phutil_tag('em', array(), $repository->getDefaultBranch()));
$view->addProperty(pht('Default Branch'), $default_branch);
$track_only = nonempty(
$repository->getHumanReadableDetail('branch-filter', array()),
phutil_tag('em', array(), pht('Track All Branches')));
$view->addProperty(pht('Track Only'), $track_only);
$autoclose_only = nonempty(
$repository->getHumanReadableDetail('close-commits-filter', array()),
phutil_tag('em', array(), pht('Autoclose On All Branches')));
if ($repository->getDetail('disable-autoclose')) {
$autoclose_only = phutil_tag('em', array(), pht('Disabled'));
}
$view->addProperty(pht('Autoclose Only'), $autoclose_only);
return $view;
}
private function buildSubversionActions(PhabricatorRepository $repository) {
$viewer = $this->getRequest()->getUser();
$view = id(new PhabricatorActionListView())
->setObjectURI($this->getRequest()->getRequestURI())
->setUser($viewer);
$edit = id(new PhabricatorActionView())
->setIcon('fa-pencil')
->setName(pht('Edit Subversion Info'))
->setHref(
$this->getRepositoryControllerURI($repository, 'edit/subversion/'));
$view->addAction($edit);
return $view;
}
private function buildSubversionProperties(
PhabricatorRepository $repository,
PhabricatorActionListView $actions) {
$viewer = $this->getRequest()->getUser();
$view = id(new PHUIPropertyListView())
->setUser($viewer)
->setActionList($actions);
$svn_uuid = nonempty(
$repository->getUUID(),
phutil_tag('em', array(), pht('Not Configured')));
$view->addProperty(pht('Subversion UUID'), $svn_uuid);
$svn_subpath = nonempty(
$repository->getHumanReadableDetail('svn-subpath'),
phutil_tag('em', array(), pht('Import Entire Repository')));
$view->addProperty(pht('Import Only'), $svn_subpath);
return $view;
}
private function buildActionsActions(PhabricatorRepository $repository) {
$viewer = $this->getRequest()->getUser();
$view = id(new PhabricatorActionListView())
->setObjectURI($this->getRequest()->getRequestURI())
->setUser($viewer);
$edit = id(new PhabricatorActionView())
->setIcon('fa-pencil')
->setName(pht('Edit Actions'))
->setHref(
$this->getRepositoryControllerURI($repository, 'edit/actions/'));
$view->addAction($edit);
return $view;
}
private function buildActionsProperties(
PhabricatorRepository $repository,
PhabricatorActionListView $actions) {
$viewer = $this->getRequest()->getUser();
$view = id(new PHUIPropertyListView())
->setUser($viewer)
->setActionList($actions);
$notify = $repository->getDetail('herald-disabled')
? pht('Off')
: pht('On');
$notify = phutil_tag('em', array(), $notify);
$view->addProperty(pht('Publish/Notify'), $notify);
$autoclose = $repository->getDetail('disable-autoclose')
? pht('Off')
: pht('On');
$autoclose = phutil_tag('em', array(), $autoclose);
$view->addProperty(pht('Autoclose'), $autoclose);
return $view;
}
private function buildRemoteActions(PhabricatorRepository $repository) {
$viewer = $this->getRequest()->getUser();
$view = id(new PhabricatorActionListView())
->setObjectURI($this->getRequest()->getRequestURI())
->setUser($viewer);
$edit = id(new PhabricatorActionView())
->setIcon('fa-pencil')
->setName(pht('Edit Remote'))
->setHref(
$this->getRepositoryControllerURI($repository, 'edit/remote/'));
$view->addAction($edit);
return $view;
}
private function buildRemoteProperties(
PhabricatorRepository $repository,
PhabricatorActionListView $actions) {
$viewer = $this->getRequest()->getUser();
$view = id(new PHUIPropertyListView())
->setUser($viewer)
->setActionList($actions);
$view->addProperty(
pht('Remote URI'),
$repository->getHumanReadableDetail('remote-uri'));
$credential_phid = $repository->getCredentialPHID();
if ($credential_phid) {
- $this->loadHandles(array($credential_phid));
$view->addProperty(
pht('Credential'),
- $this->getHandle($credential_phid)->renderLink());
+ $viewer->renderHandle($credential_phid));
}
return $view;
}
private function buildStorageActions(PhabricatorRepository $repository) {
$viewer = $this->getRequest()->getUser();
$view = id(new PhabricatorActionListView())
->setObjectURI($this->getRequest()->getRequestURI())
->setUser($viewer);
$edit = id(new PhabricatorActionView())
->setIcon('fa-pencil')
->setName(pht('Edit Storage'))
->setHref(
$this->getRepositoryControllerURI($repository, 'edit/storage/'));
$view->addAction($edit);
return $view;
}
private function buildStorageProperties(
PhabricatorRepository $repository,
PhabricatorActionListView $actions) {
$viewer = $this->getRequest()->getUser();
$view = id(new PHUIPropertyListView())
->setUser($viewer)
->setActionList($actions);
$service_phid = $repository->getAlmanacServicePHID();
if ($service_phid) {
- $handles = $this->loadViewerHandles(array($service_phid));
- $v_service = $handles[$service_phid]->renderLink();
+ $v_service = $viewer->renderHandle($service_phid);
} else {
$v_service = phutil_tag(
'em',
array(),
pht('Local'));
}
$view->addProperty(
pht('Storage Service'),
$v_service);
$view->addProperty(
pht('Storage Path'),
$repository->getHumanReadableDetail('local-path'));
return $view;
}
private function buildHostingActions(PhabricatorRepository $repository) {
$user = $this->getRequest()->getUser();
$view = id(new PhabricatorActionListView())
->setObjectURI($this->getRequest()->getRequestURI())
->setUser($user);
$edit = id(new PhabricatorActionView())
->setIcon('fa-pencil')
->setName(pht('Edit Hosting'))
->setHref(
$this->getRepositoryControllerURI($repository, 'edit/hosting/'));
$view->addAction($edit);
if ($repository->canAllowDangerousChanges()) {
if ($repository->shouldAllowDangerousChanges()) {
$changes = id(new PhabricatorActionView())
->setIcon('fa-shield')
->setName(pht('Prevent Dangerous Changes'))
->setHref(
$this->getRepositoryControllerURI($repository, 'edit/dangerous/'))
->setWorkflow(true);
} else {
$changes = id(new PhabricatorActionView())
->setIcon('fa-bullseye')
->setName(pht('Allow Dangerous Changes'))
->setHref(
$this->getRepositoryControllerURI($repository, 'edit/dangerous/'))
->setWorkflow(true);
}
$view->addAction($changes);
}
return $view;
}
private function buildHostingProperties(
PhabricatorRepository $repository,
PhabricatorActionListView $actions) {
$user = $this->getRequest()->getUser();
$view = id(new PHUIPropertyListView())
->setUser($user)
->setActionList($actions);
$hosting = $repository->isHosted()
? pht('Hosted on Phabricator')
: pht('Hosted Elsewhere');
$view->addProperty(pht('Hosting'), phutil_tag('em', array(), $hosting));
$view->addProperty(
pht('Serve over HTTP'),
phutil_tag(
'em',
array(),
PhabricatorRepository::getProtocolAvailabilityName(
$repository->getServeOverHTTP())));
$view->addProperty(
pht('Serve over SSH'),
phutil_tag(
'em',
array(),
PhabricatorRepository::getProtocolAvailabilityName(
$repository->getServeOverSSH())));
if ($repository->canAllowDangerousChanges()) {
if ($repository->shouldAllowDangerousChanges()) {
$description = pht('Allowed');
} else {
$description = pht('Not Allowed');
}
$view->addProperty(
pht('Dangerous Changes'),
$description);
}
return $view;
}
private function buildRepositoryStatus(
PhabricatorRepository $repository) {
$viewer = $this->getRequest()->getUser();
$is_cluster = $repository->getAlmanacServicePHID();
$view = new PHUIStatusListView();
$messages = id(new PhabricatorRepositoryStatusMessage())
->loadAllWhere('repositoryID = %d', $repository->getID());
$messages = mpull($messages, null, 'getStatusType');
if ($repository->isTracked()) {
$view->addItem(
id(new PHUIStatusItemView())
->setIcon(PHUIStatusItemView::ICON_ACCEPT, 'green')
->setTarget(pht('Repository Active')));
} else {
$view->addItem(
id(new PHUIStatusItemView())
->setIcon(PHUIStatusItemView::ICON_WARNING, 'bluegrey')
->setTarget(pht('Repository Inactive'))
->setNote(
pht('Activate this repository to begin or resume import.')));
return $view;
}
$binaries = array();
$svnlook_check = false;
switch ($repository->getVersionControlSystem()) {
case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
$binaries[] = 'git';
break;
case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
$binaries[] = 'svn';
break;
case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
$binaries[] = 'hg';
break;
}
if ($repository->isHosted()) {
if ($repository->getServeOverHTTP() != PhabricatorRepository::SERVE_OFF) {
switch ($repository->getVersionControlSystem()) {
case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
$binaries[] = 'git-http-backend';
break;
case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
$binaries[] = 'svnserve';
$binaries[] = 'svnadmin';
$binaries[] = 'svnlook';
$svnlook_check = true;
break;
case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
$binaries[] = 'hg';
break;
}
}
if ($repository->getServeOverSSH() != PhabricatorRepository::SERVE_OFF) {
switch ($repository->getVersionControlSystem()) {
case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
$binaries[] = 'git-receive-pack';
$binaries[] = 'git-upload-pack';
break;
case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
$binaries[] = 'svnserve';
$binaries[] = 'svnadmin';
$binaries[] = 'svnlook';
$svnlook_check = true;
break;
case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
$binaries[] = 'hg';
break;
}
}
}
$binaries = array_unique($binaries);
if (!$is_cluster) {
// We're only checking for binaries if we aren't running with a cluster
// configuration. In theory, we could check for binaries on the
// repository host machine, but we'd need to make this more complicated
// to do that.
foreach ($binaries as $binary) {
$where = Filesystem::resolveBinary($binary);
if (!$where) {
$view->addItem(
id(new PHUIStatusItemView())
->setIcon(PHUIStatusItemView::ICON_WARNING, 'red')
->setTarget(
pht('Missing Binary %s', phutil_tag('tt', array(), $binary)))
->setNote(pht(
"Unable to find this binary in the webserver's PATH. You may ".
"need to configure %s.",
$this->getEnvConfigLink())));
} else {
$view->addItem(
id(new PHUIStatusItemView())
->setIcon(PHUIStatusItemView::ICON_ACCEPT, 'green')
->setTarget(
pht('Found Binary %s', phutil_tag('tt', array(), $binary)))
->setNote(phutil_tag('tt', array(), $where)));
}
}
// This gets checked generically above. However, for svn commit hooks, we
// need this to be in environment.append-paths because subversion strips
// PATH.
if ($svnlook_check) {
$where = Filesystem::resolveBinary('svnlook');
if ($where) {
$path = substr($where, 0, strlen($where) - strlen('svnlook'));
$dirs = PhabricatorEnv::getEnvConfig('environment.append-paths');
$in_path = false;
foreach ($dirs as $dir) {
if (Filesystem::isDescendant($path, $dir)) {
$in_path = true;
break;
}
}
if (!$in_path) {
$view->addItem(
id(new PHUIStatusItemView())
->setIcon(PHUIStatusItemView::ICON_WARNING, 'red')
->setTarget(
pht('Missing Binary %s', phutil_tag('tt', array(), $binary)))
->setNote(pht(
'Unable to find this binary in `environment.append-paths`. '.
'You need to configure %s and include %s.',
$this->getEnvConfigLink(),
$path)));
}
}
}
}
$doc_href = PhabricatorEnv::getDocLink('Managing Daemons with phd');
$daemon_instructions = pht(
'Use %s to start daemons. See %s.',
phutil_tag('tt', array(), 'bin/phd start'),
phutil_tag(
'a',
array(
'href' => $doc_href,
),
pht('Managing Daemons with phd')));
$pull_daemon = id(new PhabricatorDaemonLogQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withStatus(PhabricatorDaemonLogQuery::STATUS_ALIVE)
->withDaemonClasses(array('PhabricatorRepositoryPullLocalDaemon'))
->setLimit(1)
->execute();
if ($pull_daemon) {
// TODO: In a cluster environment, we need a daemon on this repository's
// host, specifically, and we aren't checking for that right now. This
// is a reasonable proxy for things being more-or-less correctly set up,
// though.
$view->addItem(
id(new PHUIStatusItemView())
->setIcon(PHUIStatusItemView::ICON_ACCEPT, 'green')
->setTarget(pht('Pull Daemon Running')));
} else {
$view->addItem(
id(new PHUIStatusItemView())
->setIcon(PHUIStatusItemView::ICON_WARNING, 'red')
->setTarget(pht('Pull Daemon Not Running'))
->setNote($daemon_instructions));
}
$task_daemon = id(new PhabricatorDaemonLogQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withStatus(PhabricatorDaemonLogQuery::STATUS_ALIVE)
->withDaemonClasses(array('PhabricatorTaskmasterDaemon'))
->setLimit(1)
->execute();
if ($task_daemon) {
$view->addItem(
id(new PHUIStatusItemView())
->setIcon(PHUIStatusItemView::ICON_ACCEPT, 'green')
->setTarget(pht('Task Daemon Running')));
} else {
$view->addItem(
id(new PHUIStatusItemView())
->setIcon(PHUIStatusItemView::ICON_WARNING, 'red')
->setTarget(pht('Task Daemon Not Running'))
->setNote($daemon_instructions));
}
if ($is_cluster) {
// Just omit this status check for now in cluster environments. We
// could make a service call and pull it from the repository host
// eventually.
} else if ($repository->usesLocalWorkingCopy()) {
$local_parent = dirname($repository->getLocalPath());
if (Filesystem::pathExists($local_parent)) {
$view->addItem(
id(new PHUIStatusItemView())
->setIcon(PHUIStatusItemView::ICON_ACCEPT, 'green')
->setTarget(pht('Storage Directory OK'))
->setNote(phutil_tag('tt', array(), $local_parent)));
} else {
$view->addItem(
id(new PHUIStatusItemView())
->setIcon(PHUIStatusItemView::ICON_WARNING, 'red')
->setTarget(pht('No Storage Directory'))
->setNote(
pht(
'Storage directory %s does not exist, or is not readable by '.
'the webserver. Create this directory or make it readable.',
phutil_tag('tt', array(), $local_parent))));
return $view;
}
$local_path = $repository->getLocalPath();
$message = idx($messages, PhabricatorRepositoryStatusMessage::TYPE_INIT);
if ($message) {
switch ($message->getStatusCode()) {
case PhabricatorRepositoryStatusMessage::CODE_ERROR:
$view->addItem(
id(new PHUIStatusItemView())
->setIcon(PHUIStatusItemView::ICON_WARNING, 'red')
->setTarget(pht('Initialization Error'))
->setNote($message->getParameter('message')));
return $view;
case PhabricatorRepositoryStatusMessage::CODE_OKAY:
if (Filesystem::pathExists($local_path)) {
$view->addItem(
id(new PHUIStatusItemView())
->setIcon(PHUIStatusItemView::ICON_ACCEPT, 'green')
->setTarget(pht('Working Copy OK'))
->setNote(phutil_tag('tt', array(), $local_path)));
} else {
$view->addItem(
id(new PHUIStatusItemView())
->setIcon(PHUIStatusItemView::ICON_WARNING, 'red')
->setTarget(pht('Working Copy Error'))
->setNote(
pht(
'Working copy %s has been deleted, or is not '.
'readable by the webserver. Make this directory '.
'readable. If it has been deleted, the daemons should '.
'restore it automatically.',
phutil_tag('tt', array(), $local_path))));
return $view;
}
break;
case PhabricatorRepositoryStatusMessage::CODE_WORKING:
$view->addItem(
id(new PHUIStatusItemView())
->setIcon(PHUIStatusItemView::ICON_CLOCK, 'green')
->setTarget(pht('Initializing Working Copy'))
->setNote(pht('Daemons are initializing the working copy.')));
return $view;
default:
$view->addItem(
id(new PHUIStatusItemView())
->setIcon(PHUIStatusItemView::ICON_WARNING, 'red')
->setTarget(pht('Unknown Init Status'))
->setNote($message->getStatusCode()));
return $view;
}
} else {
$view->addItem(
id(new PHUIStatusItemView())
->setIcon(PHUIStatusItemView::ICON_CLOCK, 'orange')
->setTarget(pht('No Working Copy Yet'))
->setNote(
pht('Waiting for daemons to build a working copy.')));
return $view;
}
}
$message = idx($messages, PhabricatorRepositoryStatusMessage::TYPE_FETCH);
if ($message) {
switch ($message->getStatusCode()) {
case PhabricatorRepositoryStatusMessage::CODE_ERROR:
$message = $message->getParameter('message');
$suggestion = null;
if (preg_match('/Permission denied \(publickey\)./', $message)) {
$suggestion = pht(
'Public Key Error: This error usually indicates that the '.
'keypair you have configured does not have permission to '.
'access the repository.');
}
$message = phutil_escape_html_newlines($message);
if ($suggestion !== null) {
$message = array(
phutil_tag('strong', array(), $suggestion),
phutil_tag('br'),
phutil_tag('br'),
phutil_tag('em', array(), pht('Raw Error')),
phutil_tag('br'),
$message,
);
}
$view->addItem(
id(new PHUIStatusItemView())
->setIcon(PHUIStatusItemView::ICON_WARNING, 'red')
->setTarget(pht('Update Error'))
->setNote($message));
return $view;
case PhabricatorRepositoryStatusMessage::CODE_OKAY:
$ago = (PhabricatorTime::getNow() - $message->getEpoch());
$view->addItem(
id(new PHUIStatusItemView())
->setIcon(PHUIStatusItemView::ICON_ACCEPT, 'green')
->setTarget(pht('Updates OK'))
->setNote(
pht(
'Last updated %s (%s ago).',
phabricator_datetime($message->getEpoch(), $viewer),
phutil_format_relative_time_detailed($ago))));
break;
}
} else {
$view->addItem(
id(new PHUIStatusItemView())
->setIcon(PHUIStatusItemView::ICON_CLOCK, 'orange')
->setTarget(pht('Waiting For Update'))
->setNote(
pht('Waiting for daemons to read updates.')));
}
if ($repository->isImporting()) {
$progress = queryfx_all(
$repository->establishConnection('r'),
'SELECT importStatus, count(*) N FROM %T WHERE repositoryID = %d
GROUP BY importStatus',
id(new PhabricatorRepositoryCommit())->getTableName(),
$repository->getID());
$done = 0;
$total = 0;
foreach ($progress as $row) {
$total += $row['N'] * 4;
$status = $row['importStatus'];
if ($status & PhabricatorRepositoryCommit::IMPORTED_MESSAGE) {
$done += $row['N'];
}
if ($status & PhabricatorRepositoryCommit::IMPORTED_CHANGE) {
$done += $row['N'];
}
if ($status & PhabricatorRepositoryCommit::IMPORTED_OWNERS) {
$done += $row['N'];
}
if ($status & PhabricatorRepositoryCommit::IMPORTED_HERALD) {
$done += $row['N'];
}
}
if ($total) {
$percentage = 100 * ($done / $total);
} else {
$percentage = 0;
}
// Cap this at "99.99%", because it's confusing to users when the actual
// fraction is "99.996%" and it rounds up to "100.00%".
if ($percentage > 99.99) {
$percentage = 99.99;
}
$percentage = sprintf('%.2f%%', $percentage);
$view->addItem(
id(new PHUIStatusItemView())
->setIcon(PHUIStatusItemView::ICON_CLOCK, 'green')
->setTarget(pht('Importing'))
->setNote(
pht('%s Complete', $percentage)));
} else {
$view->addItem(
id(new PHUIStatusItemView())
->setIcon(PHUIStatusItemView::ICON_ACCEPT, 'green')
->setTarget(pht('Fully Imported')));
}
if (idx($messages, PhabricatorRepositoryStatusMessage::TYPE_NEEDS_UPDATE)) {
$view->addItem(
id(new PHUIStatusItemView())
->setIcon(PHUIStatusItemView::ICON_UP, 'indigo')
->setTarget(pht('Prioritized'))
->setNote(pht('This repository will be updated soon!')));
}
return $view;
}
private function buildRepositoryUpdateInterval(
PhabricatorRepository $repository) {
$smart_wait = $repository->loadUpdateInterval();
$doc_href = PhabricatorEnv::getDoclink(
'Diffusion User Guide: Repository Updates');
return array(
phutil_format_relative_time_detailed($smart_wait),
" \xC2\xB7 ",
phutil_tag(
'a',
array(
'href' => $doc_href,
'target' => '_blank',
),
pht('Learn More')),
);
}
private function buildMirrorActions(
PhabricatorRepository $repository) {
$viewer = $this->getRequest()->getUser();
$mirror_actions = id(new PhabricatorActionListView())
->setObjectURI($this->getRequest()->getRequestURI())
->setUser($viewer);
$new_mirror_uri = $this->getRepositoryControllerURI(
$repository,
'mirror/edit/');
$mirror_actions->addAction(
id(new PhabricatorActionView())
->setName(pht('Add Mirror'))
->setIcon('fa-plus')
->setHref($new_mirror_uri)
->setWorkflow(true));
return $mirror_actions;
}
private function buildMirrorProperties(
PhabricatorRepository $repository,
PhabricatorActionListView $actions) {
$viewer = $this->getRequest()->getUser();
$mirror_properties = id(new PHUIPropertyListView())
->setUser($viewer)
->setActionList($actions);
$mirror_properties->addProperty(
'',
phutil_tag(
'em',
array(),
pht('Automatically push changes into other remotes.')));
return $mirror_properties;
}
private function buildMirrorList(
PhabricatorRepository $repository,
array $mirrors) {
assert_instances_of($mirrors, 'PhabricatorRepositoryMirror');
$mirror_list = id(new PHUIObjectItemListView())
->setNoDataString(pht('This repository has no configured mirrors.'));
foreach ($mirrors as $mirror) {
$item = id(new PHUIObjectItemView())
->setHeader($mirror->getRemoteURI());
$edit_uri = $this->getRepositoryControllerURI(
$repository,
'mirror/edit/'.$mirror->getID().'/');
$delete_uri = $this->getRepositoryControllerURI(
$repository,
'mirror/delete/'.$mirror->getID().'/');
$item->addAction(
id(new PHUIListItemView())
->setIcon('fa-pencil')
->setHref($edit_uri)
->setWorkflow(true));
$item->addAction(
id(new PHUIListItemView())
->setIcon('fa-times')
->setHref($delete_uri)
->setWorkflow(true));
$mirror_list->addItem($item);
}
return $mirror_list;
}
private function getEnvConfigLink() {
$config_href = '/config/edit/environment.append-paths/';
return phutil_tag(
'a',
array(
'href' => $config_href,
),
'environment.append-paths');
}
}
diff --git a/src/applications/phid/view/PHUIHandleListView.php b/src/applications/phid/view/PHUIHandleListView.php
index 4ea52ed58a..7ec0ce7606 100644
--- a/src/applications/phid/view/PHUIHandleListView.php
+++ b/src/applications/phid/view/PHUIHandleListView.php
@@ -1,51 +1,51 @@
<?php
/**
* Convenience class for rendering a list of handles.
*
* This class simplifies rendering a list of handles and improves loading and
* caching semantics in the rendering pipeline by delaying bulk loads until the
* last possible moment.
*/
final class PHUIHandleListView
extends AphrontTagView {
private $handleList;
- private $inline;
+ private $asInline;
public function setHandleList(PhabricatorHandleList $list) {
$this->handleList = $list;
return $this;
}
- public function setInline($inline) {
- $this->inline = $inline;
+ public function setAsInline($inline) {
+ $this->asInline = $inline;
return $this;
}
- public function getInline() {
- return $this->inline;
+ public function getAsInline() {
+ return $this->asInline;
}
protected function getTagName() {
// TODO: It would be nice to render this with a proper <ul />, at least in
// block mode, but don't stir the waters up too much for now.
return 'span';
}
protected function getTagContent() {
$items = array();
foreach ($this->handleList as $handle) {
$items[] = $handle->renderLink();
}
- if ($this->getInline()) {
+ if ($this->getAsInline()) {
$items = phutil_implode_html(', ', $items);
} else {
$items = phutil_implode_html(phutil_tag('br'), $items);
}
return $items;
}
}
diff --git a/src/applications/phid/view/PHUIHandleView.php b/src/applications/phid/view/PHUIHandleView.php
index daa66cd20b..db4b9e43ab 100644
--- a/src/applications/phid/view/PHUIHandleView.php
+++ b/src/applications/phid/view/PHUIHandleView.php
@@ -1,31 +1,42 @@
<?php
/**
* Convenience class for rendering a single handle.
*
* This class simplifies rendering a single handle, and improves loading and
* caching semantics in the rendering pipeline by loading data at the last
* moment.
*/
final class PHUIHandleView
extends AphrontView {
private $handleList;
private $handlePHID;
+ private $asTag;
public function setHandleList(PhabricatorHandleList $list) {
$this->handleList = $list;
return $this;
}
public function setHandlePHID($phid) {
$this->handlePHID = $phid;
return $this;
}
+ public function setAsTag($tag) {
+ $this->asTag = $tag;
+ return $this;
+ }
+
public function render() {
- return $this->handleList[$this->handlePHID]->renderLink();
+ $handle = $this->handleList[$this->handlePHID];
+ if ($this->asTag) {
+ return $handle->renderTag();
+ } else {
+ return $handle->renderLink();
+ }
}
}
diff --git a/src/applications/project/controller/PhabricatorProjectProfileController.php b/src/applications/project/controller/PhabricatorProjectProfileController.php
index 58551f2c64..9679ddab5c 100644
--- a/src/applications/project/controller/PhabricatorProjectProfileController.php
+++ b/src/applications/project/controller/PhabricatorProjectProfileController.php
@@ -1,224 +1,221 @@
<?php
final class PhabricatorProjectProfileController
extends PhabricatorProjectController {
public function shouldAllowPublic() {
return true;
}
public function handleRequest(AphrontRequest $request) {
$user = $request->getUser();
$query = id(new PhabricatorProjectQuery())
->setViewer($user)
->needMembers(true)
->needWatchers(true)
->needImages(true)
->needSlugs(true);
$id = $request->getURIData('id');
$slug = $request->getURIData('slug');
if ($slug) {
$query->withSlugs(array($slug));
} else {
$query->withIDs(array($id));
}
$project = $query->executeOne();
if (!$project) {
return new Aphront404Response();
}
if ($slug && $slug != $project->getPrimarySlug()) {
return id(new AphrontRedirectResponse())
->setURI('/tag/'.$project->getPrimarySlug().'/');
}
$picture = $project->getProfileImageURI();
$header = id(new PHUIHeaderView())
->setHeader($project->getName())
->setUser($user)
->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'));
}
$actions = $this->buildActionListView($project);
$properties = $this->buildPropertyListView($project, $actions);
$object_box = id(new PHUIObjectBoxView())
->setHeader($header)
->addPropertyList($properties);
$timeline = $this->buildTransactionTimeline(
$project,
new PhabricatorProjectTransactionQuery());
$timeline->setShouldTerminate(true);
$nav = $this->buildIconNavView($project);
$nav->selectFilter("profile/{$id}/");
$nav->appendChild($object_box);
$nav->appendChild($timeline);
return $this->buildApplicationPage(
$nav,
array(
'title' => $project->getName(),
));
}
private function buildActionListView(PhabricatorProject $project) {
$request = $this->getRequest();
$viewer = $request->getUser();
$id = $project->getID();
$view = id(new PhabricatorActionListView())
->setUser($viewer)
->setObject($project)
->setObjectURI($request->getRequestURI());
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$project,
PhabricatorPolicyCapability::CAN_EDIT);
$view->addAction(
id(new PhabricatorActionView())
->setName(pht('Edit Details'))
->setIcon('fa-pencil')
->setHref($this->getApplicationURI("details/{$id}/")));
$view->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()) {
$view->addAction(
id(new PhabricatorActionView())
->setName(pht('Activate Project'))
->setIcon('fa-check')
->setHref($this->getApplicationURI("archive/{$id}/"))
->setDisabled(!$can_edit)
->setWorkflow(true));
} else {
$view->addAction(
id(new PhabricatorActionView())
->setName(pht('Archive Project'))
->setIcon('fa-ban')
->setHref($this->getApplicationURI("archive/{$id}/"))
->setDisabled(!$can_edit)
->setWorkflow(true));
}
$action = null;
if (!$project->isUserMember($viewer->getPHID())) {
$can_join = PhabricatorPolicyFilter::hasCapability(
$viewer,
$project,
PhabricatorPolicyCapability::CAN_JOIN);
$action = id(new PhabricatorActionView())
->setUser($viewer)
->setRenderAsForm(true)
->setHref('/project/update/'.$project->getID().'/join/')
->setIcon('fa-plus')
->setDisabled(!$can_join)
->setName(pht('Join Project'));
$view->addAction($action);
} else {
$action = id(new PhabricatorActionView())
->setWorkflow(true)
->setHref('/project/update/'.$project->getID().'/leave/')
->setIcon('fa-times')
->setName(pht('Leave Project...'));
$view->addAction($action);
if (!$project->isUserWatcher($viewer->getPHID())) {
$action = id(new PhabricatorActionView())
->setWorkflow(true)
->setHref('/project/watch/'.$project->getID().'/')
->setIcon('fa-eye')
->setName(pht('Watch Project'));
$view->addAction($action);
} else {
$action = id(new PhabricatorActionView())
->setWorkflow(true)
->setHref('/project/unwatch/'.$project->getID().'/')
->setIcon('fa-eye-slash')
->setName(pht('Unwatch Project'));
$view->addAction($action);
}
}
return $view;
}
private function buildPropertyListView(
PhabricatorProject $project,
PhabricatorActionListView $actions) {
$request = $this->getRequest();
$viewer = $request->getUser();
- $this->loadHandles(
- array_merge(
- $project->getMemberPHIDs(),
- $project->getWatcherPHIDs()));
-
$view = id(new PHUIPropertyListView())
->setUser($viewer)
->setObject($project)
->setActionList($actions);
$hashtags = array();
foreach ($project->getSlugs() as $slug) {
$hashtags[] = id(new PHUITagView())
->setType(PHUITagView::TYPE_OBJECT)
->setName('#'.$slug->getSlug());
}
$view->addProperty(pht('Hashtags'), phutil_implode_html(' ', $hashtags));
$view->addProperty(
pht('Members'),
$project->getMemberPHIDs()
- ? $this->renderHandlesForPHIDs($project->getMemberPHIDs(), ',')
+ ? $viewer
+ ->renderHandleList($project->getMemberPHIDs())
+ ->setAsInline(true)
: phutil_tag('em', array(), pht('None')));
$view->addProperty(
pht('Watchers'),
$project->getWatcherPHIDs()
- ? $this->renderHandlesForPHIDs($project->getWatcherPHIDs(), ',')
+ ? $viewer
+ ->renderHandleList($project->getWatcherPHIDs())
+ ->setAsInline(true)
: phutil_tag('em', array(), pht('None')));
$descriptions = PhabricatorPolicyQuery::renderPolicyDescriptions(
$viewer,
$project);
- $this->loadHandles(array($project->getPHID()));
-
$view->addProperty(
pht('Looks Like'),
- $this->getHandle($project->getPHID())->renderTag());
+ $viewer->renderHandle($project->getPHID())->setAsTag(true));
$view->addProperty(
pht('Joinable By'),
$descriptions[PhabricatorPolicyCapability::CAN_JOIN]);
$field_list = PhabricatorCustomField::getObjectFields(
$project,
PhabricatorCustomField::ROLE_VIEW);
$field_list->appendFieldsToPropertyList($project, $viewer, $view);
return $view;
}
}
diff --git a/src/applications/releeph/controller/product/ReleephProductViewController.php b/src/applications/releeph/controller/product/ReleephProductViewController.php
index af6debd808..8b6cc03272 100644
--- a/src/applications/releeph/controller/product/ReleephProductViewController.php
+++ b/src/applications/releeph/controller/product/ReleephProductViewController.php
@@ -1,251 +1,247 @@
<?php
final class ReleephProductViewController extends ReleephProductController
implements PhabricatorApplicationSearchResultsControllerInterface {
private $productID;
private $queryKey;
public function shouldAllowPublic() {
return true;
}
public function willProcessRequest(array $data) {
$this->productID = idx($data, 'projectID');
$this->queryKey = idx($data, 'queryKey');
}
public function processRequest() {
$request = $this->getRequest();
$viewer = $request->getUser();
$product = id(new ReleephProductQuery())
->setViewer($viewer)
->withIDs(array($this->productID))
->executeOne();
if (!$product) {
return new Aphront404Response();
}
$this->setProduct($product);
$controller = id(new PhabricatorApplicationSearchController())
->setQueryKey($this->queryKey)
->setPreface($this->renderPreface())
->setSearchEngine(
id(new ReleephBranchSearchEngine())
->setProduct($product))
->setNavigation($this->buildSideNavView());
return $this->delegateToController($controller);
}
public function renderResultsList(
array $branches,
PhabricatorSavedQuery $saved) {
assert_instances_of($branches, 'ReleephBranch');
$viewer = $this->getRequest()->getUser();
$products = mpull($branches, 'getProduct');
$repo_phids = mpull($products, 'getRepositoryPHID');
$repos = id(new PhabricatorRepositoryQuery())
->setViewer($viewer)
->withPHIDs($repo_phids)
->execute();
$repos = mpull($repos, null, 'getPHID');
- $phids = mpull($branches, 'getCreatedByUserPHID');
- $this->loadHandles($phids);
-
$requests = array();
if ($branches) {
$requests = id(new ReleephRequestQuery())
->setViewer($viewer)
->withBranchIDs(mpull($branches, 'getID'))
->withStatus(ReleephRequestQuery::STATUS_OPEN)
->execute();
$requests = mgroup($requests, 'getBranchID');
}
$list = id(new PHUIObjectItemListView())
->setUser($viewer);
foreach ($branches as $branch) {
$diffusion_href = null;
$repo = idx($repos, $branch->getProduct()->getRepositoryPHID());
if ($repo) {
$drequest = DiffusionRequest::newFromDictionary(
array(
'user' => $viewer,
'repository' => $repo,
));
$diffusion_href = $drequest->generateURI(
array(
'action' => 'branch',
'branch' => $branch->getName(),
));
}
$branch_link = $branch->getName();
if ($diffusion_href) {
$branch_link = phutil_tag(
'a',
array(
'href' => $diffusion_href,
),
$branch_link);
}
$item = id(new PHUIObjectItemView())
->setHeader($branch->getDisplayName())
->setHref($this->getApplicationURI('branch/'.$branch->getID().'/'))
->addAttribute($branch_link);
if (!$branch->getIsActive()) {
$item->setDisabled(true);
}
$commit = $branch->getCutPointCommit();
if ($commit) {
$item->addIcon(
'none',
phabricator_datetime($commit->getEpoch(), $viewer));
}
$open_count = count(idx($requests, $branch->getID(), array()));
if ($open_count) {
$item->setBarColor('orange');
$item->addIcon(
'fa-code-fork',
pht('%d Open Pull Request(s)', new PhutilNumber($open_count)));
}
$list->addItem($item);
}
return $list;
}
public function buildSideNavView($for_app = false) {
$viewer = $this->getRequest()->getUser();
$product = $this->getProduct();
$nav = new AphrontSideNavFilterView();
$nav->setBaseURI(new PhutilURI($this->getApplicationURI()));
if ($for_app) {
$nav->addFilter('product/create/', pht('Create Product'));
}
id(new ReleephBranchSearchEngine())
->setProduct($product)
->setViewer($viewer)
->addNavigationItems($nav->getMenu());
$nav->selectFilter(null);
return $nav;
}
protected function buildApplicationCrumbs() {
$crumbs = parent::buildApplicationCrumbs();
$product = $this->getProduct();
if ($product) {
$crumbs->addAction(
id(new PHUIListItemView())
->setHref($product->getURI('cutbranch/'))
->setName(pht('Cut New Branch'))
->setIcon('fa-plus'));
}
return $crumbs;
}
private function renderPreface() {
$viewer = $this->getRequest()->getUser();
$product = $this->getProduct();
$id = $product->getID();
$header = id(new PHUIHeaderView())
->setHeader($product->getName())
->setUser($viewer)
->setPolicyObject($product);
if ($product->getIsActive()) {
$header->setStatus('fa-check', 'bluegrey', pht('Active'));
} else {
$header->setStatus('fa-ban', 'dark', pht('Inactive'));
}
$actions = id(new PhabricatorActionListView())
->setUser($viewer)
->setObject($product)
->setObjectURI($this->getRequest()->getRequestURI());
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$product,
PhabricatorPolicyCapability::CAN_EDIT);
$edit_uri = $this->getApplicationURI("product/{$id}/edit/");
$history_uri = $this->getApplicationURI("product/{$id}/history/");
$actions->addAction(
id(new PhabricatorActionView())
->setName(pht('Edit Product'))
->setHref($edit_uri)
->setIcon('fa-pencil')
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit));
if ($product->getIsActive()) {
$status_name = pht('Deactivate Product');
$status_href = "product/{$id}/action/deactivate/";
$status_icon = 'fa-times';
} else {
$status_name = pht('Reactivate Product');
$status_href = "product/{$id}/action/activate/";
$status_icon = 'fa-plus-circle-o';
}
$actions->addAction(
id(new PhabricatorActionView())
->setName($status_name)
->setHref($this->getApplicationURI($status_href))
->setIcon($status_icon)
->setDisabled(!$can_edit)
->setWorkflow(true));
$actions->addAction(
id(new PhabricatorActionView())
->setName(pht('View History'))
->setHref($history_uri)
->setIcon('fa-list'));
$properties = id(new PHUIPropertyListView())
->setUser($viewer)
->setObject($product);
$properties->addProperty(
pht('Repository'),
$product->getRepository()->getName());
$properties->setActionList($actions);
$pushers = $product->getPushers();
if ($pushers) {
- $this->loadHandles($pushers);
$properties->addProperty(
pht('Pushers'),
- $this->renderHandlesForPHIDs($pushers));
+ $viewer->renderHandleList($pushers));
}
return id(new PHUIObjectBoxView())
->setHeader($header)
->addPropertyList($properties);
}
}
diff --git a/src/applications/transactions/controller/PhabricatorApplicationTransactionValueController.php b/src/applications/transactions/controller/PhabricatorApplicationTransactionValueController.php
index 17edba68b4..7905b8c070 100644
--- a/src/applications/transactions/controller/PhabricatorApplicationTransactionValueController.php
+++ b/src/applications/transactions/controller/PhabricatorApplicationTransactionValueController.php
@@ -1,147 +1,149 @@
<?php
final class PhabricatorApplicationTransactionValueController
extends PhabricatorApplicationTransactionController {
private $value;
private $phid;
public function willProcessRequest(array $data) {
$this->phid = $data['phid'];
$this->value = $data['value'];
}
public function processRequest() {
$request = $this->getRequest();
$viewer = $request->getUser();
$xaction = id(new PhabricatorObjectQuery())
->setViewer($viewer)
->withPHIDs(array($this->phid))
->executeOne();
if (!$xaction) {
return new Aphront404Response();
}
// For now, this pathway only supports policy transactions
// to show the details of custom policies. If / when this pathway
// supports more transaction types, rendering coding should be moved
// into PhabricatorTransactions e.g. feed rendering code.
// TODO: This should be some kind of "hey do you support this?" thing on
// the transactions themselves.
switch ($xaction->getTransactionType()) {
case PhabricatorTransactions::TYPE_VIEW_POLICY:
case PhabricatorTransactions::TYPE_EDIT_POLICY:
case PhabricatorTransactions::TYPE_JOIN_POLICY:
case PhabricatorRepositoryTransaction::TYPE_PUSH_POLICY:
break;
default:
return new Aphront404Response();
break;
}
if ($this->value == 'old') {
$value = $xaction->getOldValue();
} else {
$value = $xaction->getNewValue();
}
$policy = id(new PhabricatorPolicyQuery())
->setViewer($viewer)
->withPHIDs(array($value))
->executeOne();
if (!$policy) {
return new Aphront404Response();
}
if ($policy->getType() != PhabricatorPolicyType::TYPE_CUSTOM) {
return new Aphront404Response();
}
$rule_objects = array();
foreach ($policy->getCustomRuleClasses() as $class) {
$rule_objects[$class] = newv($class, array());
}
$policy->attachRuleObjects($rule_objects);
- $handle_phids = $this->extractPHIDs($policy, $rule_objects);
- $handles = $this->loadHandles($handle_phids);
$this->requireResource('policy-transaction-detail-css');
$cancel_uri = $this->guessCancelURI($viewer, $xaction);
$dialog = id(new AphrontDialogView())
->setUser($viewer)
->setTitle($policy->getFullName())
->setWidth(AphrontDialogView::WIDTH_FORM)
->appendChild(
$this->renderPolicyDetails($policy, $rule_objects))
->addCancelButton($cancel_uri, pht('Close'));
return id(new AphrontDialogResponse())->setDialog($dialog);
}
private function extractPHIDs(
PhabricatorPolicy $policy,
array $rule_objects) {
$phids = array();
foreach ($policy->getRules() as $rule) {
$rule_object = $rule_objects[$rule['rule']];
$phids[] =
$rule_object->getRequiredHandlePHIDsForSummary($rule['value']);
}
return array_filter(array_mergev($phids));
}
private function renderPolicyDetails(
PhabricatorPolicy $policy,
array $rule_objects) {
$details = array();
$details[] = phutil_tag(
'p',
array(
'class' => 'policy-transaction-detail-intro',
),
pht('These rules are processed in order:'));
foreach ($policy->getRules() as $index => $rule) {
$rule_object = $rule_objects[$rule['rule']];
if ($rule['action'] == 'allow') {
$icon = 'fa-check-circle green';
} else {
$icon = 'fa-minus-circle red';
}
$icon = id(new PHUIIconView())
->setIconFont($icon)
->setText(
ucfirst($rule['action']).' '.$rule_object->getRuleDescription());
- $handle_phids =
- $rule_object->getRequiredHandlePHIDsForSummary($rule['value']);
+
+ $handle_phids = $rule_object->getRequiredHandlePHIDsForSummary(
+ $rule['value']);
if ($handle_phids) {
- $value = $this->renderHandlesForPHIDs($handle_phids, ',');
+ $value = $this->getViewer()
+ ->renderHandleList($handle_phids)
+ ->setInline(true);
} else {
$value = $rule['value'];
}
+
$details[] = phutil_tag('div',
array(
'class' => 'policy-transaction-detail-row',
),
array(
$icon,
$value,
));
}
$details[] = phutil_tag(
'p',
array(
'class' => 'policy-transaction-detail-end',
),
pht(
'If no rules match, %s all other users.',
phutil_tag('b',
array(),
$policy->getDefaultAction())));
return $details;
}
}

File Metadata

Mime Type
text/x-diff
Expires
Mon, Jul 28, 6:33 PM (1 w, 3 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
187347
Default Alt Text
(137 KB)

Event Timeline