diff --git a/src/applications/people/controller/PhabricatorPeopleProfileViewController.php b/src/applications/people/controller/PhabricatorPeopleProfileViewController.php index 1453ab7d71..cdbbddd8d8 100644 --- a/src/applications/people/controller/PhabricatorPeopleProfileViewController.php +++ b/src/applications/people/controller/PhabricatorPeopleProfileViewController.php @@ -1,187 +1,187 @@ getViewer(); $username = $request->getURIData('username'); $user = id(new PhabricatorPeopleQuery()) ->setViewer($viewer) ->withUsernames(array($username)) ->needBadges(true) ->needProfileImage(true) ->needAvailability(true) ->executeOne(); if (!$user) { return new Aphront404Response(); } $this->setUser($user); $profile = $user->loadUserProfile(); $picture = $user->getProfileImageURI(); $profile_icon = PhabricatorPeopleIconSet::getIconIcon($profile->getIcon()); $profile_icon = id(new PHUIIconView()) ->setIcon($profile_icon); $profile_title = $profile->getDisplayTitle(); $header = id(new PHUIHeaderView()) ->setHeader($user->getFullName()) ->setSubheader(array($profile_icon, $profile_title)) ->setImage($picture) ->setProfileHeader(true); $can_edit = PhabricatorPolicyFilter::hasCapability( $viewer, $user, PhabricatorPolicyCapability::CAN_EDIT); if ($can_edit) { $id = $user->getID(); $header->setImageEditURL($this->getApplicationURI("picture/{$id}/")); } $properties = $this->buildPropertyView($user); $name = $user->getUsername(); $feed = $this->buildPeopleFeed($user, $viewer); $feed = phutil_tag_div('project-view-feed', $feed); $badges = $this->buildBadgesView($user); if ($badges) { $columns = id(new PHUITwoColumnView()) ->addClass('project-view-badges') ->setMainColumn( array( $properties, $feed, )) ->setSideColumn( array( $badges, )); } else { $columns = array($properties, $feed); } $nav = $this->getProfileMenu(); $nav->selectFilter(PhabricatorPeopleProfilePanelEngine::PANEL_PROFILE); $crumbs = $this->buildApplicationCrumbs(); $crumbs->setBorder(true); require_celerity_resource('project-view-css'); $home = phutil_tag( 'div', array( 'class' => 'project-view-home', ), array( $header, $columns, )); return $this->newPage() ->setTitle($user->getUsername()) ->setNavigation($nav) ->setCrumbs($crumbs) ->appendChild( array( $home, )); } private function buildPropertyView( PhabricatorUser $user) { $viewer = $this->getRequest()->getUser(); $view = id(new PHUIPropertyListView()) ->setUser($viewer) ->setObject($user); $field_list = PhabricatorCustomField::getObjectFields( $user, PhabricatorCustomField::ROLE_VIEW); $field_list->appendFieldsToPropertyList($user, $viewer, $view); - if ($view->isEmpty()) { + if (!$view->hasAnyProperties()) { return null; } $view = id(new PHUIBoxView()) ->setColor(PHUIBoxView::GREY) ->appendChild($view) ->addClass('project-view-properties'); return $view; } private function buildBadgesView( PhabricatorUser $user) { $viewer = $this->getViewer(); $class = 'PhabricatorBadgesApplication'; $box = null; if (PhabricatorApplication::isClassInstalledForViewer($class, $viewer)) { $badge_phids = $user->getBadgePHIDs(); if ($badge_phids) { $badges = id(new PhabricatorBadgesQuery()) ->setViewer($viewer) ->withPHIDs($badge_phids) ->withStatuses(array(PhabricatorBadgesBadge::STATUS_ACTIVE)) ->execute(); $flex = new PHUIBadgeBoxView(); foreach ($badges as $badge) { $item = id(new PHUIBadgeView()) ->setIcon($badge->getIcon()) ->setHeader($badge->getName()) ->setSubhead($badge->getFlavor()) ->setQuality($badge->getQuality()); $flex->addItem($item); } $box = id(new PHUIObjectBoxView()) ->setHeaderText(pht('Badges')) ->appendChild($flex) ->setBackground(PHUIBoxView::GREY); } } return $box; } private function buildPeopleFeed( PhabricatorUser $user, $viewer) { $query = new PhabricatorFeedQuery(); $query->setFilterPHIDs( array( $user->getPHID(), )); $query->setLimit(100); $query->setViewer($viewer); $stories = $query->execute(); $builder = new PhabricatorFeedBuilder($stories); $builder->setUser($viewer); $builder->setShowHovercards(true); $builder->setNoDataString(pht('To begin on such a grand journey, '. 'requires but just a single step.')); $view = $builder->buildView(); return $view->render(); } } diff --git a/src/applications/project/controller/PhabricatorProjectProfileController.php b/src/applications/project/controller/PhabricatorProjectProfileController.php index c03b82b05d..74acd1edbf 100644 --- a/src/applications/project/controller/PhabricatorProjectProfileController.php +++ b/src/applications/project/controller/PhabricatorProjectProfileController.php @@ -1,180 +1,180 @@ loadProject(); if ($response) { return $response; } $viewer = $request->getUser(); $project = $this->getProject(); $id = $project->getID(); $picture = $project->getProfileImageURI(); $header = id(new PHUIHeaderView()) ->setHeader($project->getName()) ->setUser($viewer) ->setPolicyObject($project) ->setImage($picture) ->setProfileHeader(true); if ($project->getStatus() == PhabricatorProjectStatus::STATUS_ACTIVE) { $header->setStatus('fa-check', 'bluegrey', pht('Active')); } else { $header->setStatus('fa-ban', 'red', pht('Archived')); } $can_edit = PhabricatorPolicyFilter::hasCapability( $viewer, $project, PhabricatorPolicyCapability::CAN_EDIT); if ($can_edit) { $header->setImageEditURL($this->getApplicationURI("picture/{$id}/")); } $properties = $this->buildPropertyListView($project); $watch_action = $this->renderWatchAction($project); $header->addActionLink($watch_action); $member_list = id(new PhabricatorProjectMemberListView()) ->setUser($viewer) ->setProject($project) ->setLimit(5) ->setBackground(PHUIBoxView::GREY) ->setUserPHIDs($project->getMemberPHIDs()); $watcher_list = id(new PhabricatorProjectWatcherListView()) ->setUser($viewer) ->setProject($project) ->setLimit(5) ->setBackground(PHUIBoxView::GREY) ->setUserPHIDs($project->getWatcherPHIDs()); $nav = $this->getProfileMenu(); $nav->selectFilter(PhabricatorProject::PANEL_PROFILE); $stories = id(new PhabricatorFeedQuery()) ->setViewer($viewer) ->setFilterPHIDs( array( $project->getPHID(), )) ->setLimit(50) ->execute(); $feed = $this->renderStories($stories); $feed = phutil_tag_div('project-view-feed', $feed); $columns = id(new PHUITwoColumnView()) ->setMainColumn( array( $properties, $feed, )) ->setSideColumn( array( $member_list, $watcher_list, )); $crumbs = $this->buildApplicationCrumbs(); $crumbs->setBorder(true); require_celerity_resource('project-view-css'); $home = phutil_tag( 'div', array( 'class' => 'project-view-home', ), array( $header, $columns, )); return $this->newPage() ->setNavigation($nav) ->setCrumbs($crumbs) ->setTitle($project->getName()) ->setPageObjectPHIDs(array($project->getPHID())) ->appendChild( array( $home, )); } private function buildPropertyListView( PhabricatorProject $project) { $request = $this->getRequest(); $viewer = $request->getUser(); $view = id(new PHUIPropertyListView()) ->setUser($viewer) ->setObject($project); $field_list = PhabricatorCustomField::getObjectFields( $project, PhabricatorCustomField::ROLE_VIEW); $field_list->appendFieldsToPropertyList($project, $viewer, $view); - if ($view->isEmpty()) { + if (!$view->hasAnyProperties()) { return null; } $view = id(new PHUIBoxView()) ->setColor(PHUIBoxView::GREY) ->appendChild($view) ->addClass('project-view-properties'); return $view; } private function renderStories(array $stories) { assert_instances_of($stories, 'PhabricatorFeedStory'); $builder = new PhabricatorFeedBuilder($stories); $builder->setUser($this->getRequest()->getUser()); $builder->setShowHovercards(true); $view = $builder->buildView(); return $view; } private function renderWatchAction(PhabricatorProject $project) { $viewer = $this->getViewer(); $viewer_phid = $viewer->getPHID(); $id = $project->getID(); $is_watcher = ($viewer_phid && $project->isUserWatcher($viewer_phid)); if (!$is_watcher) { $watch_icon = 'fa-eye'; $watch_text = pht('Watch Project'); $watch_href = "/project/watch/{$id}/?via=profile"; } else { $watch_icon = 'fa-eye-slash'; $watch_text = pht('Unwatch Project'); $watch_href = "/project/unwatch/{$id}/?via=profile"; } $watch_icon = id(new PHUIIconView()) ->setIcon($watch_icon); return id(new PHUIButtonView()) ->setTag('a') ->setWorkflow(true) ->setIcon($watch_icon) ->setText($watch_text) ->setHref($watch_href); } } diff --git a/src/applications/project/view/ProjectBoardTaskCard.php b/src/applications/project/view/ProjectBoardTaskCard.php index 9ae3ef83c8..86f4359c08 100644 --- a/src/applications/project/view/ProjectBoardTaskCard.php +++ b/src/applications/project/view/ProjectBoardTaskCard.php @@ -1,101 +1,100 @@ viewer = $viewer; return $this; } public function getViewer() { return $this->viewer; } public function setProject(PhabricatorProject $project) { $this->project = $project; return $this; } public function getProject() { return $this->project; } public function setTask(ManiphestTask $task) { $this->task = $task; return $this; } public function getTask() { return $this->task; } public function setOwner(PhabricatorObjectHandle $owner = null) { $this->owner = $owner; return $this; } public function getOwner() { return $this->owner; } public function setCanEdit($can_edit) { $this->canEdit = $can_edit; return $this; } public function getCanEdit() { return $this->canEdit; } public function getItem() { $task = $this->getTask(); $owner = $this->getOwner(); $can_edit = $this->getCanEdit(); $viewer = $this->getViewer(); $color_map = ManiphestTaskPriority::getColorMap(); $bar_color = idx($color_map, $task->getPriority(), 'grey'); $card = id(new PHUIObjectItemView()) ->setObject($task) ->setUser($viewer) ->setObjectName('T'.$task->getID()) ->setHeader($task->getTitle()) ->setGrippable($can_edit) ->setHref('/T'.$task->getID()) ->addSigil('project-card') ->setDisabled($task->isClosed()) ->setMetadata( array( 'objectPHID' => $task->getPHID(), )) ->addAction( id(new PHUIListItemView()) ->setName(pht('Edit')) ->setIcon('fa-pencil') ->addSigil('edit-project-card') ->setHref('/maniphest/task/edit/'.$task->getID().'/')) ->setBarColor($bar_color); if ($owner) { $card->addAttribute($owner->renderLink()); } $project_phids = array_fuse($task->getProjectPHIDs()); unset($project_phids[$this->project->getPHID()]); - $handle_list = $viewer->loadHandles($project_phids); - $tag_list = id(new PHUIHandleTagListView()) - ->setSlim(true) - ->setHandles($handle_list); - - if (!$tag_list->isEmpty()) { + if ($project_phids) { + $handle_list = $viewer->loadHandles($project_phids); + $tag_list = id(new PHUIHandleTagListView()) + ->setSlim(true) + ->setHandles($handle_list); $card->addAttribute($tag_list); } return $card; } } diff --git a/src/view/AphrontTagView.php b/src/view/AphrontTagView.php index b82ebef9c3..a6eb722383 100644 --- a/src/view/AphrontTagView.php +++ b/src/view/AphrontTagView.php @@ -1,158 +1,154 @@ workflow = $workflow; return $this; } public function getWorkflow() { return $this->workflow; } public function setMustCapture($must_capture) { $this->mustCapture = $must_capture; return $this; } public function getMustCapture() { return $this->mustCapture; } final public function setMetadata(array $metadata) { $this->metadata = $metadata; return $this; } final public function getMetadata() { return $this->metadata; } final public function setStyle($style) { $this->style = $style; return $this; } final public function getStyle() { return $this->style; } final public function addSigil($sigil) { $this->sigils[] = $sigil; return $this; } final public function getSigils() { return $this->sigils; } public function addClass($class) { $this->classes[] = $class; return $this; } public function getClasses() { return $this->classes; } public function setID($id) { $this->id = $id; return $this; } public function getID() { return $this->id; } - public function isEmpty() { - return empty($this->getTagContent()); - } - protected function getTagName() { return 'div'; } protected function getTagAttributes() { return array(); } protected function getTagContent() { return $this->renderChildren(); } final public function render() { $this->willRender(); $attributes = $this->getTagAttributes(); $implode = array('class', 'sigil'); foreach ($implode as $attr) { if (isset($attributes[$attr])) { if (is_array($attributes[$attr])) { $attributes[$attr] = implode(' ', $attributes[$attr]); } } } if (!is_array($attributes)) { $class = get_class($this); throw new Exception( pht("View '%s' did not return an array from getTagAttributes()!", $class)); } $sigils = $this->sigils; if ($this->workflow) { $sigils[] = 'workflow'; } $tag_view_attributes = array( 'id' => $this->id, 'class' => implode(' ', $this->classes), 'style' => $this->style, 'meta' => $this->metadata, 'sigil' => $sigils ? implode(' ', $sigils) : null, 'mustcapture' => $this->mustCapture, ); foreach ($tag_view_attributes as $key => $value) { if ($value === null) { continue; } if (!isset($attributes[$key])) { $attributes[$key] = $value; continue; } switch ($key) { case 'class': case 'sigil': $attributes[$key] = $attributes[$key].' '.$value; break; default: // Use the explicitly set value rather than the tag default value. $attributes[$key] = $value; break; } } return javelin_tag( $this->getTagName(), $attributes, $this->getTagContent()); } } diff --git a/src/view/phui/PHUIPropertyListView.php b/src/view/phui/PHUIPropertyListView.php index 0d60ed25b0..336c494a3c 100644 --- a/src/view/phui/PHUIPropertyListView.php +++ b/src/view/phui/PHUIPropertyListView.php @@ -1,303 +1,303 @@ object = $object; return $this; } public function setActionList(PhabricatorActionListView $list) { $this->actionList = $list; return $this; } public function getActionList() { return $this->actionList; } public function setStacked($stacked) { $this->stacked = $stacked; return $this; } public function addClass($class) { $this->classes[] = $class; return $this; } public function setHasKeyboardShortcuts($has_keyboard_shortcuts) { $this->hasKeyboardShortcuts = $has_keyboard_shortcuts; return $this; } public function addProperty($key, $value) { $current = array_pop($this->parts); if (!$current || $current['type'] != 'property') { if ($current) { $this->parts[] = $current; } $current = array( 'type' => 'property', 'list' => array(), ); } $current['list'][] = array( 'key' => $key, 'value' => $value, ); $this->parts[] = $current; return $this; } public function addSectionHeader($name, $icon = null) { $this->parts[] = array( 'type' => 'section', 'name' => $name, 'icon' => $icon, ); return $this; } public function addTextContent($content) { $this->parts[] = array( 'type' => 'text', 'content' => $content, ); return $this; } public function addRawContent($content) { $this->parts[] = array( 'type' => 'raw', 'content' => $content, ); return $this; } public function addImageContent($content) { $this->parts[] = array( 'type' => 'image', 'content' => $content, ); return $this; } public function invokeWillRenderEvent() { if ($this->object && $this->getUser() && !$this->invokedWillRenderEvent) { $event = new PhabricatorEvent( PhabricatorEventType::TYPE_UI_WILLRENDERPROPERTIES, array( 'object' => $this->object, 'view' => $this, )); $event->setUser($this->getUser()); PhutilEventEngine::dispatchEvent($event); } $this->invokedWillRenderEvent = true; } - public function isEmpty() { + public function hasAnyProperties() { $this->invokeWillRenderEvent(); if ($this->parts) { - return false; + return true; } - return true; + return false; } public function render() { $this->invokeWillRenderEvent(); require_celerity_resource('phui-property-list-view-css'); $items = array(); $parts = $this->parts; // If we have an action list, make sure we render a property part, even // if there are no properties. Otherwise, the action list won't render. if ($this->actionList) { $this->classes[] = 'phui-property-list-has-actions'; $have_property_part = false; foreach ($this->parts as $part) { if ($part['type'] == 'property') { $have_property_part = true; break; } } if (!$have_property_part) { $parts[] = array( 'type' => 'property', 'list' => array(), ); } } foreach ($parts as $part) { $type = $part['type']; switch ($type) { case 'property': $items[] = $this->renderPropertyPart($part); break; case 'section': $items[] = $this->renderSectionPart($part); break; case 'text': case 'image': $items[] = $this->renderTextPart($part); break; case 'raw': $items[] = $this->renderRawPart($part); break; default: throw new Exception(pht("Unknown part type '%s'!", $type)); } } $this->classes[] = 'phui-property-list-section'; $classes = implode(' ', $this->classes); return phutil_tag( 'div', array( 'class' => $classes, ), array( $items, )); } private function renderPropertyPart(array $part) { $items = array(); foreach ($part['list'] as $spec) { $key = $spec['key']; $value = $spec['value']; // NOTE: We append a space to each value to improve the behavior when the // user double-clicks a property value (like a URI) to select it. Without // the space, the label is also selected. $items[] = phutil_tag( 'dt', array( 'class' => 'phui-property-list-key', ), array($key, ' ')); $items[] = phutil_tag( 'dd', array( 'class' => 'phui-property-list-value', ), array($value, ' ')); } $stacked = ''; if ($this->stacked) { $stacked = 'phui-property-list-stacked'; } $list = phutil_tag( 'dl', array( 'class' => 'phui-property-list-properties', ), $items); $shortcuts = null; if ($this->hasKeyboardShortcuts) { $shortcuts = new AphrontKeyboardShortcutsAvailableView(); } $list = phutil_tag( 'div', array( 'class' => 'phui-property-list-properties-wrap '.$stacked, ), array($shortcuts, $list)); $action_list = null; if ($this->actionList) { $action_list = phutil_tag( 'div', array( 'class' => 'phui-property-list-actions', ), $this->actionList); $this->actionList = null; } return phutil_tag( 'div', array( 'class' => 'phui-property-list-container grouped', ), array($action_list, $list)); } private function renderSectionPart(array $part) { $name = $part['name']; if ($part['icon']) { $icon = id(new PHUIIconView()) ->setIcon($part['icon'].' bluegrey'); $name = phutil_tag( 'span', array( 'class' => 'phui-property-list-section-header-icon', ), array($icon, $name)); } return phutil_tag( 'div', array( 'class' => 'phui-property-list-section-header', ), $name); } private function renderTextPart(array $part) { $classes = array(); $classes[] = 'phui-property-list-text-content'; if ($part['type'] == 'image') { $classes[] = 'phui-property-list-image-content'; } return phutil_tag( 'div', array( 'class' => implode($classes, ' '), ), $part['content']); } private function renderRawPart(array $part) { $classes = array(); $classes[] = 'phui-property-list-raw-content'; return phutil_tag( 'div', array( 'class' => implode($classes, ' '), ), $part['content']); } }