1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-12-27 07:50:57 +01:00

Make default view of "Applications" app a full-page launcher

Summary:
This probably needs some tweaks, but the idea is to make it easier to browse and access applications without necessarily needing them to be on the homepage.

Open to feedback.

Test Plan:
(This screenshot merges "Organization", "Communication" and "Core" into a single "Core" group. We can't actually do this yet because it wrecks the homepage.)

{F160052}

Reviewers: btrahan, chad

Reviewed By: chad

Subscribers: epriestley

Maniphest Tasks: T5176

Differential Revision: https://secure.phabricator.com/D9297
This commit is contained in:
epriestley 2014-05-29 12:17:54 -07:00
parent 2f668493a0
commit 81d95cf682
51 changed files with 338 additions and 84 deletions

View file

@ -2,10 +2,6 @@
final class PhabricatorApplicationAudit extends PhabricatorApplication {
public function getShortDescription() {
return pht('Audit Code');
}
public function getBaseURI() {
return '/audit/';
}
@ -14,6 +10,10 @@ final class PhabricatorApplicationAudit extends PhabricatorApplication {
return 'audit';
}
public function getShortDescription() {
return pht('Browse and Audit Commits');
}
public function getHelpURI() {
return PhabricatorEnv::getDoclink('Audit User Guide');
}

View file

@ -14,6 +14,10 @@ final class PhabricatorApplicationAuth extends PhabricatorApplication {
return 'authentication';
}
public function getShortDescription() {
return pht('Configure Login and Registration');
}
public function getHelpURI() {
// NOTE: Although reasonable help exists for this in "Configuring Accounts
// and Registration", specifying a help URI here means we get the menu

View file

@ -155,12 +155,10 @@ abstract class PhabricatorApplication
}
public function getHelpURI() {
// TODO: When these applications get created, link to their docs:
//
// - Drydock
// - OAuth Server
return null;
}
public function getOverview() {
return null;
}

View file

@ -3,7 +3,7 @@
final class PhabricatorApplicationCalendar extends PhabricatorApplication {
public function getShortDescription() {
return pht('Dates and Stuff');
return pht('Upcoming Events');
}
public function getFlavorText() {

View file

@ -7,7 +7,7 @@ final class PhabricatorApplicationChatLog extends PhabricatorApplication {
}
public function getShortDescription() {
return pht('Chat Log');
return pht('IRC Logs');
}
public function getIconName() {

View file

@ -19,7 +19,7 @@ final class PhabricatorApplicationConduit extends PhabricatorApplication {
}
public function getShortDescription() {
return 'Conduit API Console';
return pht('Phabricator Developer API Console');
}
public function getTitleGlyph() {

View file

@ -22,6 +22,10 @@ final class PhabricatorApplicationConfig extends PhabricatorApplication {
return false;
}
public function getShortDescription() {
return pht('Configure Phabricator');
}
public function getRoutes() {
return array(
'/config/' => array(

View file

@ -10,7 +10,7 @@ final class PhabricatorApplicationConpherence extends PhabricatorApplication {
}
public function getShortDescription() {
return pht('Messaging');
return pht('Send Messages');
}
public function getIconName() {

View file

@ -11,7 +11,7 @@ final class PhabricatorApplicationCountdown extends PhabricatorApplication {
}
public function getShortDescription() {
return pht('Countdown Timers');
return pht('Countdown to Events');
}
public function getTitleGlyph() {

View file

@ -7,7 +7,7 @@ final class PhabricatorApplicationDaemons extends PhabricatorApplication {
}
public function getShortDescription() {
return pht('Manage Daemons');
return pht('Manage Phabricator Daemons');
}
public function getBaseURI() {

View file

@ -7,7 +7,7 @@ final class PhabricatorApplicationDashboard extends PhabricatorApplication {
}
public function getShortDescription() {
return pht('Such Data');
return pht('Create Custom Pages');
}
public function getIconName() {

View file

@ -36,6 +36,14 @@ final class PhabricatorApplicationDifferential extends PhabricatorApplication {
);
}
public function getOverview() {
return pht(<<<EOTEXT
Differential is a **code review application** which allows engineers to review,
discuss and approve changes to software.
EOTEXT
);
}
public function getRoutes() {
return array(
'/D(?P<id>[1-9]\d*)' => 'DifferentialRevisionViewController',

View file

@ -3,7 +3,7 @@
final class PhabricatorApplicationDiffusion extends PhabricatorApplication {
public function getShortDescription() {
return pht('Repository Browser');
return pht('Host and Browse Repositories');
}
public function getBaseURI() {

View file

@ -36,7 +36,7 @@ final class PhabricatorApplicationDiviner extends PhabricatorApplication {
}
public function getApplicationGroup() {
return self::GROUP_COMMUNICATION;
return self::GROUP_UTILITIES;
}
public function getRemarkupRules() {

View file

@ -6,14 +6,14 @@ final class PhabricatorApplicationDoorkeeper extends PhabricatorApplication {
return false;
}
public function getBaseURI() {
return '/doorkeeper/';
}
public function shouldAppearInLaunchView() {
return false;
}
public function getShortDescription() {
return pht('Connect to Other Software');
}
public function getRemarkupRules() {
return array(
new DoorkeeperRemarkupRuleAsana(),

View file

@ -30,6 +30,10 @@ final class PhabricatorApplicationDrydock extends PhabricatorApplication {
return true;
}
public function getHelpURI() {
return PhabricatorEnv::getDoclink('Drydock User Guide');
}
public function getRoutes() {
return array(
'/drydock/' => array(

View file

@ -3,7 +3,7 @@
final class PhabricatorApplicationFact extends PhabricatorApplication {
public function getShortDescription() {
return 'Analyze Data';
return pht('Chart and Analyze Data');
}
public function getName() {

View file

@ -7,7 +7,7 @@ final class PhabricatorApplicationFeed extends PhabricatorApplication {
}
public function getShortDescription() {
return pht('Review Activity');
return pht('Review Recent Activity');
}
public function getIconName() {

View file

@ -3,7 +3,7 @@
final class PhabricatorApplicationFlags extends PhabricatorApplication {
public function getShortDescription() {
return pht('Reminders');
return pht('Personal Bookmarks and Reminders');
}
public function getBaseURI() {
@ -25,7 +25,7 @@ final class PhabricatorApplicationFlags extends PhabricatorApplication {
}
public function getApplicationGroup() {
return self::GROUP_ORGANIZATION;
return self::GROUP_UTILITIES;
}
public function loadStatus(PhabricatorUser $user) {

View file

@ -7,7 +7,7 @@ final class PhabricatorApplicationHarbormaster extends PhabricatorApplication {
}
public function getShortDescription() {
return pht('Continuous Build');
return pht('Builds and Continuous Integration');
}
public function getIconName() {

View file

@ -7,7 +7,7 @@ final class PhabricatorApplicationLegalpad extends PhabricatorApplication {
}
public function getShortDescription() {
return pht('Legal Documents');
return pht('Agreements and Signatures');
}
public function getIconName() {

View file

@ -11,7 +11,7 @@ final class PhabricatorApplicationApplications extends PhabricatorApplication {
}
public function getShortDescription() {
return 'Installed Applications';
return pht('Explore More Applications');
}
public function getIconName() {
@ -26,7 +26,7 @@ final class PhabricatorApplicationApplications extends PhabricatorApplication {
return self::GROUP_ADMIN;
}
public function getRoutes() {
public function getRoutes() {
return array(
'/applications/' => array(
'(?:query/(?P<queryKey>[^/]+)/)?' =>
@ -38,7 +38,6 @@ final class PhabricatorApplicationApplications extends PhabricatorApplication {
'(?P<application>\w+)/(?P<action>install|uninstall)/' =>
'PhabricatorApplicationUninstallController',
),
);
}

View file

@ -1,10 +1,14 @@
<?php
final class PhabricatorApplicationDetailViewController
extends PhabricatorApplicationsController{
extends PhabricatorApplicationsController {
private $application;
public function shouldAllowPublic() {
return true;
}
public function willProcessRequest(array $data) {
$this->application = $data['application'];
}
@ -61,16 +65,37 @@ final class PhabricatorApplicationDetailViewController
$viewer = $this->getRequest()->getUser();
$properties = id(new PHUIPropertyListView())
->addProperty(pht('Description'), $application->getShortDescription());
$properties = id(new PHUIPropertyListView());
$properties->setActionList($actions);
$properties->addProperty(
pht('Description'),
$application->getShortDescription());
if ($application->getFlavorText()) {
$properties->addProperty(
null,
phutil_tag('em', array(), $application->getFlavorText()));
}
if ($application->isBeta()) {
$properties->addProperty(
pht('Release'),
pht('Beta'));
}
$overview = $application->getOverview();
if ($overview) {
$properties->addSectionHeader(
pht('Overview'),
PHUIPropertyListView::ICON_SUMMARY);
$properties->addTextContent(
PhabricatorMarkupEngine::renderOneObject(
id(new PhabricatorMarkupOneOff())->setContent($overview),
'default',
$viewer));
}
$descriptions = PhabricatorPolicyQuery::renderPolicyDescriptions(
$viewer,
$application);
@ -94,6 +119,14 @@ final class PhabricatorApplicationDetailViewController
->setUser($user)
->setObjectURI($this->getRequest()->getRequestURI());
if ($selected->getHelpURI()) {
$view->addAction(
id(new PhabricatorActionView())
->setName(pht('Help / Documentation'))
->setIcon('fa-life-ring')
->setHref($selected->getHelpURI()));
}
$can_edit = PhabricatorPolicyFilter::hasCapability(
$user,
$selected,

View file

@ -1,10 +1,14 @@
<?php
final class PhabricatorApplicationEditController
extends PhabricatorApplicationsController{
extends PhabricatorApplicationsController {
private $application;
public function shouldRequireAdmin() {
return true;
}
public function willProcessRequest(array $data) {
$this->application = $data['application'];
}

View file

@ -6,6 +6,10 @@ final class PhabricatorApplicationUninstallController
private $application;
private $action;
public function shouldRequireAdmin() {
return true;
}
public function willProcessRequest(array $data) {
$this->application = $data['application'];
$this->action = $data['action'];

View file

@ -2,10 +2,6 @@
abstract class PhabricatorApplicationsController extends PhabricatorController {
public function shouldRequireAdmin() {
return true;
}
public function buildSideNavView($for_app = false) {
$user = $this->getRequest()->getUser();

View file

@ -5,6 +5,10 @@ final class PhabricatorApplicationsListController
private $queryKey;
public function shouldAllowPublic() {
return true;
}
public function willProcessRequest(array $data) {
$this->queryKey = idx($data, 'queryKey');
}

View file

@ -25,6 +25,9 @@ final class PhabricatorAppSearchEngine
$saved->setParameter(
'firstParty',
$this->readBoolFromRequest($request, 'firstParty'));
$saved->setParameter(
'launchable',
$this->readBoolFromRequest($request, 'launchable'));
return $saved;
}
@ -54,6 +57,11 @@ final class PhabricatorAppSearchEngine
$query->withFirstParty($first_party);
}
$launchable = $saved->getParameter('launchable');
if ($launchable !== null) {
$query->withLaunchable($launchable);
}
return $query;
}
@ -99,6 +107,17 @@ final class PhabricatorAppSearchEngine
'' => pht('Show All Applications'),
'true' => pht('Show First-Party Applications'),
'false' => pht('Show Third-Party Applications'),
)))
->appendChild(
id(new AphrontFormSelectControl())
->setLabel(pht('Launchable'))
->setName('launchable')
->setValue($this->getBoolFromQuery($saved, 'launchable'))
->setOptions(
array(
'' => pht('Show All Applications'),
'true' => pht('Show Launchable Applications'),
'false' => pht('Show Non-Launchable Applications'),
)));
}
@ -109,6 +128,7 @@ final class PhabricatorAppSearchEngine
public function getBuiltinQueryNames() {
$names = array(
'launcher' => pht('Launcher'),
'all' => pht('All Applications'),
);
@ -121,6 +141,10 @@ final class PhabricatorAppSearchEngine
$query->setQueryKey($query_key);
switch ($query_key) {
case 'launcher':
return $query
->setParameter('installed', true)
->setParameter('launchable', true);
case 'all':
return $query;
}
@ -129,33 +153,93 @@ final class PhabricatorAppSearchEngine
}
protected function renderResultList(
array $applications,
array $all_applications,
PhabricatorSavedQuery $query,
array $handle) {
assert_instances_of($applications, 'PhabricatorApplication');
assert_instances_of($all_applications, 'PhabricatorApplication');
$list = new PHUIObjectItemListView();
$all_applications = msort($all_applications, 'getName');
$applications = msort($applications, 'getName');
foreach ($applications as $application) {
$item = id(new PHUIObjectItemView())
->setHeader($application->getName())
->setHref('/applications/view/'.get_class($application).'/')
->addAttribute($application->getShortDescription());
if (!$application->isInstalled()) {
$item->addIcon('delete', pht('Uninstalled'));
}
if ($application->isBeta()) {
$item->addIcon('lint-warning', pht('Beta'));
}
$list->addItem($item);
if ($query->getQueryKey() == 'launcher') {
$groups = mgroup($all_applications, 'getApplicationGroup');
} else {
$groups = array($all_applications);
}
return $list;
$group_names = PhabricatorApplication::getApplicationGroups();
$groups = array_select_keys($groups, array_keys($group_names)) + $groups;
$results = array();
foreach ($groups as $group => $applications) {
if (count($groups) > 1) {
$results[] = phutil_tag(
'h1',
array(
'class' => 'launcher-header',
),
idx($group_names, $group, $group));
}
$list = new PHUIObjectItemListView();
$list->addClass('phui-object-item-launcher-list');
foreach ($applications as $application) {
$icon = $application->getIconName();
if (!$icon) {
$icon = 'application';
}
// TODO: This sheet doesn't work the same way other sheets do so it
// ends up with the wrong classes if we try to use PHUIIconView. This
// is probably all changing in the redesign anyway.
$icon_view = javelin_tag(
'span',
array(
'class' => 'phui-icon-view '.
'sprite-apps-large apps-'.$icon.'-dark-large',
'aural' => false,
),
'');
$description = phutil_tag(
'div',
array(
'style' => 'white-space: nowrap; '.
'overflow: hidden; '.
'text-overflow: ellipsis;',
),
$application->getShortDescription());
$item = id(new PHUIObjectItemView())
->setHeader($application->getName())
->setImageIcon($icon_view)
->addAttribute($description)
->addAction(
id(new PHUIListItemView())
->setName(pht('Help/Options'))
->setIcon('fa-cog')
->setHref('/applications/view/'.get_class($application).'/'));
if ($application->getBaseURI()) {
$item->setHref($application->getBaseURI());
}
if (!$application->isInstalled()) {
$item->addIcon('delete', pht('Uninstalled'));
}
if ($application->isBeta()) {
$item->addIcon('fa-star-half-o grey', pht('Beta'));
}
$list->addItem($item);
}
$results[] = $list;
}
return $results;
}
}

View file

@ -9,6 +9,7 @@ final class PhabricatorApplicationQuery
private $nameContains;
private $unlisted;
private $classes;
private $launchable;
private $phids;
const ORDER_APPLICATION = 'order:application';
@ -41,6 +42,11 @@ final class PhabricatorApplicationQuery
return $this;
}
public function withLaunchable($launchable) {
$this->launchable = $launchable;
return $this;
}
public function withClasses(array $classes) {
$this->classes = $classes;
return $this;
@ -117,6 +123,15 @@ final class PhabricatorApplicationQuery
}
}
if ($this->launchable !== null) {
foreach ($apps as $key => $app) {
if ($app->shouldAppearInLaunchView() != $this->launchable) {
unset($apps[$key]);
}
}
}
switch ($this->order) {
case self::ORDER_NAME:
$apps = msort($apps, 'getName');

View file

@ -2,14 +2,14 @@
final class PhabricatorApplicationMetaMTA extends PhabricatorApplication {
public function getBaseURI() {
return '/mail/';
}
public function getIconName() {
return 'metamta';
}
public function getShortDescription() {
return pht('Delivers Mail');
}
public function getFlavorText() {
return pht('Yo dawg, we heard you like MTAs.');
}

View file

@ -7,7 +7,7 @@ final class PhabricatorApplicationNotifications extends PhabricatorApplication {
}
public function getShortDescription() {
return pht('Beep Beep Bloop');
return pht('Real-Time Updates and Alerts');
}
public function getRoutes() {

View file

@ -27,6 +27,10 @@ final class PhabricatorApplicationNuance extends PhabricatorApplication {
return '/nuance/';
}
public function getShortDescription() {
return pht('High-Volume Task Queues');
}
public function getRoutes() {
return array(
'/nuance/' => array(

View file

@ -7,7 +7,7 @@ final class PhabricatorApplicationOAuthServer extends PhabricatorApplication {
}
public function getShortDescription() {
return pht('OAuth Provider');
return pht('OAuth Login Provider');
}
public function getIconName() {
@ -23,13 +23,17 @@ final class PhabricatorApplicationOAuthServer extends PhabricatorApplication {
}
public function getApplicationGroup() {
return self::GROUP_UTILITIES;
return self::GROUP_ADMIN;
}
public function isBeta() {
return true;
}
public function getHelpURI() {
return PhabricatorEnv::getDoclink('Using the Phabricator OAuth Server');
}
public function getRoutes() {
return array(
'/oauthserver/' => array(

View file

@ -11,7 +11,7 @@ final class PhabricatorApplicationOwners extends PhabricatorApplication {
}
public function getShortDescription() {
return pht('Group Source Code');
return pht('Track Ownership of Source Code');
}
public function getTitleGlyph() {
@ -27,7 +27,7 @@ final class PhabricatorApplicationOwners extends PhabricatorApplication {
}
public function getApplicationGroup() {
return self::GROUP_ORGANIZATION;
return self::GROUP_UTILITIES;
}
public function getRoutes() {

View file

@ -7,7 +7,7 @@ final class PhabricatorApplicationPassphrase extends PhabricatorApplication {
}
public function getShortDescription() {
return pht('Credential Management');
return pht('Store Passwords and Credentials');
}
public function getIconName() {

View file

@ -18,6 +18,10 @@ final class PhabricatorApplicationPaste extends PhabricatorApplication {
return self::GROUP_UTILITIES;
}
public function getShortDescription() {
return pht('Share Text Snippets');
}
public function getRemarkupRules() {
return array(
new PhabricatorPasteRemarkupRule(),

View file

@ -3,7 +3,7 @@
final class PhabricatorApplicationPeople extends PhabricatorApplication {
public function getShortDescription() {
return pht('User Accounts');
return pht('User Accounts and Profiles');
}
public function getBaseURI() {

View file

@ -7,7 +7,7 @@ final class PhabricatorApplicationPhlux extends PhabricatorApplication {
}
public function getShortDescription() {
return pht('Configuration Store');
return pht('Key/Value Configuration Store');
}
public function getIconName() {

View file

@ -7,7 +7,7 @@ final class PhabricatorApplicationPholio extends PhabricatorApplication {
}
public function getShortDescription() {
return pht('Design Review');
return pht('Review Mocks and Design');
}
public function getIconName() {

View file

@ -7,7 +7,7 @@ final class PhabricatorApplicationPhortune extends PhabricatorApplication {
}
public function getShortDescription() {
return pht('Account and Billing');
return pht('Accounts and Billing');
}
public function getIconName() {

View file

@ -3,7 +3,7 @@
final class PhabricatorApplicationPhrequent extends PhabricatorApplication {
public function getShortDescription() {
return pht('Track Time');
return pht('Track Time Spent');
}
public function getBaseURI() {
@ -19,7 +19,7 @@ final class PhabricatorApplicationPhrequent extends PhabricatorApplication {
}
public function getApplicationGroup() {
return self::GROUP_ORGANIZATION;
return self::GROUP_UTILITIES;
}
public function getApplicationOrder() {

View file

@ -7,7 +7,7 @@ final class PhabricatorApplicationPonder extends PhabricatorApplication {
}
public function getShortDescription() {
return pht('Find Answers');
return pht('Questions and Answers');
}
public function getIconName() {

View file

@ -7,7 +7,7 @@ final class PhabricatorApplicationProject extends PhabricatorApplication {
}
public function getShortDescription() {
return pht('Organize Work');
return pht('Create Groups, Tags, and Projects');
}
public function getBaseURI() {

View file

@ -7,7 +7,7 @@ final class PhabricatorApplicationReleeph extends PhabricatorApplication {
}
public function getShortDescription() {
return pht('Release Branches');
return pht('Pull Requests');
}
public function getBaseURI() {

View file

@ -14,7 +14,7 @@ final class PhabricatorApplicationRepositories extends PhabricatorApplication {
}
public function getShortDescription() {
return 'Track Repositories';
return pht('(Deprecated)');
}
public function getTitleGlyph() {

View file

@ -11,7 +11,7 @@ final class PhabricatorApplicationSearch extends PhabricatorApplication {
}
public function getShortDescription() {
return pht('Search & Find');
return pht('Full-Text Search');
}
public function getFlavorText() {

View file

@ -7,7 +7,7 @@ final class PhabricatorApplicationSettings extends PhabricatorApplication {
}
public function getShortDescription() {
return 'User Preferences';
return pht('User Preferences');
}
public function getIconName() {
@ -18,6 +18,10 @@ final class PhabricatorApplicationSettings extends PhabricatorApplication {
return false;
}
public function shouldAppearInLaunchView() {
return false;
}
public function getRoutes() {
return array(
'/settings/' => array(

View file

@ -19,7 +19,7 @@ final class PhabricatorApplicationTokens extends PhabricatorApplication {
}
public function getShortDescription() {
return pht('Acquire Trinkets');
return pht('Award and Acquire Trinkets');
}
public function getApplicationGroup() {

View file

@ -7,7 +7,7 @@ final class PhabricatorApplicationUIExamples extends PhabricatorApplication {
}
public function getShortDescription() {
return 'Developer UI Examples';
return pht('Phabricator Developer UI Examples');
}
public function getIconName() {

View file

@ -21,6 +21,7 @@ final class PHUIObjectItemView extends AphrontTagView {
private $imageURI;
private $state;
private $fontIcon;
private $imageIcon;
const AGE_FRESH = 'fresh';
const AGE_STALE = 'stale';
@ -115,6 +116,15 @@ final class PHUIObjectItemView extends AphrontTagView {
return $this->imageURI;
}
public function setImageIcon($image_icon) {
$this->imageIcon = $image_icon;
return $this;
}
public function getImageIcon() {
return $this->imageIcon;
}
public function setState($state) {
$this->state = $state;
switch ($state) {
@ -288,6 +298,10 @@ final class PHUIObjectItemView extends AphrontTagView {
$item_classes[] = 'phui-object-item-with-image';
}
if ($this->getImageIcon()) {
$item_classes[] = 'phui-object-item-with-image-icon';
}
if ($this->fontIcon) {
$item_classes[] = 'phui-object-item-with-ficon';
}
@ -520,6 +534,22 @@ final class PHUIObjectItemView extends AphrontTagView {
'style' => 'background-image: url('.$this->getImageURI().')',
),
'');
} else if ($this->getImageIcon()) {
$image = phutil_tag(
'div',
array(
'class' => 'phui-object-item-image-icon',
),
$this->getImageIcon());
}
if ($image && $this->href) {
$image = phutil_tag(
'a',
array(
'href' => $this->href,
),
$image);
}
$ficon = null;

View file

@ -671,3 +671,54 @@
border: none;
border-bottom: 1px solid {$thinblueborder};
}
/* - Launcher List ---------------------------------------------------------- */
.launcher-header {
margin: 8px 16px -4px;
clear: both;
color: {$darkbluetext};
}
.launcher-header:nth-of-type(1) {
margin-top: 24px;
}
.phui-object-item-launcher-list {
overflow: hidden;
}
.device-desktop .phui-object-item-launcher-list .phui-object-item {
width: 32.333%;
float: left;
margin-right: 1%;
box-sizing: border-box;
}
.phui-object-item-image-icon {
background: none;
}
.phui-object-item-image-icon {
width: 30px;
height: 30px;
margin: 4px 4px 4px 4px;
position: absolute;
}
.phui-object-item-image-icon .phui-icon-view {
position: absolute;
width: 28px;
height: 28px;
left: 6px;
top: 6px;
}
.phui-object-item-with-image-icon .phui-object-item-frame {
min-height: 48px;
}
.phui-object-item-with-image-icon .phui-object-item-content-box {
margin-left: 44px;
}