Page MenuHomePhorge

No OneTemporary

diff --git a/src/applications/base/PhabricatorApplication.php b/src/applications/base/PhabricatorApplication.php
index 4062e4e2fe..420d126507 100644
--- a/src/applications/base/PhabricatorApplication.php
+++ b/src/applications/base/PhabricatorApplication.php
@@ -1,670 +1,670 @@
<?php
/**
* @task info Application Information
* @task ui UI Integration
* @task uri URI Routing
* @task mail Email integration
* @task fact Fact Integration
* @task meta Application Management
*/
abstract class PhabricatorApplication
extends PhabricatorLiskDAO
implements
PhabricatorPolicyInterface,
PhabricatorApplicationTransactionInterface {
const GROUP_CORE = 'core';
const GROUP_UTILITIES = 'util';
const GROUP_ADMIN = 'admin';
const GROUP_DEVELOPER = 'developer';
final public static function getApplicationGroups() {
return array(
self::GROUP_CORE => pht('Core Applications'),
self::GROUP_UTILITIES => pht('Utilities'),
self::GROUP_ADMIN => pht('Administration'),
self::GROUP_DEVELOPER => pht('Developer Tools'),
);
}
final public function getApplicationName() {
return 'application';
}
final public function getTableName() {
return 'application_application';
}
final protected function getConfiguration() {
return array(
self::CONFIG_AUX_PHID => true,
) + parent::getConfiguration();
}
final public function generatePHID() {
return $this->getPHID();
}
final public function save() {
// When "save()" is called on applications, we just return without
// actually writing anything to the database.
return $this;
}
/* -( Application Information )-------------------------------------------- */
abstract public function getName();
public function getMenuName() {
return $this->getName();
}
public function getShortDescription() {
return pht('%s Application', $this->getName());
}
final public function isInstalled() {
if (!$this->canUninstall()) {
return true;
}
$prototypes = PhabricatorEnv::getEnvConfig('phabricator.show-prototypes');
if (!$prototypes && $this->isPrototype()) {
return false;
}
$uninstalled = PhabricatorEnv::getEnvConfig(
'phabricator.uninstalled-applications');
return empty($uninstalled[get_class($this)]);
}
public function isPrototype() {
return false;
}
/**
* Return `true` if this application should never appear in application lists
* in the UI. Primarily intended for unit test applications or other
* pseudo-applications.
*
* Few applications should be unlisted. For most applications, use
* @{method:isLaunchable} to hide them from main launch views instead.
*
* @return bool True to remove application from UI lists.
*/
public function isUnlisted() {
return false;
}
/**
* Return `true` if this application is a normal application with a base
* URI and a web interface.
*
* Launchable applications can be pinned to the home page, and show up in the
* "Launcher" view of the Applications application. Making an application
* unlaunchable prevents pinning and hides it from this view.
*
* Usually, an application should be marked unlaunchable if:
*
* - it is available on every page anyway (like search); or
* - it does not have a web interface (like subscriptions); or
* - it is still pre-release and being intentionally buried.
*
* To hide applications more completely, use @{method:isUnlisted}.
*
* @return bool True if the application is launchable.
*/
public function isLaunchable() {
return true;
}
/**
* Return `true` if this application should be pinned by default.
*
* Users who have not yet set preferences see a default list of applications.
*
* @param PhabricatorUser User viewing the pinned application list.
* @return bool True if this application should be pinned by default.
*/
public function isPinnedByDefault(PhabricatorUser $viewer) {
return false;
}
/**
* Returns true if an application is first-party (developed by Phacility)
* and false otherwise.
*
* @return bool True if this application is developed by Phacility.
*/
final public function isFirstParty() {
$where = id(new ReflectionClass($this))->getFileName();
$root = phutil_get_library_root('phabricator');
if (!Filesystem::isDescendant($where, $root)) {
return false;
}
if (Filesystem::isDescendant($where, $root.'/extensions')) {
return false;
}
return true;
}
public function canUninstall() {
return true;
}
final public function getPHID() {
return 'PHID-APPS-'.get_class($this);
}
public function getTypeaheadURI() {
return $this->isLaunchable() ? $this->getBaseURI() : null;
}
public function getBaseURI() {
return null;
}
final public function getApplicationURI($path = '') {
return $this->getBaseURI().ltrim($path, '/');
}
public function getIcon() {
return 'fa-puzzle-piece';
}
public function getApplicationOrder() {
return PHP_INT_MAX;
}
public function getApplicationGroup() {
return self::GROUP_CORE;
}
public function getTitleGlyph() {
return null;
}
final public function getHelpMenuItems(PhabricatorUser $viewer) {
$items = array();
$articles = $this->getHelpDocumentationArticles($viewer);
if ($articles) {
foreach ($articles as $article) {
$item = id(new PhabricatorActionView())
->setName($article['name'])
->setHref($article['href'])
->addSigil('help-item')
->setOpenInNewWindow(true);
$items[] = $item;
}
}
$command_specs = $this->getMailCommandObjects();
if ($command_specs) {
foreach ($command_specs as $key => $spec) {
$object = $spec['object'];
$class = get_class($this);
$href = '/applications/mailcommands/'.$class.'/'.$key.'/';
$item = id(new PhabricatorActionView())
->setName($spec['name'])
->setHref($href)
->addSigil('help-item')
->setOpenInNewWindow(true);
$items[] = $item;
}
}
if ($items) {
$divider = id(new PhabricatorActionView())
->addSigil('help-item')
->setType(PhabricatorActionView::TYPE_DIVIDER);
array_unshift($items, $divider);
}
return array_values($items);
}
public function getHelpDocumentationArticles(PhabricatorUser $viewer) {
return array();
}
public function getOverview() {
return null;
}
public function getEventListeners() {
return array();
}
public function getRemarkupRules() {
return array();
}
public function getQuicksandURIPatternBlacklist() {
return array();
}
public function getMailCommandObjects() {
return array();
}
/* -( URI Routing )-------------------------------------------------------- */
public function getRoutes() {
return array();
}
public function getResourceRoutes() {
return array();
}
/* -( Email Integration )-------------------------------------------------- */
public function supportsEmailIntegration() {
return false;
}
final protected function getInboundEmailSupportLink() {
return PhabricatorEnv::getDoclink('Configuring Inbound Email');
}
public function getAppEmailBlurb() {
throw new PhutilMethodNotImplementedException();
}
/* -( Fact Integration )--------------------------------------------------- */
public function getFactObjectsForAnalysis() {
return array();
}
/* -( UI Integration )----------------------------------------------------- */
/**
* You can provide an optional piece of flavor text for the application. This
* is currently rendered in application launch views if the application has no
* status elements.
*
* @return string|null Flavor text.
* @task ui
*/
public function getFlavorText() {
return null;
}
/**
* Build items for the main menu.
*
* @param PhabricatorUser The viewing user.
* @param AphrontController The current controller. May be null for special
* pages like 404, exception handlers, etc.
* @return list<PHUIListItemView> List of menu items.
* @task ui
*/
public function buildMainMenuItems(
PhabricatorUser $user,
PhabricatorController $controller = null) {
return array();
}
/* -( Application Management )--------------------------------------------- */
final public static function getByClass($class_name) {
$selected = null;
$applications = self::getAllApplications();
foreach ($applications as $application) {
if (get_class($application) == $class_name) {
$selected = $application;
break;
}
}
if (!$selected) {
throw new Exception(pht("No application '%s'!", $class_name));
}
return $selected;
}
final public static function getAllApplications() {
static $applications;
if ($applications === null) {
$apps = id(new PhutilClassMapQuery())
->setAncestorClass(__CLASS__)
->setSortMethod('getApplicationOrder')
->execute();
// Reorder the applications into "application order". Notably, this
// ensures their event handlers register in application order.
$apps = mgroup($apps, 'getApplicationGroup');
$group_order = array_keys(self::getApplicationGroups());
$apps = array_select_keys($apps, $group_order) + $apps;
$apps = array_mergev($apps);
$applications = $apps;
}
return $applications;
}
final public static function getAllInstalledApplications() {
$all_applications = self::getAllApplications();
$apps = array();
foreach ($all_applications as $app) {
if (!$app->isInstalled()) {
continue;
}
$apps[] = $app;
}
return $apps;
}
/**
* Determine if an application is installed, by application class name.
*
* To check if an application is installed //and// available to a particular
* viewer, user @{method:isClassInstalledForViewer}.
*
* @param string Application class name.
* @return bool True if the class is installed.
* @task meta
*/
final public static function isClassInstalled($class) {
return self::getByClass($class)->isInstalled();
}
/**
* Determine if an application is installed and available to a viewer, by
* application class name.
*
* To check if an application is installed at all, use
* @{method:isClassInstalled}.
*
* @param string Application class name.
* @param PhabricatorUser Viewing user.
* @return bool True if the class is installed for the viewer.
* @task meta
*/
final public static function isClassInstalledForViewer(
$class,
PhabricatorUser $viewer) {
if ($viewer->isOmnipotent()) {
return true;
}
$cache = PhabricatorCaches::getRequestCache();
$viewer_fragment = $viewer->getCacheFragment();
$key = 'app.'.$class.'.installed.'.$viewer_fragment;
$result = $cache->getKey($key);
if ($result === null) {
if (!self::isClassInstalled($class)) {
$result = false;
} else {
$application = self::getByClass($class);
if (!$application->canUninstall()) {
// If the application can not be uninstalled, always allow viewers
// to see it. In particular, this allows logged-out viewers to see
// Settings and load global default settings even if the install
// does not allow public viewers.
$result = true;
} else {
$result = PhabricatorPolicyFilter::hasCapability(
$viewer,
self::getByClass($class),
PhabricatorPolicyCapability::CAN_VIEW);
}
}
$cache->setKey($key, $result);
}
return $result;
}
/* -( PhabricatorPolicyInterface )----------------------------------------- */
public function getCapabilities() {
return array_merge(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
),
array_keys($this->getCustomCapabilities()));
}
public function getPolicy($capability) {
$default = $this->getCustomPolicySetting($capability);
if ($default) {
return $default;
}
switch ($capability) {
case PhabricatorPolicyCapability::CAN_VIEW:
return PhabricatorPolicies::getMostOpenPolicy();
case PhabricatorPolicyCapability::CAN_EDIT:
return PhabricatorPolicies::POLICY_ADMIN;
default:
$spec = $this->getCustomCapabilitySpecification($capability);
return idx($spec, 'default', PhabricatorPolicies::POLICY_USER);
}
}
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
return false;
}
/* -( Policies )----------------------------------------------------------- */
protected function getCustomCapabilities() {
return array();
}
final private function getCustomPolicySetting($capability) {
if (!$this->isCapabilityEditable($capability)) {
return null;
}
$policy_locked = PhabricatorEnv::getEnvConfig('policy.locked');
if (isset($policy_locked[$capability])) {
return $policy_locked[$capability];
}
$config = PhabricatorEnv::getEnvConfig('phabricator.application-settings');
$app = idx($config, $this->getPHID());
if (!$app) {
return null;
}
$policy = idx($app, 'policy');
if (!$policy) {
return null;
}
return idx($policy, $capability);
}
final private function getCustomCapabilitySpecification($capability) {
$custom = $this->getCustomCapabilities();
if (!isset($custom[$capability])) {
throw new Exception(pht("Unknown capability '%s'!", $capability));
}
return $custom[$capability];
}
final public function getCapabilityLabel($capability) {
switch ($capability) {
case PhabricatorPolicyCapability::CAN_VIEW:
return pht('Can Use Application');
case PhabricatorPolicyCapability::CAN_EDIT:
return pht('Can Configure Application');
}
$capobj = PhabricatorPolicyCapability::getCapabilityByKey($capability);
if ($capobj) {
return $capobj->getCapabilityName();
}
return null;
}
final public function isCapabilityEditable($capability) {
switch ($capability) {
case PhabricatorPolicyCapability::CAN_VIEW:
return $this->canUninstall();
case PhabricatorPolicyCapability::CAN_EDIT:
return false;
default:
$spec = $this->getCustomCapabilitySpecification($capability);
return idx($spec, 'edit', true);
}
}
final public function getCapabilityCaption($capability) {
switch ($capability) {
case PhabricatorPolicyCapability::CAN_VIEW:
if (!$this->canUninstall()) {
return pht(
'This application is required for Phabricator to operate, so all '.
'users must have access to it.');
} else {
return null;
}
case PhabricatorPolicyCapability::CAN_EDIT:
return null;
default:
$spec = $this->getCustomCapabilitySpecification($capability);
return idx($spec, 'caption');
}
}
final public function getCapabilityTemplatePHIDType($capability) {
switch ($capability) {
case PhabricatorPolicyCapability::CAN_VIEW:
case PhabricatorPolicyCapability::CAN_EDIT:
return null;
}
$spec = $this->getCustomCapabilitySpecification($capability);
return idx($spec, 'template');
}
final public function getDefaultObjectTypePolicyMap() {
$map = array();
foreach ($this->getCustomCapabilities() as $capability => $spec) {
if (empty($spec['template'])) {
continue;
}
if (empty($spec['capability'])) {
continue;
}
$default = $this->getPolicy($capability);
$map[$spec['template']][$spec['capability']] = $default;
}
return $map;
}
public function getApplicationSearchDocumentTypes() {
return array();
}
protected function getEditRoutePattern($base = null) {
return $base.'(?:'.
'(?P<id>[0-9]\d*)/)?'.
'(?:'.
'(?:'.
'(?P<editAction>parameters|nodefault|nocreate|nomanage|comment)/'.
'|'.
'(?:form/(?P<formKey>[^/]+)/)?(?:page/(?P<pageKey>[^/]+)/)?'.
')'.
')?';
}
protected function getBulkRoutePattern($base = null) {
return $base.'(?:query/(?P<queryKey>[^/]+)/)?';
}
protected function getQueryRoutePattern($base = null) {
- return $base.'(?:query/(?P<queryKey>[^/]+)/(?:(?P<queryAction>[^/]+)/))?';
+ return $base.'(?:query/(?P<queryKey>[^/]+)/(?:(?P<queryAction>[^/]+)/)?)?';
}
protected function getProfileMenuRouting($controller) {
$edit_route = $this->getEditRoutePattern();
$mode_route = '(?P<itemEditMode>global|custom)/';
return array(
'(?P<itemAction>view)/(?P<itemID>[^/]+)/' => $controller,
'(?P<itemAction>hide)/(?P<itemID>[^/]+)/' => $controller,
'(?P<itemAction>default)/(?P<itemID>[^/]+)/' => $controller,
'(?P<itemAction>configure)/' => $controller,
'(?P<itemAction>configure)/'.$mode_route => $controller,
'(?P<itemAction>reorder)/'.$mode_route => $controller,
'(?P<itemAction>edit)/'.$edit_route => $controller,
'(?P<itemAction>new)/'.$mode_route.'(?<itemKey>[^/]+)/'.$edit_route
=> $controller,
'(?P<itemAction>builtin)/(?<itemID>[^/]+)/'.$edit_route
=> $controller,
);
}
/* -( PhabricatorApplicationTransactionInterface )------------------------- */
public function getApplicationTransactionEditor() {
return new PhabricatorApplicationEditor();
}
public function getApplicationTransactionObject() {
return $this;
}
public function getApplicationTransactionTemplate() {
return new PhabricatorApplicationApplicationTransaction();
}
public function willRenderTimeline(
PhabricatorApplicationTransactionView $timeline,
AphrontRequest $request) {
return $timeline;
}
}
diff --git a/src/applications/people/application/PhabricatorPeopleApplication.php b/src/applications/people/application/PhabricatorPeopleApplication.php
index dde82f1d3a..28405ca92c 100644
--- a/src/applications/people/application/PhabricatorPeopleApplication.php
+++ b/src/applications/people/application/PhabricatorPeopleApplication.php
@@ -1,109 +1,109 @@
<?php
final class PhabricatorPeopleApplication extends PhabricatorApplication {
public function getName() {
return pht('People');
}
public function getShortDescription() {
return pht('User Accounts and Profiles');
}
public function getBaseURI() {
return '/people/';
}
public function getTitleGlyph() {
return "\xE2\x99\x9F";
}
public function getIcon() {
return 'fa-users';
}
public function isPinnedByDefault(PhabricatorUser $viewer) {
return $viewer->getIsAdmin();
}
public function getFlavorText() {
return pht('Sort of a social utility.');
}
public function getApplicationGroup() {
return self::GROUP_UTILITIES;
}
public function canUninstall() {
return false;
}
public function getRoutes() {
return array(
'/people/' => array(
- '(query/(?P<key>[^/]+)/)?' => 'PhabricatorPeopleListController',
+ $this->getQueryRoutePattern() => 'PhabricatorPeopleListController',
'logs/(?:query/(?P<queryKey>[^/]+)/)?'
=> 'PhabricatorPeopleLogsController',
'invite/' => array(
'(?:query/(?P<queryKey>[^/]+)/)?'
=> 'PhabricatorPeopleInviteListController',
'send/'
=> 'PhabricatorPeopleInviteSendController',
),
'approve/(?P<id>[1-9]\d*)/' => 'PhabricatorPeopleApproveController',
'(?P<via>disapprove)/(?P<id>[1-9]\d*)/'
=> 'PhabricatorPeopleDisableController',
'(?P<via>disable)/(?P<id>[1-9]\d*)/'
=> 'PhabricatorPeopleDisableController',
'empower/(?P<id>[1-9]\d*)/' => 'PhabricatorPeopleEmpowerController',
'delete/(?P<id>[1-9]\d*)/' => 'PhabricatorPeopleDeleteController',
'rename/(?P<id>[1-9]\d*)/' => 'PhabricatorPeopleRenameController',
'welcome/(?P<id>[1-9]\d*)/' => 'PhabricatorPeopleWelcomeController',
'create/' => 'PhabricatorPeopleCreateController',
'new/(?P<type>[^/]+)/' => 'PhabricatorPeopleNewController',
'ldap/' => 'PhabricatorPeopleLdapController',
'editprofile/(?P<id>[1-9]\d*)/' =>
'PhabricatorPeopleProfileEditController',
'badges/(?P<id>[1-9]\d*)/' =>
'PhabricatorPeopleProfileBadgesController',
'tasks/(?P<id>[1-9]\d*)/' =>
'PhabricatorPeopleProfileTasksController',
'commits/(?P<id>[1-9]\d*)/' =>
'PhabricatorPeopleProfileCommitsController',
'revisions/(?P<id>[1-9]\d*)/' =>
'PhabricatorPeopleProfileRevisionsController',
'picture/(?P<id>[1-9]\d*)/' =>
'PhabricatorPeopleProfilePictureController',
'manage/(?P<id>[1-9]\d*)/' =>
'PhabricatorPeopleProfileManageController',
- ),
+ ),
'/p/(?P<username>[\w._-]+)/' => array(
'' => 'PhabricatorPeopleProfileViewController',
'item/' => $this->getProfileMenuRouting(
'PhabricatorPeopleProfileMenuController'),
),
);
}
public function getRemarkupRules() {
return array(
new PhabricatorMentionRemarkupRule(),
);
}
protected function getCustomCapabilities() {
return array(
PeopleCreateUsersCapability::CAPABILITY => array(
'default' => PhabricatorPolicies::POLICY_ADMIN,
),
PeopleBrowseUserDirectoryCapability::CAPABILITY => array(),
);
}
public function getApplicationSearchDocumentTypes() {
return array(
PhabricatorPeopleUserPHIDType::TYPECONST,
);
}
}
diff --git a/src/applications/people/controller/PhabricatorPeopleListController.php b/src/applications/people/controller/PhabricatorPeopleListController.php
index edcfc7ba0f..511899070c 100644
--- a/src/applications/people/controller/PhabricatorPeopleListController.php
+++ b/src/applications/people/controller/PhabricatorPeopleListController.php
@@ -1,42 +1,42 @@
<?php
final class PhabricatorPeopleListController
extends PhabricatorPeopleController {
public function shouldAllowPublic() {
return true;
}
public function shouldRequireAdmin() {
return false;
}
public function handleRequest(AphrontRequest $request) {
$this->requireApplicationCapability(
PeopleBrowseUserDirectoryCapability::CAPABILITY);
$controller = id(new PhabricatorApplicationSearchController())
- ->setQueryKey($request->getURIData('key'))
+ ->setQueryKey($request->getURIData('queryKey'))
->setSearchEngine(new PhabricatorPeopleSearchEngine())
->setNavigation($this->buildSideNavView());
return $this->delegateToController($controller);
}
protected function buildApplicationCrumbs() {
$crumbs = parent::buildApplicationCrumbs();
$viewer = $this->getRequest()->getUser();
if ($viewer->getIsAdmin()) {
$crumbs->addAction(
id(new PHUIListItemView())
->setName(pht('Create New User'))
->setHref($this->getApplicationURI('create/'))
->setIcon('fa-plus-square'));
}
return $crumbs;
}
}
diff --git a/src/applications/people/query/PhabricatorPeopleSearchEngine.php b/src/applications/people/query/PhabricatorPeopleSearchEngine.php
index 0a4367d367..db2256a8b8 100644
--- a/src/applications/people/query/PhabricatorPeopleSearchEngine.php
+++ b/src/applications/people/query/PhabricatorPeopleSearchEngine.php
@@ -1,323 +1,360 @@
<?php
final class PhabricatorPeopleSearchEngine
extends PhabricatorApplicationSearchEngine {
public function getResultTypeDescription() {
return pht('Users');
}
public function getApplicationClassName() {
return 'PhabricatorPeopleApplication';
}
public function newQuery() {
return id(new PhabricatorPeopleQuery())
->needPrimaryEmail(true)
->needProfileImage(true);
}
protected function buildCustomSearchFields() {
$fields = array(
id(new PhabricatorSearchStringListField())
->setLabel(pht('Usernames'))
->setKey('usernames')
->setAliases(array('username'))
->setDescription(pht('Find users by exact username.')),
id(new PhabricatorSearchTextField())
->setLabel(pht('Name Contains'))
->setKey('nameLike')
->setDescription(
pht('Find users whose usernames contain a substring.')),
id(new PhabricatorSearchThreeStateField())
->setLabel(pht('Administrators'))
->setKey('isAdmin')
->setOptions(
pht('(Show All)'),
pht('Show Only Administrators'),
pht('Hide Administrators'))
->setDescription(
pht(
'Pass true to find only administrators, or false to omit '.
'administrators.')),
id(new PhabricatorSearchThreeStateField())
->setLabel(pht('Disabled'))
->setKey('isDisabled')
->setOptions(
pht('(Show All)'),
pht('Show Only Disabled Users'),
pht('Hide Disabled Users'))
->setDescription(
pht(
'Pass true to find only disabled users, or false to omit '.
'disabled users.')),
id(new PhabricatorSearchThreeStateField())
->setLabel(pht('Bots'))
->setKey('isBot')
->setAliases(array('isSystemAgent'))
->setOptions(
pht('(Show All)'),
pht('Show Only Bots'),
pht('Hide Bots'))
->setDescription(
pht(
'Pass true to find only bots, or false to omit bots.')),
id(new PhabricatorSearchThreeStateField())
->setLabel(pht('Mailing Lists'))
->setKey('isMailingList')
->setOptions(
pht('(Show All)'),
pht('Show Only Mailing Lists'),
pht('Hide Mailing Lists'))
->setDescription(
pht(
'Pass true to find only mailing lists, or false to omit '.
'mailing lists.')),
id(new PhabricatorSearchThreeStateField())
->setLabel(pht('Needs Approval'))
->setKey('needsApproval')
->setOptions(
pht('(Show All)'),
pht('Show Only Unapproved Users'),
pht('Hide Unapproved Users'))
->setDescription(
pht(
'Pass true to find only users awaiting administrative approval, '.
'or false to omit these users.')),
);
$viewer = $this->requireViewer();
if ($viewer->getIsAdmin()) {
$fields[] = id(new PhabricatorSearchThreeStateField())
->setLabel(pht('Has MFA'))
->setKey('mfa')
->setOptions(
pht('(Show All)'),
pht('Show Only Users With MFA'),
pht('Hide Users With MFA'))
->setDescription(
pht(
'Pass true to find only users who are enrolled in MFA, or false '.
'to omit these users.'));
}
$fields[] = id(new PhabricatorSearchDateField())
->setKey('createdStart')
->setLabel(pht('Joined After'))
->setDescription(
pht('Find user accounts created after a given time.'));
$fields[] = id(new PhabricatorSearchDateField())
->setKey('createdEnd')
->setLabel(pht('Joined Before'))
->setDescription(
pht('Find user accounts created before a given time.'));
return $fields;
}
protected function getDefaultFieldOrder() {
return array(
'...',
'createdStart',
'createdEnd',
);
}
protected function buildQueryFromParameters(array $map) {
$query = $this->newQuery();
$viewer = $this->requireViewer();
// If the viewer can't browse the user directory, restrict the query to
// just the user's own profile. This is a little bit silly, but serves to
// restrict users from creating a dashboard panel which essentially just
// contains a user directory anyway.
$can_browse = PhabricatorPolicyFilter::hasCapability(
$viewer,
$this->getApplication(),
PeopleBrowseUserDirectoryCapability::CAPABILITY);
if (!$can_browse) {
$query->withPHIDs(array($viewer->getPHID()));
}
if ($map['usernames']) {
$query->withUsernames($map['usernames']);
}
if ($map['nameLike']) {
$query->withNameLike($map['nameLike']);
}
if ($map['isAdmin'] !== null) {
$query->withIsAdmin($map['isAdmin']);
}
if ($map['isDisabled'] !== null) {
$query->withIsDisabled($map['isDisabled']);
}
if ($map['isMailingList'] !== null) {
$query->withIsMailingList($map['isMailingList']);
}
if ($map['isBot'] !== null) {
$query->withIsSystemAgent($map['isBot']);
}
if ($map['needsApproval'] !== null) {
$query->withIsApproved(!$map['needsApproval']);
}
if (idx($map, 'mfa') !== null) {
$viewer = $this->requireViewer();
if (!$viewer->getIsAdmin()) {
throw new PhabricatorSearchConstraintException(
pht(
'The "Has MFA" query constraint may only be used by '.
'administrators, to prevent attackers from using it to target '.
'weak accounts.'));
}
$query->withIsEnrolledInMultiFactor($map['mfa']);
}
if ($map['createdStart']) {
$query->withDateCreatedAfter($map['createdStart']);
}
if ($map['createdEnd']) {
$query->withDateCreatedBefore($map['createdEnd']);
}
return $query;
}
protected function getURI($path) {
return '/people/'.$path;
}
protected function getBuiltinQueryNames() {
$names = array(
'active' => pht('Active'),
'all' => pht('All'),
);
$viewer = $this->requireViewer();
if ($viewer->getIsAdmin()) {
$names['approval'] = pht('Approval Queue');
}
return $names;
}
public function buildSavedQueryFromBuiltin($query_key) {
$query = $this->newSavedQuery();
$query->setQueryKey($query_key);
switch ($query_key) {
case 'all':
return $query;
case 'active':
return $query
->setParameter('isDisabled', false);
case 'approval':
return $query
->setParameter('needsApproval', true)
->setParameter('isDisabled', false);
}
return parent::buildSavedQueryFromBuiltin($query_key);
}
protected function renderResultList(
array $users,
PhabricatorSavedQuery $query,
array $handles) {
assert_instances_of($users, 'PhabricatorUser');
$request = $this->getRequest();
$viewer = $this->requireViewer();
$list = new PHUIObjectItemListView();
$is_approval = ($query->getQueryKey() == 'approval');
foreach ($users as $user) {
$primary_email = $user->loadPrimaryEmail();
if ($primary_email && $primary_email->getIsVerified()) {
$email = pht('Verified');
} else {
$email = pht('Unverified');
}
$item = new PHUIObjectItemView();
$item->setHeader($user->getFullName())
->setHref('/p/'.$user->getUsername().'/')
->addAttribute(phabricator_datetime($user->getDateCreated(), $viewer))
->addAttribute($email)
->setImageURI($user->getProfileImageURI());
if ($is_approval && $primary_email) {
$item->addAttribute($primary_email->getAddress());
}
if ($user->getIsDisabled()) {
$item->addIcon('fa-ban', pht('Disabled'));
$item->setDisabled(true);
}
if (!$is_approval) {
if (!$user->getIsApproved()) {
$item->addIcon('fa-clock-o', pht('Needs Approval'));
}
}
if ($user->getIsAdmin()) {
$item->addIcon('fa-star', pht('Admin'));
}
if ($user->getIsSystemAgent()) {
$item->addIcon('fa-desktop', pht('Bot'));
}
if ($user->getIsMailingList()) {
$item->addIcon('fa-envelope-o', pht('Mailing List'));
}
if ($viewer->getIsAdmin()) {
if ($user->getIsEnrolledInMultiFactor()) {
$item->addIcon('fa-lock', pht('Has MFA'));
}
}
if ($viewer->getIsAdmin()) {
$user_id = $user->getID();
if ($is_approval) {
$item->addAction(
id(new PHUIListItemView())
->setIcon('fa-ban')
->setName(pht('Disable'))
->setWorkflow(true)
->setHref($this->getApplicationURI('disapprove/'.$user_id.'/')));
$item->addAction(
id(new PHUIListItemView())
->setIcon('fa-thumbs-o-up')
->setName(pht('Approve'))
->setWorkflow(true)
->setHref($this->getApplicationURI('approve/'.$user_id.'/')));
}
}
$list->addItem($item);
}
$result = new PhabricatorApplicationSearchResultView();
$result->setObjectList($list);
$result->setNoDataString(pht('No accounts found.'));
return $result;
}
+ protected function newExportFields() {
+ return array(
+ id(new PhabricatorIDExportField())
+ ->setKey('id')
+ ->setLabel(pht('ID')),
+ id(new PhabricatorPHIDExportField())
+ ->setKey('phid')
+ ->setLabel(pht('PHID')),
+ id(new PhabricatorStringExportField())
+ ->setKey('username')
+ ->setLabel(pht('Username')),
+ id(new PhabricatorStringExportField())
+ ->setKey('realName')
+ ->setLabel(pht('Real Name')),
+ id(new PhabricatorEpochExportField())
+ ->setKey('created')
+ ->setLabel(pht('Date Created')),
+ );
+ }
+
+ public function newExport(array $users) {
+ $viewer = $this->requireViewer();
+
+ $export = array();
+ foreach ($users as $user) {
+ $export[] = array(
+ 'id' => $user->getID(),
+ 'phid' => $user->getPHID(),
+ 'username' => $user->getUsername(),
+ 'realName' => $user->getRealName(),
+ 'created' => $user->getDateCreated(),
+ );
+ }
+
+ return $export;
+ }
+
}
diff --git a/src/applications/search/controller/PhabricatorApplicationSearchController.php b/src/applications/search/controller/PhabricatorApplicationSearchController.php
index d241a046a1..d3b05d1e3e 100644
--- a/src/applications/search/controller/PhabricatorApplicationSearchController.php
+++ b/src/applications/search/controller/PhabricatorApplicationSearchController.php
@@ -1,914 +1,917 @@
<?php
final class PhabricatorApplicationSearchController
extends PhabricatorSearchBaseController {
private $searchEngine;
private $navigation;
private $queryKey;
private $preface;
public function setPreface($preface) {
$this->preface = $preface;
return $this;
}
public function getPreface() {
return $this->preface;
}
public function setQueryKey($query_key) {
$this->queryKey = $query_key;
return $this;
}
protected function getQueryKey() {
return $this->queryKey;
}
public function setNavigation(AphrontSideNavFilterView $navigation) {
$this->navigation = $navigation;
return $this;
}
protected function getNavigation() {
return $this->navigation;
}
public function setSearchEngine(
PhabricatorApplicationSearchEngine $search_engine) {
$this->searchEngine = $search_engine;
return $this;
}
protected function getSearchEngine() {
return $this->searchEngine;
}
protected function validateDelegatingController() {
$parent = $this->getDelegatingController();
if (!$parent) {
throw new Exception(
pht('You must delegate to this controller, not invoke it directly.'));
}
$engine = $this->getSearchEngine();
if (!$engine) {
throw new PhutilInvalidStateException('setEngine');
}
$engine->setViewer($this->getRequest()->getUser());
$parent = $this->getDelegatingController();
}
public function processRequest() {
$this->validateDelegatingController();
$query_action = $this->getRequest()->getURIData('queryAction');
if ($query_action == 'export') {
return $this->processExportRequest();
}
$key = $this->getQueryKey();
if ($key == 'edit') {
return $this->processEditRequest();
} else {
return $this->processSearchRequest();
}
}
private function processSearchRequest() {
$parent = $this->getDelegatingController();
$request = $this->getRequest();
$user = $request->getUser();
$engine = $this->getSearchEngine();
$nav = $this->getNavigation();
if (!$nav) {
$nav = $this->buildNavigation();
}
if ($request->isFormPost()) {
$saved_query = $engine->buildSavedQueryFromRequest($request);
$engine->saveQuery($saved_query);
return id(new AphrontRedirectResponse())->setURI(
$engine->getQueryResultsPageURI($saved_query->getQueryKey()).'#R');
}
$named_query = null;
$run_query = true;
$query_key = $this->queryKey;
if ($this->queryKey == 'advanced') {
$run_query = false;
$query_key = $request->getStr('query');
} else if (!strlen($this->queryKey)) {
$found_query_data = false;
if ($request->isHTTPGet() || $request->isQuicksand()) {
// If this is a GET request and it has some query data, don't
// do anything unless it's only before= or after=. We'll build and
// execute a query from it below. This allows external tools to build
// URIs like "/query/?users=a,b".
$pt_data = $request->getPassthroughRequestData();
$exempt = array(
'before' => true,
'after' => true,
'nux' => true,
'overheated' => true,
);
foreach ($pt_data as $pt_key => $pt_value) {
if (isset($exempt[$pt_key])) {
continue;
}
$found_query_data = true;
break;
}
}
if (!$found_query_data) {
// Otherwise, there's no query data so just run the user's default
// query for this application.
$query_key = $engine->getDefaultQueryKey();
}
}
if ($engine->isBuiltinQuery($query_key)) {
$saved_query = $engine->buildSavedQueryFromBuiltin($query_key);
$named_query = idx($engine->loadEnabledNamedQueries(), $query_key);
} else if ($query_key) {
$saved_query = id(new PhabricatorSavedQueryQuery())
->setViewer($user)
->withQueryKeys(array($query_key))
->executeOne();
if (!$saved_query) {
return new Aphront404Response();
}
$named_query = idx($engine->loadEnabledNamedQueries(), $query_key);
} else {
$saved_query = $engine->buildSavedQueryFromRequest($request);
// Save the query to generate a query key, so "Save Custom Query..." and
// other features like Maniphest's "Export..." work correctly.
$engine->saveQuery($saved_query);
}
$nav->selectFilter(
'query/'.$saved_query->getQueryKey(),
'query/advanced');
$form = id(new AphrontFormView())
->setUser($user)
->setAction($request->getPath());
$engine->buildSearchForm($form, $saved_query);
$errors = $engine->getErrors();
if ($errors) {
$run_query = false;
}
$submit = id(new AphrontFormSubmitControl())
->setValue(pht('Search'));
if ($run_query && !$named_query && $user->isLoggedIn()) {
$save_button = id(new PHUIButtonView())
->setTag('a')
->setHref('/search/edit/key/'.$saved_query->getQueryKey().'/')
->setText(pht('Save Query'))
->setIcon('fa-floppy-o');
$submit->addButton($save_button);
}
// TODO: A "Create Dashboard Panel" action goes here somewhere once
// we sort out T5307.
$form->appendChild($submit);
$body = array();
if ($this->getPreface()) {
$body[] = $this->getPreface();
}
if ($named_query) {
$title = $named_query->getQueryName();
} else {
$title = pht('Advanced Search');
}
$header = id(new PHUIHeaderView())
->setHeader($title)
->setProfileHeader(true);
$box = id(new PHUIObjectBoxView())
->setHeader($header)
->addClass('application-search-results');
if ($run_query || $named_query) {
$box->setShowHide(
pht('Edit Query'),
pht('Hide Query'),
$form,
$this->getApplicationURI('query/advanced/?query='.$query_key),
(!$named_query ? true : false));
} else {
$box->setForm($form);
}
$body[] = $box;
$more_crumbs = null;
if ($run_query) {
$exec_errors = array();
$box->setAnchor(
id(new PhabricatorAnchorView())
->setAnchorName('R'));
try {
$engine->setRequest($request);
$query = $engine->buildQueryFromSavedQuery($saved_query);
$pager = $engine->newPagerForSavedQuery($saved_query);
$pager->readFromRequest($request);
$objects = $engine->executeQuery($query, $pager);
$force_nux = $request->getBool('nux');
if (!$objects || $force_nux) {
$nux_view = $this->renderNewUserView($engine, $force_nux);
} else {
$nux_view = null;
}
$is_overflowing =
$pager->willShowPagingControls() &&
$engine->getResultBucket($saved_query);
$force_overheated = $request->getBool('overheated');
$is_overheated = $query->getIsOverheated() || $force_overheated;
if ($nux_view) {
$box->appendChild($nux_view);
} else {
$list = $engine->renderResults($objects, $saved_query);
if (!($list instanceof PhabricatorApplicationSearchResultView)) {
throw new Exception(
pht(
'SearchEngines must render a "%s" object, but this engine '.
'(of class "%s") rendered something else.',
'PhabricatorApplicationSearchResultView',
get_class($engine)));
}
if ($list->getObjectList()) {
$box->setObjectList($list->getObjectList());
}
if ($list->getTable()) {
$box->setTable($list->getTable());
}
if ($list->getInfoView()) {
$box->setInfoView($list->getInfoView());
}
if ($is_overflowing) {
$box->appendChild($this->newOverflowingView());
}
if ($list->getContent()) {
$box->appendChild($list->getContent());
}
if ($is_overheated) {
$box->appendChild($this->newOverheatedView($objects));
}
$result_header = $list->getHeader();
if ($result_header) {
$box->setHeader($result_header);
$header = $result_header;
}
$actions = $list->getActions();
if ($actions) {
foreach ($actions as $action) {
$header->addActionLink($action);
}
}
$use_actions = $engine->newUseResultsActions($saved_query);
// TODO: Eventually, modularize all this stuff.
$builtin_use_actions = $this->newBuiltinUseActions();
if ($builtin_use_actions) {
foreach ($builtin_use_actions as $builtin_use_action) {
$use_actions[] = $builtin_use_action;
}
}
if ($use_actions) {
$use_dropdown = $this->newUseResultsDropdown(
$saved_query,
$use_actions);
$header->addActionLink($use_dropdown);
}
$more_crumbs = $list->getCrumbs();
if ($pager->willShowPagingControls()) {
$pager_box = id(new PHUIBoxView())
->setColor(PHUIBoxView::GREY)
->addClass('application-search-pager')
->appendChild($pager);
$body[] = $pager_box;
}
}
} catch (PhabricatorTypeaheadInvalidTokenException $ex) {
$exec_errors[] = pht(
'This query specifies an invalid parameter. Review the '.
'query parameters and correct errors.');
} catch (PhutilSearchQueryCompilerSyntaxException $ex) {
$exec_errors[] = $ex->getMessage();
} catch (PhabricatorSearchConstraintException $ex) {
$exec_errors[] = $ex->getMessage();
}
// The engine may have encountered additional errors during rendering;
// merge them in and show everything.
foreach ($engine->getErrors() as $error) {
$exec_errors[] = $error;
}
$errors = $exec_errors;
}
if ($errors) {
$box->setFormErrors($errors, pht('Query Errors'));
}
$crumbs = $parent
->buildApplicationCrumbs()
->setBorder(true);
if ($more_crumbs) {
$query_uri = $engine->getQueryResultsPageURI($saved_query->getQueryKey());
$crumbs->addTextCrumb($title, $query_uri);
foreach ($more_crumbs as $crumb) {
$crumbs->addCrumb($crumb);
}
} else {
$crumbs->addTextCrumb($title);
}
require_celerity_resource('application-search-view-css');
return $this->newPage()
->setApplicationMenu($this->buildApplicationMenu())
->setTitle(pht('Query: %s', $title))
->setCrumbs($crumbs)
->setNavigation($nav)
->addClass('application-search-view')
->appendChild($body);
}
private function processExportRequest() {
$viewer = $this->getViewer();
$engine = $this->getSearchEngine();
$request = $this->getRequest();
if (!$this->canExport()) {
return new Aphront404Response();
}
$query_key = $this->getQueryKey();
if ($engine->isBuiltinQuery($query_key)) {
$saved_query = $engine->buildSavedQueryFromBuiltin($query_key);
} else if ($query_key) {
$saved_query = id(new PhabricatorSavedQueryQuery())
->setViewer($viewer)
->withQueryKeys(array($query_key))
->executeOne();
- if (!$saved_query) {
- return new Aphront404Response();
- }
+ } else {
+ $saved_query = null;
+ }
+
+ if (!$saved_query) {
+ return new Aphront404Response();
}
$cancel_uri = $engine->getQueryResultsPageURI($query_key);
$named_query = idx($engine->loadEnabledNamedQueries(), $query_key);
if ($named_query) {
$filename = $named_query->getQueryName();
} else {
$filename = $engine->getResultTypeDescription();
}
$filename = phutil_utf8_strtolower($filename);
$filename = PhabricatorFile::normalizeFileName($filename);
$formats = PhabricatorExportFormat::getAllEnabledExportFormats();
$format_options = mpull($formats, 'getExportFormatName');
$errors = array();
$e_format = null;
if ($request->isFormPost()) {
$format_key = $request->getStr('format');
$format = idx($formats, $format_key);
if (!$format) {
$e_format = pht('Invalid');
$errors[] = pht('Choose a valid export format.');
}
if (!$errors) {
$query = $engine->buildQueryFromSavedQuery($saved_query);
// NOTE: We aren't reading the pager from the request. Exports always
// affect the entire result set.
$pager = $engine->newPagerForSavedQuery($saved_query);
$pager->setPageSize(0x7FFFFFFF);
$objects = $engine->executeQuery($query, $pager);
$extension = $format->getFileExtension();
$mime_type = $format->getMIMEContentType();
$filename = $filename.'.'.$extension;
$format = clone $format;
$format->setViewer($viewer);
$export_data = $engine->newExport($objects);
if (count($export_data) !== count($objects)) {
throw new Exception(
pht(
'Search engine exported the wrong number of objects, expected '.
'%s but got %s.',
phutil_count($objects),
phutil_count($export_data)));
}
$objects = array_values($objects);
$export_data = array_values($export_data);
$field_list = $engine->newExportFieldList();
$field_list = mpull($field_list, null, 'getKey');
for ($ii = 0; $ii < count($objects); $ii++) {
$format->addObject($objects[$ii], $field_list, $export_data[$ii]);
}
$export_result = $format->newFileData();
$file = PhabricatorFile::newFromFileData(
$export_result,
array(
'name' => $filename,
'authorPHID' => $viewer->getPHID(),
'ttl.relative' => phutil_units('15 minutes in seconds'),
'viewPolicy' => PhabricatorPolicies::POLICY_NOONE,
'mime-type' => $mime_type,
));
return $this->newDialog()
->setTitle(pht('Download Results'))
->appendParagraph(
pht('Click the download button to download the exported data.'))
->addCancelButton($cancel_uri, pht('Done'))
->setSubmitURI($file->getDownloadURI())
->setDisableWorkflowOnSubmit(true)
->addSubmitButton(pht('Download Data'));
}
}
$export_form = id(new AphrontFormView())
->setViewer($viewer)
->appendControl(
id(new AphrontFormSelectControl())
->setName('format')
->setLabel(pht('Format'))
->setError($e_format)
->setOptions($format_options));
return $this->newDialog()
->setTitle(pht('Export Results'))
->setErrors($errors)
->appendForm($export_form)
->addCancelButton($cancel_uri)
->addSubmitButton(pht('Continue'));
}
private function processEditRequest() {
$parent = $this->getDelegatingController();
$request = $this->getRequest();
$viewer = $request->getUser();
$engine = $this->getSearchEngine();
$nav = $this->getNavigation();
if (!$nav) {
$nav = $this->buildNavigation();
}
$named_queries = $engine->loadAllNamedQueries();
$can_global = $viewer->getIsAdmin();
$groups = array(
'personal' => array(
'name' => pht('Personal Saved Queries'),
'items' => array(),
'edit' => true,
),
'global' => array(
'name' => pht('Global Saved Queries'),
'items' => array(),
'edit' => $can_global,
),
);
foreach ($named_queries as $named_query) {
if ($named_query->isGlobal()) {
$group = 'global';
} else {
$group = 'personal';
}
$groups[$group]['items'][] = $named_query;
}
$default_key = $engine->getDefaultQueryKey();
$lists = array();
foreach ($groups as $group) {
$lists[] = $this->newQueryListView(
$group['name'],
$group['items'],
$default_key,
$group['edit']);
}
$crumbs = $parent
->buildApplicationCrumbs()
->addTextCrumb(pht('Saved Queries'), $engine->getQueryManagementURI())
->setBorder(true);
$nav->selectFilter('query/edit');
$header = id(new PHUIHeaderView())
->setHeader(pht('Saved Queries'))
->setProfileHeader(true);
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setFooter($lists);
return $this->newPage()
->setApplicationMenu($this->buildApplicationMenu())
->setTitle(pht('Saved Queries'))
->setCrumbs($crumbs)
->setNavigation($nav)
->appendChild($view);
}
private function newQueryListView(
$list_name,
array $named_queries,
$default_key,
$can_edit) {
$engine = $this->getSearchEngine();
$viewer = $this->getViewer();
$list = id(new PHUIObjectItemListView())
->setViewer($viewer);
if ($can_edit) {
$list_id = celerity_generate_unique_node_id();
$list->setID($list_id);
Javelin::initBehavior(
'search-reorder-queries',
array(
'listID' => $list_id,
'orderURI' => '/search/order/'.get_class($engine).'/',
));
}
foreach ($named_queries as $named_query) {
$class = get_class($engine);
$key = $named_query->getQueryKey();
$item = id(new PHUIObjectItemView())
->setHeader($named_query->getQueryName())
->setHref($engine->getQueryResultsPageURI($key));
if ($named_query->getIsDisabled()) {
if ($can_edit) {
$item->setDisabled(true);
} else {
// If an item is disabled and you don't have permission to edit it,
// just skip it.
continue;
}
}
if ($can_edit) {
if ($named_query->getIsBuiltin() && $named_query->getIsDisabled()) {
$icon = 'fa-plus';
$disable_name = pht('Enable');
} else {
$icon = 'fa-times';
if ($named_query->getIsBuiltin()) {
$disable_name = pht('Disable');
} else {
$disable_name = pht('Delete');
}
}
if ($named_query->getID()) {
$disable_href = '/search/delete/id/'.$named_query->getID().'/';
} else {
$disable_href = '/search/delete/key/'.$key.'/'.$class.'/';
}
$item->addAction(
id(new PHUIListItemView())
->setIcon($icon)
->setHref($disable_href)
->setRenderNameAsTooltip(true)
->setName($disable_name)
->setWorkflow(true));
}
$default_disabled = $named_query->getIsDisabled();
$default_icon = 'fa-thumb-tack';
if ($default_key === $key) {
$default_color = 'green';
} else {
$default_color = null;
}
$item->addAction(
id(new PHUIListItemView())
->setIcon("{$default_icon} {$default_color}")
->setHref('/search/default/'.$key.'/'.$class.'/')
->setRenderNameAsTooltip(true)
->setName(pht('Make Default'))
->setWorkflow(true)
->setDisabled($default_disabled));
if ($can_edit) {
if ($named_query->getIsBuiltin()) {
$edit_icon = 'fa-lock lightgreytext';
$edit_disabled = true;
$edit_name = pht('Builtin');
$edit_href = null;
} else {
$edit_icon = 'fa-pencil';
$edit_disabled = false;
$edit_name = pht('Edit');
$edit_href = '/search/edit/id/'.$named_query->getID().'/';
}
$item->addAction(
id(new PHUIListItemView())
->setIcon($edit_icon)
->setHref($edit_href)
->setRenderNameAsTooltip(true)
->setName($edit_name)
->setDisabled($edit_disabled));
}
$item->setGrippable($can_edit);
$item->addSigil('named-query');
$item->setMetadata(
array(
'queryKey' => $named_query->getQueryKey(),
));
$list->addItem($item);
}
$list->setNoDataString(pht('No saved queries.'));
return id(new PHUIObjectBoxView())
->setHeaderText($list_name)
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->setObjectList($list);
}
public function buildApplicationMenu() {
$menu = $this->getDelegatingController()
->buildApplicationMenu();
if ($menu instanceof PHUIApplicationMenuView) {
$menu->setSearchEngine($this->getSearchEngine());
}
return $menu;
}
private function buildNavigation() {
$viewer = $this->getViewer();
$engine = $this->getSearchEngine();
$nav = id(new AphrontSideNavFilterView())
->setUser($viewer)
->setBaseURI(new PhutilURI($this->getApplicationURI()));
$engine->addNavigationItems($nav->getMenu());
return $nav;
}
private function renderNewUserView(
PhabricatorApplicationSearchEngine $engine,
$force_nux) {
// Don't render NUX if the user has clicked away from the default page.
if (strlen($this->getQueryKey())) {
return null;
}
// Don't put NUX in panels because it would be weird.
if ($engine->isPanelContext()) {
return null;
}
// Try to render the view itself first, since this should be very cheap
// (just returning some text).
$nux_view = $engine->renderNewUserView();
if (!$nux_view) {
return null;
}
$query = $engine->newQuery();
if (!$query) {
return null;
}
// Try to load any object at all. If we can, the application has seen some
// use so we just render the normal view.
if (!$force_nux) {
$object = $query
->setViewer(PhabricatorUser::getOmnipotentUser())
->setLimit(1)
->execute();
if ($object) {
return null;
}
}
return $nux_view;
}
private function newUseResultsDropdown(
PhabricatorSavedQuery $query,
array $dropdown_items) {
$viewer = $this->getViewer();
$action_list = id(new PhabricatorActionListView())
->setViewer($viewer);
foreach ($dropdown_items as $dropdown_item) {
$action_list->addAction($dropdown_item);
}
return id(new PHUIButtonView())
->setTag('a')
->setHref('#')
->setText(pht('Use Results'))
->setIcon('fa-bars')
->setDropdownMenu($action_list)
->addClass('dropdown');
}
private function newOverflowingView() {
$message = pht(
'The query matched more than one page of results. Results are '.
'paginated before bucketing, so later pages may contain additional '.
'results in any bucket.');
return id(new PHUIInfoView())
->setSeverity(PHUIInfoView::SEVERITY_WARNING)
->setFlush(true)
->setTitle(pht('Buckets Overflowing'))
->setErrors(
array(
$message,
));
}
private function newOverheatedView(array $results) {
if ($results) {
$message = pht(
'Most objects matching your query are not visible to you, so '.
'filtering results is taking a long time. Only some results are '.
'shown. Refine your query to find results more quickly.');
} else {
$message = pht(
'Most objects matching your query are not visible to you, so '.
'filtering results is taking a long time. Refine your query to '.
'find results more quickly.');
}
return id(new PHUIInfoView())
->setSeverity(PHUIInfoView::SEVERITY_WARNING)
->setFlush(true)
->setTitle(pht('Query Overheated'))
->setErrors(
array(
$message,
));
}
private function newBuiltinUseActions() {
$actions = array();
$request = $this->getRequest();
$viewer = $request->getUser();
$is_dev = PhabricatorEnv::getEnvConfig('phabricator.developer-mode');
$engine = $this->getSearchEngine();
$engine_class = get_class($engine);
$query_key = $this->getQueryKey();
if (!$query_key) {
$query_key = $engine->getDefaultQueryKey();
}
$can_use = $engine->canUseInPanelContext();
$is_installed = PhabricatorApplication::isClassInstalledForViewer(
'PhabricatorDashboardApplication',
$viewer);
if ($can_use && $is_installed) {
$actions[] = id(new PhabricatorActionView())
->setIcon('fa-dashboard')
->setName(pht('Add to Dashboard'))
->setWorkflow(true)
->setHref("/dashboard/panel/install/{$engine_class}/{$query_key}/");
}
if ($this->canExport()) {
$export_uri = $engine->getExportURI($query_key);
$actions[] = id(new PhabricatorActionView())
->setIcon('fa-download')
->setName(pht('Export Data'))
->setWorkflow(true)
->setHref($export_uri);
}
if ($is_dev) {
$engine = $this->getSearchEngine();
$nux_uri = $engine->getQueryBaseURI();
$nux_uri = id(new PhutilURI($nux_uri))
->setQueryParam('nux', true);
$actions[] = id(new PhabricatorActionView())
->setIcon('fa-user-plus')
->setName(pht('DEV: New User State'))
->setHref($nux_uri);
}
if ($is_dev) {
$overheated_uri = $this->getRequest()->getRequestURI()
->setQueryParam('overheated', true);
$actions[] = id(new PhabricatorActionView())
->setIcon('fa-fire')
->setName(pht('DEV: Overheated State'))
->setHref($overheated_uri);
}
return $actions;
}
private function canExport() {
$engine = $this->getSearchEngine();
if (!$engine->canExport()) {
return false;
}
// Don't allow logged-out users to perform exports. There's no technical
// or policy reason they can't, but we don't normally give them access
// to write files or jobs. For now, just err on the side of caution.
$viewer = $this->getViewer();
if (!$viewer->getPHID()) {
return false;
}
return true;
}
}

File Metadata

Mime Type
text/x-diff
Expires
Sun, Jan 19, 16:34 (2 w, 5 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1126517
Default Alt Text
(61 KB)

Event Timeline