diff --git a/conf/default.conf.php b/conf/default.conf.php
index e719e03ab1..892e395046 100644
--- a/conf/default.conf.php
+++ b/conf/default.conf.php
@@ -570,6 +570,11 @@ return array(
// not actually be receiving thorough review.
'differential.enable-email-accept' => false,
+ // If you set this to true, users won't need to login to view differential
+ // revisions. Anonymous users will have read-only access and won't be able to
+ // interact with the revisions.
+ 'differential.anonymous-access' => false,
+
// -- Maniphest ------------------------------------------------------------- //
diff --git a/src/applications/differential/controller/base/DifferentialController.php b/src/applications/differential/controller/base/DifferentialController.php
index 50300b0210..12dabd77d2 100644
--- a/src/applications/differential/controller/base/DifferentialController.php
+++ b/src/applications/differential/controller/base/DifferentialController.php
@@ -18,10 +18,16 @@
abstract class DifferentialController extends PhabricatorController {
+ protected function allowsAnonymousAccess() {
+ return PhabricatorEnv::getEnvConfig('differential.anonymous-access');
+ }
+
public function buildStandardPageResponse($view, array $data) {
require_celerity_resource('differential-core-view-css');
+ $viewer_is_anonymous = !$this->getRequest()->getUser()->isLoggedIn();
+
$page = $this->buildStandardPageView();
$page->setApplicationName('Differential');
@@ -29,18 +35,22 @@ abstract class DifferentialController extends PhabricatorController {
$page->setTitle(idx($data, 'title'));
$page->setGlyph("\xE2\x9A\x99");
$page->appendChild($view);
- $page->setTabs(
- array(
- 'revisions' => array(
- 'name' => 'Revisions',
- 'href' => '/differential/',
- ),
+ $tabs = array(
+ 'revisions' => array(
+ 'name' => 'Revisions',
+ 'href' => '/differential/',
+ )
+ );
+ if (!$viewer_is_anonymous) {
+ $tabs = array_merge($tabs, array(
'create' => array(
'name' => 'Create Diff',
'href' => '/differential/diff/create/',
- ),
- ),
- idx($data, 'tab'));
+ )
+ ));
+ }
+ $page->setTabs($tabs, idx($data, 'tab'));
+ $page->setIsLoggedOut($viewer_is_anonymous);
$response = new AphrontWebpageResponse();
return $response->setContent($page->render());
diff --git a/src/applications/differential/controller/base/__init__.php b/src/applications/differential/controller/base/__init__.php
index 015845d45e..00b02d92ec 100644
--- a/src/applications/differential/controller/base/__init__.php
+++ b/src/applications/differential/controller/base/__init__.php
@@ -9,6 +9,7 @@
phutil_require_module('phabricator', 'aphront/response/webpage');
phutil_require_module('phabricator', 'applications/base/controller/base');
phutil_require_module('phabricator', 'infrastructure/celerity/api');
+phutil_require_module('phabricator', 'infrastructure/env');
phutil_require_module('phutil', 'utils');
diff --git a/src/applications/differential/controller/changesetview/DifferentialChangesetViewController.php b/src/applications/differential/controller/changesetview/DifferentialChangesetViewController.php
index 176b4f505e..8d55996ff0 100644
--- a/src/applications/differential/controller/changesetview/DifferentialChangesetViewController.php
+++ b/src/applications/differential/controller/changesetview/DifferentialChangesetViewController.php
@@ -19,6 +19,10 @@
class DifferentialChangesetViewController extends DifferentialController {
+ public function shouldRequireLogin() {
+ return !$this->allowsAnonymousAccess();
+ }
+
public function processRequest() {
$request = $this->getRequest();
diff --git a/src/applications/differential/controller/revisionlist/DifferentialRevisionListController.php b/src/applications/differential/controller/revisionlist/DifferentialRevisionListController.php
index 2ec76b63f5..4d9f144bc4 100644
--- a/src/applications/differential/controller/revisionlist/DifferentialRevisionListController.php
+++ b/src/applications/differential/controller/revisionlist/DifferentialRevisionListController.php
@@ -20,6 +20,10 @@ class DifferentialRevisionListController extends DifferentialController {
private $filter;
+ public function shouldRequireLogin() {
+ return !$this->allowsAnonymousAccess();
+ }
+
public function willProcessRequest(array $data) {
$this->filter = idx($data, 'filter');
}
@@ -27,6 +31,7 @@ class DifferentialRevisionListController extends DifferentialController {
public function processRequest() {
$request = $this->getRequest();
$user = $request->getUser();
+ $viewer_is_anonymous = !$user->isLoggedIn();
if ($request->isFormPost()) {
$phid_arr = $request->getArr('view_user');
@@ -35,72 +40,77 @@ class DifferentialRevisionListController extends DifferentialController {
->setURI($request->getRequestURI()->alter('phid', $view_target));
}
- $filters = array(
- 'User Revisions',
- 'active' => array(
- 'name' => 'Active Revisions',
- 'queries' => array(
- array(
- 'query'
- => DifferentialRevisionListData::QUERY_NEED_ACTION_FROM_SELF,
- 'header' => 'Action Required',
- 'nodata' => 'You have no revisions requiring action.',
- ),
- array(
- 'query'
- => DifferentialRevisionListData::QUERY_NEED_ACTION_FROM_OTHERS,
- 'header' => 'Waiting on Others',
- 'nodata' => 'You have no revisions waiting on others',
+ $filters = array();
+ if (!$viewer_is_anonymous) {
+ $filters = array(
+ 'User Revisions',
+ 'active' => array(
+ 'name' => 'Active Revisions',
+ 'queries' => array(
+ array(
+ 'query'
+ => DifferentialRevisionListData::QUERY_NEED_ACTION_FROM_SELF,
+ 'header' => 'Action Required',
+ 'nodata' => 'You have no revisions requiring action.',
+ ),
+ array(
+ 'query'
+ => DifferentialRevisionListData::QUERY_NEED_ACTION_FROM_OTHERS,
+ 'header' => 'Waiting on Others',
+ 'nodata' => 'You have no revisions waiting on others',
+ ),
),
),
- ),
- 'open' => array(
- 'name' => 'Open Revisions',
- 'queries' => array(
- array(
- 'query' => DifferentialRevisionListData::QUERY_OPEN_OWNED,
- 'header' => 'Your Open Revisions',
+ 'open' => array(
+ 'name' => 'Open Revisions',
+ 'queries' => array(
+ array(
+ 'query' => DifferentialRevisionListData::QUERY_OPEN_OWNED,
+ 'header' => 'Your Open Revisions',
+ ),
),
),
- ),
- 'reviews' => array(
- 'name' => 'Open Reviews',
- 'queries' => array(
- array(
- 'query' => DifferentialRevisionListData::QUERY_OPEN_REVIEWER,
- 'header' => 'Your Open Reviews',
+ 'reviews' => array(
+ 'name' => 'Open Reviews',
+ 'queries' => array(
+ array(
+ 'query' => DifferentialRevisionListData::QUERY_OPEN_REVIEWER,
+ 'header' => 'Your Open Reviews',
+ ),
),
),
- ),
- 'all' => array(
- 'name' => 'All Revisions',
- 'queries' => array(
- array(
- 'query' => DifferentialRevisionListData::QUERY_OWNED,
- 'header' => 'Your Revisions',
+ 'all' => array(
+ 'name' => 'All Revisions',
+ 'queries' => array(
+ array(
+ 'query' => DifferentialRevisionListData::QUERY_OWNED,
+ 'header' => 'Your Revisions',
+ ),
),
),
- ),
- 'related' => array(
- 'name' => 'All Revisions and Reviews',
- 'queries' => array(
- array(
- 'query' => DifferentialRevisionListData::QUERY_OWNED_OR_REVIEWER,
- 'header' => 'Your Revisions and Reviews',
+ 'related' => array(
+ 'name' => 'All Revisions and Reviews',
+ 'queries' => array(
+ array(
+ 'query' => DifferentialRevisionListData::QUERY_OWNED_OR_REVIEWER,
+ 'header' => 'Your Revisions and Reviews',
+ ),
),
),
- ),
- 'updates' => array(
- 'name' => 'Updates',
- 'queries' => array(
- array(
- 'query' => DifferentialRevisionListData::QUERY_UPDATED_SINCE,
- 'header' =>
- 'Diffs that have been updated since you\'ve last viewed them',
+ 'updates' => array(
+ 'name' => 'Updates',
+ 'queries' => array(
+ array(
+ 'query' => DifferentialRevisionListData::QUERY_UPDATED_SINCE,
+ 'header' =>
+ 'Diffs that have been updated since you\'ve last viewed them',
+ ),
),
),
- ),
- '
',
+ '
'
+ );
+ }
+ $filters = array_merge($filters, array(
'All Revisions',
'allopen' => array(
'name' => 'Open',
@@ -112,10 +122,14 @@ class DifferentialRevisionListController extends DifferentialController {
),
),
),
- );
+ ));
if (empty($filters[$this->filter])) {
- $this->filter = 'active';
+ if (!$viewer_is_anonymous) {
+ $this->filter = 'active';
+ } else {
+ $this->filter = 'allopen';
+ }
}
$view_phid = nonempty($request->getStr('phid'), $user->getPHID());
diff --git a/src/applications/differential/controller/revisionview/DifferentialRevisionViewController.php b/src/applications/differential/controller/revisionview/DifferentialRevisionViewController.php
index e752acc52d..aa6b215d1e 100644
--- a/src/applications/differential/controller/revisionview/DifferentialRevisionViewController.php
+++ b/src/applications/differential/controller/revisionview/DifferentialRevisionViewController.php
@@ -20,6 +20,10 @@ class DifferentialRevisionViewController extends DifferentialController {
private $revisionID;
+ public function shouldRequireLogin() {
+ return !$this->allowsAnonymousAccess();
+ }
+
public function willProcessRequest(array $data) {
$this->revisionID = $data['id'];
}
@@ -28,6 +32,7 @@ class DifferentialRevisionViewController extends DifferentialController {
$request = $this->getRequest();
$user = $request->getUser();
+ $viewer_is_anonymous = !$user->isLoggedIn();
$revision = id(new DifferentialRevision())->load($this->revisionID);
if (!$revision) {
@@ -197,7 +202,7 @@ class DifferentialRevisionViewController extends DifferentialController {
$changeset_view = new DifferentialChangesetListView();
$changeset_view->setChangesets($visible_changesets);
- $changeset_view->setEditable(true);
+ $changeset_view->setEditable(!$viewer_is_anonymous);
$changeset_view->setStandaloneViews(true);
$changeset_view->setRevision($revision);
$changeset_view->setRenderingReferences($rendering_references);
@@ -221,25 +226,27 @@ class DifferentialRevisionViewController extends DifferentialController {
$toc_view->setRevisionID($revision->getID());
$toc_view->setWhitespace($whitespace);
- $draft = id(new PhabricatorDraft())->loadOneWhere(
- 'authorPHID = %s AND draftKey = %s',
- $user->getPHID(),
- 'differential-comment-'.$revision->getID());
- if ($draft) {
- $draft = $draft->getDraft();
- } else {
- $draft = null;
+ if (!$viewer_is_anonymous) {
+ $draft = id(new PhabricatorDraft())->loadOneWhere(
+ 'authorPHID = %s AND draftKey = %s',
+ $user->getPHID(),
+ 'differential-comment-'.$revision->getID());
+ if ($draft) {
+ $draft = $draft->getDraft();
+ } else {
+ $draft = null;
+ }
+
+ $comment_form = new DifferentialAddCommentView();
+ $comment_form->setRevision($revision);
+ $comment_form->setActions($this->getRevisionCommentActions($revision));
+ $comment_form->setActionURI('/differential/comment/save/');
+ $comment_form->setUser($user);
+ $comment_form->setDraft($draft);
+
+ $this->updateViewTime($user->getPHID(), $revision->getPHID());
}
- $comment_form = new DifferentialAddCommentView();
- $comment_form->setRevision($revision);
- $comment_form->setActions($this->getRevisionCommentActions($revision));
- $comment_form->setActionURI('/differential/comment/save/');
- $comment_form->setUser($user);
- $comment_form->setDraft($draft);
-
- $this->updateViewTime($user->getPHID(), $revision->getPHID());
-
$pane_id = celerity_generate_unique_node_id();
Javelin::initBehavior(
'differential-keyboard-navigation',
@@ -247,19 +254,22 @@ class DifferentialRevisionViewController extends DifferentialController {
'haunt' => $pane_id,
));
+ $page_pane = id(new DifferentialPrimaryPaneView())
+ ->setLineWidthFromChangesets($changesets)
+ ->setID($pane_id)
+ ->appendChild(
+ $revision_detail->render().
+ $comment_view->render().
+ $diff_history->render().
+ $warning.
+ $local_view->render().
+ $toc_view->render().
+ $changeset_view->render());
+ if ($comment_form) {
+ $page_pane->appendChild($comment_form->render());
+ }
return $this->buildStandardPageResponse(
- id(new DifferentialPrimaryPaneView())
- ->setLineWidthFromChangesets($changesets)
- ->setID($pane_id)
- ->appendChild(
- $revision_detail->render().
- $comment_view->render().
- $diff_history->render().
- $warning.
- $local_view->render().
- $toc_view->render().
- $changeset_view->render().
- $comment_form->render()),
+ $page_pane,
array(
'title' => 'D'.$revision->getID().' '.$revision->getTitle(),
));
@@ -296,6 +306,7 @@ class DifferentialRevisionViewController extends DifferentialController {
$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());
+ $viewer_is_anonymous = !$this->getRequest()->getUser()->isLoggedIn();
$status = $revision->getStatus();
$revision_id = $revision->getID();
$revision_phid = $revision->getPHID();
@@ -310,52 +321,54 @@ class DifferentialRevisionViewController extends DifferentialController {
);
}
- if (!$viewer_is_owner && !$viewer_is_reviewer) {
- $action = $viewer_is_cc ? 'rem' : 'add';
- $links[] = array(
- 'class' => $viewer_is_cc ? 'subscribe-rem' : 'subscribe-add',
- 'href' => "/differential/subscribe/{$action}/{$revision_id}/",
- 'name' => $viewer_is_cc ? 'Unsubscribe' : 'Subscribe',
- 'instant' => true,
- );
- } else {
- $links[] = array(
- 'class' => 'subscribe-rem unavailable',
- 'name' => 'Automatically Subscribed',
- );
- }
+ if (!$viewer_is_anonymous) {
+ if (!$viewer_is_owner && !$viewer_is_reviewer) {
+ $action = $viewer_is_cc ? 'rem' : 'add';
+ $links[] = array(
+ 'class' => $viewer_is_cc ? 'subscribe-rem' : 'subscribe-add',
+ 'href' => "/differential/subscribe/{$action}/{$revision_id}/",
+ 'name' => $viewer_is_cc ? 'Unsubscribe' : 'Subscribe',
+ 'instant' => true,
+ );
+ } else {
+ $links[] = array(
+ 'class' => 'subscribe-rem unavailable',
+ 'name' => 'Automatically Subscribed',
+ );
+ }
- require_celerity_resource('phabricator-object-selector-css');
- require_celerity_resource('javelin-behavior-phabricator-object-selector');
+ require_celerity_resource('phabricator-object-selector-css');
+ require_celerity_resource('javelin-behavior-phabricator-object-selector');
- $links[] = array(
- 'class' => 'action-dependencies',
- 'name' => 'Edit Dependencies',
- 'href' => "/search/attach/{$revision_phid}/DREV/dependencies/",
- 'sigil' => 'workflow',
- );
-
- if (PhabricatorEnv::getEnvConfig('maniphest.enabled')) {
$links[] = array(
- 'class' => 'attach-maniphest',
- 'name' => 'Edit Maniphest Tasks',
- 'href' => "/search/attach/{$revision_phid}/TASK/",
+ 'class' => 'action-dependencies',
+ 'name' => 'Edit Dependencies',
+ 'href' => "/search/attach/{$revision_phid}/DREV/dependencies/",
'sigil' => 'workflow',
);
+
+ if (PhabricatorEnv::getEnvConfig('maniphest.enabled')) {
+ $links[] = array(
+ 'class' => 'attach-maniphest',
+ 'name' => 'Edit Maniphest Tasks',
+ 'href' => "/search/attach/{$revision_phid}/TASK/",
+ 'sigil' => 'workflow',
+ );
+ }
+
+ $links[] = array(
+ 'class' => 'transcripts-metamta',
+ 'name' => 'MetaMTA Transcripts',
+ 'href' => "/mail/?phid={$revision_phid}",
+ );
+
+ $links[] = array(
+ 'class' => 'transcripts-herald',
+ 'name' => 'Herald Transcripts',
+ 'href' => "/herald/transcript/?phid={$revision_phid}",
+ );
}
- $links[] = array(
- 'class' => 'transcripts-metamta',
- 'name' => 'MetaMTA Transcripts',
- 'href' => "/mail/?phid={$revision_phid}",
- );
-
- $links[] = array(
- 'class' => 'transcripts-herald',
- 'name' => 'Herald Transcripts',
- 'href' => "/herald/transcript/?phid={$revision_phid}",
- );
-
return $links;
}
diff --git a/src/applications/people/storage/user/PhabricatorUser.php b/src/applications/people/storage/user/PhabricatorUser.php
index b7313ca68a..433cfd2c17 100644
--- a/src/applications/people/storage/user/PhabricatorUser.php
+++ b/src/applications/people/storage/user/PhabricatorUser.php
@@ -86,6 +86,10 @@ class PhabricatorUser extends PhabricatorUserDAO {
return $this;
}
+ public function isLoggedIn() {
+ return !($this->getPHID() === null);
+ }
+
public function save() {
if (!$this->getConduitCertificate()) {
$this->setConduitCertificate($this->generateConduitCertificate());
diff --git a/src/view/page/standard/PhabricatorStandardPageView.php b/src/view/page/standard/PhabricatorStandardPageView.php
index 9ac48deaba..a68baf7d92 100644
--- a/src/view/page/standard/PhabricatorStandardPageView.php
+++ b/src/view/page/standard/PhabricatorStandardPageView.php
@@ -35,6 +35,18 @@ class PhabricatorStandardPageView extends AphrontPageView {
return $this;
}
+ public function setIsLoggedOut($is_logged_out) {
+ if ($is_logged_out) {
+ $this->tabs = array_merge($this->tabs, array(
+ 'login' => array(
+ 'name' => 'Login',
+ 'href' => '/login/'
+ )
+ ));
+ }
+ return $this;
+ }
+
public function getIsAdminInterface() {
return $this->isAdminInterface;
}