[^/]+)/' =>
'PhabricatorEmailVerificationController',
);
}
protected function getResourceURIMapRules() {
return array(
'/res/' => array(
'(?Ppkg/)?'.
'(?P[a-f0-9]{8})/'.
'(?P.+\.(?:css|js|jpg|png|swf|gif))'
=> 'CelerityResourceController',
),
);
}
public function buildRequest() {
$request = new AphrontRequest($this->getHost(), $this->getPath());
$request->setRequestData($_GET + $_POST);
$request->setApplicationConfiguration($this);
return $request;
}
public function handleException(Exception $ex) {
$request = $this->getRequest();
// For Conduit requests, return a Conduit response.
if ($request->isConduit()) {
$response = new ConduitAPIResponse();
$response->setErrorCode(get_class($ex));
$response->setErrorInfo($ex->getMessage());
return id(new AphrontJSONResponse())
->setContent($response->toDictionary());
}
// For non-workflow requests, return a Ajax response.
if ($request->isAjax() && !$request->isJavelinWorkflow()) {
$response = new AphrontAjaxResponse();
$response->setError(
array(
'code' => get_class($ex),
'info' => $ex->getMessage(),
));
return $response;
}
$is_serious = PhabricatorEnv::getEnvConfig('phabricator.serious-business');
$user = $request->getUser();
if (!$user) {
// If we hit an exception very early, we won't have a user.
$user = new PhabricatorUser();
}
if ($ex instanceof PhabricatorPolicyException) {
$content =
''.
phutil_escape_html($ex->getMessage()).
'';
$dialog = new AphrontDialogView();
$dialog
->setTitle(
$is_serious
? 'Access Denied'
: "You Shall Not Pass")
->setClass('aphront-access-dialog')
->setUser($user)
->appendChild($content);
if ($this->getRequest()->isAjax()) {
$dialog->addCancelButton('/', 'Close');
} else {
$dialog->addCancelButton('/', $is_serious ? 'OK' : 'Away With Thee');
}
$response = new AphrontDialogResponse();
$response->setDialog($dialog);
return $response;
}
if ($ex instanceof AphrontUsageException) {
$error = new AphrontErrorView();
$error->setTitle(phutil_escape_html($ex->getTitle()));
$error->appendChild(phutil_escape_html($ex->getMessage()));
$view = new PhabricatorStandardPageView();
$view->setRequest($this->getRequest());
$view->appendChild($error);
$response = new AphrontWebpageResponse();
$response->setContent($view->render());
return $response;
}
// Always log the unhandled exception.
phlog($ex);
$class = phutil_escape_html(get_class($ex));
$message = phutil_escape_html($ex->getMessage());
if (PhabricatorEnv::getEnvConfig('phabricator.show-stack-traces')) {
$trace = $this->renderStackTrace($ex->getTrace(), $user);
} else {
$trace = null;
}
$content =
''.
''.
$trace.
'';
$dialog = new AphrontDialogView();
$dialog
->setTitle('Unhandled Exception ("'.$class.'")')
->setClass('aphront-exception-dialog')
->setUser($user)
->appendChild($content);
if ($this->getRequest()->isAjax()) {
$dialog->addCancelButton('/', 'Close');
}
$response = new AphrontDialogResponse();
$response->setDialog($dialog);
return $response;
}
public function willSendResponse(AphrontResponse $response) {
$request = $this->getRequest();
$response->setRequest($request);
if ($response instanceof AphrontDialogResponse) {
if (!$request->isAjax()) {
$view = new PhabricatorStandardPageView();
$view->setRequest($request);
$view->appendChild(
''.
$response->buildResponseString().
'');
$response = new AphrontWebpageResponse();
$response->setContent($view->render());
return $response;
} else {
return id(new AphrontAjaxResponse())
->setContent(array(
'dialog' => $response->buildResponseString(),
));
}
} else if ($response instanceof AphrontRedirectResponse) {
if ($request->isAjax()) {
return id(new AphrontAjaxResponse())
->setContent(
array(
'redirect' => $response->getURI(),
));
}
}
return $response;
}
public function build404Controller() {
return array(new Phabricator404Controller($this->getRequest()), array());
}
public function buildRedirectController($uri) {
return array(
new PhabricatorRedirectController($this->getRequest()),
array(
'uri' => $uri,
));
}
private function renderStackTrace($trace, PhabricatorUser $user) {
$libraries = PhutilBootloader::getInstance()->getAllLibraries();
// TODO: Make this configurable?
$path = 'https://secure.phabricator.com/diffusion/%s/browse/master/src/';
$callsigns = array(
'arcanist' => 'ARC',
'phutil' => 'PHU',
'phabricator' => 'P',
);
$rows = array();
$depth = count($trace);
foreach ($trace as $part) {
$lib = null;
$file = idx($part, 'file');
$relative = $file;
foreach ($libraries as $library) {
$root = phutil_get_library_root($library);
if (Filesystem::isDescendant($file, $root)) {
$lib = $library;
$relative = Filesystem::readablePath($file, $root);
break;
}
}
$where = '';
if (isset($part['class'])) {
$where .= $part['class'].'::';
}
if (isset($part['function'])) {
$where .= $part['function'].'()';
}
if ($file) {
if (isset($callsigns[$lib])) {
$attrs = array('title' => $file);
try {
$attrs['href'] = $user->loadEditorLink(
'/src/'.$relative,
$part['line'],
$callsigns[$lib]);
} catch (Exception $ex) {
// The database can be inaccessible.
}
if (empty($attrs['href'])) {
$attrs['href'] = sprintf($path, $callsigns[$lib]).
str_replace(DIRECTORY_SEPARATOR, '/', $relative).
'$'.$part['line'];
$attrs['target'] = '_blank';
}
$file_name = phutil_render_tag(
'a',
$attrs,
phutil_escape_html($relative));
} else {
$file_name = phutil_render_tag(
'span',
array(
'title' => $file,
),
phutil_escape_html($relative));
}
$file_name = $file_name.' : '.(int)$part['line'];
} else {
$file_name = '(Internal)';
}
$rows[] = array(
$depth--,
phutil_escape_html($lib),
$file_name,
phutil_escape_html($where),
);
}
$table = new AphrontTableView($rows);
$table->setHeaders(
array(
'Depth',
'Library',
'File',
'Where',
));
$table->setColumnClasses(
array(
'n',
'',
'',
'wide',
));
return
''.
'Stack Trace'.
$table->render().
'';
}
}
diff --git a/src/applications/notification/controller/PhabricatorNotificationPanelController.php b/src/applications/notification/controller/PhabricatorNotificationPanelController.php
index cb80063899..c16991127b 100644
--- a/src/applications/notification/controller/PhabricatorNotificationPanelController.php
+++ b/src/applications/notification/controller/PhabricatorNotificationPanelController.php
@@ -1,52 +1,58 @@
getRequest();
$user = $request->getUser();
$query = new PhabricatorNotificationQuery();
$query->setUserPHID($user->getPHID());
$query->setLimit(15);
$stories = $query->execute();
- $builder = new PhabricatorNotificationBuilder($stories);
- $notifications_view = $builder->buildView();
-
$num_unconsumed = 0;
- foreach ($stories as $story) {
- if (!$story->getHasViewed()) {
- $num_unconsumed++;
+ if ($stories) {
+ $builder = new PhabricatorNotificationBuilder($stories);
+ $notifications_view = $builder->buildView();
+
+ foreach ($stories as $story) {
+ if (!$story->getHasViewed()) {
+ $num_unconsumed++;
+ }
}
+ $content = $notifications_view->render();
+ } else {
+ $content =
+ ''.
+ 'You have no notifications.'.
+ '';
}
$json = array(
- "content" => $stories ?
- $notifications_view->render() :
- "You currently have no notifications",
- "number" => $num_unconsumed,
+ 'content' => $content,
+ 'number' => $num_unconsumed,
);
return id(new AphrontAjaxResponse())->setContent($json);
}
}
diff --git a/src/applications/notification/controller/PhabricatorNotificationStatusController.php b/src/applications/notification/controller/PhabricatorNotificationStatusController.php
new file mode 100644
index 0000000000..83484abb8a
--- /dev/null
+++ b/src/applications/notification/controller/PhabricatorNotificationStatusController.php
@@ -0,0 +1,94 @@
+setPath('/status/');
+
+ $future = id(new HTTPSFuture($uri))
+ ->setTimeout(3);
+
+ try {
+ list($body) = $future->resolvex();
+ $body = json_decode($body, true);
+ if (!is_array($body)) {
+ throw new Exception("Expected JSON response from server!");
+ }
+
+ $status = $this->renderServerStatus($body);
+ } catch (Exception $ex) {
+ $status = new AphrontErrorView();
+ $status->setTitle("Notification Server Issue");
+ $status->appendChild(
+ 'Unable to determine server status. This probably means the server '.
+ 'is not in great shape. The specific issue encountered was:'.
+ '
'.
+ '
'.
+ ''.phutil_escape_html(get_class($ex)).' '.
+ nl2br(phutil_escape_html($ex->getMessage())));
+ }
+
+ return $this->buildStandardPageResponse(
+ $status,
+ array(
+ 'title' => 'Aphlict Server Status',
+ ));
+ }
+
+ private function renderServerStatus(array $status) {
+
+ $rows = array();
+ foreach ($status as $key => $value) {
+ $label = phutil_escape_html($key);
+
+ switch ($key) {
+ case 'uptime':
+ $value /= 1000;
+ $value = phabricator_format_relative_time_detailed($value);
+ break;
+ case 'log':
+ $value = phutil_escape_html($value);
+ break;
+ default:
+ $value = phutil_escape_html(number_format($value));
+ break;
+ }
+
+ $rows[] = array($label, $value);
+ }
+
+ $table = new AphrontTableView($rows);
+ $table->setColumnClasses(
+ array(
+ 'header',
+ 'wide',
+ ));
+
+ $panel = new AphrontPanelView();
+ $panel->setHeader('Server Status');
+ $panel->appendChild($table);
+
+ return $panel;
+ }
+}
diff --git a/src/applications/notification/controller/PhabricatorNotificationTestController.php b/src/applications/notification/controller/PhabricatorNotificationTestController.php
deleted file mode 100644
index fb7ba42300..0000000000
--- a/src/applications/notification/controller/PhabricatorNotificationTestController.php
+++ /dev/null
@@ -1,53 +0,0 @@
-getRequest();
- $user = $request->getUser();
-
- $query = new PhabricatorNotificationQuery();
- $query->setUserPHID($user->getPHID());
-
- $stories = $query->execute();
-
- $builder = new PhabricatorNotificationBuilder($stories);
- $notifications_view = $builder->buildView();
-
- $num_unconsumed = 0;
-
- foreach ($stories as $story) {
- if (!$story->getHasViewed()) {
- $num_unconsumed++;
- }
-
- }
-
- $json = array(
- $notifications_view->render()
- );
-
-
- return $this->buildStandardPageResponse(
- $json,
- array('title' => 'Notification Test Page'));
- }
-}
diff --git a/src/applications/notifications/controller/PhabricatorAphlictTestPageController.php b/src/applications/notifications/controller/PhabricatorAphlictTestPageController.php
deleted file mode 100644
index 9772bb75d3..0000000000
--- a/src/applications/notifications/controller/PhabricatorAphlictTestPageController.php
+++ /dev/null
@@ -1,56 +0,0 @@
-Check Your Javascript Console!';
-
- $object_id = 'aphlictswfobject';
-
- $content = phutil_render_tag(
- 'object',
- array(
- 'classid' => 'clsid:d27cdb6e-ae6d-11cf-96b8-444553540000',
- ),
- ''.
- ''.
- '');
-
- Javelin::initBehavior(
- 'aphlict-listen',
- array(
- 'id' => $object_id,
- 'server' => '127.0.0.1',
- 'port' => 22280,
- ));
-
- return $this->buildStandardPageResponse(
- array(
- $instructions,
- $content,
- ),
- array(
- 'title' => 'Aphlict Test Page',
- ));
- }
-
-
-}
diff --git a/src/applications/notifications/controller/PhabricatorNotificationsController.php b/src/applications/notifications/controller/PhabricatorNotificationsController.php
deleted file mode 100644
index 00a3a2eca3..0000000000
--- a/src/applications/notifications/controller/PhabricatorNotificationsController.php
+++ /dev/null
@@ -1,37 +0,0 @@
-buildStandardPageView();
-
- $page->setApplicationName('Notifications');
- $page->setBaseURI('/notifications/');
- $page->setTitle(idx($data, 'title'));
- $page->setGlyph('!');
- $page->appendChild($view);
-
- $response = new AphrontWebpageResponse();
- return $response->setContent($page->render());
-
- }
-
-}
diff --git a/src/view/page/PhabricatorStandardPageView.php b/src/view/page/PhabricatorStandardPageView.php
index fcb2330997..562169074d 100644
--- a/src/view/page/PhabricatorStandardPageView.php
+++ b/src/view/page/PhabricatorStandardPageView.php
@@ -1,541 +1,545 @@
isAdminInterface = $is_admin_interface;
return $this;
}
public function setIsLoggedOut($is_logged_out) {
if ($is_logged_out) {
$this->tabs = array_merge($this->tabs, array(
'login' => array(
'name' => 'Login',
'href' => '/login/'
)
));
}
return $this;
}
public function getIsAdminInterface() {
return $this->isAdminInterface;
}
public function setRequest($request) {
$this->request = $request;
return $this;
}
public function getRequest() {
return $this->request;
}
public function setApplicationName($application_name) {
$this->applicationName = $application_name;
return $this;
}
public function setFrameable($frameable) {
$this->isFrameable = $frameable;
return $this;
}
public function setDisableConsole($disable) {
$this->disableConsole = $disable;
return $this;
}
public function getApplicationName() {
return $this->applicationName;
}
public function setBaseURI($base_uri) {
$this->baseURI = $base_uri;
return $this;
}
public function getBaseURI() {
return $this->baseURI;
}
public function setTabs(array $tabs, $selected_tab) {
$this->tabs = $tabs;
$this->selectedTab = $selected_tab;
return $this;
}
public function setShowChrome($show_chrome) {
$this->showChrome = $show_chrome;
return $this;
}
public function getShowChrome() {
return $this->showChrome;
}
public function setSearchDefaultScope($search_default_scope) {
$this->searchDefaultScope = $search_default_scope;
return $this;
}
public function getSearchDefaultScope() {
return $this->searchDefaultScope;
}
public function appendPageObjects(array $objs) {
foreach ($objs as $obj) {
$this->pageObjects[] = $obj;
}
}
public function getTitle() {
$use_glyph = true;
$request = $this->getRequest();
if ($request) {
$user = $request->getUser();
if ($user && $user->loadPreferences()->getPreference(
PhabricatorUserPreferences::PREFERENCE_TITLES) !== 'glyph') {
$use_glyph = false;
}
}
return ($use_glyph ?
$this->getGlyph() : '['.$this->getApplicationName().']').
' '.parent::getTitle();
}
protected function willRenderPage() {
if (!$this->getRequest()) {
throw new Exception(
"You must set the Request to render a PhabricatorStandardPageView.");
}
$console = $this->getConsole();
require_celerity_resource('phabricator-core-css');
require_celerity_resource('phabricator-core-buttons-css');
require_celerity_resource('phabricator-standard-page-view');
if (PhabricatorEnv::getEnvConfig('notification.enabled')) {
require_celerity_resource('phabricator-notification-css');
}
$current_token = null;
$request = $this->getRequest();
if ($request) {
$user = $request->getUser();
if ($user) {
$current_token = $user->getCSRFToken();
}
}
Javelin::initBehavior('workflow', array());
Javelin::initBehavior(
'refresh-csrf',
array(
'tokenName' => AphrontRequest::getCSRFTokenName(),
'header' => AphrontRequest::getCSRFHeaderName(),
'current' => $current_token,
));
$pref_shortcut = PhabricatorUserPreferences::PREFERENCE_SEARCH_SHORTCUT;
if ($user) {
$shortcut = $user->loadPreferences()->getPreference($pref_shortcut, 1);
} else {
$shortcut = 1;
}
Javelin::initBehavior(
'phabricator-keyboard-shortcuts',
array(
'helpURI' => '/help/keyboardshortcut/',
'search_shortcut' => $shortcut,
));
if ($console) {
require_celerity_resource('aphront-dark-console-css');
Javelin::initBehavior(
'dark-console',
array(
'uri' => '/~/',
'request_uri' => $request ? (string) $request->getRequestURI() : '/',
));
// Change this to initBehavior when there is some behavior to initialize
require_celerity_resource('javelin-behavior-error-log');
}
$this->bodyContent = $this->renderChildren();
}
protected function getHead() {
$framebust = null;
if (!$this->isFrameable) {
$framebust = '(top != self) && top.location.replace(self.location.href);';
}
$response = CelerityAPI::getStaticResourceResponse();
$head =
''.
$response->renderResourcesOfType('css').
$response->renderSingleResource('javelin-magical-init');
$request = $this->getRequest();
if ($request) {
$user = $request->getUser();
if ($user) {
$monospaced = $user->loadPreferences()->getPreference(
PhabricatorUserPreferences::PREFERENCE_MONOSPACED
);
if (strlen($monospaced)) {
$head .=
'';
}
}
}
return $head;
}
public function setGlyph($glyph) {
$this->glyph = $glyph;
return $this;
}
public function getGlyph() {
return $this->glyph;
}
protected function willSendResponse($response) {
$console = $this->getRequest()->getApplicationConfiguration()->getConsole();
if ($console) {
$response = str_replace(
' ',
$console->render($this->getRequest()),
$response);
}
return $response;
}
protected function getBody() {
$console = $this->getConsole();
$tabs = array();
foreach ($this->tabs as $name => $tab) {
$tab_markup = phutil_render_tag(
'a',
array(
'href' => idx($tab, 'href'),
),
phutil_escape_html(idx($tab, 'name')));
$tab_markup = phutil_render_tag(
'td',
array(
'class' => ($name == $this->selectedTab)
? 'phabricator-selected-tab'
: null,
),
$tab_markup);
$tabs[] = $tab_markup;
}
$tabs = implode('', $tabs);
$login_stuff = null;
$request = $this->getRequest();
$user = null;
if ($request) {
$user = $request->getUser();
// NOTE: user may not be set here if we caught an exception early
// in the execution workflow.
if ($user && $user->getPHID()) {
$login_stuff =
phutil_render_tag(
'a',
array(
'href' => '/p/'.$user->getUsername().'/',
),
phutil_escape_html($user->getUsername())).
' · '.
'Settings'.
' · '.
phabricator_render_form(
$user,
array(
'action' => '/search/',
'method' => 'post',
'style' => 'display: inline',
),
''.
' in '.
AphrontFormSelectControl::renderSelectTag(
$this->getSearchDefaultScope(),
PhabricatorSearchScope::getScopeOptions(),
array(
'name' => 'scope',
)).
' '.
'');
}
}
$foot_links = array();
$version = PhabricatorEnv::getEnvConfig('phabricator.version');
$foot_links[] = phutil_escape_html('Phabricator '.$version);
if (PhabricatorEnv::getEnvConfig('darkconsole.enabled') &&
!PhabricatorEnv::getEnvConfig('darkconsole.always-on')) {
if ($console) {
$link = javelin_render_tag(
'a',
array(
'href' => '/~/',
'sigil' => 'workflow',
),
'Disable DarkConsole');
} else {
$link = javelin_render_tag(
'a',
array(
'href' => '/~/',
'sigil' => 'workflow',
),
'Enable DarkConsole');
}
$foot_links[] = $link;
}
if ($user && $user->getPHID()) {
// This ends up very early in tab order at the top of the page and there's
// a bunch of junk up there anyway, just shove it down here.
$foot_links[] = phabricator_render_form(
$user,
array(
'action' => '/logout/',
'method' => 'post',
'style' => 'display: inline',
),
'');
}
$foot_links = implode(' · ', $foot_links);
$admin_class = null;
if ($this->getIsAdminInterface()) {
$admin_class = 'phabricator-admin-page-view';
}
$custom_logo = null;
$with_custom = null;
$custom_conf = PhabricatorEnv::getEnvConfig('phabricator.custom.logo');
if ($custom_conf) {
$with_custom = 'phabricator-logo-with-custom';
$custom_logo = phutil_render_tag(
'a',
array(
'class' => 'logo-custom',
'href' => $custom_conf,
),
' ');
}
$notification_indicator = '';
$notification_dropdown = '';
$notification_container = '';
if (PhabricatorEnv::getEnvConfig('notification.enabled') &&
$user &&
$user->isLoggedIn()) {
$aphlict_object_id = 'aphlictswfobject';
- $server_uri = new PhutilURI(PhabricatorEnv::getURI(''));
- $server_domain = $server_uri->getDomain();
+ $client_uri = PhabricatorEnv::getEnvConfig('notification.client-uri');
+ $client_uri = new PhutilURI($client_uri);
+ if ($client_uri->getDomain() == 'localhost') {
+ $this_host = new PhutilURI($this->getRequest()->getHost());
+ $client_uri->setDomain($this_host->getDomain());
+ }
Javelin::initBehavior(
'aphlict-listen',
array(
'id' => $aphlict_object_id,
- 'server' => $server_domain,
- 'port' => 22280,
- 'pageObjects' => $this->pageObjects,
+ 'server' => $client_uri->getDomain(),
+ 'port' => $client_uri->getPort(),
+ 'pageObjects' => $this->pageObjects,
));
Javelin::initBehavior('aphlict-dropdown', array());
$notification_count = id(new PhabricatorFeedStoryNotification())
->countUnread($user);
$indicator_classes = array(
'phabricator-notification-indicator',
);
if ($notification_count) {
$indicator_classes[] = 'phabricator-notification-indicator-unread';
}
$notification_indicator = javelin_render_tag(
'div',
array(
'id' => 'phabricator-notification-indicator',
'class' => implode(' ', $indicator_classes),
),
$notification_count);
$notification_indicator = javelin_render_tag(
'div',
array(
'id' => 'phabricator-notification-menu',
'class' => 'phabricator-icon-menu icon-menu-notifications',
'sigil' => 'aphlict-indicator',
),
$notification_indicator);
$notification_indicator = javelin_render_tag(
'td',
array(
'class' => 'phabricator-icon-menu-cell',
),
$notification_indicator);
$notification_container =
''.
'';
$notification_dropdown =
javelin_render_tag(
'div',
array(
'sigil' => 'aphlict-dropdown',
'id' => 'phabricator-notification-dropdown',
'style' => 'display: none',
),
'');
}
$header_chrome = null;
$footer_chrome = null;
if ($this->getShowChrome()) {
$header_chrome =
''.
$foot_links.
'';
}
$developer_warning = null;
if (PhabricatorEnv::getEnvConfig('phabricator.show-error-callout') &&
DarkConsoleErrorLogPluginAPI::getErrors()) {
$developer_warning =
''.
'This page raised PHP errors. Find them in DarkConsole '.
'or the error log.'.
'';
}
return
($console ? ' ' : null).
$developer_warning.
''.
$header_chrome.
$this->bodyContent.
''.
''.
$footer_chrome;
}
protected function getTail() {
$response = CelerityAPI::getStaticResourceResponse();
return
$response->renderResourcesOfType('js').
$response->renderHTMLFooter();
}
protected function getBodyClasses() {
$classes = array();
if (!$this->getShowChrome()) {
$classes[] = 'phabricator-chromeless-page';
}
return implode(' ', $classes);
}
private function getConsole() {
if ($this->disableConsole) {
return null;
}
return $this->getRequest()->getApplicationConfiguration()->getConsole();
}
}
diff --git a/webroot/rsrc/css/application/base/standard-page-view.css b/webroot/rsrc/css/application/base/standard-page-view.css
index 7ff237ed4c..878c36b49b 100644
--- a/webroot/rsrc/css/application/base/standard-page-view.css
+++ b/webroot/rsrc/css/application/base/standard-page-view.css
@@ -1,308 +1,312 @@
/**
* @provides phabricator-standard-page-view
*/
.phabricator-standard-page {
background: #ffffff;
}
.phabricator-chromeless-page .phabricator-standard-page {
background: transparent;
border-width: 0px;
-webkit-box-shadow: none;
-mox-box-shadow: none;
box-shadow: none;
}
.phabricator-standard-header {
background: #005588;
color: white;
overflow: hidden;
position: relative;
width: 100%;
}
.phabricator-standard-header td {
vertical-align: bottom;
padding: 0;
margin: 0;
}
.phabricator-primary-navigation {
padding-top: 24px;
padding-left: 24px;
}
.phabricator-standard-header a {
color: white;
}
.phabricator-primary-navigation th,
.phabricator-primary-navigation td {
vertical-align: bottom;
font-size: 13px;
border-bottom: 6px solid transparent;
padding-top: 14px;
padding-bottom: 4px;
white-space: nowrap;
}
.phabricator-logo {
width: 220px;
}
.phabricator-logo-with-custom {
width: 440px;
}
.phabricator-logo a {
display: block;
width: 220px;
height: 40px;
padding: 0;
margin: 0;
}
.phabricator-logo a.logo-custom {
position: absolute;
background: url(/rsrc/image/custom/custom.png) no-repeat 0 0;
}
.phabricator-logo a.logo-standard {
background: url(/rsrc/image/phabricator_logo.png) no-repeat -220px 0;
}
.phabricator-admin-page-view .phabricator-logo a.logo-standard {
background-image: url(/rsrc/image/phabricator_logo_admin.png);
}
.phabricator-logo-with-custom a.logo-standard {
padding-left: 220px;
background-position: 0 0;
}
.phabricator-logo a.logo-standard:hover {
background-position: -220px -40px;
}
.phabricator-logo a.logo-custom:hover,
.phabricator-logo-with-custom a.logo-standard:hover {
background-position: 0 -40px;
}
.phabricator-primary-navigation td {
padding-left: 10px;
padding-right: 10px;
}
.phabricator-primary-navigation td.phabricator-selected-tab {
border-bottom-color: #ffffff;
background: #336699;
}
.phabricator-standard-header .phabricator-head-appname {
padding: 0 1em;
text-transform: uppercase;
}
td.phabricator-login-details {
text-align: right;
vertical-align: middle;
padding: 0px 24px;
font-size: 12px;
white-space: nowrap;
}
.phabricator-page-foot {
text-align: right;
margin: 2em;
border-top: 1px solid #afafaf;
padding: .5em 1em;
font-size: 11px;
color: #666666;
}
.phabricator-admin-page-view .phabricator-standard-header {
background: #aa0000;
}
.phabricator-admin-page-view td.phabricator-selected-tab {
background: #cc3333;
}
.keyboard-shortcut-help td,
.keyboard-shortcut-help th {
padding: 8px;
vertical-align: middle;
}
.keyboard-shortcut-help th {
white-space: nowrap;
color: #666666;
}
.keyboard-shortcut-help kbd {
background: #222222;
padding: 6px;
color: #ffffff;
font-weight: bold;
border: 1px solid #555555;
}
.keyboard-focus-focus-reticle {
z-index: 1;
background: #ffffd3;
position: absolute;
border: 1px solid #999900;
}
.keyboard-shortcuts-available {
height: 16px;
vertical-align: middle;
color: #666666;
text-align: right;
padding-right: 24px;
font-size: 11px;
background:
url('/rsrc/image/icon/fatcow/key_question.png') right center no-repeat;
}
.workflow-header {
background: #efefef;
padding: 6px 2em;
text-align: right;
margin-bottom: 6px;
border-bottom: 1px solid #dfdfdf;
}
.workflow-header button {
float: right;
}
.handle-status-closed {
text-decoration: line-through;
}
a.handle-disabled,
a.handle-status-away,
a.handle-status-sporadic {
padding-left: 12px;
background-repeat: no-repeat;
background-position: -4px center;
}
a.handle-status-away {
background-image: url(/rsrc/image/icon/fatcow/bullet_red.png);
}
a.handle-status-sporadic {
background-image: url(/rsrc/image/icon/fatcow/bullet_orange.png);
}
a.handle-disabled {
background-image: url(/rsrc/image/icon/fatcow/bullet_black.png);
}
.PhabricatorMonospaced {
font-family: "Menlo", "Consolas", "Monaco", monospace;
font-size: 10px;
}
.aphront-developer-error-callout {
padding: 2em;
background: #aa0000;
color: white;
text-align: center;
font-size: 11px;
font-family: "Verdana";
}
.buoyant {
position: fixed;
top: 0px;
left: 0px;
z-index: 8;
padding: 6px;
color: #dddddd;
font-size: 11px;
opacity: 0.90;
width: 100%;
background: #222222;
border-bottom: 1px solid #dfdfdf;
cursor: pointer;
}
.phabricator-icon-menu {
height: 40px;
width: 60px;
left: -22px;
position: relative;
cursor: pointer;
}
.phabricator-icon-menu-cell {
width: 60px;
}
.icon-menu-notifications {
background: url(/rsrc/image/notification_menu.png);
}
.phabricator-icon-menu:hover {
background-position: 0 -40px;
}
.phabricator-notification-indicator {
display: none;
}
.phabricator-notification-indicator-unread {
display: block;
position: absolute;
right: 8px;
top: 2px;
padding: 1px 3px;
background: #dd3333;
border: 1px solid #aa0000;
font-size: 11px;
box-shadow: 0px 0px 4px rgba(255, 255, 255, 0.75);
}
#phabricator-notification-dropdown {
position: absolute;
top: 40px;
border: 1px solid #99c4d7;
border-top-width: 0;
background: #fdfdff;
width: 360px;
box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.25);
font-size: 11px;
z-index: 3;
word-wrap: break-word;
overflow-y: auto;
}
.phabricator-notification {
padding: 6px 6px;
}
+.no-notifications {
+ color: #999999;
+}
+
.phabricator-notification-unread {
background: #aacfef;
}