2013-07-22 17:34:35 +02:00
|
|
|
<?php
|
|
|
|
|
|
|
|
final class PhabricatorProjectSearchEngine
|
|
|
|
extends PhabricatorApplicationSearchEngine {
|
|
|
|
|
2014-06-12 22:22:20 +02:00
|
|
|
public function getResultTypeDescription() {
|
|
|
|
return pht('Projects');
|
|
|
|
}
|
|
|
|
|
2015-02-05 00:47:48 +01:00
|
|
|
public function getApplicationClassName() {
|
2014-07-23 02:03:09 +02:00
|
|
|
return 'PhabricatorProjectApplication';
|
2014-05-09 21:25:52 +02:00
|
|
|
}
|
|
|
|
|
2015-06-08 21:21:48 +02:00
|
|
|
public function newQuery() {
|
|
|
|
return id(new PhabricatorProjectQuery())
|
2017-05-15 02:32:24 +02:00
|
|
|
->needImages(true)
|
|
|
|
->needMembers(true)
|
|
|
|
->needWatchers(true);
|
2014-02-10 23:31:34 +01:00
|
|
|
}
|
|
|
|
|
2015-06-08 21:20:16 +02:00
|
|
|
protected function buildCustomSearchFields() {
|
|
|
|
return array(
|
|
|
|
id(new PhabricatorSearchTextField())
|
|
|
|
->setLabel(pht('Name'))
|
|
|
|
->setKey('name'),
|
2015-07-06 14:52:04 +02:00
|
|
|
id(new PhabricatorUsersSearchField())
|
2015-06-08 21:20:16 +02:00
|
|
|
->setLabel(pht('Members'))
|
|
|
|
->setKey('memberPHIDs')
|
2017-01-09 16:35:54 +01:00
|
|
|
->setConduitKey('members')
|
2015-06-08 21:20:16 +02:00
|
|
|
->setAliases(array('member', 'members')),
|
2016-02-17 20:37:01 +01:00
|
|
|
id(new PhabricatorUsersSearchField())
|
|
|
|
->setLabel(pht('Watchers'))
|
|
|
|
->setKey('watcherPHIDs')
|
2017-01-09 16:35:54 +01:00
|
|
|
->setConduitKey('watchers')
|
2016-02-17 20:37:01 +01:00
|
|
|
->setAliases(array('watcher', 'watchers')),
|
2015-06-08 21:20:16 +02:00
|
|
|
id(new PhabricatorSearchSelectField())
|
|
|
|
->setLabel(pht('Status'))
|
|
|
|
->setKey('status')
|
|
|
|
->setOptions($this->getStatusOptions()),
|
Return milestone information in project.search
Summary:
Ref T12074.
- `project.search` now returns milestones by default.
- A new constraint, `isMilestone`, allows filtering to milestones, non-milestones, or both (API and web UI).
- `project.search` now returns a milestone number for milestones, or `null` for non-milestones.
NOTE: Existing custom saved queries in projects which previously did not return milestones now will. I expect this to have little-to-no impact on users, and these queries are easy to correct, but I'll note this in changelogs.
Test Plan:
- Ran various queries with `project.search` and in the web UI, searching for milestones, non-milestones, and both.
- Web UI default behavior (no milestones) is unchanged, but you can now get milestones if you want them.
- Queried a milestone by ID/PHID via API.
Reviewers: chad
Reviewed By: chad
Maniphest Tasks: T12074
Differential Revision: https://secure.phabricator.com/D17153
2017-01-08 20:29:23 +01:00
|
|
|
id(new PhabricatorSearchThreeStateField())
|
|
|
|
->setLabel(pht('Milestones'))
|
|
|
|
->setKey('isMilestone')
|
|
|
|
->setOptions(
|
|
|
|
pht('(Show All)'),
|
|
|
|
pht('Show Only Milestones'),
|
|
|
|
pht('Hide Milestones'))
|
|
|
|
->setDescription(
|
|
|
|
pht(
|
|
|
|
'Pass true to find only milestones, or false to omit '.
|
|
|
|
'milestones.')),
|
2015-06-08 21:20:16 +02:00
|
|
|
id(new PhabricatorSearchCheckboxesField())
|
|
|
|
->setLabel(pht('Icons'))
|
|
|
|
->setKey('icons')
|
|
|
|
->setOptions($this->getIconOptions()),
|
|
|
|
id(new PhabricatorSearchCheckboxesField())
|
|
|
|
->setLabel(pht('Colors'))
|
|
|
|
->setKey('colors')
|
|
|
|
->setOptions($this->getColorOptions()),
|
2017-01-08 20:51:17 +01:00
|
|
|
id(new PhabricatorPHIDsSearchField())
|
|
|
|
->setLabel(pht('Parent Projects'))
|
|
|
|
->setKey('parentPHIDs')
|
2017-01-09 16:35:54 +01:00
|
|
|
->setConduitKey('parents')
|
2017-01-08 20:51:17 +01:00
|
|
|
->setAliases(array('parent', 'parents', 'parentPHID'))
|
|
|
|
->setDescription(pht('Find direct subprojects of specified parents.')),
|
|
|
|
id(new PhabricatorPHIDsSearchField())
|
|
|
|
->setLabel(pht('Ancestor Projects'))
|
|
|
|
->setKey('ancestorPHIDs')
|
2017-01-09 16:35:54 +01:00
|
|
|
->setConduitKey('ancestors')
|
2017-01-08 20:51:17 +01:00
|
|
|
->setAliases(array('ancestor', 'ancestors', 'ancestorPHID'))
|
|
|
|
->setDescription(
|
|
|
|
pht('Find all subprojects beneath specified ancestors.')),
|
2015-06-08 21:20:16 +02:00
|
|
|
);
|
2013-07-22 17:34:35 +02:00
|
|
|
}
|
|
|
|
|
2015-06-08 21:20:16 +02:00
|
|
|
|
2016-01-22 13:33:23 +01:00
|
|
|
protected function buildQueryFromParameters(array $map) {
|
2015-06-08 21:21:48 +02:00
|
|
|
$query = $this->newQuery();
|
2013-07-22 17:34:35 +02:00
|
|
|
|
2015-06-08 21:20:16 +02:00
|
|
|
if (strlen($map['name'])) {
|
|
|
|
$tokens = PhabricatorTypeaheadDatasource::tokenizeString($map['name']);
|
2015-04-15 20:49:07 +02:00
|
|
|
$query->withNameTokens($tokens);
|
2014-07-18 01:35:54 +02:00
|
|
|
}
|
|
|
|
|
2015-06-08 21:20:16 +02:00
|
|
|
if ($map['memberPHIDs']) {
|
|
|
|
$query->withMemberPHIDs($map['memberPHIDs']);
|
2014-08-12 17:04:38 +02:00
|
|
|
}
|
|
|
|
|
2016-02-17 20:37:01 +01:00
|
|
|
if ($map['watcherPHIDs']) {
|
|
|
|
$query->withWatcherPHIDs($map['watcherPHIDs']);
|
|
|
|
}
|
|
|
|
|
2015-06-08 21:20:16 +02:00
|
|
|
if ($map['status']) {
|
|
|
|
$status = idx($this->getStatusValues(), $map['status']);
|
|
|
|
if ($status) {
|
|
|
|
$query->withStatus($status);
|
|
|
|
}
|
2014-08-12 17:04:38 +02:00
|
|
|
}
|
|
|
|
|
2015-06-08 21:20:16 +02:00
|
|
|
if ($map['icons']) {
|
|
|
|
$query->withIcons($map['icons']);
|
2014-08-12 17:04:38 +02:00
|
|
|
}
|
|
|
|
|
2015-06-08 21:20:16 +02:00
|
|
|
if ($map['colors']) {
|
|
|
|
$query->withColors($map['colors']);
|
2014-08-12 17:04:38 +02:00
|
|
|
}
|
2013-07-22 17:34:35 +02:00
|
|
|
|
Return milestone information in project.search
Summary:
Ref T12074.
- `project.search` now returns milestones by default.
- A new constraint, `isMilestone`, allows filtering to milestones, non-milestones, or both (API and web UI).
- `project.search` now returns a milestone number for milestones, or `null` for non-milestones.
NOTE: Existing custom saved queries in projects which previously did not return milestones now will. I expect this to have little-to-no impact on users, and these queries are easy to correct, but I'll note this in changelogs.
Test Plan:
- Ran various queries with `project.search` and in the web UI, searching for milestones, non-milestones, and both.
- Web UI default behavior (no milestones) is unchanged, but you can now get milestones if you want them.
- Queried a milestone by ID/PHID via API.
Reviewers: chad
Reviewed By: chad
Maniphest Tasks: T12074
Differential Revision: https://secure.phabricator.com/D17153
2017-01-08 20:29:23 +01:00
|
|
|
if ($map['isMilestone'] !== null) {
|
|
|
|
$query->withIsMilestone($map['isMilestone']);
|
|
|
|
}
|
|
|
|
|
2017-01-08 20:51:17 +01:00
|
|
|
if ($map['parentPHIDs']) {
|
|
|
|
$query->withParentProjectPHIDs($map['parentPHIDs']);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($map['ancestorPHIDs']) {
|
|
|
|
$query->withAncestorProjectPHIDs($map['ancestorPHIDs']);
|
|
|
|
}
|
|
|
|
|
2015-06-08 21:20:16 +02:00
|
|
|
return $query;
|
2013-07-22 17:34:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
protected function getURI($path) {
|
|
|
|
return '/project/'.$path;
|
|
|
|
}
|
|
|
|
|
2015-01-06 21:34:51 +01:00
|
|
|
protected function getBuiltinQueryNames() {
|
2013-07-22 17:34:35 +02:00
|
|
|
$names = array();
|
|
|
|
|
|
|
|
if ($this->requireViewer()->isLoggedIn()) {
|
|
|
|
$names['joined'] = pht('Joined');
|
|
|
|
}
|
|
|
|
|
2017-05-14 18:13:59 +02:00
|
|
|
if ($this->requireViewer()->isLoggedIn()) {
|
|
|
|
$names['watching'] = pht('Watching');
|
|
|
|
}
|
|
|
|
|
2013-07-22 17:34:35 +02:00
|
|
|
$names['active'] = pht('Active');
|
|
|
|
$names['all'] = pht('All');
|
|
|
|
|
|
|
|
return $names;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function buildSavedQueryFromBuiltin($query_key) {
|
|
|
|
$query = $this->newSavedQuery();
|
|
|
|
$query->setQueryKey($query_key);
|
|
|
|
|
|
|
|
$viewer_phid = $this->requireViewer()->getPHID();
|
|
|
|
|
Return milestone information in project.search
Summary:
Ref T12074.
- `project.search` now returns milestones by default.
- A new constraint, `isMilestone`, allows filtering to milestones, non-milestones, or both (API and web UI).
- `project.search` now returns a milestone number for milestones, or `null` for non-milestones.
NOTE: Existing custom saved queries in projects which previously did not return milestones now will. I expect this to have little-to-no impact on users, and these queries are easy to correct, but I'll note this in changelogs.
Test Plan:
- Ran various queries with `project.search` and in the web UI, searching for milestones, non-milestones, and both.
- Web UI default behavior (no milestones) is unchanged, but you can now get milestones if you want them.
- Queried a milestone by ID/PHID via API.
Reviewers: chad
Reviewed By: chad
Maniphest Tasks: T12074
Differential Revision: https://secure.phabricator.com/D17153
2017-01-08 20:29:23 +01:00
|
|
|
// By default, do not show milestones in the list view.
|
|
|
|
$query->setParameter('isMilestone', false);
|
|
|
|
|
2013-07-22 17:34:35 +02:00
|
|
|
switch ($query_key) {
|
|
|
|
case 'all':
|
|
|
|
return $query;
|
|
|
|
case 'active':
|
|
|
|
return $query
|
|
|
|
->setParameter('status', 'active');
|
|
|
|
case 'joined':
|
|
|
|
return $query
|
|
|
|
->setParameter('memberPHIDs', array($viewer_phid))
|
|
|
|
->setParameter('status', 'active');
|
2017-05-14 18:13:59 +02:00
|
|
|
case 'watching':
|
|
|
|
return $query
|
|
|
|
->setParameter('watcherPHIDs', array($viewer_phid))
|
|
|
|
->setParameter('status', 'active');
|
2013-07-22 17:34:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return parent::buildSavedQueryFromBuiltin($query_key);
|
|
|
|
}
|
|
|
|
|
|
|
|
private function getStatusOptions() {
|
|
|
|
return array(
|
2015-05-01 01:06:59 +02:00
|
|
|
'active' => pht('Show Only Active Projects'),
|
|
|
|
'archived' => pht('Show Only Archived Projects'),
|
|
|
|
'all' => pht('Show All Projects'),
|
2013-07-22 17:34:35 +02:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
private function getStatusValues() {
|
|
|
|
return array(
|
2015-05-01 01:06:59 +02:00
|
|
|
'active' => PhabricatorProjectQuery::STATUS_ACTIVE,
|
|
|
|
'archived' => PhabricatorProjectQuery::STATUS_ARCHIVED,
|
|
|
|
'all' => PhabricatorProjectQuery::STATUS_ANY,
|
2013-07-22 17:34:35 +02:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2015-06-08 21:20:16 +02:00
|
|
|
private function getIconOptions() {
|
|
|
|
$options = array();
|
2014-08-12 17:04:38 +02:00
|
|
|
|
2015-12-16 16:53:13 +01:00
|
|
|
$set = new PhabricatorProjectIconSet();
|
|
|
|
foreach ($set->getIcons() as $icon) {
|
Allow installs to customize project icons
Summary:
Ref T10010. Ref T5819. General alignment of the stars:
- There were some hacks in Conduit around stripping `fa-...` off icons when reading and writing that I wanted to get rid of.
- We probably have room for a subtitle in the new heavy nav, and using the icon name is a good starting point (and maybe good enough on its own?)
- The project list was real bad looking with redundant tag/names, now it is very slightly less bad looking with non-redundant types?
- Some installs will want to call Milestones something else, and this gets us a big part of the way there.
- This may slightly help to reinforce "tag" vs "policy" vs "group" stuff?
---
I'm letting installs have enough rope to shoot themselves in the foot (e.g., define 100 icons). It isn't the end of the world if they reuse icons, and is clearly their fault.
I think the cases where 100 icons will break down are:
- Icon selector dialog may get very unwieldy.
- Query UI will be pretty iffy/huge with 100 icons.
We could improve these fairly easily if an install comes up with a reasonable use case for having 100 icons.
---
The UI on the icon itself in the list views is a little iffy -- mostly, it's too saturated/bold.
I'd ideally like to try either:
- rendering a "shade" version (i.e. lighter, less-saturated color); or
- rendering a "shade" tag with just the icon in it.
However, there didn't seem to be a way to do the first one right now (`fa-example sh-blue` doesn't work) and the second one had weird margins/padding, so I left it like this for now. I figure we can clean it up once we build the thick nav, since that will probably also want an identical element.
(I don't want to render a full tag with the icon + name since I think that's confusing -- it looks like a project/object tag, but is not.)
Test Plan:
{F1049905}
{F1049906}
Reviewers: chad
Reviewed By: chad
Subscribers: 20after4, Luke081515.2
Maniphest Tasks: T5819, T10010
Differential Revision: https://secure.phabricator.com/D14918
2015-12-30 13:36:48 +01:00
|
|
|
if ($icon->getIsDisabled()) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2015-12-16 16:53:13 +01:00
|
|
|
$options[$icon->getKey()] = array(
|
2015-06-08 21:20:16 +02:00
|
|
|
id(new PHUIIconView())
|
2016-01-28 05:38:01 +01:00
|
|
|
->setIcon($icon->getIcon()),
|
2015-06-08 21:20:16 +02:00
|
|
|
' ',
|
2015-12-16 16:53:13 +01:00
|
|
|
$icon->getLabel(),
|
2015-06-08 21:20:16 +02:00
|
|
|
);
|
|
|
|
}
|
2014-08-12 17:04:38 +02:00
|
|
|
|
2015-06-08 21:20:16 +02:00
|
|
|
return $options;
|
|
|
|
}
|
|
|
|
|
|
|
|
private function getColorOptions() {
|
|
|
|
$options = array();
|
|
|
|
|
2015-12-16 16:53:13 +01:00
|
|
|
foreach (PhabricatorProjectIconSet::getColorMap() as $color => $name) {
|
2015-06-08 21:20:16 +02:00
|
|
|
$options[$color] = array(
|
|
|
|
id(new PHUITagView())
|
|
|
|
->setType(PHUITagView::TYPE_SHADE)
|
|
|
|
->setShade($color)
|
|
|
|
->setName($name),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
return $options;
|
2014-08-12 17:04:38 +02:00
|
|
|
}
|
|
|
|
|
2014-05-09 21:25:52 +02:00
|
|
|
protected function renderResultList(
|
|
|
|
array $projects,
|
|
|
|
PhabricatorSavedQuery $query,
|
|
|
|
array $handles) {
|
|
|
|
assert_instances_of($projects, 'PhabricatorProject');
|
|
|
|
$viewer = $this->requireViewer();
|
2015-06-19 12:46:20 +02:00
|
|
|
|
2015-12-27 14:16:36 +01:00
|
|
|
$list = id(new PhabricatorProjectListView())
|
|
|
|
->setUser($viewer)
|
|
|
|
->setProjects($projects)
|
|
|
|
->renderList();
|
2015-06-19 12:46:20 +02:00
|
|
|
|
2015-12-27 14:16:36 +01:00
|
|
|
return id(new PhabricatorApplicationSearchResultView())
|
|
|
|
->setObjectList($list)
|
|
|
|
->setNoDataString(pht('No projects found.'));
|
2014-05-09 21:25:52 +02:00
|
|
|
}
|
|
|
|
|
2015-12-21 20:15:44 +01:00
|
|
|
protected function getNewUserBody() {
|
|
|
|
$create_button = id(new PHUIButtonView())
|
|
|
|
->setTag('a')
|
|
|
|
->setText(pht('Create a Project'))
|
2015-12-31 14:57:12 +01:00
|
|
|
->setHref('/project/edit/')
|
2015-12-21 20:15:44 +01:00
|
|
|
->setColor(PHUIButtonView::GREEN);
|
|
|
|
|
2016-01-28 17:40:22 +01:00
|
|
|
$icon = $this->getApplication()->getIcon();
|
2015-12-21 20:15:44 +01:00
|
|
|
$app_name = $this->getApplication()->getName();
|
|
|
|
$view = id(new PHUIBigInfoView())
|
|
|
|
->setIcon($icon)
|
|
|
|
->setTitle(pht('Welcome to %s', $app_name))
|
|
|
|
->setDescription(
|
|
|
|
pht('Projects are flexible storage containers used as '.
|
|
|
|
'tags, teams, projects, or anything you need to group.'))
|
|
|
|
->addAction($create_button);
|
|
|
|
|
|
|
|
return $view;
|
|
|
|
}
|
|
|
|
|
2013-07-22 17:34:35 +02:00
|
|
|
}
|