diff --git a/src/applications/project/controller/PhabricatorProjectMembersViewController.php b/src/applications/project/controller/PhabricatorProjectMembersViewController.php index 88bc56be2..e98412ff3 100644 --- a/src/applications/project/controller/PhabricatorProjectMembersViewController.php +++ b/src/applications/project/controller/PhabricatorProjectMembersViewController.php @@ -1,283 +1,294 @@ 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); + $curtain = $this->buildCurtainView($project); $object_box = id(new PHUIObjectBoxView()) - ->setHeaderText($title) + ->setHeaderText(pht('Details')) + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->addPropertyList($properties); $member_list = id(new PhabricatorProjectMemberListView()) ->setUser($viewer) ->setProject($project) + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->setUserPHIDs($project->getMemberPHIDs()); $watcher_list = id(new PhabricatorProjectWatcherListView()) ->setUser($viewer) ->setProject($project) + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->setUserPHIDs($project->getWatcherPHIDs()); $nav = $this->getProfileMenu(); $nav->selectFilter(PhabricatorProject::PANEL_MEMBERS); $crumbs = $this->buildApplicationCrumbs(); $crumbs->addTextCrumb(pht('Members')); + $crumbs->setBorder(true); + + $header = id(new PHUIHeaderView()) + ->setHeader($title) + ->setHeaderIcon('fa-group'); + + $view = id(new PHUITwoColumnView()) + ->setHeader($header) + ->setCurtain($curtain) + ->setMainColumn(array( + $object_box, + $member_list, + $watcher_list, + )); + return $this->newPage() ->setNavigation($nav) ->setCrumbs($crumbs) - ->setTitle(array($project->getDisplayName(), $title)) - ->appendChild( - array( - $object_box, - $member_list, - $watcher_list, - )); + ->setTitle(array($project->getName(), $title)) + ->appendChild($view); } 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]); } $viewer_phid = $viewer->getPHID(); if ($project->isUserWatcher($viewer_phid)) { $watch_item = id(new PHUIStatusItemView()) ->setIcon('fa-eye green') ->setTarget(phutil_tag('strong', array(), pht('Watching'))) ->setNote( pht( 'You will receive mail about changes made to any related '. 'object.')); $watch_status = id(new PHUIStatusListView()) ->addItem($watch_item); $view->addProperty(pht('Watching'), $watch_status); } if ($project->isUserMember($viewer_phid)) { $is_silenced = $this->isProjectSilenced($project); if ($is_silenced) { $mail_icon = 'fa-envelope-o grey'; $mail_target = pht('Disabled'); $mail_note = pht( 'When mail is sent to project members, you will not receive '. 'a copy.'); } else { $mail_icon = 'fa-envelope-o green'; $mail_target = pht('Enabled'); $mail_note = pht( 'You will receive mail that is sent to project members.'); } $mail_item = id(new PHUIStatusItemView()) ->setIcon($mail_icon) ->setTarget(phutil_tag('strong', array(), $mail_target)) ->setNote($mail_note); $mail_status = id(new PHUIStatusListView()) ->addItem($mail_item); $view->addProperty(pht('Mail to Members'), $mail_status); } return $view; } - private function buildActions(PhabricatorProject $project) { + private function buildCurtainView(PhabricatorProject $project) { $viewer = $this->getViewer(); $id = $project->getID(); - $view = id(new PhabricatorActionListView()) - ->setUser($viewer); + $curtain = $this->newCurtainView($project); $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); $viewer_phid = $viewer->getPHID(); if (!$project->isUserMember($viewer_phid)) { - $view->addAction( + $curtain->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( + $curtain->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( + $curtain->addAction( id(new PhabricatorActionView()) ->setWorkflow(true) ->setHref('/project/watch/'.$project->getID().'/') ->setIcon('fa-eye') ->setName(pht('Watch Project'))); } else { - $view->addAction( + $curtain->addAction( id(new PhabricatorActionView()) ->setWorkflow(true) ->setHref('/project/unwatch/'.$project->getID().'/') ->setIcon('fa-eye-slash') ->setName(pht('Unwatch Project'))); } $can_silence = $project->isUserMember($viewer_phid); $is_silenced = $this->isProjectSilenced($project); if ($is_silenced) { $silence_text = pht('Enable Mail'); } else { $silence_text = pht('Disable Mail'); } - $view->addAction( + $curtain->addAction( id(new PhabricatorActionView()) ->setName($silence_text) ->setIcon('fa-envelope-o') ->setHref("/project/silence/{$id}/") ->setWorkflow(true) ->setDisabled(!$can_silence)); $can_add = $can_edit && $supports_edit; - $view->addAction( + $curtain->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( + $curtain->addAction( id(new PhabricatorActionView()) ->setName($lock_name) ->setIcon($lock_icon) ->setHref($this->getApplicationURI("lock/{$id}/")) ->setDisabled(!$can_lock) ->setWorkflow(true)); - return $view; + return $curtain; } private function isProjectSilenced(PhabricatorProject $project) { $viewer = $this->getViewer(); $viewer_phid = $viewer->getPHID(); if (!$viewer_phid) { return false; } $edge_type = PhabricatorProjectSilencedEdgeType::EDGECONST; $silenced = PhabricatorEdgeQuery::loadDestinationPHIDs( $project->getPHID(), $edge_type); $silenced = array_fuse($silenced); return isset($silenced[$viewer_phid]); } } diff --git a/src/applications/project/controller/PhabricatorProjectSubprojectsController.php b/src/applications/project/controller/PhabricatorProjectSubprojectsController.php index eb32d00b9..1232d4010 100644 --- a/src/applications/project/controller/PhabricatorProjectSubprojectsController.php +++ b/src/applications/project/controller/PhabricatorProjectSubprojectsController.php @@ -1,252 +1,262 @@ getViewer(); $response = $this->loadProject(); if ($response) { return $response; } $project = $this->getProject(); $id = $project->getID(); $can_edit = PhabricatorPolicyFilter::hasCapability( $viewer, $project, PhabricatorPolicyCapability::CAN_EDIT); $allows_subprojects = $project->supportsSubprojects(); $allows_milestones = $project->supportsMilestones(); if ($allows_subprojects) { $subprojects = id(new PhabricatorProjectQuery()) ->setViewer($viewer) ->withParentProjectPHIDs(array($project->getPHID())) ->needImages(true) ->withIsMilestone(false) ->execute(); } else { $subprojects = array(); } if ($allows_milestones) { $milestones = id(new PhabricatorProjectQuery()) ->setViewer($viewer) ->withParentProjectPHIDs(array($project->getPHID())) ->needImages(true) ->withIsMilestone(true) ->setOrderVector(array('milestoneNumber', 'id')) ->execute(); } else { $milestones = array(); } if ($milestones) { $milestone_list = id(new PHUIObjectBoxView()) ->setHeaderText(pht('Milestones')) + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->setObjectList( id(new PhabricatorProjectListView()) ->setUser($viewer) ->setProjects($milestones) ->renderList()); } else { $milestone_list = null; } if ($subprojects) { $subproject_list = id(new PHUIObjectBoxView()) ->setHeaderText(pht('Subprojects')) + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->setObjectList( id(new PhabricatorProjectListView()) ->setUser($viewer) ->setProjects($subprojects) ->renderList()); } else { $subproject_list = null; } $property_list = $this->buildPropertyList( $project, $milestones, $subprojects); - $action_list = $this->buildActionList( + $curtain = $this->buildCurtainView( $project, $milestones, $subprojects); - $property_list->setActionList($action_list); - $header_box = id(new PHUIObjectBoxView()) - ->setHeaderText(pht('Subprojects and Milestones')) + $details = id(new PHUIObjectBoxView()) + ->setHeaderText(pht('Details')) + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->addPropertyList($property_list); $nav = $this->getProfileMenu(); $nav->selectFilter(PhabricatorProject::PANEL_SUBPROJECTS); $crumbs = $this->buildApplicationCrumbs(); $crumbs->addTextCrumb(pht('Subprojects')); + $crumbs->setBorder(true); + + $header = id(new PHUIHeaderView()) + ->setHeader(pht('Subprojects and Milestones')) + ->setHeaderIcon('fa-sitemap'); + + $view = id(new PHUITwoColumnView()) + ->setHeader($header) + ->setCurtain($curtain) + ->setMainColumn(array( + $details, + $milestone_list, + $subproject_list, + )); return $this->newPage() ->setNavigation($nav) ->setCrumbs($crumbs) ->setTitle(array($project->getName(), pht('Subprojects'))) - ->appendChild( - array( - $header_box, - $milestone_list, - $subproject_list, - )); + ->appendChild($view); } private function buildPropertyList( PhabricatorProject $project, array $milestones, array $subprojects) { $viewer = $this->getViewer(); $view = id(new PHUIPropertyListView()) ->setUser($viewer); $view->addProperty( pht('Prototype'), $this->renderStatus( 'fa-exclamation-triangle red', pht('Warning'), pht('Subprojects and milestones are only partially implemented.'))); if (!$project->supportsMilestones()) { $milestone_status = $this->renderStatus( 'fa-times grey', pht('Already Milestone'), pht( 'This project is already a milestone, and milestones may not '. 'have their own milestones.')); } else { if (!$milestones) { $milestone_status = $this->renderStatus( 'fa-check grey', pht('None Created'), pht( 'You can create milestones for this project.')); } else { $milestone_status = $this->renderStatus( 'fa-check green', pht('Has Milestones'), pht('This project has milestones.')); } } $view->addProperty(pht('Milestones'), $milestone_status); if (!$project->supportsSubprojects()) { $subproject_status = $this->renderStatus( 'fa-times grey', pht('Milestone'), pht( 'This project is a milestone, and milestones may not have '. 'subprojects.')); } else { if (!$subprojects) { $subproject_status = $this->renderStatus( 'fa-check grey', pht('None Created'), pht('You can create subprojects for this project.')); } else { $subproject_status = $this->renderStatus( 'fa-check green', pht('Has Subprojects'), pht( 'This project has subprojects.')); } } $view->addProperty(pht('Subprojects'), $subproject_status); return $view; } - private function buildActionList( + private function buildCurtainView( PhabricatorProject $project, array $milestones, array $subprojects) { $viewer = $this->getViewer(); $id = $project->getID(); $can_create = $this->hasApplicationCapability( ProjectCreateProjectsCapability::CAPABILITY); $can_edit = PhabricatorPolicyFilter::hasCapability( $viewer, $project, PhabricatorPolicyCapability::CAN_EDIT); $allows_milestones = $project->supportsMilestones(); $allows_subprojects = $project->supportsSubprojects(); - $view = id(new PhabricatorActionListView()) - ->setUser($viewer); + $curtain = $this->newCurtainView($project); if ($allows_milestones && $milestones) { $milestone_text = pht('Create Next Milestone'); } else { $milestone_text = pht('Create Milestone'); } $can_milestone = ($can_create && $can_edit && $allows_milestones); $milestone_href = "/project/edit/?milestone={$id}"; - $view->addAction( + $curtain->addAction( id(new PhabricatorActionView()) ->setName($milestone_text) ->setIcon('fa-plus') ->setHref($milestone_href) ->setDisabled(!$can_milestone) ->setWorkflow(!$can_milestone)); $can_subproject = ($can_create && $can_edit && $allows_subprojects); // If we're offering to create the first subproject, we're going to warn // the user about the effects before moving forward. if ($can_subproject && !$subprojects) { $subproject_href = "/project/warning/{$id}/"; $subproject_disabled = false; $subproject_workflow = true; } else { $subproject_href = "/project/edit/?parent={$id}"; $subproject_disabled = !$can_subproject; $subproject_workflow = !$can_subproject; } - $view->addAction( + $curtain->addAction( id(new PhabricatorActionView()) ->setName(pht('Create Subproject')) ->setIcon('fa-plus') ->setHref($subproject_href) ->setDisabled($subproject_disabled) ->setWorkflow($subproject_workflow)); - return $view; + return $curtain; } private function renderStatus($icon, $target, $note) { $item = id(new PHUIStatusItemView()) ->setIcon($icon) ->setTarget(phutil_tag('strong', array(), $target)) ->setNote($note); return id(new PHUIStatusListView()) ->addItem($item); } }