Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F14850836
D15059.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
36 KB
Referenced Files
None
Subscribers
None
D15059.diff
View Options
diff --git a/resources/celerity/map.php b/resources/celerity/map.php
--- a/resources/celerity/map.php
+++ b/resources/celerity/map.php
@@ -7,7 +7,7 @@
*/
return array(
'names' => array(
- 'core.pkg.css' => '7fce81fc',
+ 'core.pkg.css' => 'bd4f3259',
'core.pkg.js' => '573e6664',
'darkconsole.pkg.js' => 'e7393ebb',
'differential.pkg.css' => '2de124c9',
@@ -143,7 +143,7 @@
'rsrc/css/phui/phui-object-item-list-view.css' => '26c30d3f',
'rsrc/css/phui/phui-pager.css' => 'bea33d23',
'rsrc/css/phui/phui-pinboard-view.css' => '2495140e',
- 'rsrc/css/phui/phui-profile-menu.css' => '72d69773',
+ 'rsrc/css/phui/phui-profile-menu.css' => '84966ae9',
'rsrc/css/phui/phui-property-list-view.css' => '27b2849e',
'rsrc/css/phui/phui-remarkup-preview.css' => '1a8f2591',
'rsrc/css/phui/phui-spacing.css' => '042804d6',
@@ -819,7 +819,7 @@
'phui-object-item-list-view-css' => '26c30d3f',
'phui-pager-css' => 'bea33d23',
'phui-pinboard-view-css' => '2495140e',
- 'phui-profile-menu-css' => '72d69773',
+ 'phui-profile-menu-css' => '84966ae9',
'phui-property-list-view-css' => '27b2849e',
'phui-remarkup-preview-css' => '1a8f2591',
'phui-spacing-css' => '042804d6',
diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -2895,12 +2895,14 @@
'PhabricatorProjectLogicalUserDatasource' => 'applications/project/typeahead/PhabricatorProjectLogicalUserDatasource.php',
'PhabricatorProjectLogicalViewerDatasource' => 'applications/project/typeahead/PhabricatorProjectLogicalViewerDatasource.php',
'PhabricatorProjectMaterializedMemberEdgeType' => 'applications/project/edge/PhabricatorProjectMaterializedMemberEdgeType.php',
+ 'PhabricatorProjectMemberListView' => 'applications/project/view/PhabricatorProjectMemberListView.php',
'PhabricatorProjectMemberOfProjectEdgeType' => 'applications/project/edge/PhabricatorProjectMemberOfProjectEdgeType.php',
+ 'PhabricatorProjectMembersAddController' => 'applications/project/controller/PhabricatorProjectMembersAddController.php',
'PhabricatorProjectMembersDatasource' => 'applications/project/typeahead/PhabricatorProjectMembersDatasource.php',
- 'PhabricatorProjectMembersEditController' => 'applications/project/controller/PhabricatorProjectMembersEditController.php',
'PhabricatorProjectMembersPolicyRule' => 'applications/project/policyrule/PhabricatorProjectMembersPolicyRule.php',
'PhabricatorProjectMembersProfilePanel' => 'applications/project/profilepanel/PhabricatorProjectMembersProfilePanel.php',
'PhabricatorProjectMembersRemoveController' => 'applications/project/controller/PhabricatorProjectMembersRemoveController.php',
+ 'PhabricatorProjectMembersViewController' => 'applications/project/controller/PhabricatorProjectMembersViewController.php',
'PhabricatorProjectMilestonesController' => 'applications/project/controller/PhabricatorProjectMilestonesController.php',
'PhabricatorProjectMoveController' => 'applications/project/controller/PhabricatorProjectMoveController.php',
'PhabricatorProjectNameContextFreeGrammar' => 'applications/project/lipsum/PhabricatorProjectNameContextFreeGrammar.php',
@@ -2932,8 +2934,10 @@
'PhabricatorProjectUIEventListener' => 'applications/project/events/PhabricatorProjectUIEventListener.php',
'PhabricatorProjectUpdateController' => 'applications/project/controller/PhabricatorProjectUpdateController.php',
'PhabricatorProjectUserFunctionDatasource' => 'applications/project/typeahead/PhabricatorProjectUserFunctionDatasource.php',
+ 'PhabricatorProjectUserListView' => 'applications/project/view/PhabricatorProjectUserListView.php',
'PhabricatorProjectViewController' => 'applications/project/controller/PhabricatorProjectViewController.php',
'PhabricatorProjectWatchController' => 'applications/project/controller/PhabricatorProjectWatchController.php',
+ 'PhabricatorProjectWatcherListView' => 'applications/project/view/PhabricatorProjectWatcherListView.php',
'PhabricatorProjectWorkboardProfilePanel' => 'applications/project/profilepanel/PhabricatorProjectWorkboardProfilePanel.php',
'PhabricatorProjectsEditEngineExtension' => 'applications/project/engineextension/PhabricatorProjectsEditEngineExtension.php',
'PhabricatorProjectsEditField' => 'applications/transactions/editfield/PhabricatorProjectsEditField.php',
@@ -7292,12 +7296,14 @@
'PhabricatorProjectLogicalUserDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
'PhabricatorProjectLogicalViewerDatasource' => 'PhabricatorTypeaheadDatasource',
'PhabricatorProjectMaterializedMemberEdgeType' => 'PhabricatorEdgeType',
+ 'PhabricatorProjectMemberListView' => 'PhabricatorProjectUserListView',
'PhabricatorProjectMemberOfProjectEdgeType' => 'PhabricatorEdgeType',
+ 'PhabricatorProjectMembersAddController' => 'PhabricatorProjectController',
'PhabricatorProjectMembersDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
- 'PhabricatorProjectMembersEditController' => 'PhabricatorProjectController',
'PhabricatorProjectMembersPolicyRule' => 'PhabricatorPolicyRule',
'PhabricatorProjectMembersProfilePanel' => 'PhabricatorProfilePanel',
'PhabricatorProjectMembersRemoveController' => 'PhabricatorProjectController',
+ 'PhabricatorProjectMembersViewController' => 'PhabricatorProjectController',
'PhabricatorProjectMilestonesController' => 'PhabricatorProjectController',
'PhabricatorProjectMoveController' => 'PhabricatorProjectController',
'PhabricatorProjectNameContextFreeGrammar' => 'PhutilContextFreeGrammar',
@@ -7332,8 +7338,10 @@
'PhabricatorProjectUIEventListener' => 'PhabricatorEventListener',
'PhabricatorProjectUpdateController' => 'PhabricatorProjectController',
'PhabricatorProjectUserFunctionDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
+ 'PhabricatorProjectUserListView' => 'AphrontView',
'PhabricatorProjectViewController' => 'PhabricatorProjectController',
'PhabricatorProjectWatchController' => 'PhabricatorProjectController',
+ 'PhabricatorProjectWatcherListView' => 'PhabricatorProjectUserListView',
'PhabricatorProjectWorkboardProfilePanel' => 'PhabricatorProfilePanel',
'PhabricatorProjectsEditEngineExtension' => 'PhabricatorEditEngineExtension',
'PhabricatorProjectsEditField' => 'PhabricatorTokenizerEditField',
diff --git a/src/applications/project/application/PhabricatorProjectApplication.php b/src/applications/project/application/PhabricatorProjectApplication.php
--- a/src/applications/project/application/PhabricatorProjectApplication.php
+++ b/src/applications/project/application/PhabricatorProjectApplication.php
@@ -48,7 +48,9 @@
'lock/(?P<id>[1-9]\d*)/'
=> 'PhabricatorProjectLockController',
'members/(?P<id>[1-9]\d*)/'
- => 'PhabricatorProjectMembersEditController',
+ => 'PhabricatorProjectMembersViewController',
+ 'members/(?P<id>[1-9]\d*)/add/'
+ => 'PhabricatorProjectMembersAddController',
'members/(?P<id>[1-9]\d*)/remove/'
=> 'PhabricatorProjectMembersRemoveController',
'profile/(?P<id>[1-9]\d*)/'
diff --git a/src/applications/project/controller/PhabricatorProjectLockController.php b/src/applications/project/controller/PhabricatorProjectLockController.php
--- a/src/applications/project/controller/PhabricatorProjectLockController.php
+++ b/src/applications/project/controller/PhabricatorProjectLockController.php
@@ -27,7 +27,16 @@
return new Aphront404Response();
}
- $done_uri = $project->getURI();
+ $done_uri = "/project/members/{$id}/";
+
+ if (!$project->supportsEditMembers()) {
+ return $this->newDialog()
+ ->setTitle(pht('Membership Immutable'))
+ ->appendChild(
+ pht('This project does not support editing membership.'))
+ ->addCancelButton($done_uri);
+ }
+
$is_locked = $project->getIsMembershipLocked();
if ($request->isFormPost()) {
diff --git a/src/applications/project/controller/PhabricatorProjectMembersAddController.php b/src/applications/project/controller/PhabricatorProjectMembersAddController.php
new file mode 100644
--- /dev/null
+++ b/src/applications/project/controller/PhabricatorProjectMembersAddController.php
@@ -0,0 +1,72 @@
+<?php
+
+final class PhabricatorProjectMembersAddController
+ extends PhabricatorProjectController {
+
+ public function handleRequest(AphrontRequest $request) {
+ $viewer = $request->getViewer();
+ $id = $request->getURIData('id');
+
+ $project = id(new PhabricatorProjectQuery())
+ ->setViewer($viewer)
+ ->withIDs(array($id))
+ ->requireCapabilities(
+ array(
+ PhabricatorPolicyCapability::CAN_VIEW,
+ PhabricatorPolicyCapability::CAN_EDIT,
+ ))
+ ->executeOne();
+ if (!$project) {
+ return new Aphront404Response();
+ }
+
+ $this->setProject($project);
+
+ if (!$project->supportsEditMembers()) {
+ return new Aphront404Response();
+ }
+
+ $done_uri = "/project/members/{$id}/";
+
+ if ($request->isFormPost()) {
+ $member_phids = $request->getArr('memberPHIDs');
+
+ $type_member = PhabricatorProjectProjectHasMemberEdgeType::EDGECONST;
+
+ $xactions = array();
+
+ $xactions[] = id(new PhabricatorProjectTransaction())
+ ->setTransactionType(PhabricatorTransactions::TYPE_EDGE)
+ ->setMetadataValue('edge:type', $type_member)
+ ->setNewValue(
+ array(
+ '+' => array_fuse($member_phids),
+ ));
+
+ $editor = id(new PhabricatorProjectTransactionEditor($project))
+ ->setActor($viewer)
+ ->setContentSourceFromRequest($request)
+ ->setContinueOnNoEffect(true)
+ ->setContinueOnMissingFields(true)
+ ->applyTransactions($project, $xactions);
+
+ return id(new AphrontRedirectResponse())
+ ->setURI($done_uri);
+ }
+
+ $form = id(new AphrontFormView())
+ ->setUser($viewer)
+ ->appendControl(
+ id(new AphrontFormTokenizerControl())
+ ->setName('memberPHIDs')
+ ->setLabel(pht('Members'))
+ ->setDatasource(new PhabricatorPeopleDatasource()));
+
+ return $this->newDialog()
+ ->setTitle(pht('Add Members'))
+ ->appendForm($form)
+ ->addCancelButton($done_uri)
+ ->addSubmitButton(pht('Add Members'));
+ }
+
+}
diff --git a/src/applications/project/controller/PhabricatorProjectMembersEditController.php b/src/applications/project/controller/PhabricatorProjectMembersEditController.php
deleted file mode 100644
--- a/src/applications/project/controller/PhabricatorProjectMembersEditController.php
+++ /dev/null
@@ -1,156 +0,0 @@
-<?php
-
-final class PhabricatorProjectMembersEditController
- extends PhabricatorProjectController {
-
- public function handleRequest(AphrontRequest $request) {
- $viewer = $request->getViewer();
- $id = $request->getURIData('id');
-
- $project = id(new PhabricatorProjectQuery())
- ->setViewer($viewer)
- ->withIDs(array($id))
- ->needMembers(true)
- ->needImages(true)
- ->executeOne();
- if (!$project) {
- return new Aphront404Response();
- }
-
- $this->setProject($project);
-
- $member_phids = $project->getMemberPHIDs();
-
- if ($request->isFormPost()) {
- $member_spec = array();
-
- $remove = $request->getStr('remove');
- if ($remove) {
- $member_spec['-'] = array_fuse(array($remove));
- }
-
- $add_members = $request->getArr('phids');
- if ($add_members) {
- $member_spec['+'] = array_fuse($add_members);
- }
-
- $type_member = PhabricatorProjectProjectHasMemberEdgeType::EDGECONST;
-
- $xactions = array();
-
- $xactions[] = id(new PhabricatorProjectTransaction())
- ->setTransactionType(PhabricatorTransactions::TYPE_EDGE)
- ->setMetadataValue('edge:type', $type_member)
- ->setNewValue($member_spec);
-
- $editor = id(new PhabricatorProjectTransactionEditor($project))
- ->setActor($viewer)
- ->setContentSourceFromRequest($request)
- ->setContinueOnNoEffect(true)
- ->setContinueOnMissingFields(true)
- ->applyTransactions($project, $xactions);
-
- return id(new AphrontRedirectResponse())
- ->setURI($request->getRequestURI());
- }
-
- $member_phids = array_reverse($member_phids);
- $handles = $this->loadViewerHandles($member_phids);
-
- $state = array();
- foreach ($handles as $handle) {
- $state[] = array(
- 'phid' => $handle->getPHID(),
- 'name' => $handle->getFullName(),
- );
- }
-
- $can_edit = PhabricatorPolicyFilter::hasCapability(
- $viewer,
- $project,
- PhabricatorPolicyCapability::CAN_EDIT);
-
- $supports_edit = $project->supportsEditMembers();
-
- $form_box = null;
- $title = pht('Add Members');
- if ($can_edit && $supports_edit) {
- $header_name = pht('Edit Members');
- $view_uri = $this->getApplicationURI('profile/'.$project->getID().'/');
-
- $form = new AphrontFormView();
- $form
- ->setUser($viewer)
- ->appendControl(
- id(new AphrontFormTokenizerControl())
- ->setName('phids')
- ->setLabel(pht('Add Members'))
- ->setDatasource(new PhabricatorPeopleDatasource()))
- ->appendChild(
- id(new AphrontFormSubmitControl())
- ->addCancelButton($view_uri)
- ->setValue(pht('Add Members')));
- $form_box = id(new PHUIObjectBoxView())
- ->setHeaderText($title)
- ->setForm($form);
- }
-
- $member_list = $this->renderMemberList($project, $handles);
-
- $nav = $this->getProfileMenu();
- $nav->selectFilter(PhabricatorProject::PANEL_MEMBERS);
-
- $crumbs = $this->buildApplicationCrumbs();
- $crumbs->addTextCrumb(pht('Members'));
-
- return $this->newPage()
- ->setNavigation($nav)
- ->setCrumbs($crumbs)
- ->setTitle(array($project->getName(), $title))
- ->appendChild($form_box)
- ->appendChild($member_list);
- }
-
- private function renderMemberList(
- PhabricatorProject $project,
- array $handles) {
-
- $request = $this->getRequest();
- $viewer = $request->getUser();
-
- $can_edit = PhabricatorPolicyFilter::hasCapability(
- $viewer,
- $project,
- PhabricatorPolicyCapability::CAN_EDIT);
-
- $list = id(new PHUIObjectItemListView())
- ->setNoDataString(pht('This project does not have any members.'));
-
- foreach ($handles as $handle) {
- $remove_uri = $this->getApplicationURI(
- '/members/'.$project->getID().'/remove/?phid='.$handle->getPHID());
-
- $item = id(new PHUIObjectItemView())
- ->setHeader($handle->getFullName())
- ->setHref($handle->getURI())
- ->setImageURI($handle->getImageURI());
-
- if ($can_edit) {
- $item->addAction(
- id(new PHUIListItemView())
- ->setIcon('fa-times')
- ->setName(pht('Remove'))
- ->setHref($remove_uri)
- ->setWorkflow(true));
- }
-
- $list->addItem($item);
- }
-
- $box = id(new PHUIObjectBoxView())
- ->setHeaderText(pht('Members'))
- ->setObjectList($list);
-
- return $box;
- }
-}
diff --git a/src/applications/project/controller/PhabricatorProjectMembersViewController.php b/src/applications/project/controller/PhabricatorProjectMembersViewController.php
new file mode 100644
--- /dev/null
+++ b/src/applications/project/controller/PhabricatorProjectMembersViewController.php
@@ -0,0 +1,205 @@
+<?php
+
+final class PhabricatorProjectMembersViewController
+ extends PhabricatorProjectController {
+
+ public function handleRequest(AphrontRequest $request) {
+ $viewer = $request->getViewer();
+ $id = $request->getURIData('id');
+
+ $project = id(new PhabricatorProjectQuery())
+ ->setViewer($viewer)
+ ->withIDs(array($id))
+ ->needMembers(true)
+ ->needWatchers(true)
+ ->needImages(true)
+ ->executeOne();
+ if (!$project) {
+ return new Aphront404Response();
+ }
+
+ $this->setProject($project);
+ $title = pht('Members and Watchers');
+
+ $properties = $this->buildProperties($project);
+ $actions = $this->buildActions($project);
+ $properties->setActionList($actions);
+
+ $object_box = id(new PHUIObjectBoxView())
+ ->setHeaderText($title)
+ ->addPropertyList($properties);
+
+ $member_list = id(new PhabricatorProjectMemberListView())
+ ->setUser($viewer)
+ ->setProject($project)
+ ->setUserPHIDs($project->getMemberPHIDs());
+
+ $watcher_list = id(new PhabricatorProjectWatcherListView())
+ ->setUser($viewer)
+ ->setProject($project)
+ ->setUserPHIDs($project->getWatcherPHIDs());
+
+ $nav = $this->getProfileMenu();
+ $nav->selectFilter(PhabricatorProject::PANEL_MEMBERS);
+
+ $crumbs = $this->buildApplicationCrumbs();
+ $crumbs->addTextCrumb(pht('Members'));
+
+ return $this->newPage()
+ ->setNavigation($nav)
+ ->setCrumbs($crumbs)
+ ->setTitle(array($project->getName(), $title))
+ ->appendChild(
+ array(
+ $object_box,
+ $member_list,
+ $watcher_list,
+ ));
+ }
+
+ private function buildProperties(PhabricatorProject $project) {
+ $viewer = $this->getViewer();
+
+ $view = id(new PHUIPropertyListView())
+ ->setUser($viewer)
+ ->setObject($project);
+
+ if ($project->isMilestone()) {
+ $icon_key = PhabricatorProjectIconSet::getMilestoneIconKey();
+ $icon = PhabricatorProjectIconSet::getIconIcon($icon_key);
+ $target = PhabricatorProjectIconSet::getIconName($icon_key);
+ $note = pht(
+ 'Members of the parent project are members of this project.');
+ $show_join = false;
+ } else if ($project->getHasSubprojects()) {
+ $icon = 'fa-sitemap';
+ $target = pht('Parent Project');
+ $note = pht(
+ 'Members of all subprojects are members of this project.');
+ $show_join = false;
+ } else if ($project->getIsMembershipLocked()) {
+ $icon = 'fa-lock';
+ $target = pht('Locked Project');
+ $note = pht(
+ 'Users with access may join this project, but may not leave.');
+ $show_join = true;
+ } else {
+ $icon = 'fa-briefcase';
+ $target = pht('Normal Project');
+ $note = pht('Users with access may join and leave this project.');
+ $show_join = true;
+ }
+
+ $item = id(new PHUIStatusItemView())
+ ->setIcon($icon)
+ ->setTarget(phutil_tag('strong', array(), $target))
+ ->setNote($note);
+
+ $status = id(new PHUIStatusListView())
+ ->addItem($item);
+
+ $view->addProperty(pht('Membership'), $status);
+
+ if ($show_join) {
+ $descriptions = PhabricatorPolicyQuery::renderPolicyDescriptions(
+ $viewer,
+ $project);
+
+ $view->addProperty(
+ pht('Joinable By'),
+ $descriptions[PhabricatorPolicyCapability::CAN_JOIN]);
+ }
+
+ return $view;
+ }
+
+ private function buildActions(PhabricatorProject $project) {
+ $viewer = $this->getViewer();
+ $id = $project->getID();
+
+ $view = id(new PhabricatorActionListView())
+ ->setUser($viewer);
+
+ $is_locked = $project->getIsMembershipLocked();
+
+ $can_edit = PhabricatorPolicyFilter::hasCapability(
+ $viewer,
+ $project,
+ PhabricatorPolicyCapability::CAN_EDIT);
+
+ $supports_edit = $project->supportsEditMembers();
+
+ $can_join = $supports_edit && PhabricatorPolicyFilter::hasCapability(
+ $viewer,
+ $project,
+ PhabricatorPolicyCapability::CAN_JOIN);
+
+ $can_leave = $supports_edit && (!$is_locked || $can_edit);
+
+ if (!$project->isUserMember($viewer->getPHID())) {
+ $view->addAction(
+ id(new PhabricatorActionView())
+ ->setHref('/project/update/'.$project->getID().'/join/')
+ ->setIcon('fa-plus')
+ ->setDisabled(!$can_join)
+ ->setWorkflow(true)
+ ->setName(pht('Join Project')));
+ } else {
+ $view->addAction(
+ id(new PhabricatorActionView())
+ ->setHref('/project/update/'.$project->getID().'/leave/')
+ ->setIcon('fa-times')
+ ->setDisabled(!$can_leave)
+ ->setWorkflow(true)
+ ->setName(pht('Leave Project')));
+
+ if (!$project->isUserWatcher($viewer->getPHID())) {
+ $view->addAction(
+ id(new PhabricatorActionView())
+ ->setWorkflow(true)
+ ->setHref('/project/watch/'.$project->getID().'/')
+ ->setIcon('fa-eye')
+ ->setName(pht('Watch Project')));
+ } else {
+ $view->addAction(
+ id(new PhabricatorActionView())
+ ->setWorkflow(true)
+ ->setHref('/project/unwatch/'.$project->getID().'/')
+ ->setIcon('fa-eye-slash')
+ ->setName(pht('Unwatch Project')));
+ }
+ }
+
+ $can_add = $can_edit && $supports_edit;
+
+ $view->addAction(
+ id(new PhabricatorActionView())
+ ->setName(pht('Add Members'))
+ ->setIcon('fa-user-plus')
+ ->setHref("/project/members/{$id}/add/")
+ ->setWorkflow(true)
+ ->setDisabled(!$can_add));
+
+ $can_lock = $can_edit && $supports_edit && $this->hasApplicationCapability(
+ ProjectCanLockProjectsCapability::CAPABILITY);
+
+ if ($is_locked) {
+ $lock_name = pht('Unlock Project');
+ $lock_icon = 'fa-unlock';
+ } else {
+ $lock_name = pht('Lock Project');
+ $lock_icon = 'fa-lock';
+ }
+
+ $view->addAction(
+ id(new PhabricatorActionView())
+ ->setName($lock_name)
+ ->setIcon($lock_icon)
+ ->setHref($this->getApplicationURI("lock/{$id}/"))
+ ->setDisabled(!$can_lock)
+ ->setWorkflow(true));
+
+ return $view;
+ }
+
+}
diff --git a/src/applications/project/controller/PhabricatorProjectProfileController.php b/src/applications/project/controller/PhabricatorProjectProfileController.php
--- a/src/applications/project/controller/PhabricatorProjectProfileController.php
+++ b/src/applications/project/controller/PhabricatorProjectProfileController.php
@@ -106,65 +106,6 @@
->setWorkflow(true));
}
- $can_lock = $can_edit && $this->hasApplicationCapability(
- ProjectCanLockProjectsCapability::CAPABILITY);
-
- if ($project->getIsMembershipLocked()) {
- $lock_name = pht('Unlock Project');
- $lock_icon = 'fa-unlock';
- } else {
- $lock_name = pht('Lock Project');
- $lock_icon = 'fa-lock';
- }
-
- $view->addAction(
- id(new PhabricatorActionView())
- ->setName($lock_name)
- ->setIcon($lock_icon)
- ->setHref($this->getApplicationURI("lock/{$id}/"))
- ->setDisabled(!$can_lock)
- ->setWorkflow(true));
-
- $action = null;
- if (!$project->isUserMember($viewer->getPHID())) {
- $can_join = PhabricatorPolicyFilter::hasCapability(
- $viewer,
- $project,
- PhabricatorPolicyCapability::CAN_JOIN);
-
- $action = id(new PhabricatorActionView())
- ->setUser($viewer)
- ->setRenderAsForm(true)
- ->setHref('/project/update/'.$project->getID().'/join/')
- ->setIcon('fa-plus')
- ->setDisabled(!$can_join)
- ->setName(pht('Join Project'));
- $view->addAction($action);
- } else {
- $action = id(new PhabricatorActionView())
- ->setWorkflow(true)
- ->setHref('/project/update/'.$project->getID().'/leave/')
- ->setIcon('fa-times')
- ->setName(pht('Leave Project...'));
- $view->addAction($action);
-
- if (!$project->isUserWatcher($viewer->getPHID())) {
- $action = id(new PhabricatorActionView())
- ->setWorkflow(true)
- ->setHref('/project/watch/'.$project->getID().'/')
- ->setIcon('fa-eye')
- ->setName(pht('Watch Project'));
- $view->addAction($action);
- } else {
- $action = id(new PhabricatorActionView())
- ->setWorkflow(true)
- ->setHref('/project/unwatch/'.$project->getID().'/')
- ->setIcon('fa-eye-slash')
- ->setName(pht('Unwatch Project'));
- $view->addAction($action);
- }
- }
-
return $view;
}
@@ -206,18 +147,10 @@
->setAsInline(true)
: phutil_tag('em', array(), pht('None')));
- $descriptions = PhabricatorPolicyQuery::renderPolicyDescriptions(
- $viewer,
- $project);
-
$view->addProperty(
pht('Looks Like'),
$viewer->renderHandle($project->getPHID())->setAsTag(true));
- $view->addProperty(
- pht('Joinable By'),
- $descriptions[PhabricatorPolicyCapability::CAN_JOIN]);
-
$field_list = PhabricatorCustomField::getObjectFields(
$project,
PhabricatorCustomField::ROLE_VIEW);
diff --git a/src/applications/project/controller/PhabricatorProjectUpdateController.php b/src/applications/project/controller/PhabricatorProjectUpdateController.php
--- a/src/applications/project/controller/PhabricatorProjectUpdateController.php
+++ b/src/applications/project/controller/PhabricatorProjectUpdateController.php
@@ -12,14 +12,11 @@
PhabricatorPolicyCapability::CAN_VIEW,
);
- $process_action = false;
switch ($action) {
case 'join':
$capabilities[] = PhabricatorPolicyCapability::CAN_JOIN;
- $process_action = $request->isFormPost();
break;
case 'leave':
- $process_action = $request->isDialogFormPost();
break;
default:
return new Aphront404Response();
@@ -35,10 +32,13 @@
return new Aphront404Response();
}
- $project_uri = $this->getApplicationURI('profile/'.$project->getID().'/');
+ if (!$project->supportsEditMembers()) {
+ return new Aphront404Response();
+ }
- if ($process_action) {
+ $done_uri = "/project/members/{$id}/";
+ if ($request->isFormPost()) {
$edge_action = null;
switch ($action) {
case 'join':
@@ -50,6 +50,7 @@
}
$type_member = PhabricatorProjectProjectHasMemberEdgeType::EDGECONST;
+
$member_spec = array(
$edge_action => array($viewer->getPHID() => $viewer->getPHID()),
);
@@ -67,46 +68,47 @@
->setContinueOnMissingFields(true)
->applyTransactions($project, $xactions);
- return id(new AphrontRedirectResponse())->setURI($project_uri);
+ return id(new AphrontRedirectResponse())->setURI($done_uri);
}
- $dialog = null;
- switch ($action) {
- case 'leave':
- $dialog = new AphrontDialogView();
- $dialog->setUser($viewer);
- if ($this->userCannotLeave($project)) {
- $dialog->setTitle(pht('You can not leave this project.'));
- $body = pht('The membership is locked for this project.');
- } else {
- $dialog->setTitle(pht('Really leave project?'));
- $body = pht(
- 'Your tremendous contributions to this project will be sorely '.
- 'missed. Are you sure you want to leave?');
- $dialog->addSubmitButton(pht('Leave Project'));
- }
- $dialog->appendParagraph($body);
- $dialog->addCancelButton($project_uri);
- break;
- default:
- return new Aphront404Response();
+ $is_locked = $project->getIsMembershipLocked();
+ $can_edit = PhabricatorPolicyFilter::hasCapability(
+ $viewer,
+ $project,
+ PhabricatorPolicyCapability::CAN_EDIT);
+ $can_leave = ($can_edit || !$is_locked);
+
+ $button = null;
+ if ($action == 'leave') {
+ if ($can_leave) {
+ $title = pht('Leave Project');
+ $body = pht(
+ 'Your tremendous contributions to this project will be sorely '.
+ 'missed. Are you sure you want to leave?');
+ $button = pht('Leave Project');
+ } else {
+ $title = pht('Membership Locked');
+ $body = pht(
+ 'Membership for this project is locked. You can not leave.');
+ }
+ } else {
+ $title = pht('Join Project');
+ $body = pht(
+ 'Join this project? You will become a member and enjoy whatever '.
+ 'benefits membership may confer.');
+ $button = pht('Join Project');
}
- return id(new AphrontDialogResponse())->setDialog($dialog);
- }
+ $dialog = $this->newDialog()
+ ->setTitle($title)
+ ->appendParagraph($body)
+ ->addCancelButton($done_uri);
- /**
- * This is enforced in @{class:PhabricatorProjectTransactionEditor}. We use
- * this logic to render a better form for users hitting this case.
- */
- private function userCannotLeave(PhabricatorProject $project) {
- $viewer = $this->getViewer();
-
- return
- $project->getIsMembershipLocked() &&
- !PhabricatorPolicyFilter::hasCapability(
- $viewer,
- $project,
- PhabricatorPolicyCapability::CAN_EDIT);
+ if ($button) {
+ $dialog->addSubmitButton($button);
+ }
+
+ return $dialog;
}
+
}
diff --git a/src/applications/project/controller/PhabricatorProjectWatchController.php b/src/applications/project/controller/PhabricatorProjectWatchController.php
--- a/src/applications/project/controller/PhabricatorProjectWatchController.php
+++ b/src/applications/project/controller/PhabricatorProjectWatchController.php
@@ -18,9 +18,9 @@
return new Aphront404Response();
}
- $project_uri = $this->getApplicationURI('profile/'.$project->getID().'/');
+ $done_uri = "/project/members/{$id}/";
- // You must be a member of a project to
+ // You must be a member of a project to watch it.
if (!$project->isUserMember($viewer->getPHID())) {
return new Aphront400Response();
}
@@ -56,7 +56,7 @@
->setContinueOnMissingFields(true)
->applyTransactions($project, $xactions);
- return id(new AphrontRedirectResponse())->setURI($project_uri);
+ return id(new AphrontRedirectResponse())->setURI($done_uri);
}
$dialog = null;
@@ -83,7 +83,7 @@
return $this->newDialog()
->setTitle($title)
->appendParagraph($body)
- ->addCancelButton($project_uri)
+ ->addCancelButton($done_uri)
->addSubmitButton($submit);
}
diff --git a/src/applications/project/view/PhabricatorProjectMemberListView.php b/src/applications/project/view/PhabricatorProjectMemberListView.php
new file mode 100644
--- /dev/null
+++ b/src/applications/project/view/PhabricatorProjectMemberListView.php
@@ -0,0 +1,34 @@
+<?php
+
+final class PhabricatorProjectMemberListView
+ extends PhabricatorProjectUserListView {
+
+ protected function canEditList() {
+ $viewer = $this->getUser();
+ $project = $this->getProject();
+
+ if (!$project->supportsEditMembers()) {
+ return false;
+ }
+
+ return PhabricatorPolicyFilter::hasCapability(
+ $viewer,
+ $project,
+ PhabricatorPolicyCapability::CAN_EDIT);
+ }
+
+ protected function getNoDataString() {
+ return pht('This project does not have any members.');
+ }
+
+ protected function getRemoveURI($phid) {
+ $project = $this->getProject();
+ $id = $project->getID();
+ return "/project/members/{$id}/remove/?phid={$phid}";
+ }
+
+ protected function getHeaderText() {
+ return pht('Members');
+ }
+
+}
diff --git a/src/applications/project/view/PhabricatorProjectUserListView.php b/src/applications/project/view/PhabricatorProjectUserListView.php
new file mode 100644
--- /dev/null
+++ b/src/applications/project/view/PhabricatorProjectUserListView.php
@@ -0,0 +1,78 @@
+<?php
+
+abstract class PhabricatorProjectUserListView extends AphrontView {
+
+ private $project;
+ private $userPHIDs;
+
+ public function setProject(PhabricatorProject $project) {
+ $this->project = $project;
+ return $this;
+ }
+
+ public function getProject() {
+ return $this->project;
+ }
+
+ public function setUserPHIDs(array $user_phids) {
+ $this->userPHIDs = $user_phids;
+ return $this;
+ }
+
+ public function getUserPHIDs() {
+ return $this->userPHIDs;
+ }
+
+ abstract protected function canEditList();
+ abstract protected function getNoDataString();
+ abstract protected function getRemoveURI($phid);
+ abstract protected function getHeaderText();
+
+ public function render() {
+ $viewer = $this->getUser();
+ $project = $this->getProject();
+ $user_phids = $this->getUserPHIDs();
+
+ $can_edit = $this->canEditList();
+ $no_data = $this->getNoDataString();
+
+ $list = id(new PHUIObjectItemListView())
+ ->setNoDataString($no_data);
+
+ $user_phids = array_reverse($user_phids);
+ $handles = $viewer->loadHandles($user_phids);
+
+ // Always put the viewer first if they are on the list.
+ $user_phids = array_fuse($user_phids);
+ $user_phids =
+ array_select_keys($user_phids, array($viewer->getPHID())) +
+ $user_phids;
+
+ foreach ($user_phids as $user_phid) {
+ $handle = $handles[$user_phid];
+
+ $item = id(new PHUIObjectItemView())
+ ->setHeader($handle->getFullName())
+ ->setHref($handle->getURI())
+ ->setImageURI($handle->getImageURI());
+
+ if ($can_edit) {
+ $remove_uri = $this->getRemoveURI($user_phid);
+
+ $item->addAction(
+ id(new PHUIListItemView())
+ ->setIcon('fa-times')
+ ->setName(pht('Remove'))
+ ->setHref($remove_uri)
+ ->setWorkflow(true));
+ }
+
+ $list->addItem($item);
+ }
+
+ return id(new PHUIObjectBoxView())
+ ->setHeaderText($this->getHeaderText())
+ ->setObjectList($list);
+ }
+
+}
diff --git a/src/applications/project/view/PhabricatorProjectWatcherListView.php b/src/applications/project/view/PhabricatorProjectWatcherListView.php
new file mode 100644
--- /dev/null
+++ b/src/applications/project/view/PhabricatorProjectWatcherListView.php
@@ -0,0 +1,22 @@
+<?php
+
+final class PhabricatorProjectWatcherListView
+ extends PhabricatorProjectUserListView {
+
+ protected function canEditList() {
+ return false;
+ }
+
+ protected function getNoDataString() {
+ return pht('This project does not have any watchers.');
+ }
+
+ protected function getRemoveURI($phid) {
+ return null;
+ }
+
+ protected function getHeaderText() {
+ return pht('Watchers');
+ }
+
+}
diff --git a/src/view/phui/PHUIStatusItemView.php b/src/view/phui/PHUIStatusItemView.php
--- a/src/view/phui/PHUIStatusItemView.php
+++ b/src/view/phui/PHUIStatusItemView.php
@@ -24,7 +24,6 @@
const ICON_CLOCK = 'fa-clock-o';
const ICON_STAR = 'fa-star';
- /* render_textarea */
public function setIcon($icon, $color = null, $label = null) {
$this->icon = $icon;
$this->iconLabel = $label;
diff --git a/webroot/rsrc/css/phui/phui-profile-menu.css b/webroot/rsrc/css/phui/phui-profile-menu.css
--- a/webroot/rsrc/css/phui/phui-profile-menu.css
+++ b/webroot/rsrc/css/phui/phui-profile-menu.css
@@ -73,21 +73,24 @@
background-size: 100%;
}
-.phui-profile-menu .phui-profile-menu-collapsed .phui-list-item-href {
+.phui-profile-menu .phui-profile-menu-collapsed .phabricator-side-menu
+ .phui-list-item-href {
text-align: center;
padding: 42px 8px 12px;
font-size: 11px;
line-height: 13px;
}
-.phui-profile-menu .phui-profile-menu-collapsed .phui-list-item-name {
+.phui-profile-menu .phui-profile-menu-collapsed .phabricator-side-menu
+ .phui-list-item-name {
display: block;
overflow: hidden;
text-overflow: ellipsis;
}
-.phui-profile-menu .phui-profile-menu-collapsed .phui-list-item-icon,
-.phui-profile-menu .phui-profile-menu-collapsed
+.phui-profile-menu .phui-profile-menu-collapsed .phabricator-side-menu
+ .phui-list-item-icon,
+.phui-profile-menu .phui-profile-menu-collapsed .phabricator-side-menu
.phui-list-item-href .phui-icon-view {
top: 10px;
left: 29px;
@@ -166,27 +169,31 @@
left: 120px;
}
-.phui-profile-menu .phui-profile-menu-collapsed .phui-profile-menu-footer {
+.phui-profile-menu .phui-profile-menu-collapsed .phabricator-side-menu
+ .phui-profile-menu-footer {
width: 40px;
height: {$menu.profile.item.height};
bottom: 0px;
}
-.phui-profile-menu .phui-profile-menu-collapsed .phui-profile-menu-footer-1 {
+.phui-profile-menu .phui-profile-menu-collapsed .phabricator-side-menu
+ .phui-profile-menu-footer-1 {
left: 0;
}
-.phui-profile-menu .phui-profile-menu-collapsed .phui-profile-menu-footer-2 {
+.phui-profile-menu .phui-profile-menu-collapsed .phabricator-side-menu
+ .phui-profile-menu-footer-2 {
left: 40px;
}
-
-.phui-profile-menu .phui-profile-menu-collapsed .phui-profile-menu-footer
+.phui-profile-menu .phui-profile-menu-collapsed .phabricator-side-menu
+ .phui-profile-menu-footer
.phui-list-item-name {
display: none;
}
-.phui-profile-menu .phui-profile-menu-collapsed .phui-profile-menu-footer
+.phui-profile-menu .phui-profile-menu-collapsed .phabricator-side-menu
+ .phui-profile-menu-footer
.phui-list-item-icon {
top: 10px;
left: 10px;
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Wed, Feb 5, 9:53 AM (21 h, 13 m)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
7092038
Default Alt Text
D15059.diff (36 KB)
Attached To
Mode
D15059: Move member/watch actions to "Members/Watchers" page
Attached
Detach File
Event Timeline
Log In to Comment