Page MenuHomePhorge

No OneTemporary

diff --git a/src/aphront/AphrontController.php b/src/aphront/AphrontController.php
index 6d84c46195..a591271270 100644
--- a/src/aphront/AphrontController.php
+++ b/src/aphront/AphrontController.php
@@ -1,67 +1,86 @@
<?php
/**
* @group aphront
*/
abstract class AphrontController extends Phobject {
private $request;
private $currentApplication;
private $delegatingController;
-
public function setDelegatingController(
AphrontController $delegating_controller) {
$this->delegatingController = $delegating_controller;
return $this;
}
public function getDelegatingController() {
return $this->delegatingController;
}
public function willBeginExecution() {
return;
}
public function willProcessRequest(array $uri_data) {
return;
}
public function didProcessRequest($response) {
return $response;
}
abstract public function processRequest();
final public function __construct(AphrontRequest $request) {
$this->request = $request;
}
final public function getRequest() {
return $this->request;
}
final public function delegateToController(AphrontController $controller) {
$controller->setDelegatingController($this);
$application = $this->getCurrentApplication();
if ($application) {
$controller->setCurrentApplication($application);
}
return $controller->processRequest();
}
final public function setCurrentApplication(
PhabricatorApplication $current_application) {
$this->currentApplication = $current_application;
return $this;
}
final public function getCurrentApplication() {
return $this->currentApplication;
}
+ public function getDefaultResourceSource() {
+ throw new Exception(
+ pht(
+ 'A Controller must implement getDefaultResourceSource() before you '.
+ 'can invoke requireResource() or initBehavior().'));
+ }
+
+ public function requireResource($symbol) {
+ $response = CelerityAPI::getStaticResourceResponse();
+ $response->requireResource($symbol, $this->getDefaultResourceSource());
+ return $this;
+ }
+
+ public function initBehavior($name, $config = array()) {
+ Javelin::initBehavior(
+ $name,
+ $config,
+ $this->getDefaultResourceSource());
+ }
+
}
diff --git a/src/applications/base/controller/PhabricatorController.php b/src/applications/base/controller/PhabricatorController.php
index 86009fd9fc..4d7d6e3243 100644
--- a/src/applications/base/controller/PhabricatorController.php
+++ b/src/applications/base/controller/PhabricatorController.php
@@ -1,411 +1,416 @@
<?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 shouldRequireEmailVerification() {
return PhabricatorUserEmail::isEmailVerificationRequired();
}
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();
$phusr = $request->getCookie('phusr');
$phsid = $request->getCookie('phsid');
if (strlen($phusr) && $phsid) {
$info = queryfx_one(
$user->establishConnection('r'),
'SELECT u.* FROM %T u JOIN %T s ON u.phid = s.userPHID
AND s.type LIKE %> AND s.sessionKey = %s',
$user->getTableName(),
'phabricator_session',
'web-',
PhabricatorHash::digest($phsid));
if ($info) {
$user->loadFromArray($info);
}
}
$request->setUser($user);
}
$translation = $user->getTranslation();
if ($translation &&
$translation != PhabricatorEnv::getEnvConfig('translation.provider')) {
$translation = newv($translation, array());
PhutilTranslator::getInstance()
->setLanguage($translation->getLanguage())
->addTranslations($translation->getTranslations());
}
$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);
}
}
if ($this->shouldRequireEnabledUser()) {
if ($user->isLoggedIn() && !$user->getIsApproved()) {
$controller = new PhabricatorAuthNeedsApprovalController($request);
return $this->delegateToController($controller);
}
if ($user->getIsDisabled()) {
$controller = new PhabricatorDisabledUserController($request);
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);
}
if ($this->shouldRequireLogin()) {
// This actually means we need either:
// - a valid user, or a public controller; and
// - permission to see the application.
$auth_class = 'PhabricatorApplicationAuth';
$auth_application = PhabricatorApplication::getByClass($auth_class);
$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($request);
$this->setCurrentApplication($auth_application);
return $this->delegateToController($login_controller);
}
if ($user->isLoggedIn()) {
if ($this->shouldRequireEmailVerification()) {
if (!$user->getIsEmailVerified()) {
$controller = new PhabricatorMustVerifyEmailController($request);
$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();
}
}
// 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);
$response = new AphrontWebpageResponse();
$response->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')) {
$page->setDeviceReady(true);
}
$page->setShowChrome(idx($options, 'chrome', true));
$application_menu = $this->buildApplicationMenu();
if ($application_menu) {
$page->setApplicationMenu($application_menu);
}
$response = new AphrontWebpageResponse();
return $response->setContent($page->render());
}
public function didProcessRequest($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()) {
$view = new PhabricatorStandardPageView();
$view->setRequest($request);
$view->setController($this);
$view->appendChild(phutil_tag(
'div',
array('style' => 'padding: 2em 0;'),
$response->buildResponseString()));
$page_response = new AphrontWebpageResponse();
$page_response->setContent($view->render());
$page_response->setHTTPResponseCode($response->getHTTPResponseCode());
return $page_response;
} else {
$response->getDialog()->setIsStandalone(true);
return id(new AphrontAjaxResponse())
->setContent(array(
'dialog' => $response->buildResponseString(),
));
}
} else if ($response instanceof AphrontRedirectResponse) {
if ($request->isAjax()) {
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 getLoadedHandles() {
return $this->handles;
}
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->getLoadedHandles(),
array_filter($phids));
}
protected function buildApplicationMenu() {
return null;
}
protected function buildApplicationCrumbs() {
$crumbs = array();
$application = $this->getCurrentApplication();
if ($application) {
$sprite = $application->getIconName();
if (!$sprite) {
$sprite = 'application';
}
$crumbs[] = id(new PhabricatorCrumbView())
->setHref($this->getApplicationURI())
->setIcon($sprite);
}
$view = new PhabricatorCrumbsView();
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 = 'enable-grey';
} else {
$message = $negative_message;
$icon_name = 'lock';
}
$icon = id(new PHUIIconView())
->setSpriteSheet(PHUIIconView::SPRITE_ICONS)
->setSpriteIcon($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';
+ }
+
+
}
diff --git a/src/applications/differential/controller/DifferentialRevisionViewController.php b/src/applications/differential/controller/DifferentialRevisionViewController.php
index 14d37ffb29..8e15dd7de3 100644
--- a/src/applications/differential/controller/DifferentialRevisionViewController.php
+++ b/src/applications/differential/controller/DifferentialRevisionViewController.php
@@ -1,928 +1,928 @@
<?php
final class DifferentialRevisionViewController extends DifferentialController {
private $revisionID;
public function shouldAllowPublic() {
return true;
}
public function willProcessRequest(array $data) {
$this->revisionID = $data['id'];
}
public function processRequest() {
$request = $this->getRequest();
$user = $request->getUser();
$viewer_is_anonymous = !$user->isLoggedIn();
$revision = id(new DifferentialRevisionQuery())
->withIDs(array($this->revisionID))
->setViewer($request->getUser())
->needRelationships(true)
->needReviewerStatus(true)
->needReviewerAuthority(true)
->executeOne();
if (!$revision) {
return new Aphront404Response();
}
$diffs = id(new DifferentialDiffQuery())
->setViewer($request->getUser())
->withRevisionIDs(array($this->revisionID))
->execute();
$diffs = array_reverse($diffs, $preserve_keys = true);
if (!$diffs) {
throw new Exception(
"This revision has no diffs. Something has gone quite wrong.");
}
$diff_vs = $request->getInt('vs');
$target_id = $request->getInt('id');
$target = idx($diffs, $target_id, end($diffs));
$target_manual = $target;
if (!$target_id) {
foreach ($diffs as $diff) {
if ($diff->getCreationMethod() != 'commit') {
$target_manual = $diff;
}
}
}
if (empty($diffs[$diff_vs])) {
$diff_vs = null;
}
$arc_project = $target->loadArcanistProject();
$repository = ($arc_project ? $arc_project->loadRepository() : null);
list($changesets, $vs_map, $vs_changesets, $rendering_references) =
$this->loadChangesetsAndVsMap(
$target,
idx($diffs, $diff_vs),
$repository);
if ($request->getExists('download')) {
return $this->buildRawDiffResponse(
$revision,
$changesets,
$vs_changesets,
$vs_map,
$repository);
}
$props = id(new DifferentialDiffProperty())->loadAllWhere(
'diffID = %d',
$target_manual->getID());
$props = mpull($props, 'getData', 'getName');
$aux_fields = $this->loadAuxiliaryFields($revision);
$comments = $revision->loadComments();
$all_changesets = $changesets;
$inlines = $this->loadInlineComments(
$revision,
$all_changesets);
$object_phids = array_merge(
$revision->getReviewers(),
$revision->getCCPHIDs(),
$revision->loadCommitPHIDs(),
array(
$revision->getAuthorPHID(),
$user->getPHID(),
),
mpull($comments, 'getAuthorPHID'));
foreach ($comments as $comment) {
foreach ($comment->getRequiredHandlePHIDs() as $phid) {
$object_phids[] = $phid;
}
}
foreach ($revision->getAttached() as $type => $phids) {
foreach ($phids as $phid => $info) {
$object_phids[] = $phid;
}
}
$aux_phids = array();
foreach ($aux_fields as $key => $aux_field) {
$aux_field->setDiff($target);
$aux_field->setManualDiff($target_manual);
$aux_field->setDiffProperties($props);
$aux_phids[$key] = $aux_field->getRequiredHandlePHIDsForRevisionView();
}
$object_phids = array_merge($object_phids, array_mergev($aux_phids));
$object_phids = array_unique($object_phids);
$handles = $this->loadViewerHandles($object_phids);
foreach ($aux_fields as $key => $aux_field) {
// Make sure each field only has access to handles it specifically
// requested, not all handles. Otherwise you can get a field which works
// only in the presence of other fields.
$aux_field->setHandles(array_select_keys($handles, $aux_phids[$key]));
}
$reviewer_warning = null;
if ($revision->getStatus() ==
ArcanistDifferentialRevisionStatus::NEEDS_REVIEW) {
$has_live_reviewer = false;
foreach ($revision->getReviewers() as $reviewer) {
if (!$handles[$reviewer]->isDisabled()) {
$has_live_reviewer = true;
break;
}
}
if (!$has_live_reviewer) {
$reviewer_warning = new AphrontErrorView();
$reviewer_warning->setSeverity(AphrontErrorView::SEVERITY_WARNING);
$reviewer_warning->setTitle(pht('No Active Reviewers'));
if ($revision->getReviewers()) {
$reviewer_warning->appendChild(
phutil_tag(
'p',
array(),
pht('All specified reviewers are disabled and this revision '.
'needs review. You may want to add some new reviewers.')));
} else {
$reviewer_warning->appendChild(
phutil_tag(
'p',
array(),
pht('This revision has no specified reviewers and needs '.
'review. You may want to add some reviewers.')));
}
}
}
$request_uri = $request->getRequestURI();
$limit = 100;
$large = $request->getStr('large');
if (count($changesets) > $limit && !$large) {
$count = count($changesets);
$warning = new AphrontErrorView();
$warning->setTitle('Very Large Diff');
$warning->setSeverity(AphrontErrorView::SEVERITY_WARNING);
$warning->appendChild(hsprintf(
'%s <strong>%s</strong>',
pht(
'This diff is very large and affects %s files. Load each file '.
'individually.',
new PhutilNumber($count)),
phutil_tag(
'a',
array(
'href' => $request_uri
->alter('large', 'true')
->setFragment('toc'),
),
pht('Show All Files Inline'))));
$warning = $warning->render();
$my_inlines = id(new DifferentialInlineCommentQuery())
->withDraftComments($user->getPHID(), $this->revisionID)
->execute();
$visible_changesets = array();
foreach ($inlines + $my_inlines as $inline) {
$changeset_id = $inline->getChangesetID();
if (isset($changesets[$changeset_id])) {
$visible_changesets[$changeset_id] = $changesets[$changeset_id];
}
}
if (!empty($props['arc:lint'])) {
$changeset_paths = mpull($changesets, null, 'getFilename');
foreach ($props['arc:lint'] as $lint) {
$changeset = idx($changeset_paths, $lint['path']);
if ($changeset) {
$visible_changesets[$changeset->getID()] = $changeset;
}
}
}
} else {
$warning = null;
$visible_changesets = $changesets;
}
$revision_detail = id(new DifferentialRevisionDetailView())
->setUser($user)
->setRevision($revision)
->setDiff(end($diffs))
->setAuxiliaryFields($aux_fields)
->setURI($request->getRequestURI());
$actions = $this->getRevisionActions($revision);
$custom_renderer_class = PhabricatorEnv::getEnvConfig(
'differential.revision-custom-detail-renderer');
if ($custom_renderer_class) {
// TODO: build a better version of the action links and deprecate the
// whole DifferentialRevisionDetailRenderer class.
$custom_renderer = newv($custom_renderer_class, array());
$custom_renderer->setUser($user);
$custom_renderer->setDiff($target);
if ($diff_vs) {
$custom_renderer->setVSDiff($diffs[$diff_vs]);
}
$actions = array_merge(
$actions,
$custom_renderer->generateActionLinks($revision, $target_manual));
}
$whitespace = $request->getStr(
'whitespace',
DifferentialChangesetParser::WHITESPACE_IGNORE_ALL);
if ($arc_project) {
list($symbol_indexes, $project_phids) = $this->buildSymbolIndexes(
$arc_project,
$visible_changesets);
} else {
$symbol_indexes = array();
$project_phids = null;
}
$revision_detail->setActions($actions);
$revision_detail->setUser($user);
$comment_view = new DifferentialRevisionCommentListView();
$comment_view->setComments($comments);
$comment_view->setHandles($handles);
$comment_view->setInlineComments($inlines);
$comment_view->setChangesets($all_changesets);
$comment_view->setUser($user);
$comment_view->setTargetDiff($target);
$comment_view->setVersusDiffID($diff_vs);
if ($arc_project) {
Javelin::initBehavior(
'repository-crossreference',
array(
'section' => $comment_view->getID(),
'projects' => $project_phids,
));
}
$changeset_view = new DifferentialChangesetListView();
$changeset_view->setChangesets($changesets);
$changeset_view->setVisibleChangesets($visible_changesets);
if (!$viewer_is_anonymous) {
$changeset_view->setInlineCommentControllerURI(
'/differential/comment/inline/edit/'.$revision->getID().'/');
}
$changeset_view->setStandaloneURI('/differential/changeset/');
$changeset_view->setRawFileURIs(
'/differential/changeset/?view=old',
'/differential/changeset/?view=new');
$changeset_view->setUser($user);
$changeset_view->setDiff($target);
$changeset_view->setRenderingReferences($rendering_references);
$changeset_view->setVsMap($vs_map);
$changeset_view->setWhitespace($whitespace);
if ($repository) {
$changeset_view->setRepository($repository);
}
$changeset_view->setSymbolIndexes($symbol_indexes);
$changeset_view->setTitle('Diff '.$target->getID());
$diff_history = new DifferentialRevisionUpdateHistoryView();
$diff_history->setDiffs($diffs);
$diff_history->setSelectedVersusDiffID($diff_vs);
$diff_history->setSelectedDiffID($target->getID());
$diff_history->setSelectedWhitespace($whitespace);
$diff_history->setUser($user);
$local_view = new DifferentialLocalCommitsView();
$local_view->setUser($user);
$local_view->setLocalCommits(idx($props, 'local:commits'));
if ($repository) {
$other_revisions = $this->loadOtherRevisions(
$changesets,
$target,
$repository);
} else {
$other_revisions = array();
}
$other_view = null;
if ($other_revisions) {
$other_view = $this->renderOtherRevisions($other_revisions);
}
$toc_view = new DifferentialDiffTableOfContentsView();
$toc_view->setChangesets($changesets);
$toc_view->setVisibleChangesets($visible_changesets);
$toc_view->setRenderingReferences($rendering_references);
$toc_view->setUnitTestData(idx($props, 'arc:unit', array()));
if ($repository) {
$toc_view->setRepository($repository);
}
$toc_view->setDiff($target);
$toc_view->setUser($user);
$toc_view->setRevisionID($revision->getID());
$toc_view->setWhitespace($whitespace);
$comment_form = null;
if (!$viewer_is_anonymous) {
$draft = id(new PhabricatorDraft())->loadOneWhere(
'authorPHID = %s AND draftKey = %s',
$user->getPHID(),
'differential-comment-'.$revision->getID());
$reviewers = array();
$ccs = array();
if ($draft) {
$reviewers = idx($draft->getMetadata(), 'reviewers', array());
$ccs = idx($draft->getMetadata(), 'ccs', array());
if ($reviewers || $ccs) {
$handles = $this->loadViewerHandles(array_merge($reviewers, $ccs));
$reviewers = array_select_keys($handles, $reviewers);
$ccs = array_select_keys($handles, $ccs);
}
}
$comment_form = new DifferentialAddCommentView();
$comment_form->setRevision($revision);
$comment_form->setAuxFields($aux_fields);
$comment_form->setActions($this->getRevisionCommentActions($revision));
$comment_form->setActionURI('/differential/comment/save/');
$comment_form->setUser($user);
$comment_form->setDraft($draft);
$comment_form->setReviewers(mpull($reviewers, 'getFullName', 'getPHID'));
$comment_form->setCCs(mpull($ccs, 'getFullName', 'getPHID'));
// TODO: This just makes the "Z" key work. Generalize this and remove
// it at some point.
$comment_form = phutil_tag(
'div',
array(
'class' => 'differential-add-comment-panel',
),
$comment_form);
}
$pane_id = celerity_generate_unique_node_id();
Javelin::initBehavior(
'differential-keyboard-navigation',
array(
'haunt' => $pane_id,
));
Javelin::initBehavior('differential-user-select');
$page_pane = id(new DifferentialPrimaryPaneView())
->setID($pane_id)
->appendChild(array(
$comment_view,
$diff_history,
$warning,
$local_view,
$toc_view,
$other_view,
$changeset_view,
));
if ($comment_form) {
$page_pane->appendChild($comment_form);
} else {
// TODO: For now, just use this to get "Login to Comment".
$page_pane->appendChild(
id(new PhabricatorApplicationTransactionCommentView())
->setUser($user)
->setRequestURI($request->getRequestURI()));
}
$object_id = 'D'.$revision->getID();
$top_anchor = id(new PhabricatorAnchorView())
->setAnchorName('top')
->setNavigationMarker(true);
$content = array(
$reviewer_warning,
$top_anchor,
$revision_detail,
$page_pane,
);
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb($object_id, '/'.$object_id);
$prefs = $user->loadPreferences();
$pref_filetree = PhabricatorUserPreferences::PREFERENCE_DIFF_FILETREE;
if ($prefs->getPreference($pref_filetree)) {
$collapsed = $prefs->getPreference(
PhabricatorUserPreferences::PREFERENCE_NAV_COLLAPSED,
false);
$nav = id(new DifferentialChangesetFileTreeSideNavBuilder())
->setAnchorName('top')
->setTitle('D'.$revision->getID())
->setBaseURI(new PhutilURI('/D'.$revision->getID()))
->setCollapsed((bool)$collapsed)
->build($changesets);
$nav->appendChild($content);
$nav->setCrumbs($crumbs);
$content = $nav;
} else {
array_unshift($content, $crumbs);
}
return $this->buildApplicationPage(
$content,
array(
'title' => $object_id.' '.$revision->getTitle(),
'pageObjects' => array($revision->getPHID()),
));
}
private function getRevisionActions(DifferentialRevision $revision) {
$user = $this->getRequest()->getUser();
$viewer_phid = $user->getPHID();
$viewer_is_owner = ($revision->getAuthorPHID() == $viewer_phid);
$viewer_is_reviewer = in_array($viewer_phid, $revision->getReviewers());
$viewer_is_cc = in_array($viewer_phid, $revision->getCCPHIDs());
$logged_in = $this->getRequest()->getUser()->isLoggedIn();
$status = $revision->getStatus();
$revision_id = $revision->getID();
$revision_phid = $revision->getPHID();
$links = array();
$can_edit = PhabricatorPolicyFilter::hasCapability(
$user,
$revision,
PhabricatorPolicyCapability::CAN_EDIT);
$links[] = array(
'icon' => 'edit',
'href' => "/differential/revision/edit/{$revision_id}/",
'name' => pht('Edit Revision'),
'disabled' => !$can_edit,
'sigil' => $can_edit ? null : 'workflow',
);
if (!$viewer_is_owner && !$viewer_is_reviewer) {
$action = $viewer_is_cc ? 'rem' : 'add';
$links[] = array(
'icon' => $viewer_is_cc ? 'disable' : 'check',
'href' => "/differential/subscribe/{$action}/{$revision_id}/",
'name' => $viewer_is_cc ? pht('Unsubscribe') : pht('Subscribe'),
'instant' => $logged_in,
'disabled' => !$logged_in,
'sigil' => $can_edit ? null : 'workflow',
);
} else {
$links[] = array(
'icon' => 'enable',
'name' => pht('Automatically Subscribed'),
'disabled' => true,
);
}
- require_celerity_resource('phabricator-object-selector-css');
- require_celerity_resource('javelin-behavior-phabricator-object-selector');
+ $this->requireResource('phabricator-object-selector-css');
+ $this->requireResource('javelin-behavior-phabricator-object-selector');
$links[] = array(
'icon' => 'link',
'name' => pht('Edit Dependencies'),
'href' => "/search/attach/{$revision_phid}/DREV/dependencies/",
'sigil' => 'workflow',
'disabled' => !$can_edit,
);
$maniphest = 'PhabricatorApplicationManiphest';
if (PhabricatorApplication::isClassInstalled($maniphest)) {
$links[] = array(
'icon' => 'attach',
'name' => pht('Edit Maniphest Tasks'),
'href' => "/search/attach/{$revision_phid}/TASK/",
'sigil' => 'workflow',
'disabled' => !$can_edit,
);
}
$request_uri = $this->getRequest()->getRequestURI();
$links[] = array(
'icon' => 'download',
'name' => pht('Download Raw Diff'),
'href' => $request_uri->alter('download', 'true')
);
return $links;
}
private function getRevisionCommentActions(DifferentialRevision $revision) {
$actions = array(
DifferentialAction::ACTION_COMMENT => true,
);
$viewer = $this->getRequest()->getUser();
$viewer_phid = $viewer->getPHID();
$viewer_is_owner = ($viewer_phid == $revision->getAuthorPHID());
$viewer_is_reviewer = in_array($viewer_phid, $revision->getReviewers());
$status = $revision->getStatus();
$viewer_has_accepted = false;
$viewer_has_rejected = false;
$status_accepted = DifferentialReviewerStatus::STATUS_ACCEPTED;
$status_rejected = DifferentialReviewerStatus::STATUS_REJECTED;
foreach ($revision->getReviewerStatus() as $reviewer) {
if ($reviewer->getReviewerPHID() == $viewer_phid) {
if ($reviewer->getStatus() == $status_accepted) {
$viewer_has_accepted = true;
}
if ($reviewer->getStatus() == $status_rejected) {
$viewer_has_rejected = true;
}
break;
}
}
$allow_self_accept = PhabricatorEnv::getEnvConfig(
'differential.allow-self-accept');
$always_allow_close = PhabricatorEnv::getEnvConfig(
'differential.always-allow-close');
$allow_reopen = PhabricatorEnv::getEnvConfig(
'differential.allow-reopen');
if ($viewer_is_owner) {
switch ($status) {
case ArcanistDifferentialRevisionStatus::NEEDS_REVIEW:
$actions[DifferentialAction::ACTION_ACCEPT] = $allow_self_accept;
$actions[DifferentialAction::ACTION_ABANDON] = true;
$actions[DifferentialAction::ACTION_RETHINK] = true;
break;
case ArcanistDifferentialRevisionStatus::NEEDS_REVISION:
$actions[DifferentialAction::ACTION_ACCEPT] = $allow_self_accept;
$actions[DifferentialAction::ACTION_ABANDON] = true;
$actions[DifferentialAction::ACTION_REQUEST] = true;
break;
case ArcanistDifferentialRevisionStatus::ACCEPTED:
$actions[DifferentialAction::ACTION_ABANDON] = true;
$actions[DifferentialAction::ACTION_REQUEST] = true;
$actions[DifferentialAction::ACTION_RETHINK] = true;
$actions[DifferentialAction::ACTION_CLOSE] = true;
break;
case ArcanistDifferentialRevisionStatus::CLOSED:
break;
case ArcanistDifferentialRevisionStatus::ABANDONED:
$actions[DifferentialAction::ACTION_RECLAIM] = true;
break;
}
} else {
switch ($status) {
case ArcanistDifferentialRevisionStatus::NEEDS_REVIEW:
$actions[DifferentialAction::ACTION_ACCEPT] = true;
$actions[DifferentialAction::ACTION_REJECT] = true;
$actions[DifferentialAction::ACTION_RESIGN] = $viewer_is_reviewer;
break;
case ArcanistDifferentialRevisionStatus::NEEDS_REVISION:
$actions[DifferentialAction::ACTION_ACCEPT] = true;
$actions[DifferentialAction::ACTION_REJECT] = !$viewer_has_rejected;
$actions[DifferentialAction::ACTION_RESIGN] = $viewer_is_reviewer;
break;
case ArcanistDifferentialRevisionStatus::ACCEPTED:
$actions[DifferentialAction::ACTION_ACCEPT] = !$viewer_has_accepted;
$actions[DifferentialAction::ACTION_REJECT] = true;
$actions[DifferentialAction::ACTION_RESIGN] = $viewer_is_reviewer;
break;
case ArcanistDifferentialRevisionStatus::CLOSED:
case ArcanistDifferentialRevisionStatus::ABANDONED:
break;
}
if ($status != ArcanistDifferentialRevisionStatus::CLOSED) {
$actions[DifferentialAction::ACTION_CLAIM] = true;
$actions[DifferentialAction::ACTION_CLOSE] = $always_allow_close;
}
}
$actions[DifferentialAction::ACTION_ADDREVIEWERS] = true;
$actions[DifferentialAction::ACTION_ADDCCS] = true;
$actions[DifferentialAction::ACTION_REOPEN] = $allow_reopen &&
($status == ArcanistDifferentialRevisionStatus::CLOSED);
$actions = array_keys(array_filter($actions));
$actions_dict = array();
foreach ($actions as $action) {
$actions_dict[$action] = DifferentialAction::getActionVerb($action);
}
return $actions_dict;
}
private function loadInlineComments(
DifferentialRevision $revision,
array &$changesets) {
assert_instances_of($changesets, 'DifferentialChangeset');
$inline_comments = array();
$inline_comments = id(new DifferentialInlineCommentQuery())
->withRevisionIDs(array($revision->getID()))
->withNotDraft(true)
->execute();
$load_changesets = array();
foreach ($inline_comments as $inline) {
$changeset_id = $inline->getChangesetID();
if (isset($changesets[$changeset_id])) {
continue;
}
$load_changesets[$changeset_id] = true;
}
$more_changesets = array();
if ($load_changesets) {
$changeset_ids = array_keys($load_changesets);
$more_changesets += id(new DifferentialChangeset())
->loadAllWhere(
'id IN (%Ld)',
$changeset_ids);
}
if ($more_changesets) {
$changesets += $more_changesets;
$changesets = msort($changesets, 'getSortKey');
}
return $inline_comments;
}
private function loadChangesetsAndVsMap(
DifferentialDiff $target,
DifferentialDiff $diff_vs = null,
PhabricatorRepository $repository = null) {
$load_ids = array();
if ($diff_vs) {
$load_ids[] = $diff_vs->getID();
}
$load_ids[] = $target->getID();
$raw_changesets = id(new DifferentialChangeset())
->loadAllWhere(
'diffID IN (%Ld)',
$load_ids);
$changeset_groups = mgroup($raw_changesets, 'getDiffID');
$changesets = idx($changeset_groups, $target->getID(), array());
$changesets = mpull($changesets, null, 'getID');
$refs = array();
$vs_map = array();
$vs_changesets = array();
if ($diff_vs) {
$vs_id = $diff_vs->getID();
$vs_changesets_path_map = array();
foreach (idx($changeset_groups, $vs_id, array()) as $changeset) {
$path = $changeset->getAbsoluteRepositoryPath($repository, $diff_vs);
$vs_changesets_path_map[$path] = $changeset;
$vs_changesets[$changeset->getID()] = $changeset;
}
foreach ($changesets as $key => $changeset) {
$path = $changeset->getAbsoluteRepositoryPath($repository, $target);
if (isset($vs_changesets_path_map[$path])) {
$vs_map[$changeset->getID()] =
$vs_changesets_path_map[$path]->getID();
$refs[$changeset->getID()] =
$changeset->getID().'/'.$vs_changesets_path_map[$path]->getID();
unset($vs_changesets_path_map[$path]);
} else {
$refs[$changeset->getID()] = $changeset->getID();
}
}
foreach ($vs_changesets_path_map as $path => $changeset) {
$changesets[$changeset->getID()] = $changeset;
$vs_map[$changeset->getID()] = -1;
$refs[$changeset->getID()] = $changeset->getID().'/-1';
}
} else {
foreach ($changesets as $changeset) {
$refs[$changeset->getID()] = $changeset->getID();
}
}
$changesets = msort($changesets, 'getSortKey');
return array($changesets, $vs_map, $vs_changesets, $refs);
}
private function loadAuxiliaryFields(DifferentialRevision $revision) {
$aux_fields = DifferentialFieldSelector::newSelector()
->getFieldSpecifications();
foreach ($aux_fields as $key => $aux_field) {
if (!$aux_field->shouldAppearOnRevisionView()) {
unset($aux_fields[$key]);
} else {
$aux_field->setUser($this->getRequest()->getUser());
}
}
$aux_fields = DifferentialAuxiliaryField::loadFromStorage(
$revision,
$aux_fields);
return $aux_fields;
}
private function buildSymbolIndexes(
PhabricatorRepositoryArcanistProject $arc_project,
array $visible_changesets) {
assert_instances_of($visible_changesets, 'DifferentialChangeset');
$engine = PhabricatorSyntaxHighlighter::newEngine();
$langs = $arc_project->getSymbolIndexLanguages();
if (!$langs) {
return array(array(), array());
}
$symbol_indexes = array();
$project_phids = array_merge(
array($arc_project->getPHID()),
nonempty($arc_project->getSymbolIndexProjects(), array()));
$indexed_langs = array_fill_keys($langs, true);
foreach ($visible_changesets as $key => $changeset) {
$lang = $engine->getLanguageFromFilename($changeset->getFilename());
if (isset($indexed_langs[$lang])) {
$symbol_indexes[$key] = array(
'lang' => $lang,
'projects' => $project_phids,
);
}
}
return array($symbol_indexes, $project_phids);
}
private function loadOtherRevisions(
array $changesets,
DifferentialDiff $target,
PhabricatorRepository $repository) {
assert_instances_of($changesets, 'DifferentialChangeset');
$paths = array();
foreach ($changesets as $changeset) {
$paths[] = $changeset->getAbsoluteRepositoryPath(
$repository,
$target);
}
if (!$paths) {
return array();
}
$path_map = id(new DiffusionPathIDQuery($paths))->loadPathIDs();
if (!$path_map) {
return array();
}
$query = id(new DifferentialRevisionQuery())
->setViewer($this->getRequest()->getUser())
->withStatus(DifferentialRevisionQuery::STATUS_OPEN)
->setOrder(DifferentialRevisionQuery::ORDER_PATH_MODIFIED)
->setLimit(10)
->needRelationships(true);
foreach ($path_map as $path => $path_id) {
$query->withPath($repository->getID(), $path_id);
}
$results = $query->execute();
// Strip out *this* revision.
foreach ($results as $key => $result) {
if ($result->getID() == $this->revisionID) {
unset($results[$key]);
}
}
return $results;
}
private function renderOtherRevisions(array $revisions) {
assert_instances_of($revisions, 'DifferentialRevision');
$user = $this->getRequest()->getUser();
$view = id(new DifferentialRevisionListView())
->setRevisions($revisions)
->setFields(DifferentialRevisionListView::getDefaultFields($user))
->setUser($user)
->loadAssets();
$phids = $view->getRequiredHandlePHIDs();
$handles = $this->loadViewerHandles($phids);
$view->setHandles($handles);
return id(new PHUIObjectBoxView())
->setHeaderText(pht('Open Revisions Affecting These Files'))
->appendChild($view);
}
/**
* Note this code is somewhat similar to the buildPatch method in
* @{class:DifferentialReviewRequestMail}.
*
* @return @{class:AphrontRedirectResponse}
*/
private function buildRawDiffResponse(
DifferentialRevision $revision,
array $changesets,
array $vs_changesets,
array $vs_map,
PhabricatorRepository $repository = null) {
assert_instances_of($changesets, 'DifferentialChangeset');
assert_instances_of($vs_changesets, 'DifferentialChangeset');
$viewer = $this->getRequest()->getUser();
foreach ($changesets as $changeset) {
$changeset->attachHunks($changeset->loadHunks());
}
$diff = new DifferentialDiff();
$diff->attachChangesets($changesets);
$raw_changes = $diff->buildChangesList();
$changes = array();
foreach ($raw_changes as $changedict) {
$changes[] = ArcanistDiffChange::newFromDictionary($changedict);
}
$loader = id(new PhabricatorFileBundleLoader())
->setViewer($viewer);
$bundle = ArcanistBundle::newFromChanges($changes);
$bundle->setLoadFileDataCallback(array($loader, 'loadFileData'));
$vcs = $repository ? $repository->getVersionControlSystem() : null;
switch ($vcs) {
case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
$raw_diff = $bundle->toGitPatch();
break;
case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
default:
$raw_diff = $bundle->toUnifiedDiff();
break;
}
$request_uri = $this->getRequest()->getRequestURI();
// this ends up being something like
// D123.diff
// or the verbose
// D123.vs123.id123.whitespaceignore-all.diff
// lame but nice to include these options
$file_name = ltrim($request_uri->getPath(), '/').'.';
foreach ($request_uri->getQueryParams() as $key => $value) {
if ($key == 'download') {
continue;
}
$file_name .= $key.$value.'.';
}
$file_name .= 'diff';
$file = PhabricatorFile::buildFromFileDataOrHash(
$raw_diff,
array(
'name' => $file_name,
'ttl' => (60 * 60 * 24),
'viewPolicy' => PhabricatorPolicies::POLICY_NOONE,
));
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
$file->attachToObject(
$this->getRequest()->getUser(),
$revision->getPHID());
unset($unguarded);
return id(new AphrontRedirectResponse())->setURI($file->getBestURI());
}
}
diff --git a/src/applications/differential/view/DifferentialAddCommentView.php b/src/applications/differential/view/DifferentialAddCommentView.php
index 822a85d6ac..a95ee9254f 100644
--- a/src/applications/differential/view/DifferentialAddCommentView.php
+++ b/src/applications/differential/view/DifferentialAddCommentView.php
@@ -1,205 +1,205 @@
<?php
final class DifferentialAddCommentView extends AphrontView {
private $revision;
private $actions;
private $actionURI;
private $draft;
private $auxFields;
private $reviewers = array();
private $ccs = array();
public function setRevision($revision) {
$this->revision = $revision;
return $this;
}
public function setAuxFields(array $aux_fields) {
assert_instances_of($aux_fields, 'DifferentialFieldSpecification');
$this->auxFields = $aux_fields;
return $this;
}
public function setActions(array $actions) {
$this->actions = $actions;
return $this;
}
public function setActionURI($uri) {
$this->actionURI = $uri;
return $this;
}
public function setDraft(PhabricatorDraft $draft = null) {
$this->draft = $draft;
return $this;
}
public function setReviewers(array $names) {
$this->reviewers = $names;
return $this;
}
public function setCCs(array $names) {
$this->ccs = $names;
return $this;
}
public function render() {
- require_celerity_resource('differential-revision-add-comment-css');
+ $this->requireResource('differential-revision-add-comment-css');
$is_serious = PhabricatorEnv::getEnvConfig('phabricator.serious-business');
$revision = $this->revision;
$action = null;
if ($this->draft) {
$action = idx($this->draft->getMetadata(), 'action');
}
$enable_reviewers = DifferentialAction::allowReviewers($action);
$enable_ccs = ($action == DifferentialAction::ACTION_ADDCCS);
$add_reviewers_labels = array(
'add_reviewers' => pht('Add Reviewers'),
'request_review' => pht('Add Reviewers'),
'resign' => pht('Suggest Reviewers'),
);
$form = new AphrontFormView();
$form
->setWorkflow(true)
->setUser($this->user)
->setAction($this->actionURI)
->addHiddenInput('revision_id', $revision->getID())
->appendChild(
id(new AphrontFormSelectControl())
->setLabel(pht('Action'))
->setName('action')
->setValue($action)
->setID('comment-action')
->setOptions($this->actions))
->appendChild(
id(new AphrontFormTokenizerControl())
->setLabel($enable_reviewers ? $add_reviewers_labels[$action] :
$add_reviewers_labels['add_reviewers'])
->setName('reviewers')
->setControlID('add-reviewers')
->setControlStyle($enable_reviewers ? null : 'display: none')
->setID('add-reviewers-tokenizer')
->setDisableBehavior(true))
->appendChild(
id(new AphrontFormTokenizerControl())
->setLabel(pht('Add CCs'))
->setName('ccs')
->setControlID('add-ccs')
->setControlStyle($enable_ccs ? null : 'display: none')
->setID('add-ccs-tokenizer')
->setDisableBehavior(true))
->appendChild(
id(new PhabricatorRemarkupControl())
->setName('comment')
->setID('comment-content')
->setLabel(pht('Comment'))
->setValue($this->draft ? $this->draft->getDraft() : null)
->setUser($this->user))
->appendChild(
id(new AphrontFormSubmitControl())
->setValue($is_serious ? pht('Submit') : pht('Clowncopterize')));
Javelin::initBehavior(
'differential-add-reviewers-and-ccs',
array(
'dynamic' => array(
'add-reviewers-tokenizer' => array(
'actions' => array(
'request_review' => 1,
'add_reviewers' => 1,
'resign' => 1,
),
'src' => '/typeahead/common/usersorprojects/',
'value' => $this->reviewers,
'row' => 'add-reviewers',
'ondemand' => PhabricatorEnv::getEnvConfig('tokenizer.ondemand'),
'labels' => $add_reviewers_labels,
'placeholder' => pht('Type a user or project name...'),
),
'add-ccs-tokenizer' => array(
'actions' => array('add_ccs' => 1),
'src' => '/typeahead/common/mailable/',
'value' => $this->ccs,
'row' => 'add-ccs',
'ondemand' => PhabricatorEnv::getEnvConfig('tokenizer.ondemand'),
'placeholder' => pht('Type a user or mailing list...'),
),
),
'select' => 'comment-action',
));
$diff = $revision->loadActiveDiff();
$warnings = mpull($this->auxFields, 'renderWarningBoxForRevisionAccept');
Javelin::initBehavior(
'differential-accept-with-errors',
array(
'select' => 'comment-action',
'warnings' => 'warnings',
));
$rev_id = $revision->getID();
Javelin::initBehavior(
'differential-feedback-preview',
array(
'uri' => '/differential/comment/preview/'.$rev_id.'/',
'preview' => 'comment-preview',
'action' => 'comment-action',
'content' => 'comment-content',
'previewTokenizers' => array(
'reviewers' => 'add-reviewers-tokenizer',
'ccs' => 'add-ccs-tokenizer',
),
'inlineuri' => '/differential/comment/inline/preview/'.$rev_id.'/',
'inline' => 'inline-comment-preview',
));
$warning_container = array();
foreach ($warnings as $warning) {
if ($warning) {
$warning_container[] = $warning->render();
}
}
$header = id(new PHUIHeaderView())
->setHeader($is_serious ? pht('Add Comment') : pht('Leap Into Action'));
$anchor = id(new PhabricatorAnchorView())
->setAnchorName('comment')
->setNavigationMarker(true);
$warn = phutil_tag('div', array('id' => 'warnings'), $warning_container);
$loading = phutil_tag(
'span',
array('class' => 'aphront-panel-preview-loading-text'),
pht('Loading comment preview...'));
$preview = phutil_tag_div(
'aphront-panel-preview aphront-panel-flush',
array(
phutil_tag('div', array('id' => 'comment-preview'), $loading),
phutil_tag('div', array('id' => 'inline-comment-preview')),
));
$comment_box = id(new PHUIObjectBoxView())
->setHeader($header)
->appendChild($anchor)
->appendChild($warn)
->appendChild($form);
return array($comment_box, $preview);
}
}
diff --git a/src/applications/differential/view/DifferentialChangesetDetailView.php b/src/applications/differential/view/DifferentialChangesetDetailView.php
index 029bd0a2a0..0e9a1711f3 100644
--- a/src/applications/differential/view/DifferentialChangesetDetailView.php
+++ b/src/applications/differential/view/DifferentialChangesetDetailView.php
@@ -1,164 +1,164 @@
<?php
final class DifferentialChangesetDetailView extends AphrontView {
private $changeset;
private $buttons = array();
private $editable;
private $symbolIndex;
private $id;
private $vsChangesetID;
public function setChangeset($changeset) {
$this->changeset = $changeset;
return $this;
}
public function addButton($button) {
$this->buttons[] = $button;
return $this;
}
public function setEditable($editable) {
$this->editable = $editable;
return $this;
}
public function setSymbolIndex($symbol_index) {
$this->symbolIndex = $symbol_index;
return $this;
}
public function getID() {
if (!$this->id) {
$this->id = celerity_generate_unique_node_id();
}
return $this->id;
}
public function setVsChangesetID($vs_changeset_id) {
$this->vsChangesetID = $vs_changeset_id;
return $this;
}
public function getVsChangesetID() {
return $this->vsChangesetID;
}
public function getFileIcon($filename) {
$path_info = pathinfo($filename);
$extension = idx($path_info, 'extension');
switch ($extension) {
case 'psd':
case 'ai':
$icon = 'preview';
break;
case 'conf':
$icon = 'wrench';
break;
case 'wav':
case 'mp3':
case 'aiff':
$icon = 'music';
break;
case 'm4v':
case 'mov':
$icon = 'film';
break;
case 'sql';
case 'db':
case 'csv':
$icon = 'data';
break;
case 'ics':
$icon = 'calendar';
break;
case 'zip':
case 'tar':
case 'bz':
case 'tgz':
case 'gz':
$icon = 'zip';
break;
case 'png':
case 'jpg':
case 'bmp':
case 'gif':
$icon = 'image';
break;
default:
$icon = 'file';
break;
}
return $icon;
}
public function render() {
- require_celerity_resource('differential-changeset-view-css');
- require_celerity_resource('syntax-highlighting-css');
+ $this->requireResource('differential-changeset-view-css');
+ $this->requireResource('syntax-highlighting-css');
Javelin::initBehavior('phabricator-oncopy', array());
$changeset = $this->changeset;
$class = 'differential-changeset';
if (!$this->editable) {
$class .= ' differential-changeset-immutable';
}
$buttons = null;
if ($this->buttons) {
$buttons = phutil_tag(
'div',
array(
'class' => 'differential-changeset-buttons',
),
$this->buttons);
}
$id = $this->getID();
if ($this->symbolIndex) {
Javelin::initBehavior(
'repository-crossreference',
array(
'container' => $id,
) + $this->symbolIndex);
}
$display_filename = $changeset->getDisplayFilename();
$display_icon = $this->getFileIcon($display_filename);
$icon = id(new PHUIIconView())
->setSpriteSheet(PHUIIconView::SPRITE_ICONS)
->setSpriteIcon($display_icon);
return javelin_tag(
'div',
array(
'sigil' => 'differential-changeset',
'meta' => array(
'left' => nonempty(
$this->getVsChangesetID(),
$this->changeset->getID()),
'right' => $this->changeset->getID(),
),
'class' => $class,
'id' => $id,
),
array(
id(new PhabricatorAnchorView())
->setAnchorName($changeset->getAnchorName())
->setNavigationMarker(true)
->render(),
$buttons,
phutil_tag('h1',
array(
'class' => 'differential-file-icon-header'),
array(
$icon,
$display_filename)),
phutil_tag('div', array('style' => 'clear: both'), ''),
$this->renderChildren(),
));
}
}
diff --git a/src/applications/differential/view/DifferentialChangesetListView.php b/src/applications/differential/view/DifferentialChangesetListView.php
index 17ed42eac9..069fd2bc4b 100644
--- a/src/applications/differential/view/DifferentialChangesetListView.php
+++ b/src/applications/differential/view/DifferentialChangesetListView.php
@@ -1,348 +1,348 @@
<?php
final class DifferentialChangesetListView extends AphrontView {
private $changesets = array();
private $visibleChangesets = array();
private $references = array();
private $inlineURI;
private $renderURI = '/differential/changeset/';
private $whitespace;
private $standaloneURI;
private $leftRawFileURI;
private $rightRawFileURI;
private $symbolIndexes = array();
private $repository;
private $branch;
private $diff;
private $vsMap = array();
private $title;
public function setTitle($title) {
$this->title = $title;
return $this;
}
private function getTitle() {
return $this->title;
}
public function setBranch($branch) {
$this->branch = $branch;
return $this;
}
private function getBranch() {
return $this->branch;
}
public function setChangesets($changesets) {
$this->changesets = $changesets;
return $this;
}
public function setVisibleChangesets($visible_changesets) {
$this->visibleChangesets = $visible_changesets;
return $this;
}
public function setInlineCommentControllerURI($uri) {
$this->inlineURI = $uri;
return $this;
}
public function setRepository(PhabricatorRepository $repository) {
$this->repository = $repository;
return $this;
}
public function setDiff(DifferentialDiff $diff) {
$this->diff = $diff;
return $this;
}
public function setRenderingReferences(array $references) {
$this->references = $references;
return $this;
}
public function setSymbolIndexes(array $indexes) {
$this->symbolIndexes = $indexes;
return $this;
}
public function setRenderURI($render_uri) {
$this->renderURI = $render_uri;
return $this;
}
public function setWhitespace($whitespace) {
$this->whitespace = $whitespace;
return $this;
}
public function setVsMap(array $vs_map) {
$this->vsMap = $vs_map;
return $this;
}
public function getVsMap() {
return $this->vsMap;
}
public function setStandaloneURI($uri) {
$this->standaloneURI = $uri;
return $this;
}
public function setRawFileURIs($l, $r) {
$this->leftRawFileURI = $l;
$this->rightRawFileURI = $r;
return $this;
}
public function render() {
- require_celerity_resource('differential-changeset-view-css');
+ $this->requireResource('differential-changeset-view-css');
$changesets = $this->changesets;
Javelin::initBehavior('differential-toggle-files', array(
'pht' => array(
'undo' => pht('Undo'),
'collapsed' => pht('This file content has been collapsed.'))
));
Javelin::initBehavior(
'differential-dropdown-menus',
array(
'pht' => array(
'Open in Editor' => pht('Open in Editor'),
'Show Entire File' => pht('Show Entire File'),
'Entire File Shown' => pht('Entire File Shown'),
"Can't Toggle Unloaded File" => pht("Can't Toggle Unloaded File"),
'Expand File' => pht('Expand File'),
'Collapse File' => pht('Collapse File'),
'Browse in Diffusion' => pht('Browse in Diffusion'),
'View Standalone' => pht('View Standalone'),
'Show Raw File (Left)' => pht('Show Raw File (Left)'),
'Show Raw File (Right)' => pht('Show Raw File (Right)'),
'Configure Editor' => pht('Configure Editor'),
),
));
$output = array();
$mapping = array();
foreach ($changesets as $key => $changeset) {
$file = $changeset->getFilename();
$class = 'differential-changeset';
if (!$this->inlineURI) {
$class .= ' differential-changeset-noneditable';
}
$ref = $this->references[$key];
$detail = new DifferentialChangesetDetailView();
$view_options = $this->renderViewOptionsDropdown(
$detail,
$ref,
$changeset);
$detail->setChangeset($changeset);
$detail->addButton($view_options);
$detail->setSymbolIndex(idx($this->symbolIndexes, $key));
$detail->setVsChangesetID(idx($this->vsMap, $changeset->getID()));
$detail->setEditable(true);
$uniq_id = 'diff-'.$changeset->getAnchorName();
if (isset($this->visibleChangesets[$key])) {
$load = 'Loading...';
$mapping[$uniq_id] = $ref;
} else {
$load = javelin_tag(
'a',
array(
'href' => '#'.$uniq_id,
'meta' => array(
'id' => $uniq_id,
'ref' => $ref,
'kill' => true,
),
'sigil' => 'differential-load',
'mustcapture' => true,
),
pht('Load'));
}
$detail->appendChild(
phutil_tag(
'div',
array(
'id' => $uniq_id,
),
phutil_tag('div', array('class' => 'differential-loading'), $load)));
$output[] = $detail->render();
}
- require_celerity_resource('aphront-tooltip-css');
+ $this->requireResource('aphront-tooltip-css');
- Javelin::initBehavior('differential-populate', array(
+ $this->initBehavior('differential-populate', array(
'registry' => $mapping,
'whitespace' => $this->whitespace,
'uri' => $this->renderURI,
));
- Javelin::initBehavior('differential-show-more', array(
+ $this->initBehavior('differential-show-more', array(
'uri' => $this->renderURI,
'whitespace' => $this->whitespace,
));
- Javelin::initBehavior('differential-comment-jump', array());
+ $this->initBehavior('differential-comment-jump', array());
if ($this->inlineURI) {
$undo_templates = $this->renderUndoTemplates();
Javelin::initBehavior('differential-edit-inline-comments', array(
'uri' => $this->inlineURI,
'undo_templates' => $undo_templates,
'stage' => 'differential-review-stage',
));
}
$header = id(new PHUIHeaderView())
->setHeader($this->getTitle());
$content = phutil_tag(
'div',
array(
'class' => 'differential-review-stage',
'id' => 'differential-review-stage',
),
$output);
$object_box = id(new PHUIObjectBoxView())
->setHeader($header)
->appendChild($content);
return $object_box;
}
/**
* Render the "Undo" markup for the inline comment undo feature.
*/
private function renderUndoTemplates() {
$link = javelin_tag(
'a',
array(
'href' => '#',
'sigil' => 'differential-inline-comment-undo',
),
pht('Undo'));
$div = phutil_tag(
'div',
array(
'class' => 'differential-inline-undo',
),
array('Changes discarded. ', $link));
return array(
'l' => phutil_tag('table', array(),
phutil_tag('tr', array(), array(
phutil_tag('th', array()),
phutil_tag('td', array(), $div),
phutil_tag('th', array()),
phutil_tag('td', array('colspan' => 3)),
))),
'r' => phutil_tag('table', array(),
phutil_tag('tr', array(), array(
phutil_tag('th', array()),
phutil_tag('td', array()),
phutil_tag('th', array()),
phutil_tag('td', array('colspan' => 3), $div),
))),
);
}
private function renderViewOptionsDropdown(
DifferentialChangesetDetailView $detail,
$ref,
DifferentialChangeset $changeset) {
$meta = array();
$qparams = array(
'ref' => $ref,
'whitespace' => $this->whitespace,
);
if ($this->standaloneURI) {
$uri = new PhutilURI($this->standaloneURI);
$uri->setQueryParams($uri->getQueryParams() + $qparams);
$meta['standaloneURI'] = (string)$uri;
}
$repository = $this->repository;
if ($repository) {
try {
$meta['diffusionURI'] =
(string)$repository->getDiffusionBrowseURIForPath(
$this->user,
$changeset->getAbsoluteRepositoryPath($repository, $this->diff),
idx($changeset->getMetadata(), 'line:first'),
$this->getBranch());
} catch (DiffusionSetupException $e) {
// Ignore
}
}
$change = $changeset->getChangeType();
if ($this->leftRawFileURI) {
if ($change != DifferentialChangeType::TYPE_ADD) {
$uri = new PhutilURI($this->leftRawFileURI);
$uri->setQueryParams($uri->getQueryParams() + $qparams);
$meta['leftURI'] = (string)$uri;
}
}
if ($this->rightRawFileURI) {
if ($change != DifferentialChangeType::TYPE_DELETE &&
$change != DifferentialChangeType::TYPE_MULTICOPY) {
$uri = new PhutilURI($this->rightRawFileURI);
$uri->setQueryParams($uri->getQueryParams() + $qparams);
$meta['rightURI'] = (string)$uri;
}
}
$user = $this->user;
if ($user && $repository) {
$path = ltrim(
$changeset->getAbsoluteRepositoryPath($repository, $this->diff),
'/');
$line = idx($changeset->getMetadata(), 'line:first', 1);
$callsign = $repository->getCallsign();
$editor_link = $user->loadEditorLink($path, $line, $callsign);
if ($editor_link) {
$meta['editor'] = $editor_link;
} else {
$meta['editorConfigure'] = '/settings/panel/display/';
}
}
$meta['containerID'] = $detail->getID();
$caret = phutil_tag('span', array('class' => 'caret'), '');
return javelin_tag(
'a',
array(
'class' => 'button grey small dropdown',
'meta' => $meta,
'href' => idx($meta, 'detailURI', '#'),
'target' => '_blank',
'sigil' => 'differential-view-options',
),
array(pht('View Options'), $caret));
}
}
diff --git a/src/applications/differential/view/DifferentialDiffTableOfContentsView.php b/src/applications/differential/view/DifferentialDiffTableOfContentsView.php
index 84fae42243..74391ef5f9 100644
--- a/src/applications/differential/view/DifferentialDiffTableOfContentsView.php
+++ b/src/applications/differential/view/DifferentialDiffTableOfContentsView.php
@@ -1,309 +1,309 @@
<?php
final class DifferentialDiffTableOfContentsView extends AphrontView {
private $changesets = array();
private $visibleChangesets = array();
private $references = array();
private $repository;
private $diff;
private $renderURI = '/differential/changeset/';
private $revisionID;
private $whitespace;
private $unitTestData;
public function setChangesets($changesets) {
$this->changesets = $changesets;
return $this;
}
public function setVisibleChangesets($visible_changesets) {
$this->visibleChangesets = $visible_changesets;
return $this;
}
public function setRenderingReferences(array $references) {
$this->references = $references;
return $this;
}
public function setRepository(PhabricatorRepository $repository) {
$this->repository = $repository;
return $this;
}
public function setDiff(DifferentialDiff $diff) {
$this->diff = $diff;
return $this;
}
public function setUnitTestData($unit_test_data) {
$this->unitTestData = $unit_test_data;
return $this;
}
public function setRevisionID($revision_id) {
$this->revisionID = $revision_id;
return $this;
}
public function setWhitespace($whitespace) {
$this->whitespace = $whitespace;
return $this;
}
public function render() {
- require_celerity_resource('differential-core-view-css');
- require_celerity_resource('differential-table-of-contents-css');
+ $this->requireResource('differential-core-view-css');
+ $this->requireResource('differential-table-of-contents-css');
$rows = array();
$coverage = array();
if ($this->unitTestData) {
$coverage_by_file = array();
foreach ($this->unitTestData as $result) {
$test_coverage = idx($result, 'coverage');
if (!$test_coverage) {
continue;
}
foreach ($test_coverage as $file => $results) {
$coverage_by_file[$file][] = $results;
}
}
foreach ($coverage_by_file as $file => $coverages) {
$coverage[$file] = ArcanistUnitTestResult::mergeCoverage($coverages);
}
}
$changesets = $this->changesets;
$paths = array();
foreach ($changesets as $id => $changeset) {
$type = $changeset->getChangeType();
$ftype = $changeset->getFileType();
$ref = idx($this->references, $id);
$display_file = $changeset->getDisplayFilename();
$meta = null;
if (DifferentialChangeType::isOldLocationChangeType($type)) {
$away = $changeset->getAwayPaths();
if (count($away) > 1) {
$meta = array();
if ($type == DifferentialChangeType::TYPE_MULTICOPY) {
$meta[] = pht('Deleted after being copied to multiple locations:');
} else {
$meta[] = pht('Copied to multiple locations:');
}
foreach ($away as $path) {
$meta[] = $path;
}
$meta = phutil_implode_html(phutil_tag('br'), $meta);
} else {
if ($type == DifferentialChangeType::TYPE_MOVE_AWAY) {
$display_file = $this->renderRename(
$display_file,
reset($away),
"\xE2\x86\x92");
} else {
$meta = pht('Copied to %s', reset($away));
}
}
} else if ($type == DifferentialChangeType::TYPE_MOVE_HERE) {
$old_file = $changeset->getOldFile();
$display_file = $this->renderRename(
$display_file,
$old_file,
"\xE2\x86\x90");
} else if ($type == DifferentialChangeType::TYPE_COPY_HERE) {
$meta = pht('Copied from %s', $changeset->getOldFile());
}
$link = $this->renderChangesetLink($changeset, $ref, $display_file);
$line_count = $changeset->getAffectedLineCount();
if ($line_count == 0) {
$lines = null;
} else {
$lines = ' '.pht('(%d line(s))', $line_count);
}
$char = DifferentialChangeType::getSummaryCharacterForChangeType($type);
$chartitle = DifferentialChangeType::getFullNameForChangeType($type);
$desc = DifferentialChangeType::getShortNameForFileType($ftype);
if ($desc) {
$desc = '('.$desc.')';
}
$pchar =
($changeset->getOldProperties() === $changeset->getNewProperties())
? null
: phutil_tag('span', array('title' => pht('Properties Changed')), 'M')
;
$fname = $changeset->getFilename();
$cov = $this->renderCoverage($coverage, $fname);
if ($cov === null) {
$mcov = $cov = phutil_tag('em', array(), '-');
} else {
$mcov = phutil_tag(
'div',
array(
'id' => 'differential-mcoverage-'.md5($fname),
'class' => 'differential-mcoverage-loading',
),
(isset($this->visibleChangesets[$id]) ? 'Loading...' : '?'));
}
$rows[] = phutil_tag('tr', array(), array(
phutil_tag(
'td',
array('class' => 'differential-toc-char', 'title' => $chartitle),
$char),
phutil_tag('td', array('class' => 'differential-toc-prop'), $pchar),
phutil_tag('td', array('class' => 'differential-toc-ftype'), $desc),
phutil_tag(
'td',
array('class' => 'differential-toc-file'),
array($link, $lines)),
phutil_tag('td', array('class' => 'differential-toc-cov'), $cov),
phutil_tag('td', array('class' => 'differential-toc-mcov'), $mcov),
));
if ($meta) {
$rows[] = phutil_tag('tr', array(), array(
phutil_tag('td', array('colspan' => 3)),
phutil_tag('td', array('class' => 'differential-toc-meta'), $meta),
));
}
if ($this->diff && $this->repository) {
$paths[] =
$changeset->getAbsoluteRepositoryPath($this->repository, $this->diff);
}
}
$editor_link = null;
if ($paths && $this->user) {
$editor_link = $this->user->loadEditorLink(
$paths,
1, // line number
$this->repository->getCallsign());
if ($editor_link) {
$editor_link =
phutil_tag(
'a',
array(
'href' => $editor_link,
'class' => 'button differential-toc-edit-all',
),
pht('Open All in Editor'));
}
}
$reveal_link = javelin_tag(
'a',
array(
'sigil' => 'differential-reveal-all',
'mustcapture' => true,
'class' => 'button differential-toc-reveal-all',
),
pht('Show All Context'));
$buttons = phutil_tag('tr', array(),
phutil_tag('td', array('colspan' => 7),
array($editor_link, $reveal_link)));
$content = hsprintf(
'%s'.
'<div class="differential-toc differential-panel">'.
'<table>'.
'<tr>'.
'<th></th>'.
'<th></th>'.
'<th></th>'.
'<th>Path</th>'.
'<th class="differential-toc-cov">%s</th>'.
'<th class="differential-toc-mcov">%s</th>'.
'</tr>'.
'%s%s'.
'</table>'.
'</div>',
id(new PhabricatorAnchorView())
->setAnchorName('toc')
->setNavigationMarker(true)
->render(),
pht('Coverage (All)'),
pht('Coverage (Touched)'),
phutil_implode_html("\n", $rows),
$buttons);
return id(new PHUIObjectBoxView())
->setHeaderText(pht('Table of Contents'))
->appendChild($content);
}
private function renderRename($display_file, $other_file, $arrow) {
$old = explode('/', $display_file);
$new = explode('/', $other_file);
$start = count($old);
foreach ($old as $index => $part) {
if (!isset($new[$index]) || $part != $new[$index]) {
$start = $index;
break;
}
}
$end = count($old);
foreach (array_reverse($old) as $from_end => $part) {
$index = count($new) - $from_end - 1;
if (!isset($new[$index]) || $part != $new[$index]) {
$end = $from_end;
break;
}
}
$rename =
'{'.
implode('/', array_slice($old, $start, count($old) - $end - $start)).
' '.$arrow.' '.
implode('/', array_slice($new, $start, count($new) - $end - $start)).
'}';
array_splice($new, $start, count($new) - $end - $start, $rename);
return implode('/', $new);
}
private function renderCoverage(array $coverage, $file) {
$info = idx($coverage, $file);
if (!$info) {
return null;
}
$not_covered = substr_count($info, 'U');
$covered = substr_count($info, 'C');
if (!$not_covered && !$covered) {
return null;
}
return sprintf('%d%%', 100 * ($covered / ($covered + $not_covered)));
}
private function renderChangesetLink(
DifferentialChangeset $changeset,
$ref,
$display_file) {
return javelin_tag(
'a',
array(
'href' => '#'.$changeset->getAnchorName(),
'meta' => array(
'id' => 'diff-'.$changeset->getAnchorName(),
'ref' => $ref,
),
'sigil' => 'differential-load',
),
$display_file);
}
}
diff --git a/src/applications/differential/view/DifferentialLocalCommitsView.php b/src/applications/differential/view/DifferentialLocalCommitsView.php
index 4321807139..d4fbe45431 100644
--- a/src/applications/differential/view/DifferentialLocalCommitsView.php
+++ b/src/applications/differential/view/DifferentialLocalCommitsView.php
@@ -1,145 +1,145 @@
<?php
final class DifferentialLocalCommitsView extends AphrontView {
private $localCommits;
public function setLocalCommits($local_commits) {
$this->localCommits = $local_commits;
return $this;
}
public function render() {
$user = $this->user;
if (!$user) {
throw new Exception("Call setUser() before render()-ing this view.");
}
$local = $this->localCommits;
if (!$local) {
return null;
}
- require_celerity_resource('differential-local-commits-view-css');
+ $this->requireResource('differential-local-commits-view-css');
$has_tree = false;
$has_local = false;
foreach ($local as $commit) {
if (idx($commit, 'tree')) {
$has_tree = true;
}
if (idx($commit, 'local')) {
$has_local = true;
}
}
$rows = array();
$highlight = true;
foreach ($local as $commit) {
if ($highlight) {
$class = 'alt';
$highlight = false;
} else {
$class = '';
$highlight = true;
}
$row = array();
if (idx($commit, 'commit')) {
$commit_hash = self::formatCommit($commit['commit']);
} else if (isset($commit['rev'])) {
$commit_hash = self::formatCommit($commit['rev']);
} else {
$commit_hash = null;
}
$row[] = phutil_tag('td', array(), $commit_hash);
if ($has_tree) {
$tree = idx($commit, 'tree');
$tree = self::formatCommit($tree);
$row[] = phutil_tag('td', array(), $tree);
}
if ($has_local) {
$local_rev = idx($commit, 'local', null);
$row[] = phutil_tag('td', array(), $local_rev);
}
$parents = idx($commit, 'parents', array());
foreach ($parents as $k => $parent) {
if (is_array($parent)) {
$parent = idx($parent, 'rev');
}
$parents[$k] = self::formatCommit($parent);
}
$parents = phutil_implode_html(phutil_tag('br'), $parents);
$row[] = phutil_tag('td', array(), $parents);
$author = nonempty(
idx($commit, 'user'),
idx($commit, 'author'));
$row[] = phutil_tag('td', array(), $author);
$message = idx($commit, 'message');
$summary = idx($commit, 'summary');
$summary = phutil_utf8_shorten($summary, 80);
$view = new AphrontMoreView();
$view->setSome($summary);
if ($message && (trim($summary) != trim($message))) {
$view->setMore(phutil_escape_html_newlines($message));
}
$row[] = phutil_tag(
'td',
array(
'class' => 'summary',
),
$view->render());
$date = nonempty(
idx($commit, 'date'),
idx($commit, 'time'));
if ($date) {
$date = phabricator_datetime($date, $user);
}
$row[] = phutil_tag('td', array(), $date);
$rows[] = phutil_tag('tr', array('class' => $class), $row);
}
$headers = array();
$headers[] = phutil_tag('th', array(), pht('Commit'));
if ($has_tree) {
$headers[] = phutil_tag('th', array(), pht('Tree'));
}
if ($has_local) {
$headers[] = phutil_tag('th', array(), pht('Local'));
}
$headers[] = phutil_tag('th', array(), pht('Parents'));
$headers[] = phutil_tag('th', array(), pht('Author'));
$headers[] = phutil_tag('th', array(), pht('Summary'));
$headers[] = phutil_tag('th', array(), pht('Date'));
$headers = phutil_tag('tr', array(), $headers);
$content = phutil_tag_div('differential-panel', phutil_tag(
'table',
array('class' => 'differential-local-commits-table'),
array($headers, phutil_implode_html("\n", $rows))));
return id(new PHUIObjectBoxView())
->setHeaderText(pht('Local Commits'))
->appendChild($content);
}
private static function formatCommit($commit) {
return substr($commit, 0, 12);
}
}
diff --git a/src/applications/differential/view/DifferentialResultsTableView.php b/src/applications/differential/view/DifferentialResultsTableView.php
index f2be210728..226c68484c 100644
--- a/src/applications/differential/view/DifferentialResultsTableView.php
+++ b/src/applications/differential/view/DifferentialResultsTableView.php
@@ -1,115 +1,115 @@
<?php
final class DifferentialResultsTableView extends AphrontView {
private $rows;
private $showMoreString;
public function setRows(array $rows) {
$this->rows = $rows;
return $this;
}
public function setShowMoreString($show_more_string) {
$this->showMoreString = $show_more_string;
return $this;
}
public function render() {
$rows = array();
$any_hidden = false;
foreach ($this->rows as $row) {
$style = idx($row, 'style');
switch ($style) {
case 'section':
$cells = phutil_tag(
'th',
array(
'colspan' => 2,
),
idx($row, 'name'));
break;
default:
$name = phutil_tag(
'th',
array(
),
idx($row, 'name'));
$value = phutil_tag(
'td',
array(
),
idx($row, 'value'));
$cells = array($name, $value);
break;
}
$show = idx($row, 'show');
$rows[] = javelin_tag(
'tr',
array(
'style' => $show ? null : 'display: none',
'sigil' => $show ? null : 'differential-results-row-toggle',
'class' => 'differential-results-row-'.$style,
),
$cells);
if (!$show) {
$any_hidden = true;
}
}
if ($any_hidden) {
$show_more = javelin_tag(
'a',
array(
'href' => '#',
'mustcapture' => true,
),
$this->showMoreString);
$hide_more = javelin_tag(
'a',
array(
'href' => '#',
'mustcapture' => true,
),
'Hide');
$rows[] = javelin_tag(
'tr',
array(
'class' => 'differential-results-row-show',
'sigil' => 'differential-results-row-show',
),
phutil_tag('th', array('colspan' => 2), $show_more));
$rows[] = javelin_tag(
'tr',
array(
'class' => 'differential-results-row-show',
'sigil' => 'differential-results-row-hide',
'style' => 'display: none',
),
phutil_tag('th', array('colspan' => 2), $hide_more));
- Javelin::initBehavior('differential-show-field-details');
+ $this->initBehavior('differential-show-field-details');
}
- require_celerity_resource('differential-results-table-css');
+ $this->requireResource('differential-results-table-css');
return javelin_tag(
'table',
array(
'class' => 'differential-results-table',
'sigil' => 'differential-results-table',
),
$rows);
}
}
diff --git a/src/applications/differential/view/DifferentialRevisionCommentListView.php b/src/applications/differential/view/DifferentialRevisionCommentListView.php
index 86d0c76bfa..b03cee03db 100644
--- a/src/applications/differential/view/DifferentialRevisionCommentListView.php
+++ b/src/applications/differential/view/DifferentialRevisionCommentListView.php
@@ -1,198 +1,198 @@
<?php
final class DifferentialRevisionCommentListView extends AphrontView {
private $comments;
private $handles;
private $inlines;
private $changesets;
private $target;
private $versusDiffID;
private $id;
public function setComments(array $comments) {
assert_instances_of($comments, 'DifferentialComment');
$this->comments = $comments;
return $this;
}
public function setInlineComments(array $inline_comments) {
assert_instances_of($inline_comments, 'PhabricatorInlineCommentInterface');
$this->inlines = $inline_comments;
return $this;
}
public function setHandles(array $handles) {
assert_instances_of($handles, 'PhabricatorObjectHandle');
$this->handles = $handles;
return $this;
}
public function setChangesets(array $changesets) {
assert_instances_of($changesets, 'DifferentialChangeset');
$this->changesets = $changesets;
return $this;
}
public function setTargetDiff(DifferentialDiff $target) {
$this->target = $target;
return $this;
}
public function setVersusDiffID($diff_vs) {
$this->versusDiffID = $diff_vs;
return $this;
}
public function getID() {
if (!$this->id) {
$this->id = celerity_generate_unique_node_id();
}
return $this->id;
}
public function render() {
- require_celerity_resource('differential-revision-comment-list-css');
+ $this->requireResource('differential-revision-comment-list-css');
$engine = new PhabricatorMarkupEngine();
$engine->setViewer($this->user);
foreach ($this->comments as $comment) {
$comment->giveFacebookSomeArbitraryDiff($this->target);
$engine->addObject(
$comment,
DifferentialComment::MARKUP_FIELD_BODY);
}
foreach ($this->inlines as $inline) {
$engine->addObject(
$inline,
PhabricatorInlineCommentInterface::MARKUP_FIELD_BODY);
}
$engine->process();
$inlines = mgroup($this->inlines, 'getCommentID');
$num = 1;
$html = array();
foreach ($this->comments as $comment) {
$view = new DifferentialRevisionCommentView();
$view->setComment($comment);
$view->setUser($this->user);
$view->setHandles($this->handles);
$view->setMarkupEngine($engine);
$view->setInlineComments(idx($inlines, $comment->getID(), array()));
$view->setChangesets($this->changesets);
$view->setTargetDiff($this->target);
$view->setVersusDiffID($this->versusDiffID);
if ($comment->getAction() == DifferentialAction::ACTION_SUMMARIZE) {
$view->setAnchorName('summary');
} else if ($comment->getAction() == DifferentialAction::ACTION_TESTPLAN) {
$view->setAnchorName('test-plan');
} else {
$view->setAnchorName('comment-'.$num);
$num++;
}
$html[] = $view->render();
}
$objs = array_reverse(array_values($this->comments));
$html = array_reverse(array_values($html));
$user = $this->user;
$last_comment = null;
// Find the most recent comment by the viewer.
foreach ($objs as $position => $comment) {
if ($user && ($comment->getAuthorPHID() == $user->getPHID())) {
if ($last_comment === null) {
$last_comment = $position;
} else if ($last_comment == $position - 1) {
// If the viewer made several comments in a row, show them all. This
// is a spaz rule for epriestley.
$last_comment = $position;
}
}
}
$header = array();
$hidden = array();
if ($last_comment !== null) {
foreach ($objs as $position => $comment) {
if (!$comment->getID()) {
// These are synthetic comments with summary/test plan information.
$header[] = $html[$position];
unset($html[$position]);
continue;
}
if ($position <= $last_comment) {
// Always show comments after the viewer's last comment.
continue;
}
if ($position < 3) {
// Always show the 3 most recent comments.
continue;
}
$hidden[] = $position;
}
}
if (count($hidden) <= 3) {
// Don't hide if there's not much to hide.
$hidden = array();
}
$header = array_reverse($header);
$hidden = array_select_keys($html, $hidden);
$visible = array_diff_key($html, $hidden);
$hidden = array_reverse($hidden);
$visible = array_reverse($visible);
if ($hidden) {
- Javelin::initBehavior(
+ $this->initBehavior(
'differential-show-all-comments',
array(
'markup' => implode("\n", $hidden),
));
$hidden = javelin_tag(
'div',
array(
'sigil' => "differential-all-comments-container",
),
phutil_tag(
'div',
array(
'class' => 'differential-older-comments-are-hidden',
),
array(
pht(
'%s older comments are hidden.',
new PhutilNumber(count($hidden))),
' ',
javelin_tag(
'a',
array(
'href' => '#',
'mustcapture' => true,
'sigil' => 'differential-show-all-comments',
),
pht('Show all comments.')),
)));
} else {
$hidden = null;
}
return javelin_tag(
'div',
array(
'class' => 'differential-comment-list',
'id' => $this->getID(),
),
array_merge($header, array($hidden), $visible));
}
}
diff --git a/src/applications/differential/view/DifferentialRevisionCommentView.php b/src/applications/differential/view/DifferentialRevisionCommentView.php
index 820954dfff..293d93a4a6 100644
--- a/src/applications/differential/view/DifferentialRevisionCommentView.php
+++ b/src/applications/differential/view/DifferentialRevisionCommentView.php
@@ -1,302 +1,302 @@
<?php
final class DifferentialRevisionCommentView extends AphrontView {
private $comment;
private $handles;
private $markupEngine;
private $preview;
private $inlines;
private $changesets;
private $target;
private $anchorName;
private $versusDiffID;
public function setComment($comment) {
$this->comment = $comment;
return $this;
}
public function setHandles(array $handles) {
assert_instances_of($handles, 'PhabricatorObjectHandle');
$this->handles = $handles;
return $this;
}
public function setMarkupEngine(PhabricatorMarkupEngine $markup_engine) {
$this->markupEngine = $markup_engine;
return $this;
}
public function setPreview($preview) {
$this->preview = $preview;
return $this;
}
public function setInlineComments(array $inline_comments) {
assert_instances_of($inline_comments, 'PhabricatorInlineCommentInterface');
$this->inlines = $inline_comments;
return $this;
}
public function setChangesets(array $changesets) {
assert_instances_of($changesets, 'DifferentialChangeset');
// Ship these in sorted by getSortKey() and keyed by ID... or else!
$this->changesets = $changesets;
return $this;
}
public function setTargetDiff($target) {
$this->target = $target;
return $this;
}
public function setVersusDiffID($diff_vs) {
$this->versusDiffID = $diff_vs;
return $this;
}
public function setAnchorName($anchor_name) {
$this->anchorName = $anchor_name;
return $this;
}
public function render() {
if (!$this->user) {
throw new Exception("Call setUser() before rendering!");
}
- require_celerity_resource('phabricator-remarkup-css');
- require_celerity_resource('differential-revision-comment-css');
+ $this->requireResource('phabricator-remarkup-css');
+ $this->requireResource('differential-revision-comment-css');
$comment = $this->comment;
$action = $comment->getAction();
$action_class = 'differential-comment-action-'.$action;
$info = array();
$content = $comment->getContent();
$hide_comments = true;
if (strlen(rtrim($content))) {
$hide_comments = false;
$content = $this->markupEngine->getOutput(
$comment,
PhabricatorInlineCommentInterface::MARKUP_FIELD_BODY);
$content = phutil_tag_div('phabricator-remarkup', $content);
}
$inline_render = $this->renderInlineComments();
if ($inline_render) {
$hide_comments = false;
}
$author = $this->handles[$comment->getAuthorPHID()];
$author_link = $author->renderLink();
$metadata = $comment->getMetadata();
$added_reviewers = idx(
$metadata,
DifferentialComment::METADATA_ADDED_REVIEWERS,
array());
$removed_reviewers = idx(
$metadata,
DifferentialComment::METADATA_REMOVED_REVIEWERS,
array());
$added_ccs = idx(
$metadata,
DifferentialComment::METADATA_ADDED_CCS,
array());
$verb = DifferentialAction::getActionPastTenseVerb($comment->getAction());
$actions = array();
// TODO: i18n
switch ($comment->getAction()) {
case DifferentialAction::ACTION_ADDCCS:
$actions[] = hsprintf(
"%s added CCs: %s.",
$author_link,
$this->renderHandleList($added_ccs));
$added_ccs = null;
break;
case DifferentialAction::ACTION_ADDREVIEWERS:
$actions[] = hsprintf(
"%s added reviewers: %s.",
$author_link,
$this->renderHandleList($added_reviewers));
$added_reviewers = null;
break;
case DifferentialAction::ACTION_UPDATE:
$diff_id = idx($metadata, DifferentialComment::METADATA_DIFF_ID);
if ($diff_id) {
$diff_link = phutil_tag(
'a',
array(
'href' => '/D'.$comment->getRevisionID().'?id='.$diff_id,
),
'Diff #'.$diff_id);
$actions[] = hsprintf(
"%s updated this revision to %s.",
$author_link,
$diff_link);
} else {
$actions[] = hsprintf(
"%s %s this revision.",
$author_link,
$verb);
}
break;
default:
$actions[] = hsprintf(
"%s %s this revision.",
$author_link,
$verb);
break;
}
if ($added_reviewers) {
$actions[] = hsprintf(
"%s added reviewers: %s.",
$author_link,
$this->renderHandleList($added_reviewers));
}
if ($removed_reviewers) {
$actions[] = hsprintf(
"%s removed reviewers: %s.",
$author_link,
$this->renderHandleList($removed_reviewers));
}
if ($added_ccs) {
$actions[] = hsprintf(
"%s added CCs: %s.",
$author_link,
$this->renderHandleList($added_ccs));
}
foreach ($actions as $key => $action) {
$actions[$key] = phutil_tag('div', array(), $action);
}
$xaction_view = id(new PhabricatorTransactionView())
->setUser($this->user)
->setImageURI($author->getImageURI())
->setContentSource($comment->getContentSource())
->addClass($action_class)
->setActions($actions);
if ($this->preview) {
$xaction_view->setIsPreview($this->preview);
} else {
$xaction_view->setEpoch($comment->getDateCreated());
if ($this->anchorName) {
$anchor_text =
'D'.$comment->getRevisionID().
'#'.preg_replace('/^comment-/', '', $this->anchorName);
$xaction_view->setAnchor($this->anchorName, $anchor_text);
}
}
if (!$hide_comments) {
$xaction_view->appendChild(phutil_tag_div(
'differential-comment-core',
array($content, $inline_render)));
}
return $xaction_view->render();
}
private function renderHandleList(array $phids) {
$result = array();
foreach ($phids as $phid) {
$result[] = $this->handles[$phid]->renderLink();
}
return phutil_implode_html(', ', $result);
}
private function renderInlineComments() {
if (!$this->inlines) {
return null;
}
$inlines = $this->inlines;
$changesets = $this->changesets;
$inlines_by_changeset = mgroup($inlines, 'getChangesetID');
$inlines_by_changeset = array_select_keys(
$inlines_by_changeset,
array_keys($this->changesets));
$view = new PhabricatorInlineSummaryView();
foreach ($inlines_by_changeset as $changeset_id => $inlines) {
$changeset = $changesets[$changeset_id];
$items = array();
foreach ($inlines as $inline) {
$on_target = ($this->target) &&
($this->target->getID() == $changeset->getDiffID());
$is_visible = false;
if ($inline->getIsNewFile()) {
// This comment is on the right side of the versus diff, and visible
// on the left side of the page.
if ($this->versusDiffID) {
if ($changeset->getDiffID() == $this->versusDiffID) {
$is_visible = true;
}
}
// This comment is on the right side of the target diff, and visible
// on the right side of the page.
if ($on_target) {
$is_visible = true;
}
} else {
// Ths comment is on the left side of the target diff, and visible
// on the left side of the page.
if (!$this->versusDiffID) {
if ($on_target) {
$is_visible = true;
}
}
// TODO: We still get one edge case wrong here, when we have a
// versus diff and the file didn't exist in the old version. The
// comment is visible because we show the left side of the target
// diff when there's no corresponding file in the versus diff, but
// we incorrectly link it off-page.
}
$item = array(
'id' => $inline->getID(),
'line' => $inline->getLineNumber(),
'length' => $inline->getLineLength(),
'content' => $this->markupEngine->getOutput(
$inline,
DifferentialInlineComment::MARKUP_FIELD_BODY),
);
if (!$is_visible) {
$diff_id = $changeset->getDiffID();
$item['where'] = '(On Diff #'.$diff_id.')';
$item['href'] =
'D'.$this->comment->getRevisionID().
'?id='.$diff_id.
'#inline-'.$inline->getID();
}
$items[] = $item;
}
$view->addCommentGroup($changeset->getFilename(), $items);
}
return $view;
}
}
diff --git a/src/applications/differential/view/DifferentialRevisionDetailView.php b/src/applications/differential/view/DifferentialRevisionDetailView.php
index fad95b5ada..7e0f3c5de6 100644
--- a/src/applications/differential/view/DifferentialRevisionDetailView.php
+++ b/src/applications/differential/view/DifferentialRevisionDetailView.php
@@ -1,177 +1,177 @@
<?php
final class DifferentialRevisionDetailView extends AphrontView {
private $revision;
private $actions;
private $auxiliaryFields = array();
private $diff;
private $uri;
public function setURI($uri) {
$this->uri = $uri;
return $this;
}
public function getURI() {
return $this->uri;
}
public function setDiff(DifferentialDiff $diff) {
$this->diff = $diff;
return $this;
}
private function getDiff() {
return $this->diff;
}
public function setRevision(DifferentialRevision $revision) {
$this->revision = $revision;
return $this;
}
public function setActions(array $actions) {
$this->actions = $actions;
return $this;
}
private function getActions() {
return $this->actions;
}
public function setAuxiliaryFields(array $fields) {
assert_instances_of($fields, 'DifferentialFieldSpecification');
$this->auxiliaryFields = $fields;
return $this;
}
public function render() {
- require_celerity_resource('differential-core-view-css');
+ $this->requireResource('differential-core-view-css');
$revision = $this->revision;
$user = $this->getUser();
$header = $this->renderHeader($revision);
$actions = id(new PhabricatorActionListView())
->setUser($user)
->setObject($revision)
->setObjectURI($this->getURI());
foreach ($this->getActions() as $action) {
$obj = id(new PhabricatorActionView())
->setIcon(idx($action, 'icon', 'edit'))
->setName($action['name'])
->setHref(idx($action, 'href'))
->setWorkflow(idx($action, 'sigil') == 'workflow')
->setRenderAsForm(!empty($action['instant']))
->setUser($user)
->setDisabled(idx($action, 'disabled', false));
$actions->addAction($obj);
}
$properties = id(new PHUIPropertyListView())
->setUser($user)
->setObject($revision);
$status = $revision->getStatus();
$local_vcs = $this->getDiff()->getSourceControlSystem();
$next_step = null;
if ($status == ArcanistDifferentialRevisionStatus::ACCEPTED) {
switch ($local_vcs) {
case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
$bookmark = $this->getDiff()->getBookmark();
$next_step = ($bookmark != ''
? csprintf('arc land %s', $bookmark)
: 'arc land');
break;
case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
$branch = $this->getDiff()->getBranch();
$next_step = ($branch != ''
? csprintf('arc land %s', $branch)
: 'arc land');
break;
case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
$next_step = 'arc commit';
break;
}
}
if ($next_step) {
$next_step = phutil_tag('tt', array(), $next_step);
$properties->addProperty(pht('Next Step'), $next_step);
}
foreach ($this->auxiliaryFields as $field) {
$value = $field->renderValueForRevisionView();
if ($value !== null) {
$label = rtrim($field->renderLabelForRevisionView(), ':');
$properties->addProperty($label, $value);
}
}
$properties->setHasKeyboardShortcuts(true);
$properties->setActionList($actions);
$properties->invokeWillRenderEvent();
if (strlen($revision->getSummary())) {
$properties->addSectionHeader(
pht('Summary'),
PHUIPropertyListView::ICON_SUMMARY);
$properties->addTextContent(
PhabricatorMarkupEngine::renderOneObject(
id(new PhabricatorMarkupOneOff())
->setPreserveLinebreaks(true)
->setContent($revision->getSummary()),
'default',
$user));
}
if (strlen($revision->getTestPlan())) {
$properties->addSectionHeader(
pht('Test Plan'),
PHUIPropertyListView::ICON_TESTPLAN);
$properties->addTextContent(
PhabricatorMarkupEngine::renderOneObject(
id(new PhabricatorMarkupOneOff())
->setPreserveLinebreaks(true)
->setContent($revision->getTestPlan()),
'default',
$user));
}
$object_box = id(new PHUIObjectBoxView())
->setHeader($header)
->addPropertyList($properties);
return $object_box;
}
private function renderHeader(DifferentialRevision $revision) {
$view = id(new PHUIHeaderView())
->setHeader($revision->getTitle($revision))
->setUser($this->getUser())
->setPolicyObject($revision);
$status = $revision->getStatus();
$status_name =
DifferentialRevisionStatus::renderFullDescription($status);
$view->addProperty(PHUIHeaderView::PROPERTY_STATUS, $status_name);
return $view;
}
public static function renderTagForRevision(
DifferentialRevision $revision) {
$status = $revision->getStatus();
$status_name =
ArcanistDifferentialRevisionStatus::getNameForRevisionStatus($status);
return id(new PhabricatorTagView())
->setType(PhabricatorTagView::TYPE_STATE)
->setName($status_name);
}
}
diff --git a/src/applications/differential/view/DifferentialRevisionListView.php b/src/applications/differential/view/DifferentialRevisionListView.php
index 86bab4dbc6..0addfa5b62 100644
--- a/src/applications/differential/view/DifferentialRevisionListView.php
+++ b/src/applications/differential/view/DifferentialRevisionListView.php
@@ -1,253 +1,253 @@
<?php
/**
* Render a table of Differential revisions.
*/
final class DifferentialRevisionListView extends AphrontView {
private $revisions;
private $flags = array();
private $drafts = array();
private $handles;
private $fields;
private $highlightAge;
private $header;
private $noDataString;
public function setNoDataString($no_data_string) {
$this->noDataString = $no_data_string;
return $this;
}
public function setHeader($header) {
$this->header = $header;
return $this;
}
public function setFields(array $fields) {
assert_instances_of($fields, 'DifferentialFieldSpecification');
$this->fields = $fields;
return $this;
}
public function setRevisions(array $revisions) {
assert_instances_of($revisions, 'DifferentialRevision');
$this->revisions = $revisions;
return $this;
}
public function setHighlightAge($bool) {
$this->highlightAge = $bool;
return $this;
}
public function getRequiredHandlePHIDs() {
$phids = array();
foreach ($this->fields as $field) {
foreach ($this->revisions as $revision) {
$phids[] = $field->getRequiredHandlePHIDsForRevisionList($revision);
}
}
return array_mergev($phids);
}
public function setHandles(array $handles) {
assert_instances_of($handles, 'PhabricatorObjectHandle');
$this->handles = $handles;
return $this;
}
public function loadAssets() {
$user = $this->user;
if (!$user) {
throw new Exception("Call setUser() before loadAssets()!");
}
if ($this->revisions === null) {
throw new Exception("Call setRevisions() before loadAssets()!");
}
$this->flags = id(new PhabricatorFlagQuery())
->setViewer($user)
->withOwnerPHIDs(array($user->getPHID()))
->withObjectPHIDs(mpull($this->revisions, 'getPHID'))
->execute();
$this->drafts = id(new DifferentialRevisionQuery())
->setViewer($user)
->withIDs(mpull($this->revisions, 'getID'))
->withDraftRepliesByAuthors(array($user->getPHID()))
->execute();
return $this;
}
public function render() {
$user = $this->user;
if (!$user) {
throw new Exception("Call setUser() before render()!");
}
$fresh = PhabricatorEnv::getEnvConfig('differential.days-fresh');
if ($fresh) {
$fresh = PhabricatorCalendarHoliday::getNthBusinessDay(
time(),
-$fresh);
}
$stale = PhabricatorEnv::getEnvConfig('differential.days-stale');
if ($stale) {
$stale = PhabricatorCalendarHoliday::getNthBusinessDay(
time(),
-$stale);
}
- Javelin::initBehavior('phabricator-tooltips', array());
- require_celerity_resource('aphront-tooltip-css');
+ $this->initBehavior('phabricator-tooltips', array());
+ $this->requireResource('aphront-tooltip-css');
$flagged = mpull($this->flags, null, 'getObjectPHID');
foreach ($this->fields as $field) {
$field->setHandles($this->handles);
}
$list = new PHUIObjectItemListView();
$list->setCards(true);
foreach ($this->revisions as $revision) {
$item = id(new PHUIObjectItemView())
->setUser($user);
$rev_fields = array();
$icons = array();
$phid = $revision->getPHID();
if (isset($flagged[$phid])) {
$flag = $flagged[$phid];
$flag_class = PhabricatorFlagColor::getCSSClass($flag->getColor());
$icons['flag'] = phutil_tag(
'div',
array(
'class' => 'phabricator-flag-icon '.$flag_class,
),
'');
}
if (array_key_exists($revision->getID(), $this->drafts)) {
$icons['draft'] = true;
}
$modified = $revision->getDateModified();
$status = $revision->getStatus();
$show_age = ($fresh || $stale) &&
$this->highlightAge &&
!$revision->isClosed();
$object_age = PHUIObjectItemView::AGE_FRESH;
foreach ($this->fields as $field) {
if ($show_age) {
if ($field instanceof DifferentialDateModifiedFieldSpecification) {
if ($stale && $modified < $stale) {
$object_age = PHUIObjectItemView::AGE_OLD;
} else if ($fresh && $modified < $fresh) {
$object_age = PHUIObjectItemView::AGE_STALE;
}
}
}
$rev_header = $field->renderHeaderForRevisionList();
$rev_fields[$rev_header] = $field
->renderValueForRevisionList($revision);
}
$status_name =
ArcanistDifferentialRevisionStatus::getNameForRevisionStatus($status);
if (isset($icons['flag'])) {
$item->addHeadIcon($icons['flag']);
}
$item->setObjectName('D'.$revision->getID());
$item->setHeader(phutil_tag('a',
array('href' => '/D'.$revision->getID()),
$revision->getTitle()));
if (isset($icons['draft'])) {
$draft = id(new PHUIIconView())
->setSpriteSheet(PHUIIconView::SPRITE_ICONS)
->setSpriteIcon('file-grey')
->addSigil('has-tooltip')
->setMetadata(
array(
'tip' => pht('Unsubmitted Comments'),
));
$item->addAttribute($draft);
}
$item->addAttribute($status_name);
// Author
$author_handle = $this->handles[$revision->getAuthorPHID()];
$item->addByline(pht('Author: %s', $author_handle->renderLink()));
// Reviewers
$item->addAttribute(pht('Reviewers: %s', $rev_fields['Reviewers']));
$item->setEpoch($revision->getDateModified(), $object_age);
// First remove the fields we already have
$count = 7;
$rev_fields = array_slice($rev_fields, $count);
// Then add each one of them
// TODO: Add render-to-foot-icon support
foreach ($rev_fields as $header => $field) {
$item->addAttribute(pht('%s: %s', $header, $field));
}
switch ($status) {
case ArcanistDifferentialRevisionStatus::NEEDS_REVIEW:
break;
case ArcanistDifferentialRevisionStatus::NEEDS_REVISION:
$item->setBarColor('red');
break;
case ArcanistDifferentialRevisionStatus::ACCEPTED:
$item->setBarColor('green');
break;
case ArcanistDifferentialRevisionStatus::CLOSED:
$item->setDisabled(true);
break;
case ArcanistDifferentialRevisionStatus::ABANDONED:
$item->setBarColor('black');
break;
}
$list->addItem($item);
}
$list->setHeader($this->header);
$list->setNoDataString($this->noDataString);
return $list;
}
public static function getDefaultFields(PhabricatorUser $user) {
$selector = DifferentialFieldSelector::newSelector();
$fields = $selector->getFieldSpecifications();
foreach ($fields as $key => $field) {
$field->setUser($user);
if (!$field->shouldAppearOnRevisionList()) {
unset($fields[$key]);
}
}
if (!$fields) {
throw new Exception(
"Phabricator configuration has no fields that appear on the list ".
"interface!");
}
return $selector->sortFieldsForRevisionList($fields);
}
}
diff --git a/src/applications/differential/view/DifferentialRevisionUpdateHistoryView.php b/src/applications/differential/view/DifferentialRevisionUpdateHistoryView.php
index e4a78860df..5029a3bb44 100644
--- a/src/applications/differential/view/DifferentialRevisionUpdateHistoryView.php
+++ b/src/applications/differential/view/DifferentialRevisionUpdateHistoryView.php
@@ -1,329 +1,329 @@
<?php
final class DifferentialRevisionUpdateHistoryView extends AphrontView {
private $diffs = array();
private $selectedVersusDiffID;
private $selectedDiffID;
private $selectedWhitespace;
public function setDiffs(array $diffs) {
assert_instances_of($diffs, 'DifferentialDiff');
$this->diffs = $diffs;
return $this;
}
public function setSelectedVersusDiffID($id) {
$this->selectedVersusDiffID = $id;
return $this;
}
public function setSelectedDiffID($id) {
$this->selectedDiffID = $id;
return $this;
}
public function setSelectedWhitespace($whitespace) {
$this->selectedWhitespace = $whitespace;
return $this;
}
public function render() {
- require_celerity_resource('differential-core-view-css');
- require_celerity_resource('differential-revision-history-css');
+ $this->requireResource('differential-core-view-css');
+ $this->requireResource('differential-revision-history-css');
$data = array(
array(
'name' => 'Base',
'id' => null,
'desc' => 'Base',
'age' => null,
'obj' => null,
),
);
$seq = 0;
foreach ($this->diffs as $diff) {
$data[] = array(
'name' => 'Diff '.(++$seq),
'id' => $diff->getID(),
'desc' => $diff->getDescription(),
'age' => $diff->getDateCreated(),
'obj' => $diff,
);
}
$max_id = $diff->getID();
$idx = 0;
$rows = array();
$disable = false;
$radios = array();
$last_base = null;
foreach ($data as $row) {
$diff = $row['obj'];
$name = $row['name'];
$id = $row['id'];
$old_class = null;
$new_class = null;
if ($id) {
$new_checked = ($this->selectedDiffID == $id);
$new = javelin_tag(
'input',
array(
'type' => 'radio',
'name' => 'id',
'value' => $id,
'checked' => $new_checked ? 'checked' : null,
'sigil' => 'differential-new-radio',
));
if ($new_checked) {
$new_class = " revhistory-new-now";
$disable = true;
}
} else {
$new = null;
}
if ($max_id != $id) {
$uniq = celerity_generate_unique_node_id();
$old_checked = ($this->selectedVersusDiffID == $id);
$old = phutil_tag(
'input',
array(
'type' => 'radio',
'name' => 'vs',
'value' => $id,
'id' => $uniq,
'checked' => $old_checked ? 'checked' : null,
'disabled' => $disable ? 'disabled' : null,
));
$radios[] = $uniq;
if ($old_checked) {
$old_class = " revhistory-old-now";
}
} else {
$old = null;
}
$desc = $row['desc'];
if ($row['age']) {
$age = phabricator_datetime($row['age'], $this->getUser());
} else {
$age = null;
}
if (++$idx % 2) {
$class = 'alt';
} else {
$class = null;
}
$lint_attrs = array('class' => 'revhistory-star');
$unit_attrs = array('class' => 'revhistory-star');
if ($diff) {
$lint = self::renderDiffLintStar($row['obj']);
$unit = self::renderDiffUnitStar($row['obj']);
$lint_attrs['title'] = self::getDiffLintMessage($diff);
$unit_attrs['title'] = self::getDiffUnitMessage($diff);
$base = $this->renderBaseRevision($diff);
} else {
$lint = null;
$unit = null;
$base = null;
}
if ($last_base !== null && $base !== $last_base) {
// TODO: Render some kind of notice about rebases.
}
$last_base = $base;
$id_link = phutil_tag(
'a',
array('href' => '/differential/diff/'.$id.'/'),
$id);
$rows[] = phutil_tag(
'tr',
array('class' => $class),
array(
phutil_tag('td', array('class' => 'revhistory-name'), $name),
phutil_tag('td', array('class' => 'revhistory-id'), $id_link),
phutil_tag('td', array('class' => 'revhistory-base'), $base),
phutil_tag('td', array('class' => 'revhistory-desc'), $desc),
phutil_tag('td', array('class' => 'revhistory-age'), $age),
phutil_tag('td', $lint_attrs, $lint),
phutil_tag('td', $unit_attrs, $unit),
phutil_tag('td', array('class' => 'revhistory-old'.$old_class), $old),
phutil_tag('td', array('class' => 'revhistory-new'.$new_class), $new),
));
}
Javelin::initBehavior(
'differential-diff-radios',
array(
'radios' => $radios,
));
$options = array(
DifferentialChangesetParser::WHITESPACE_IGNORE_FORCE => 'Ignore All',
DifferentialChangesetParser::WHITESPACE_IGNORE_ALL => 'Ignore Most',
DifferentialChangesetParser::WHITESPACE_IGNORE_TRAILING =>
'Ignore Trailing',
DifferentialChangesetParser::WHITESPACE_SHOW_ALL => 'Show All',
);
foreach ($options as $value => $label) {
$options[$value] = phutil_tag(
'option',
array(
'value' => $value,
'selected' => ($value == $this->selectedWhitespace)
? 'selected'
: null,
),
$label);
}
$select = phutil_tag('select', array('name' => 'whitespace'), $options);
array_unshift($rows, phutil_tag('tr', array(), array(
phutil_tag('th', array(), pht('Diff')),
phutil_tag('th', array(), pht('ID')),
phutil_tag('th', array(), pht('Base')),
phutil_tag('th', array(), pht('Description')),
phutil_tag('th', array(), pht('Created')),
phutil_tag('th', array(), pht('Lint')),
phutil_tag('th', array(), pht('Unit')),
)));
$label = pht('Whitespace Changes: %s', $select);
$content = phutil_tag_div(
'differential-revision-history differential-panel',
phutil_tag(
'form',
array('action' => '#toc'),
phutil_tag(
'table',
array('class' => 'differential-revision-history-table'), array(
phutil_implode_html("\n", $rows),
phutil_tag('tr', array(), phutil_tag(
'td',
array('colspan' => 9, 'class' => 'diff-differ-submit'),
array(
phutil_tag('label', array(), $label),
phutil_tag('button', array(), pht('Show Diff')),
)))
))));
return id(new PHUIObjectBoxView())
->setHeaderText(pht('Revision Update History'))
->appendChild($content);
}
const STAR_NONE = 'none';
const STAR_OKAY = 'okay';
const STAR_WARN = 'warn';
const STAR_FAIL = 'fail';
const STAR_SKIP = 'skip';
public static function renderDiffLintStar(DifferentialDiff $diff) {
static $map = array(
DifferentialLintStatus::LINT_NONE => self::STAR_NONE,
DifferentialLintStatus::LINT_OKAY => self::STAR_OKAY,
DifferentialLintStatus::LINT_WARN => self::STAR_WARN,
DifferentialLintStatus::LINT_FAIL => self::STAR_FAIL,
DifferentialLintStatus::LINT_SKIP => self::STAR_SKIP,
DifferentialLintStatus::LINT_POSTPONED => self::STAR_SKIP
);
$star = idx($map, $diff->getLintStatus(), self::STAR_FAIL);
return self::renderDiffStar($star);
}
public static function renderDiffUnitStar(DifferentialDiff $diff) {
static $map = array(
DifferentialUnitStatus::UNIT_NONE => self::STAR_NONE,
DifferentialUnitStatus::UNIT_OKAY => self::STAR_OKAY,
DifferentialUnitStatus::UNIT_WARN => self::STAR_WARN,
DifferentialUnitStatus::UNIT_FAIL => self::STAR_FAIL,
DifferentialUnitStatus::UNIT_SKIP => self::STAR_SKIP,
DifferentialUnitStatus::UNIT_POSTPONED => self::STAR_SKIP,
);
$star = idx($map, $diff->getUnitStatus(), self::STAR_FAIL);
return self::renderDiffStar($star);
}
public static function getDiffLintMessage(DifferentialDiff $diff) {
switch ($diff->getLintStatus()) {
case DifferentialLintStatus::LINT_NONE:
return 'No Linters Available';
case DifferentialLintStatus::LINT_OKAY:
return 'Lint OK';
case DifferentialLintStatus::LINT_WARN:
return 'Lint Warnings';
case DifferentialLintStatus::LINT_FAIL:
return 'Lint Errors';
case DifferentialLintStatus::LINT_SKIP:
return 'Lint Skipped';
case DifferentialLintStatus::LINT_POSTPONED:
return 'Lint Postponed';
}
return '???';
}
public static function getDiffUnitMessage(DifferentialDiff $diff) {
switch ($diff->getUnitStatus()) {
case DifferentialUnitStatus::UNIT_NONE:
return 'No Unit Test Coverage';
case DifferentialUnitStatus::UNIT_OKAY:
return 'Unit Tests OK';
case DifferentialUnitStatus::UNIT_WARN:
return 'Unit Test Warnings';
case DifferentialUnitStatus::UNIT_FAIL:
return 'Unit Test Errors';
case DifferentialUnitStatus::UNIT_SKIP:
return 'Unit Tests Skipped';
case DifferentialUnitStatus::UNIT_POSTPONED:
return 'Unit Tests Postponed';
}
return '???';
}
private static function renderDiffStar($star) {
$class = 'diff-star-'.$star;
return phutil_tag(
'span',
array('class' => $class),
"\xE2\x98\x85");
}
private function renderBaseRevision(DifferentialDiff $diff) {
switch ($diff->getSourceControlSystem()) {
case 'git':
$base = $diff->getSourceControlBaseRevision();
if (strpos($base, '@') === false) {
return substr($base, 0, 7);
} else {
// The diff is from git-svn
$base = explode('@', $base);
$base = last($base);
return $base;
}
case 'svn':
$base = $diff->getSourceControlBaseRevision();
$base = explode('@', $base);
$base = last($base);
return $base;
default:
return null;
}
}
}
diff --git a/src/infrastructure/celerity/CelerityResourceMap.php b/src/infrastructure/celerity/CelerityResourceMap.php
index ebaa4fb9a6..c9beffc2d3 100644
--- a/src/infrastructure/celerity/CelerityResourceMap.php
+++ b/src/infrastructure/celerity/CelerityResourceMap.php
@@ -1,239 +1,243 @@
<?php
/**
* Interface to the static resource map, which is a graph of available
* resources, resource dependencies, and packaging information. You generally do
* not need to invoke it directly; instead, you call higher-level Celerity APIs
* and it uses the resource map to satisfy your requests.
*/
final class CelerityResourceMap {
private static $instances = array();
private $resources;
private $symbolMap;
private $requiresMap;
private $packageMap;
private $nameMap;
private $hashMap;
public function __construct(CelerityResources $resources) {
$this->resources = $resources;
$map = $resources->loadMap();
$this->symbolMap = idx($map, 'symbols', array());
$this->requiresMap = idx($map, 'requires', array());
$this->packageMap = idx($map, 'packages', array());
$this->nameMap = idx($map, 'names', array());
// We derive these reverse maps at runtime.
$this->hashMap = array_flip($this->nameMap);
$this->componentMap = array();
foreach ($this->packageMap as $package_name => $symbols) {
foreach ($symbols as $symbol) {
$this->componentMap[$symbol] = $package_name;
}
}
}
public static function getNamedInstance($name) {
if (empty(self::$instances[$name])) {
$resources_list = CelerityPhysicalResources::getAll();
if (empty($resources_list[$name])) {
throw new Exception(
pht(
'No resource source exists with name "%s"!', $name));
}
$instance = new CelerityResourceMap($resources_list[$name]);
self::$instances[$name] = $instance;
}
return self::$instances[$name];
}
public function getPackagedNamesForSymbols(array $symbols) {
$resolved = $this->resolveResources($symbols);
return $this->packageResources($resolved);
}
private function resolveResources(array $symbols) {
$map = array();
foreach ($symbols as $symbol) {
if (!empty($map[$symbol])) {
continue;
}
$this->resolveResource($map, $symbol);
}
return $map;
}
private function resolveResource(array &$map, $symbol) {
if (empty($this->symbolMap[$symbol])) {
throw new Exception(
pht(
'Attempting to resolve unknown resource, "%s".',
$symbol));
}
$hash = $this->symbolMap[$symbol];
$map[$symbol] = $hash;
if (isset($this->requiresMap[$hash])) {
$requires = $this->requiresMap[$hash];
} else {
$requires = array();
}
foreach ($requires as $required_symbol) {
if (!empty($map[$required_symbol])) {
continue;
}
$this->resolveResource($map, $required_symbol);
}
}
private function packageResources(array $resolved_map) {
$packaged = array();
$handled = array();
foreach ($resolved_map as $symbol => $hash) {
if (isset($handled[$symbol])) {
continue;
}
if (empty($this->componentMap[$symbol])) {
$packaged[] = $this->hashMap[$hash];
} else {
$package_name = $this->componentMap[$symbol];
$packaged[] = $package_name;
$package_symbols = $this->packageMap[$package_name];
foreach ($package_symbols as $package_symbol) {
$handled[$package_symbol] = true;
}
}
}
return $packaged;
}
public function getResourceDataForName($resource_name) {
return $this->resources->getResourceData($resource_name);
}
public function getResourceNamesForPackageName($package_name) {
$package_symbols = idx($this->packageMap, $package_name);
if (!$package_symbols) {
return null;
}
$resource_names = array();
foreach ($package_symbols as $symbol) {
$resource_names[] = $this->hashMap[$this->symbolMap[$symbol]];
}
return $resource_names;
}
/**
* Get the epoch timestamp of the last modification time of a symbol.
*
* @param string Resource symbol to lookup.
* @return int Epoch timestamp of last resource modification.
*/
public function getModifiedTimeForName($name) {
if ($this->isPackageResource($name)) {
$names = array();
foreach ($this->packageMap[$name] as $symbol) {
$names[] = $this->getResourceNameForSymbol($symbol);
}
} else {
$names = array($name);
}
$mtime = 0;
foreach ($names as $name) {
$mtime = max($mtime, $this->resources->getResourceModifiedTime($name));
}
return $mtime;
}
/**
* Return the absolute URI for the resource associated with a symbol. This
* method is fairly low-level and ignores packaging.
*
* @param string Resource symbol to lookup.
* @return string|null Resource URI, or null if the symbol is unknown.
*/
public function getURIForSymbol($symbol) {
$hash = idx($this->symbolMap, $symbol);
return $this->getURIForHash($hash);
}
/**
* Return the absolute URI for the resource associated with a resource name.
* This method is fairly low-level and ignores packaging.
*
* @param string Resource name to lookup.
* @return string|null Resource URI, or null if the name is unknown.
*/
public function getURIForName($name) {
$hash = idx($this->nameMap, $name);
return $this->getURIForHash($hash);
}
/**
* Return the absolute URI for a resource, identified by hash.
* This method is fairly low-level and ignores packaging.
*
* @param string Resource hash to lookup.
* @return string|null Resource URI, or null if the hash is unknown.
*/
private function getURIForHash($hash) {
if ($hash === null) {
return null;
}
return $this->resources->getResourceURI($hash, $this->hashMap[$hash]);
}
/**
* Return the resource symbols required by a named resource.
*
* @param string Resource name to lookup.
* @return list<string>|null List of required symbols, or null if the name
* is unknown.
*/
public function getRequiredSymbolsForName($name) {
$hash = idx($this->symbolMap, $name);
if ($hash === null) {
return null;
}
return idx($this->requiresMap, $hash, array());
}
/**
* Return the resource name for a given symbol.
*
* @param string Resource symbol to lookup.
* @return string|null Resource name, or null if the symbol is unknown.
*/
public function getResourceNameForSymbol($symbol) {
$hash = idx($this->symbolMap, $symbol);
return idx($this->hashMap, $hash);
}
public function isPackageResource($name) {
return isset($this->packageMap[$name]);
}
+ public function getResourceTypeForName($name) {
+ return $this->resources->getResourceType($name);
+ }
+
}
diff --git a/src/infrastructure/celerity/CelerityStaticResourceResponse.php b/src/infrastructure/celerity/CelerityStaticResourceResponse.php
index b52274f323..21ad591e87 100644
--- a/src/infrastructure/celerity/CelerityStaticResourceResponse.php
+++ b/src/infrastructure/celerity/CelerityStaticResourceResponse.php
@@ -1,256 +1,304 @@
<?php
/**
* Tracks and resolves dependencies the page declares with
* @{function:require_celerity_resource}, and then builds appropriate HTML or
* Ajax responses.
*
* @group celerity
*/
final class CelerityStaticResourceResponse {
private $symbols = array();
private $needsResolve = true;
private $resolved;
private $packaged;
private $metadata = array();
private $metadataBlock = 0;
private $behaviors = array();
private $hasRendered = array();
public function __construct() {
if (isset($_REQUEST['__metablock__'])) {
$this->metadataBlock = (int)$_REQUEST['__metablock__'];
}
}
public function addMetadata($metadata) {
$id = count($this->metadata);
$this->metadata[$id] = $metadata;
return $this->metadataBlock.'_'.$id;
}
public function getMetadataBlock() {
return $this->metadataBlock;
}
/**
* Register a behavior for initialization. NOTE: if $config is empty,
* a behavior will execute only once even if it is initialized multiple times.
* If $config is nonempty, the behavior will be invoked once for each config.
*/
- public function initBehavior($behavior, array $config = array()) {
- $this->requireResource('javelin-behavior-'.$behavior);
+ public function initBehavior(
+ $behavior,
+ array $config = array(),
+ $source_name = 'phabricator') {
+
+ $this->requireResource('javelin-behavior-'.$behavior, $source_name);
if (empty($this->behaviors[$behavior])) {
$this->behaviors[$behavior] = array();
}
if ($config) {
$this->behaviors[$behavior][] = $config;
}
return $this;
}
- public function requireResource($symbol) {
- $this->symbols[$symbol] = true;
+ public function requireResource($symbol, $source_name) {
+ if (isset($this->symbols[$source_name][$symbol])) {
+ return $this;
+ }
+
+ // Verify that the resource exists.
+ $map = CelerityResourceMap::getNamedInstance($source_name);
+ $name = $map->getResourceNameForSymbol($symbol);
+ if ($name === null) {
+ throw new Exception(
+ pht(
+ 'No resource with symbol "%s" exists in source "%s"!',
+ $symbol,
+ $source_name));
+ }
+
+ $this->symbols[$source_name][$symbol] = true;
$this->needsResolve = true;
+
return $this;
}
private function resolveResources() {
if ($this->needsResolve) {
- $map = CelerityResourceMap::getNamedInstance('phabricator');
+ $this->packaged = array();
+ foreach ($this->symbols as $source_name => $symbols_map) {
+ $symbols = array_keys($symbols_map);
- $symbols = array_keys($this->symbols);
- $this->packaged = $map->getPackagedNamesForSymbols($symbols);
+ $map = CelerityResourceMap::getNamedInstance($source_name);
+ $packaged = $map->getPackagedNamesForSymbols($symbols);
+ $this->packaged[$source_name] = $packaged;
+ }
$this->needsResolve = false;
}
return $this;
}
public function renderSingleResource($symbol, $source_name) {
$map = CelerityResourceMap::getNamedInstance($source_name);
$packaged = $map->getPackagedNamesForSymbols(array($symbol));
- return $this->renderPackagedResources($packaged);
+ return $this->renderPackagedResources($map, $packaged);
}
public function renderResourcesOfType($type) {
$this->resolveResources();
- $resources = array();
- foreach ($this->packaged as $name) {
- $resource_type = CelerityResourceTransformer::getResourceType($name);
- if ($resource_type == $type) {
- $resources[] = $name;
+ $result = array();
+ foreach ($this->packaged as $source_name => $resource_names) {
+ $map = CelerityResourceMap::getNamedInstance($source_name);
+
+ $resources_of_type = array();
+ foreach ($resource_names as $resource_name) {
+ $resource_type = $map->getResourceTypeForName($resource_name);
+ if ($resource_type == $type) {
+ $resources_of_type[] = $resource_name;
+ }
}
+
+ $result[] = $this->renderPackagedResources($map, $resources_of_type);
}
- return $this->renderPackagedResources($resources);
+ return phutil_implode_html('', $result);
}
- private function renderPackagedResources(array $resources) {
+ private function renderPackagedResources(
+ CelerityResourceMap $map,
+ array $resources) {
+
$output = array();
foreach ($resources as $name) {
if (isset($this->hasRendered[$name])) {
continue;
}
$this->hasRendered[$name] = true;
- $output[] = $this->renderResource($name);
- $output[] = "\n";
+ $output[] = $this->renderResource($map, $name);
}
- return phutil_implode_html('', $output);
+
+ return $output;
}
- private function renderResource($name) {
- $uri = $this->getURI($name);
- $type = CelerityResourceTransformer::getResourceType($name);
+ private function renderResource(
+ CelerityResourceMap $map,
+ $name) {
+
+ $uri = $this->getURI($map, $name);
+ $type = $map->getResourceTypeForName($name);
+
switch ($type) {
case 'css':
return phutil_tag(
'link',
array(
'rel' => 'stylesheet',
'type' => 'text/css',
'href' => $uri,
));
case 'js':
return phutil_tag(
'script',
array(
'type' => 'text/javascript',
'src' => $uri,
),
'');
}
- throw new Exception("Unable to render resource.");
+
+ throw new Exception(
+ pht(
+ 'Unable to render resource "%s", which has unknown type "%s".',
+ $name,
+ $type));
}
public function renderHTMLFooter() {
$data = array();
if ($this->metadata) {
$json_metadata = AphrontResponse::encodeJSONForHTTPResponse(
$this->metadata);
$this->metadata = array();
} else {
$json_metadata = '{}';
}
// Even if there is no metadata on the page, Javelin uses the mergeData()
// call to start dispatching the event queue.
$data[] = 'JX.Stratcom.mergeData('.$this->metadataBlock.', '.
$json_metadata.');';
$onload = array();
if ($this->behaviors) {
$behaviors = $this->behaviors;
$this->behaviors = array();
$higher_priority_names = array(
'refresh-csrf',
'aphront-basic-tokenizer',
'dark-console',
'history-install',
);
$higher_priority_behaviors = array_select_keys(
$behaviors,
$higher_priority_names);
foreach ($higher_priority_names as $name) {
unset($behaviors[$name]);
}
$behavior_groups = array(
$higher_priority_behaviors,
$behaviors);
foreach ($behavior_groups as $group) {
if (!$group) {
continue;
}
$group_json = AphrontResponse::encodeJSONForHTTPResponse(
$group);
$onload[] = 'JX.initBehaviors('.$group_json.')';
}
}
if ($onload) {
foreach ($onload as $func) {
$data[] = 'JX.onload(function(){'.$func.'});';
}
}
if ($data) {
$data = implode("\n", $data);
return self::renderInlineScript($data);
} else {
return '';
}
}
public static function renderInlineScript($data) {
if (stripos($data, '</script>') !== false) {
throw new Exception(
'Literal </script> is not allowed inside inline script.');
}
if (strpos($data, '<!') !== false) {
throw new Exception('Literal <! is not allowed inside inline script.');
}
// We don't use <![CDATA[ ]]> because it is ignored by HTML parsers. We
// would need to send the document with XHTML content type.
return phutil_tag(
'script',
array('type' => 'text/javascript'),
phutil_safe_html($data));
}
public function buildAjaxResponse($payload, $error = null) {
$response = array(
'error' => $error,
'payload' => $payload,
);
if ($this->metadata) {
$response['javelin_metadata'] = $this->metadata;
$this->metadata = array();
}
if ($this->behaviors) {
$response['javelin_behaviors'] = $this->behaviors;
$this->behaviors = array();
}
$this->resolveResources();
$resources = array();
- foreach ($this->packaged as $resource) {
- $resources[] = $this->getURI($resource);
+ foreach ($this->packaged as $source_name => $resource_names) {
+ $map = CelerityResourceMap::getNamedInstance($source_name);
+ foreach ($resource_names as $resource_name) {
+ $resources[] = $this->getURI($map, $resource_name);
+ }
}
if ($resources) {
$response['javelin_resources'] = $resources;
}
return $response;
}
- private function getURI($name) {
- $map = CelerityResourceMap::getNamedInstance('phabricator');
+ private function getURI(
+ CelerityResourceMap $map,
+ $name) {
+
$uri = $map->getURIForName($name);
// In developer mode, we dump file modification times into the URI. When a
// page is reloaded in the browser, any resources brought in by Ajax calls
// do not trigger revalidation, so without this it's very difficult to get
// changes to Ajaxed-in CSS to work (you must clear your cache or rerun
// the map script). In production, we can assume the map script gets run
// after changes, and safely skip this.
if (PhabricatorEnv::getEnvConfig('phabricator.developer-mode')) {
$mtime = $map->getModifiedTimeForName($name);
$uri = preg_replace('@^/res/@', '/res/'.$mtime.'T/', $uri);
}
return PhabricatorEnv::getCDNURI($uri);
}
}
diff --git a/src/infrastructure/celerity/api.php b/src/infrastructure/celerity/api.php
index 8b04b49fd3..c10b7a1d40 100644
--- a/src/infrastructure/celerity/api.php
+++ b/src/infrastructure/celerity/api.php
@@ -1,61 +1,61 @@
<?php
/**
* Include a CSS or JS static resource by name. This function records a
* dependency for the current page, so when a response is generated it can be
* included. You can call this method from any context, and it is recommended
* you invoke it as close to the actual dependency as possible so that page
* dependencies are minimized.
*
* For more information, see @{article:Adding New CSS and JS}.
*
* @param string Name of the celerity module to include. This is whatever you
* annotated as "@provides" in the file.
* @return void
*
* @group celerity
*/
-function require_celerity_resource($symbol) {
+function require_celerity_resource($symbol, $source_name = 'phabricator') {
$response = CelerityAPI::getStaticResourceResponse();
- $response->requireResource($symbol);
+ $response->requireResource($symbol, $source_name);
}
/**
* Generate a node ID which is guaranteed to be unique for the current page,
* even across Ajax requests. You should use this method to generate IDs for
* nodes which require a uniqueness guarantee.
*
* @return string A string appropriate for use as an 'id' attribute on a DOM
* node. It is guaranteed to be unique for the current page, even
* if the current request is a subsequent Ajax request.
*
* @group celerity
*/
function celerity_generate_unique_node_id() {
static $uniq = 0;
$response = CelerityAPI::getStaticResourceResponse();
$block = $response->getMetadataBlock();
return 'UQ'.$block.'_'.($uniq++);
}
/**
* Get the versioned URI for a raw resource, like an image.
*
* @param string Path to the raw image.
* @return string Versioned path to the image, if one is available.
*
* @group celerity
*/
function celerity_get_resource_uri($resource, $source = 'phabricator') {
$map = CelerityResourceMap::getNamedInstance($source);
$uri = $map->getURIForName($resource);
if ($uri) {
return $uri;
}
return $resource;
}
diff --git a/src/view/AphrontView.php b/src/view/AphrontView.php
index 33bddb701e..788ea50ab3 100644
--- a/src/view/AphrontView.php
+++ b/src/view/AphrontView.php
@@ -1,145 +1,162 @@
<?php
/**
* @task children Managing Children
*/
abstract class AphrontView extends Phobject
implements PhutilSafeHTMLProducerInterface {
protected $user;
protected $children = array();
/* -( Configuration )------------------------------------------------------ */
/**
* @task config
*/
public function setUser(PhabricatorUser $user) {
$this->user = $user;
return $this;
}
/**
* @task config
*/
protected function getUser() {
return $this->user;
}
/* -( Managing Children )-------------------------------------------------- */
/**
* Test if this View accepts children.
*
* By default, views accept children, but subclases may override this method
* to prevent children from being appended. Doing so will cause
* @{method:appendChild} to throw exceptions instead of appending children.
*
* @return bool True if the View should accept children.
* @task children
*/
protected function canAppendChild() {
return true;
}
/**
* Append a child to the list of children.
*
* This method will only work if the view supports children, which is
* determined by @{method:canAppendChild}.
*
* @param wild Something renderable.
* @return this
*/
final public function appendChild($child) {
if (!$this->canAppendChild()) {
$class = get_class($this);
throw new Exception(
pht("View '%s' does not support children.", $class));
}
$this->children[] = $child;
return $this;
}
/**
* Produce children for rendering.
*
* Historically, this method reduced children to a string representation,
* but it no longer does.
*
* @return wild Renderable children.
* @task
*/
final protected function renderChildren() {
return $this->children;
}
/**
* Test if an element has no children.
*
* @return bool True if this element has children.
* @task children
*/
final public function hasChildren() {
if ($this->children) {
$this->children = $this->reduceChildren($this->children);
}
return (bool)$this->children;
}
/**
* Reduce effectively-empty lists of children to be actually empty. This
* recursively removes `null`, `''`, and `array()` from the list of children
* so that @{method:hasChildren} can more effectively align with expectations.
*
* NOTE: Because View children are not rendered, a View which renders down
* to nothing will not be reduced by this method.
*
* @param list<wild> Renderable children.
* @return list<wild> Reduced list of children.
* @task children
*/
private function reduceChildren(array $children) {
foreach ($children as $key => $child) {
if ($child === null) {
unset($children[$key]);
} else if ($child === '') {
unset($children[$key]);
} else if (is_array($child)) {
$child = $this->reduceChildren($child);
if ($child) {
$children[$key] = $child;
} else {
unset($children[$key]);
}
}
}
return $children;
}
+ public function getDefaultResourceSource() {
+ return 'phabricator';
+ }
+
+ public function requireResource($symbol) {
+ $response = CelerityAPI::getStaticResourceResponse();
+ $response->requireResource($symbol, $this->getDefaultResourceSource());
+ return $this;
+ }
+
+ public function initBehavior($name, $config = array()) {
+ Javelin::initBehavior(
+ $name,
+ $config,
+ $this->getDefaultResourceSource());
+ }
+
/* -( Rendering )---------------------------------------------------------- */
abstract public function render();
/* -( PhutilSafeHTMLProducerInterface )------------------------------------ */
public function producePhutilSafeHTML() {
return $this->render();
}
}

File Metadata

Mime Type
text/x-diff
Expires
Sun, Jan 19, 13:56 (3 w, 3 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1125284
Default Alt Text
(145 KB)

Event Timeline