1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2025-01-13 08:11:04 +01:00
phorge-phorge/src/view/page/PhabricatorStandardPageView.php

840 lines
22 KiB
PHP
Raw Normal View History

<?php
/**
* This is a standard Phabricator page with menus, Javelin, DarkConsole, and
* basic styles.
*/
Make mobile navigation work properly by default in more cases Summary: Fixes T5752. This obsoletes a bunch of old patterns and I'll follow up on those with a big "go do a bunch of mechanical code changes" task. Major goals are: - Don't load named queries multiple times on search pages. - Don't require extra code to get standard navigation right on mobile. - Reduce the amount of boilerplate in ListControllers. - Reduce the amount of boilerplate around navigation/menus in all controllers. Specifically, here's what this does: - The StandardPage is now a smarter/more structured object with `setNavigation()` and `setCrumbs()` methods. More rendering decisions are delayed until the last possible moment. - It uses this to automatically add crumb actions to the application menu. - It uses this to automatically reuse one SearchEngine instead of running queries multiple times. - The new preferred way to build responses is `$this->newPage()` (like `$this->newDialog()`), which has structured methods for adding stuff (`setTitle()`, etc). - SearchEngine exposes a new convenience method so you don't have to do all the controller delegation stuff. - Building menus is generally simpler. Test Plan: - Tested paste list, view, edit, comment, raw controllers for functionality, mobile menu, crumbs, navigation menu. - Edited saved queries. - Tested Differential, Maniphest (no changes). - Verified the paste pages don't run any duplicate NamedQuery queries. Reviewers: chad Reviewed By: chad Maniphest Tasks: T5752 Differential Revision: https://secure.phabricator.com/D14382
2015-11-02 21:06:28 +01:00
final class PhabricatorStandardPageView extends PhabricatorBarePageView
implements AphrontResponseProducerInterface {
private $baseURI;
private $applicationName;
private $glyph;
private $menuContent;
private $showChrome = true;
private $classes = array();
private $disableConsole;
private $pageObjects = array();
private $applicationMenu;
private $showFooter = true;
private $showDurableColumn = true;
Make mobile navigation work properly by default in more cases Summary: Fixes T5752. This obsoletes a bunch of old patterns and I'll follow up on those with a big "go do a bunch of mechanical code changes" task. Major goals are: - Don't load named queries multiple times on search pages. - Don't require extra code to get standard navigation right on mobile. - Reduce the amount of boilerplate in ListControllers. - Reduce the amount of boilerplate around navigation/menus in all controllers. Specifically, here's what this does: - The StandardPage is now a smarter/more structured object with `setNavigation()` and `setCrumbs()` methods. More rendering decisions are delayed until the last possible moment. - It uses this to automatically add crumb actions to the application menu. - It uses this to automatically reuse one SearchEngine instead of running queries multiple times. - The new preferred way to build responses is `$this->newPage()` (like `$this->newDialog()`), which has structured methods for adding stuff (`setTitle()`, etc). - SearchEngine exposes a new convenience method so you don't have to do all the controller delegation stuff. - Building menus is generally simpler. Test Plan: - Tested paste list, view, edit, comment, raw controllers for functionality, mobile menu, crumbs, navigation menu. - Edited saved queries. - Tested Differential, Maniphest (no changes). - Verified the paste pages don't run any duplicate NamedQuery queries. Reviewers: chad Reviewed By: chad Maniphest Tasks: T5752 Differential Revision: https://secure.phabricator.com/D14382
2015-11-02 21:06:28 +01:00
private $quicksandConfig = array();
private $crumbs;
private $navigation;
public function setShowFooter($show_footer) {
$this->showFooter = $show_footer;
return $this;
}
public function getShowFooter() {
return $this->showFooter;
}
Make mobile navigation work properly by default in more cases Summary: Fixes T5752. This obsoletes a bunch of old patterns and I'll follow up on those with a big "go do a bunch of mechanical code changes" task. Major goals are: - Don't load named queries multiple times on search pages. - Don't require extra code to get standard navigation right on mobile. - Reduce the amount of boilerplate in ListControllers. - Reduce the amount of boilerplate around navigation/menus in all controllers. Specifically, here's what this does: - The StandardPage is now a smarter/more structured object with `setNavigation()` and `setCrumbs()` methods. More rendering decisions are delayed until the last possible moment. - It uses this to automatically add crumb actions to the application menu. - It uses this to automatically reuse one SearchEngine instead of running queries multiple times. - The new preferred way to build responses is `$this->newPage()` (like `$this->newDialog()`), which has structured methods for adding stuff (`setTitle()`, etc). - SearchEngine exposes a new convenience method so you don't have to do all the controller delegation stuff. - Building menus is generally simpler. Test Plan: - Tested paste list, view, edit, comment, raw controllers for functionality, mobile menu, crumbs, navigation menu. - Edited saved queries. - Tested Differential, Maniphest (no changes). - Verified the paste pages don't run any duplicate NamedQuery queries. Reviewers: chad Reviewed By: chad Maniphest Tasks: T5752 Differential Revision: https://secure.phabricator.com/D14382
2015-11-02 21:06:28 +01:00
public function setApplicationMenu($application_menu) {
// NOTE: For now, this can either be a PHUIListView or a
// PHUIApplicationMenuView.
$this->applicationMenu = $application_menu;
return $this;
}
public function getApplicationMenu() {
return $this->applicationMenu;
}
public function setApplicationName($application_name) {
$this->applicationName = $application_name;
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 setShowChrome($show_chrome) {
$this->showChrome = $show_chrome;
return $this;
}
public function getShowChrome() {
return $this->showChrome;
}
public function addClass($class) {
$this->classes[] = $class;
return $this;
}
Make mobile navigation work properly by default in more cases Summary: Fixes T5752. This obsoletes a bunch of old patterns and I'll follow up on those with a big "go do a bunch of mechanical code changes" task. Major goals are: - Don't load named queries multiple times on search pages. - Don't require extra code to get standard navigation right on mobile. - Reduce the amount of boilerplate in ListControllers. - Reduce the amount of boilerplate around navigation/menus in all controllers. Specifically, here's what this does: - The StandardPage is now a smarter/more structured object with `setNavigation()` and `setCrumbs()` methods. More rendering decisions are delayed until the last possible moment. - It uses this to automatically add crumb actions to the application menu. - It uses this to automatically reuse one SearchEngine instead of running queries multiple times. - The new preferred way to build responses is `$this->newPage()` (like `$this->newDialog()`), which has structured methods for adding stuff (`setTitle()`, etc). - SearchEngine exposes a new convenience method so you don't have to do all the controller delegation stuff. - Building menus is generally simpler. Test Plan: - Tested paste list, view, edit, comment, raw controllers for functionality, mobile menu, crumbs, navigation menu. - Edited saved queries. - Tested Differential, Maniphest (no changes). - Verified the paste pages don't run any duplicate NamedQuery queries. Reviewers: chad Reviewed By: chad Maniphest Tasks: T5752 Differential Revision: https://secure.phabricator.com/D14382
2015-11-02 21:06:28 +01:00
public function setPageObjectPHIDs(array $phids) {
$this->pageObjects = $phids;
return $this;
}
public function setShowDurableColumn($show) {
$this->showDurableColumn = $show;
return $this;
}
public function getShowDurableColumn() {
Conpherence - make the durable column kind of work and stuff Summary: Ref T7014. This hooks up the durable column such that when you open it up it loads your most recent Conpherence. You can then switch amongst the various widgets and stuff and everything works nicely. Except... - scroll bar does not work - also doesn't work at HEAD when I add a ton of text to the UI with no changes? (wrapped $copy in array_fill(0, 1000, $copy)) - "widget selector" does not collapse when you select something else - this part wasn't really specified so I used the aphlict dropdown stuff. didn't want to keep working on that if this was the wrong UI choice - can not edit title - do we still want that to be done by clicking on the title, which pops a dialogue? - can not add participants or calendar events - what should this UI be? maybe just a button on the top for "participants" and a button on the bottom for calendar? both on top? - this is not pixel perfect to the mock or two I've seen around. Aside from generally being bad at that, I definitely didn't get the name + timestamps formatting correctly, because the standard DOM of that has timestamp FIRST which appears second due to a "float right". Seemed like a lot of special-casing for what might not even be that important in the UI so I punted. (And again, there's likely many unknown ways in which this isn't pixel perfect) There's also code quality issues - `ConpherenceWidgetConfigConstants` is hopefully temporary or at least gets more sleek as we keep progressing here - copied some CSS from main Conpherence app - DOM structure is pretty different - there's some minor CSS tweaks too given the different width (not to mention the DOM structure being different) - copied some JS from behavior-pontificate.js to sync threads relative to aphlict updates - JS in general is like a better version of existing JS; these should collapse I'd hope? - maybe the aphlict-behavior-dropdown change was badsauce? ...but all that said, this definitely feels really nice and I feel like adding stuff is going to be really easy compared to how normal Conpherence is. Also includes a bonus bug fix - we now correctly update participation. The user would encounter this issue if they were in a conpherence that got some updates and then they went to a different page; they would have unread status for the messages that were ajax'd in. This patch fixes that by making sure we mark participation up to date with the proper transaction in all cases. Test Plan: hit "\" to invoke the column and saw nice loading UI and my latest conpherence load. sent messages and verified they received A-OK by looking in DOM console. toggled various widges and verified they rendered correctly. opened up a second browser with a second user on the thread, sent a message, and it was received in a nice asynchronous fashion Reviewers: chad, epriestley Reviewed By: epriestley Subscribers: Korvin, epriestley Maniphest Tasks: T7014 Differential Revision: https://secure.phabricator.com/D11968
2015-03-05 19:33:39 +01:00
$request = $this->getRequest();
if (!$request) {
return false;
}
$viewer = $request->getUser();
if (!$viewer->isLoggedIn()) {
return false;
}
$conpherence_installed = PhabricatorApplication::isClassInstalledForViewer(
'PhabricatorConpherenceApplication',
$viewer);
if (!$conpherence_installed) {
return false;
}
if ($this->isQuicksandBlacklistURI()) {
return false;
}
return true;
}
private function isQuicksandBlacklistURI() {
$request = $this->getRequest();
if (!$request) {
return false;
}
$patterns = $this->getQuicksandURIPatternBlacklist();
$path = $request->getRequestURI()->getPath();
foreach ($patterns as $pattern) {
if (preg_match('(^'.$pattern.'$)', $path)) {
return true;
}
Conpherence - make the durable column kind of work and stuff Summary: Ref T7014. This hooks up the durable column such that when you open it up it loads your most recent Conpherence. You can then switch amongst the various widgets and stuff and everything works nicely. Except... - scroll bar does not work - also doesn't work at HEAD when I add a ton of text to the UI with no changes? (wrapped $copy in array_fill(0, 1000, $copy)) - "widget selector" does not collapse when you select something else - this part wasn't really specified so I used the aphlict dropdown stuff. didn't want to keep working on that if this was the wrong UI choice - can not edit title - do we still want that to be done by clicking on the title, which pops a dialogue? - can not add participants or calendar events - what should this UI be? maybe just a button on the top for "participants" and a button on the bottom for calendar? both on top? - this is not pixel perfect to the mock or two I've seen around. Aside from generally being bad at that, I definitely didn't get the name + timestamps formatting correctly, because the standard DOM of that has timestamp FIRST which appears second due to a "float right". Seemed like a lot of special-casing for what might not even be that important in the UI so I punted. (And again, there's likely many unknown ways in which this isn't pixel perfect) There's also code quality issues - `ConpherenceWidgetConfigConstants` is hopefully temporary or at least gets more sleek as we keep progressing here - copied some CSS from main Conpherence app - DOM structure is pretty different - there's some minor CSS tweaks too given the different width (not to mention the DOM structure being different) - copied some JS from behavior-pontificate.js to sync threads relative to aphlict updates - JS in general is like a better version of existing JS; these should collapse I'd hope? - maybe the aphlict-behavior-dropdown change was badsauce? ...but all that said, this definitely feels really nice and I feel like adding stuff is going to be really easy compared to how normal Conpherence is. Also includes a bonus bug fix - we now correctly update participation. The user would encounter this issue if they were in a conpherence that got some updates and then they went to a different page; they would have unread status for the messages that were ajax'd in. This patch fixes that by making sure we mark participation up to date with the proper transaction in all cases. Test Plan: hit "\" to invoke the column and saw nice loading UI and my latest conpherence load. sent messages and verified they received A-OK by looking in DOM console. toggled various widges and verified they rendered correctly. opened up a second browser with a second user on the thread, sent a message, and it was received in a nice asynchronous fashion Reviewers: chad, epriestley Reviewed By: epriestley Subscribers: Korvin, epriestley Maniphest Tasks: T7014 Differential Revision: https://secure.phabricator.com/D11968
2015-03-05 19:33:39 +01:00
}
return false;
}
public function getDurableColumnVisible() {
$column_key = PhabricatorUserPreferences::PREFERENCE_CONPHERENCE_COLUMN;
return (bool)$this->getUserPreference($column_key, 0);
}
Make mobile navigation work properly by default in more cases Summary: Fixes T5752. This obsoletes a bunch of old patterns and I'll follow up on those with a big "go do a bunch of mechanical code changes" task. Major goals are: - Don't load named queries multiple times on search pages. - Don't require extra code to get standard navigation right on mobile. - Reduce the amount of boilerplate in ListControllers. - Reduce the amount of boilerplate around navigation/menus in all controllers. Specifically, here's what this does: - The StandardPage is now a smarter/more structured object with `setNavigation()` and `setCrumbs()` methods. More rendering decisions are delayed until the last possible moment. - It uses this to automatically add crumb actions to the application menu. - It uses this to automatically reuse one SearchEngine instead of running queries multiple times. - The new preferred way to build responses is `$this->newPage()` (like `$this->newDialog()`), which has structured methods for adding stuff (`setTitle()`, etc). - SearchEngine exposes a new convenience method so you don't have to do all the controller delegation stuff. - Building menus is generally simpler. Test Plan: - Tested paste list, view, edit, comment, raw controllers for functionality, mobile menu, crumbs, navigation menu. - Edited saved queries. - Tested Differential, Maniphest (no changes). - Verified the paste pages don't run any duplicate NamedQuery queries. Reviewers: chad Reviewed By: chad Maniphest Tasks: T5752 Differential Revision: https://secure.phabricator.com/D14382
2015-11-02 21:06:28 +01:00
public function addQuicksandConfig(array $config) {
$this->quicksandConfig = $config + $this->quicksandConfig;
return $this;
}
public function getQuicksandConfig() {
return $this->quicksandConfig;
}
public function setCrumbs(PHUICrumbsView $crumbs) {
$this->crumbs = $crumbs;
return $this;
}
public function getCrumbs() {
return $this->crumbs;
}
public function setNavigation(AphrontSideNavFilterView $navigation) {
$this->navigation = $navigation;
return $this;
}
public function getNavigation() {
return $this->navigation;
}
public function getTitle() {
$glyph_key = PhabricatorUserPreferences::PREFERENCE_TITLES;
if ($this->getUserPreference($glyph_key) == 'text') {
$use_glyph = false;
} else {
$use_glyph = true;
}
$title = parent::getTitle();
$prefix = null;
if ($use_glyph) {
$prefix = $this->getGlyph();
} else {
$application_name = $this->getApplicationName();
if (strlen($application_name)) {
$prefix = '['.$application_name.']';
}
}
if (strlen($prefix)) {
$title = $prefix.' '.$title;
}
return $title;
}
2011-01-25 20:31:40 +01:00
protected function willRenderPage() {
parent::willRenderPage();
2011-02-02 22:48:52 +01:00
if (!$this->getRequest()) {
throw new Exception(
pht(
'You must set the %s to render a %s.',
'Request',
__CLASS__));
2011-02-02 22:48:52 +01:00
}
$console = $this->getConsole();
2011-02-02 22:48:52 +01:00
require_celerity_resource('phabricator-core-css');
require_celerity_resource('phabricator-zindex-css');
require_celerity_resource('phui-button-css');
require_celerity_resource('phui-spacing-css');
require_celerity_resource('phui-form-css');
2011-01-25 20:31:40 +01:00
require_celerity_resource('phabricator-standard-page-view');
require_celerity_resource('conpherence-durable-column-view');
require_celerity_resource('font-lato');
require_celerity_resource('font-aleo');
2011-01-25 20:31:40 +01:00
Javelin::initBehavior('workflow', array());
$request = $this->getRequest();
$user = null;
if ($request) {
$user = $request->getUser();
}
if ($user) {
$default_img_uri =
celerity_get_resource_uri(
'rsrc/image/icon/fatcow/document_black.png');
$download_form = phabricator_form(
$user,
array(
'action' => '#',
'method' => 'POST',
'class' => 'lightbox-download-form',
'sigil' => 'download',
),
phutil_tag(
'button',
array(),
pht('Download')));
Javelin::initBehavior(
'lightbox-attachments',
array(
'defaultImageUri' => $default_img_uri,
'downloadForm' => $download_form,
));
}
Javelin::initBehavior('aphront-form-disable-on-submit');
Javelin::initBehavior('toggle-class', array());
Javelin::initBehavior('history-install');
Add support for device swipe events Summary: Ref T2700. Allow JS to listen for swipes on devices. There are a bunch of tricky cases here and I probably didn't get them all totally right, but this interaction broadly looks like this: - We implement gesture recognition for the mouse in device modes (narrow browser), and for touch events from an actual device. - The sigil `touchable` indicates that a node wants to react to touch events. - When the user touches a `touchable` node, we start listening for moves. They might be tapping/clicking (in which case we don't care), but they might also be gesturing. - Once the user moves their finger/pointer far enough away from the tap origin, we recognize it as a gesture. I hardcoded this at 20px; I wasn't able to find any "official" Apple value, but 20px seems like a common default. - At this point, we look at where their finger has moved. - If they moved it mostly up/down, we interpret the gesture as "scroll" and just stop listening. The device does its own thing. - However, if they moved it mostly left/right, we interpret it as a "swipe". We start killing the moves so the device doesn't scroll. - Once we've recognized that a gesture is underway, we send a "gesture.swipe.start" event and then "gesture.swipe.move" events for every move. - When the user ends the gesture, we send "gesture.swipe.end". - If the user cancels the gesture (currently, only by tapping with a second finger), we send "gesture.swipe.cancel". - Gesture events have raw position data and some convenience fields. Test Plan: Wrote UI example and used it from the Desktop, iPhone simulator, and a real iphone. - The code always seems to get "scroll" vs "swipe" correct (i.e., consistent with my intentions). - The threshold feels pretty good to me. - Tapping with a second finger cancels the action. Reviewers: chad, btrahan Reviewed By: chad CC: aran Maniphest Tasks: T2700 Differential Revision: https://secure.phabricator.com/D5308
2013-03-09 22:53:15 +01:00
Javelin::initBehavior('phabricator-gesture');
$current_token = null;
if ($user) {
$current_token = $user->getCSRFToken();
}
Javelin::initBehavior(
'refresh-csrf',
array(
'tokenName' => AphrontRequest::getCSRFTokenName(),
When logged-out users hit a "Login Required" dialog, try to choose a better "next" URI Summary: Ref T10004. After a user logs in, we send them to the "next" URI cookie if there is one, but currently don't always do a very good job of selecting a "next" URI, especially if they tried to do something with a dialog before being asked to log in. In particular, if a logged-out user clicks an action like "Edit Blocking Tasks" on a Maniphest task, the default behavior is to send them to the standalone page for that dialog after they log in. This can be pretty confusing. See T2691 and D6416 for earlier efforts here. At that time, we added a mechanism to //manually// override the default behavior, and fixed the most common links. This worked, but I'd like to fix the //default// beahvior so we don't need to remember to `setObjectURI()` correctly all over the place. ApplicationEditor has also introduced new cases which are more difficult to get right. While we could get them right by using the override and being careful about things, this also motivates fixing the default behavior. Finally, we have better tools for fixing the default behavior now than we did in 2013. Instead of using manual overrides, have JS include an "X-Phabricator-Via" header in Ajax requests. This is basically like a referrer header, and will contain the page the user's browser is on. In essentially every case, this should be a very good place (and often the best place) to send them after login. For all pages currently using `setObjectURI()`, it should produce the same behavior by default. I'll remove the `setObjectURI()` mechanism in the next diff. Test Plan: Clicked various workflow actions while logged out, saw "next" get set to a reasonable value, was redirected to a sensible, non-confusing page after login (the page with whatever button I clicked on it). Reviewers: chad Reviewed By: chad Maniphest Tasks: T10004 Differential Revision: https://secure.phabricator.com/D14804
2015-12-17 15:10:04 +01:00
'header' => AphrontRequest::getCSRFHeaderName(),
'viaHeader' => AphrontRequest::getViaHeaderName(),
'current' => $current_token,
));
Javelin::initBehavior('device');
Javelin::initBehavior(
'high-security-warning',
$this->getHighSecurityWarningConfig());
2011-02-02 22:48:52 +01:00
if ($console) {
require_celerity_resource('aphront-dark-console-css');
$headers = array();
if (DarkConsoleXHProfPluginAPI::isProfilerStarted()) {
$headers[DarkConsoleXHProfPluginAPI::getProfilerHeader()] = 'page';
}
if (DarkConsoleServicesPlugin::isQueryAnalyzerRequested()) {
$headers[DarkConsoleServicesPlugin::getQueryAnalyzerHeader()] = true;
}
2011-02-02 22:48:52 +01:00
Javelin::initBehavior(
'dark-console',
$this->getConsoleConfig());
// Change this to initBehavior when there is some behavior to initialize
require_celerity_resource('javelin-behavior-error-log');
2011-02-02 22:48:52 +01:00
}
if ($user) {
$viewer = $user;
} else {
$viewer = new PhabricatorUser();
}
$menu = id(new PhabricatorMainMenuView())
->setUser($viewer);
if ($this->getController()) {
$menu->setController($this->getController());
}
Make mobile navigation work properly by default in more cases Summary: Fixes T5752. This obsoletes a bunch of old patterns and I'll follow up on those with a big "go do a bunch of mechanical code changes" task. Major goals are: - Don't load named queries multiple times on search pages. - Don't require extra code to get standard navigation right on mobile. - Reduce the amount of boilerplate in ListControllers. - Reduce the amount of boilerplate around navigation/menus in all controllers. Specifically, here's what this does: - The StandardPage is now a smarter/more structured object with `setNavigation()` and `setCrumbs()` methods. More rendering decisions are delayed until the last possible moment. - It uses this to automatically add crumb actions to the application menu. - It uses this to automatically reuse one SearchEngine instead of running queries multiple times. - The new preferred way to build responses is `$this->newPage()` (like `$this->newDialog()`), which has structured methods for adding stuff (`setTitle()`, etc). - SearchEngine exposes a new convenience method so you don't have to do all the controller delegation stuff. - Building menus is generally simpler. Test Plan: - Tested paste list, view, edit, comment, raw controllers for functionality, mobile menu, crumbs, navigation menu. - Edited saved queries. - Tested Differential, Maniphest (no changes). - Verified the paste pages don't run any duplicate NamedQuery queries. Reviewers: chad Reviewed By: chad Maniphest Tasks: T5752 Differential Revision: https://secure.phabricator.com/D14382
2015-11-02 21:06:28 +01:00
$application_menu = $this->getApplicationMenu();
if ($application_menu) {
if ($application_menu instanceof PHUIApplicationMenuView) {
$crumbs = $this->getCrumbs();
if ($crumbs) {
$application_menu->setCrumbs($crumbs);
}
$application_menu = $application_menu->buildListView();
}
$menu->setApplicationMenu($application_menu);
}
$this->menuContent = $menu->render();
}
protected function getHead() {
$monospaced = null;
$request = $this->getRequest();
if ($request) {
$user = $request->getUser();
if ($user) {
$monospaced = $user->loadPreferences()->getPreference(
PhabricatorUserPreferences::PREFERENCE_MONOSPACED);
}
}
$response = CelerityAPI::getStaticResourceResponse();
$font_css = null;
if (!empty($monospaced)) {
// We can't print this normally because escaping quotation marks will
// break the CSS. Instead, filter it strictly and then mark it as safe.
$monospaced = new PhutilSafeHTML(
PhabricatorUserPreferences::filterMonospacedCSSRule(
$monospaced));
$font_css = hsprintf(
'<style type="text/css">'.
'.PhabricatorMonospaced, '.
'.phabricator-remarkup .remarkup-code-block '.
'.remarkup-code { font: %s !important; } '.
'</style>',
$monospaced);
}
2013-02-13 23:50:15 +01:00
return hsprintf(
'%s%s%s',
parent::getHead(),
$font_css,
$response->renderSingleResource('javelin-magical-init', 'phabricator'));
}
public function setGlyph($glyph) {
$this->glyph = $glyph;
return $this;
}
public function getGlyph() {
return $this->glyph;
}
2011-02-02 22:48:52 +01:00
protected function willSendResponse($response) {
$request = $this->getRequest();
$response = parent::willSendResponse($response);
$console = $request->getApplicationConfiguration()->getConsole();
2011-02-02 22:48:52 +01:00
if ($console) {
2013-02-13 23:50:15 +01:00
$response = PhutilSafeHTML::applyFunction(
'str_replace',
hsprintf('<darkconsole />'),
$console->render($request),
2011-02-02 22:48:52 +01:00
$response);
}
2011-02-02 22:48:52 +01:00
return $response;
}
protected function getBody() {
$user = null;
$request = $this->getRequest();
2011-01-26 02:17:19 +01:00
if ($request) {
$user = $request->getUser();
2011-01-26 22:21:12 +01:00
}
$header_chrome = null;
if ($this->getShowChrome()) {
$header_chrome = $this->menuContent;
}
$classes = array();
$classes[] = 'main-page-frame';
$developer_warning = null;
if (PhabricatorEnv::getEnvConfig('phabricator.developer-mode') &&
DarkConsoleErrorLogPluginAPI::getErrors()) {
$developer_warning = phutil_tag_div(
'aphront-developer-error-callout',
pht(
'This page raised PHP errors. Find them in DarkConsole '.
'or the error log.'));
}
// Render the "you have unresolved setup issues..." warning.
$setup_warning = null;
if ($user && $user->getIsAdmin()) {
$open = PhabricatorSetupCheck::getOpenSetupIssueKeys();
if ($open) {
$classes[] = 'page-has-warning';
$setup_warning = phutil_tag_div(
'setup-warning-callout',
phutil_tag(
'a',
array(
'href' => '/config/issue/',
'title' => implode(', ', $open),
),
pht('You have %d unresolved setup issue(s)...', count($open))));
}
}
$main_page = phutil_tag(
'div',
array(
'id' => 'phabricator-standard-page',
'class' => 'phabricator-standard-page',
),
array(
$developer_warning,
$header_chrome,
$setup_warning,
Quicksand, an ignoble successor to Quickling Summary: Ref T2086. Ref T7014. With the persistent column, there is significant value in retaining chrome state through navigation events, because the user may have a lot of state in the chat window (scroll position, text selection, room juggling, partially entered text, etc). We can do this by capturing navigation events and faking them with Javascript. (This can also improve performance, albeit slightly, and I believe there are better approaches to tackle performance any problems which exist with the chrome in many cases). At Facebook, this system was "Photostream" in photos and then "Quickling" in general, and the technical cost of the system was //staggering//. I am loathe to pursue it again. However: - Browsers are less junky now, and we target a smaller set of browsers. A large part of the technical cost of Quickling was the high complexity of emulating nagivation events in IE, where we needed to navigate a hidden iframe to make history entries. All desktop browsers which we might want to use this system on support the History API (although this prototype does not yet implement it). - Javelin and Phabricator's architecture are much cleaner than Facebook's was. A large part of the technical cost of Quickling was inconsistency, inlined `onclick` handlers, and general lack of coordination and abstraction. We will have //some// of this, but "correctly written" behaviors are mostly immune to it by design, and many of Javelin's architectural decisions were influenced by desire to avoid issues we encountered building this stuff for Facebook. - Some of the primitives which Quickling required (like loading resources over Ajax) have existed in a stable state in our codebase for a year or more, and adoption of these primitives was trivial and uneventful (vs a huge production at Facebook). - My hubris is bolstered by recent success with WebSockets and JX.Scrollbar, both of which I would have assessed as infeasibly complex to develop in this project a few years ago. To these points, the developer cost to prototype Photostream was several weeks; the developer cost to prototype this was a bit less than an hour. It is plausible to me that implementing and maintaining this system really will be hundreds of times less complex than it was at Facebook. Test Plan: My plan for this and D11497 is: - Get them in master. - Some secret key / relatively-hidden preference activates the column. - Quicksand activates //only// when the column is open. - We can use column + quicksand for a long period of time (i.e., over the course of Conpherence v2 development) and hammer out the long tail of issues. - When it derps up, you just hide the column and you're good to go. Reviewers: btrahan, chad Reviewed By: chad Subscribers: epriestley Maniphest Tasks: T2086, T7014 Differential Revision: https://secure.phabricator.com/D11507
2015-01-27 23:52:09 +01:00
phutil_tag(
'div',
array(
'id' => 'phabricator-standard-page-body',
'class' => 'phabricator-standard-page-body',
),
$this->renderPageBodyContent()),
));
$durable_column = null;
if ($this->getShowDurableColumn()) {
$is_visible = $this->getDurableColumnVisible();
Conpherence - make the durable column kind of work and stuff Summary: Ref T7014. This hooks up the durable column such that when you open it up it loads your most recent Conpherence. You can then switch amongst the various widgets and stuff and everything works nicely. Except... - scroll bar does not work - also doesn't work at HEAD when I add a ton of text to the UI with no changes? (wrapped $copy in array_fill(0, 1000, $copy)) - "widget selector" does not collapse when you select something else - this part wasn't really specified so I used the aphlict dropdown stuff. didn't want to keep working on that if this was the wrong UI choice - can not edit title - do we still want that to be done by clicking on the title, which pops a dialogue? - can not add participants or calendar events - what should this UI be? maybe just a button on the top for "participants" and a button on the bottom for calendar? both on top? - this is not pixel perfect to the mock or two I've seen around. Aside from generally being bad at that, I definitely didn't get the name + timestamps formatting correctly, because the standard DOM of that has timestamp FIRST which appears second due to a "float right". Seemed like a lot of special-casing for what might not even be that important in the UI so I punted. (And again, there's likely many unknown ways in which this isn't pixel perfect) There's also code quality issues - `ConpherenceWidgetConfigConstants` is hopefully temporary or at least gets more sleek as we keep progressing here - copied some CSS from main Conpherence app - DOM structure is pretty different - there's some minor CSS tweaks too given the different width (not to mention the DOM structure being different) - copied some JS from behavior-pontificate.js to sync threads relative to aphlict updates - JS in general is like a better version of existing JS; these should collapse I'd hope? - maybe the aphlict-behavior-dropdown change was badsauce? ...but all that said, this definitely feels really nice and I feel like adding stuff is going to be really easy compared to how normal Conpherence is. Also includes a bonus bug fix - we now correctly update participation. The user would encounter this issue if they were in a conpherence that got some updates and then they went to a different page; they would have unread status for the messages that were ajax'd in. This patch fixes that by making sure we mark participation up to date with the proper transaction in all cases. Test Plan: hit "\" to invoke the column and saw nice loading UI and my latest conpherence load. sent messages and verified they received A-OK by looking in DOM console. toggled various widges and verified they rendered correctly. opened up a second browser with a second user on the thread, sent a message, and it was received in a nice asynchronous fashion Reviewers: chad, epriestley Reviewed By: epriestley Subscribers: Korvin, epriestley Maniphest Tasks: T7014 Differential Revision: https://secure.phabricator.com/D11968
2015-03-05 19:33:39 +01:00
$durable_column = id(new ConpherenceDurableColumnView())
->setSelectedConpherence(null)
->setUser($user)
->setQuicksandConfig($this->buildQuicksandConfig())
->setVisible($is_visible)
->setInitialLoad(true);
}
Javelin::initBehavior('quicksand-blacklist', array(
'patterns' => $this->getQuicksandURIPatternBlacklist(),
));
return phutil_tag(
'div',
array(
'class' => implode(' ', $classes),
),
array(
$main_page,
$durable_column,
));
}
Quicksand, an ignoble successor to Quickling Summary: Ref T2086. Ref T7014. With the persistent column, there is significant value in retaining chrome state through navigation events, because the user may have a lot of state in the chat window (scroll position, text selection, room juggling, partially entered text, etc). We can do this by capturing navigation events and faking them with Javascript. (This can also improve performance, albeit slightly, and I believe there are better approaches to tackle performance any problems which exist with the chrome in many cases). At Facebook, this system was "Photostream" in photos and then "Quickling" in general, and the technical cost of the system was //staggering//. I am loathe to pursue it again. However: - Browsers are less junky now, and we target a smaller set of browsers. A large part of the technical cost of Quickling was the high complexity of emulating nagivation events in IE, where we needed to navigate a hidden iframe to make history entries. All desktop browsers which we might want to use this system on support the History API (although this prototype does not yet implement it). - Javelin and Phabricator's architecture are much cleaner than Facebook's was. A large part of the technical cost of Quickling was inconsistency, inlined `onclick` handlers, and general lack of coordination and abstraction. We will have //some// of this, but "correctly written" behaviors are mostly immune to it by design, and many of Javelin's architectural decisions were influenced by desire to avoid issues we encountered building this stuff for Facebook. - Some of the primitives which Quickling required (like loading resources over Ajax) have existed in a stable state in our codebase for a year or more, and adoption of these primitives was trivial and uneventful (vs a huge production at Facebook). - My hubris is bolstered by recent success with WebSockets and JX.Scrollbar, both of which I would have assessed as infeasibly complex to develop in this project a few years ago. To these points, the developer cost to prototype Photostream was several weeks; the developer cost to prototype this was a bit less than an hour. It is plausible to me that implementing and maintaining this system really will be hundreds of times less complex than it was at Facebook. Test Plan: My plan for this and D11497 is: - Get them in master. - Some secret key / relatively-hidden preference activates the column. - Quicksand activates //only// when the column is open. - We can use column + quicksand for a long period of time (i.e., over the course of Conpherence v2 development) and hammer out the long tail of issues. - When it derps up, you just hide the column and you're good to go. Reviewers: btrahan, chad Reviewed By: chad Subscribers: epriestley Maniphest Tasks: T2086, T7014 Differential Revision: https://secure.phabricator.com/D11507
2015-01-27 23:52:09 +01:00
private function renderPageBodyContent() {
$console = $this->getConsole();
Make mobile navigation work properly by default in more cases Summary: Fixes T5752. This obsoletes a bunch of old patterns and I'll follow up on those with a big "go do a bunch of mechanical code changes" task. Major goals are: - Don't load named queries multiple times on search pages. - Don't require extra code to get standard navigation right on mobile. - Reduce the amount of boilerplate in ListControllers. - Reduce the amount of boilerplate around navigation/menus in all controllers. Specifically, here's what this does: - The StandardPage is now a smarter/more structured object with `setNavigation()` and `setCrumbs()` methods. More rendering decisions are delayed until the last possible moment. - It uses this to automatically add crumb actions to the application menu. - It uses this to automatically reuse one SearchEngine instead of running queries multiple times. - The new preferred way to build responses is `$this->newPage()` (like `$this->newDialog()`), which has structured methods for adding stuff (`setTitle()`, etc). - SearchEngine exposes a new convenience method so you don't have to do all the controller delegation stuff. - Building menus is generally simpler. Test Plan: - Tested paste list, view, edit, comment, raw controllers for functionality, mobile menu, crumbs, navigation menu. - Edited saved queries. - Tested Differential, Maniphest (no changes). - Verified the paste pages don't run any duplicate NamedQuery queries. Reviewers: chad Reviewed By: chad Maniphest Tasks: T5752 Differential Revision: https://secure.phabricator.com/D14382
2015-11-02 21:06:28 +01:00
$body = parent::getBody();
$footer = $this->renderFooter();
Make mobile navigation work properly by default in more cases Summary: Fixes T5752. This obsoletes a bunch of old patterns and I'll follow up on those with a big "go do a bunch of mechanical code changes" task. Major goals are: - Don't load named queries multiple times on search pages. - Don't require extra code to get standard navigation right on mobile. - Reduce the amount of boilerplate in ListControllers. - Reduce the amount of boilerplate around navigation/menus in all controllers. Specifically, here's what this does: - The StandardPage is now a smarter/more structured object with `setNavigation()` and `setCrumbs()` methods. More rendering decisions are delayed until the last possible moment. - It uses this to automatically add crumb actions to the application menu. - It uses this to automatically reuse one SearchEngine instead of running queries multiple times. - The new preferred way to build responses is `$this->newPage()` (like `$this->newDialog()`), which has structured methods for adding stuff (`setTitle()`, etc). - SearchEngine exposes a new convenience method so you don't have to do all the controller delegation stuff. - Building menus is generally simpler. Test Plan: - Tested paste list, view, edit, comment, raw controllers for functionality, mobile menu, crumbs, navigation menu. - Edited saved queries. - Tested Differential, Maniphest (no changes). - Verified the paste pages don't run any duplicate NamedQuery queries. Reviewers: chad Reviewed By: chad Maniphest Tasks: T5752 Differential Revision: https://secure.phabricator.com/D14382
2015-11-02 21:06:28 +01:00
$nav = $this->getNavigation();
if ($nav) {
$crumbs = $this->getCrumbs();
if ($crumbs) {
$nav->setCrumbs($crumbs);
}
$nav->appendChild($body);
$nav->appendFooter($footer);
$content = phutil_implode_html('', array($nav->render()));
Make mobile navigation work properly by default in more cases Summary: Fixes T5752. This obsoletes a bunch of old patterns and I'll follow up on those with a big "go do a bunch of mechanical code changes" task. Major goals are: - Don't load named queries multiple times on search pages. - Don't require extra code to get standard navigation right on mobile. - Reduce the amount of boilerplate in ListControllers. - Reduce the amount of boilerplate around navigation/menus in all controllers. Specifically, here's what this does: - The StandardPage is now a smarter/more structured object with `setNavigation()` and `setCrumbs()` methods. More rendering decisions are delayed until the last possible moment. - It uses this to automatically add crumb actions to the application menu. - It uses this to automatically reuse one SearchEngine instead of running queries multiple times. - The new preferred way to build responses is `$this->newPage()` (like `$this->newDialog()`), which has structured methods for adding stuff (`setTitle()`, etc). - SearchEngine exposes a new convenience method so you don't have to do all the controller delegation stuff. - Building menus is generally simpler. Test Plan: - Tested paste list, view, edit, comment, raw controllers for functionality, mobile menu, crumbs, navigation menu. - Edited saved queries. - Tested Differential, Maniphest (no changes). - Verified the paste pages don't run any duplicate NamedQuery queries. Reviewers: chad Reviewed By: chad Maniphest Tasks: T5752 Differential Revision: https://secure.phabricator.com/D14382
2015-11-02 21:06:28 +01:00
} else {
$contnet = array();
Make mobile navigation work properly by default in more cases Summary: Fixes T5752. This obsoletes a bunch of old patterns and I'll follow up on those with a big "go do a bunch of mechanical code changes" task. Major goals are: - Don't load named queries multiple times on search pages. - Don't require extra code to get standard navigation right on mobile. - Reduce the amount of boilerplate in ListControllers. - Reduce the amount of boilerplate around navigation/menus in all controllers. Specifically, here's what this does: - The StandardPage is now a smarter/more structured object with `setNavigation()` and `setCrumbs()` methods. More rendering decisions are delayed until the last possible moment. - It uses this to automatically add crumb actions to the application menu. - It uses this to automatically reuse one SearchEngine instead of running queries multiple times. - The new preferred way to build responses is `$this->newPage()` (like `$this->newDialog()`), which has structured methods for adding stuff (`setTitle()`, etc). - SearchEngine exposes a new convenience method so you don't have to do all the controller delegation stuff. - Building menus is generally simpler. Test Plan: - Tested paste list, view, edit, comment, raw controllers for functionality, mobile menu, crumbs, navigation menu. - Edited saved queries. - Tested Differential, Maniphest (no changes). - Verified the paste pages don't run any duplicate NamedQuery queries. Reviewers: chad Reviewed By: chad Maniphest Tasks: T5752 Differential Revision: https://secure.phabricator.com/D14382
2015-11-02 21:06:28 +01:00
$crumbs = $this->getCrumbs();
if ($crumbs) {
$content[] = $crumbs;
Make mobile navigation work properly by default in more cases Summary: Fixes T5752. This obsoletes a bunch of old patterns and I'll follow up on those with a big "go do a bunch of mechanical code changes" task. Major goals are: - Don't load named queries multiple times on search pages. - Don't require extra code to get standard navigation right on mobile. - Reduce the amount of boilerplate in ListControllers. - Reduce the amount of boilerplate around navigation/menus in all controllers. Specifically, here's what this does: - The StandardPage is now a smarter/more structured object with `setNavigation()` and `setCrumbs()` methods. More rendering decisions are delayed until the last possible moment. - It uses this to automatically add crumb actions to the application menu. - It uses this to automatically reuse one SearchEngine instead of running queries multiple times. - The new preferred way to build responses is `$this->newPage()` (like `$this->newDialog()`), which has structured methods for adding stuff (`setTitle()`, etc). - SearchEngine exposes a new convenience method so you don't have to do all the controller delegation stuff. - Building menus is generally simpler. Test Plan: - Tested paste list, view, edit, comment, raw controllers for functionality, mobile menu, crumbs, navigation menu. - Edited saved queries. - Tested Differential, Maniphest (no changes). - Verified the paste pages don't run any duplicate NamedQuery queries. Reviewers: chad Reviewed By: chad Maniphest Tasks: T5752 Differential Revision: https://secure.phabricator.com/D14382
2015-11-02 21:06:28 +01:00
}
$content[] = $body;
$content[] = $footer;
$content = phutil_implode_html('', $content);
Make mobile navigation work properly by default in more cases Summary: Fixes T5752. This obsoletes a bunch of old patterns and I'll follow up on those with a big "go do a bunch of mechanical code changes" task. Major goals are: - Don't load named queries multiple times on search pages. - Don't require extra code to get standard navigation right on mobile. - Reduce the amount of boilerplate in ListControllers. - Reduce the amount of boilerplate around navigation/menus in all controllers. Specifically, here's what this does: - The StandardPage is now a smarter/more structured object with `setNavigation()` and `setCrumbs()` methods. More rendering decisions are delayed until the last possible moment. - It uses this to automatically add crumb actions to the application menu. - It uses this to automatically reuse one SearchEngine instead of running queries multiple times. - The new preferred way to build responses is `$this->newPage()` (like `$this->newDialog()`), which has structured methods for adding stuff (`setTitle()`, etc). - SearchEngine exposes a new convenience method so you don't have to do all the controller delegation stuff. - Building menus is generally simpler. Test Plan: - Tested paste list, view, edit, comment, raw controllers for functionality, mobile menu, crumbs, navigation menu. - Edited saved queries. - Tested Differential, Maniphest (no changes). - Verified the paste pages don't run any duplicate NamedQuery queries. Reviewers: chad Reviewed By: chad Maniphest Tasks: T5752 Differential Revision: https://secure.phabricator.com/D14382
2015-11-02 21:06:28 +01:00
}
Quicksand, an ignoble successor to Quickling Summary: Ref T2086. Ref T7014. With the persistent column, there is significant value in retaining chrome state through navigation events, because the user may have a lot of state in the chat window (scroll position, text selection, room juggling, partially entered text, etc). We can do this by capturing navigation events and faking them with Javascript. (This can also improve performance, albeit slightly, and I believe there are better approaches to tackle performance any problems which exist with the chrome in many cases). At Facebook, this system was "Photostream" in photos and then "Quickling" in general, and the technical cost of the system was //staggering//. I am loathe to pursue it again. However: - Browsers are less junky now, and we target a smaller set of browsers. A large part of the technical cost of Quickling was the high complexity of emulating nagivation events in IE, where we needed to navigate a hidden iframe to make history entries. All desktop browsers which we might want to use this system on support the History API (although this prototype does not yet implement it). - Javelin and Phabricator's architecture are much cleaner than Facebook's was. A large part of the technical cost of Quickling was inconsistency, inlined `onclick` handlers, and general lack of coordination and abstraction. We will have //some// of this, but "correctly written" behaviors are mostly immune to it by design, and many of Javelin's architectural decisions were influenced by desire to avoid issues we encountered building this stuff for Facebook. - Some of the primitives which Quickling required (like loading resources over Ajax) have existed in a stable state in our codebase for a year or more, and adoption of these primitives was trivial and uneventful (vs a huge production at Facebook). - My hubris is bolstered by recent success with WebSockets and JX.Scrollbar, both of which I would have assessed as infeasibly complex to develop in this project a few years ago. To these points, the developer cost to prototype Photostream was several weeks; the developer cost to prototype this was a bit less than an hour. It is plausible to me that implementing and maintaining this system really will be hundreds of times less complex than it was at Facebook. Test Plan: My plan for this and D11497 is: - Get them in master. - Some secret key / relatively-hidden preference activates the column. - Quicksand activates //only// when the column is open. - We can use column + quicksand for a long period of time (i.e., over the course of Conpherence v2 development) and hammer out the long tail of issues. - When it derps up, you just hide the column and you're good to go. Reviewers: btrahan, chad Reviewed By: chad Subscribers: epriestley Maniphest Tasks: T2086, T7014 Differential Revision: https://secure.phabricator.com/D11507
2015-01-27 23:52:09 +01:00
return array(
($console ? hsprintf('<darkconsole />') : null),
$content,
Quicksand, an ignoble successor to Quickling Summary: Ref T2086. Ref T7014. With the persistent column, there is significant value in retaining chrome state through navigation events, because the user may have a lot of state in the chat window (scroll position, text selection, room juggling, partially entered text, etc). We can do this by capturing navigation events and faking them with Javascript. (This can also improve performance, albeit slightly, and I believe there are better approaches to tackle performance any problems which exist with the chrome in many cases). At Facebook, this system was "Photostream" in photos and then "Quickling" in general, and the technical cost of the system was //staggering//. I am loathe to pursue it again. However: - Browsers are less junky now, and we target a smaller set of browsers. A large part of the technical cost of Quickling was the high complexity of emulating nagivation events in IE, where we needed to navigate a hidden iframe to make history entries. All desktop browsers which we might want to use this system on support the History API (although this prototype does not yet implement it). - Javelin and Phabricator's architecture are much cleaner than Facebook's was. A large part of the technical cost of Quickling was inconsistency, inlined `onclick` handlers, and general lack of coordination and abstraction. We will have //some// of this, but "correctly written" behaviors are mostly immune to it by design, and many of Javelin's architectural decisions were influenced by desire to avoid issues we encountered building this stuff for Facebook. - Some of the primitives which Quickling required (like loading resources over Ajax) have existed in a stable state in our codebase for a year or more, and adoption of these primitives was trivial and uneventful (vs a huge production at Facebook). - My hubris is bolstered by recent success with WebSockets and JX.Scrollbar, both of which I would have assessed as infeasibly complex to develop in this project a few years ago. To these points, the developer cost to prototype Photostream was several weeks; the developer cost to prototype this was a bit less than an hour. It is plausible to me that implementing and maintaining this system really will be hundreds of times less complex than it was at Facebook. Test Plan: My plan for this and D11497 is: - Get them in master. - Some secret key / relatively-hidden preference activates the column. - Quicksand activates //only// when the column is open. - We can use column + quicksand for a long period of time (i.e., over the course of Conpherence v2 development) and hammer out the long tail of issues. - When it derps up, you just hide the column and you're good to go. Reviewers: btrahan, chad Reviewed By: chad Subscribers: epriestley Maniphest Tasks: T2086, T7014 Differential Revision: https://secure.phabricator.com/D11507
2015-01-27 23:52:09 +01:00
);
}
protected function getTail() {
$request = $this->getRequest();
$user = $request->getUser();
$tail = array(
parent::getTail(),
);
$response = CelerityAPI::getStaticResourceResponse();
if (PhabricatorEnv::getEnvConfig('notification.enabled')) {
if ($user && $user->isLoggedIn()) {
$client_uri = PhabricatorEnv::getEnvConfig('notification.client-uri');
$client_uri = new PhutilURI($client_uri);
if ($client_uri->getDomain() == 'localhost') {
$this_host = $this->getRequest()->getHost();
$this_host = new PhutilURI('http://'.$this_host.'/');
$client_uri->setDomain($this_host->getDomain());
}
if ($request->isHTTPS()) {
$client_uri->setProtocol('wss');
} else {
$client_uri->setProtocol('ws');
}
Javelin::initBehavior(
'aphlict-listen',
array(
'websocketURI' => (string)$client_uri,
) + $this->buildAphlictListenConfigData());
}
}
$tail[] = $response->renderHTMLFooter();
return $tail;
}
protected function getBodyClasses() {
$classes = array();
if (!$this->getShowChrome()) {
$classes[] = 'phabricator-chromeless-page';
}
$agent = AphrontRequest::getHTTPHeader('User-Agent');
// Try to guess the device resolution based on UA strings to avoid a flash
// of incorrectly-styled content.
$device_guess = 'device-desktop';
if (preg_match('@iPhone|iPod|(Android.*Chrome/[.0-9]* Mobile)@', $agent)) {
$device_guess = 'device-phone device';
} else if (preg_match('@iPad|(Android.*Chrome/)@', $agent)) {
$device_guess = 'device-tablet device';
}
$classes[] = $device_guess;
if (preg_match('@Windows@', $agent)) {
$classes[] = 'platform-windows';
} else if (preg_match('@Macintosh@', $agent)) {
$classes[] = 'platform-mac';
} else if (preg_match('@X11@', $agent)) {
$classes[] = 'platform-linux';
}
if ($this->getRequest()->getStr('__print__')) {
$classes[] = 'printable';
}
if ($this->getRequest()->getStr('__aural__')) {
$classes[] = 'audible';
}
$classes[] = 'phui-theme-'.PhabricatorEnv::getEnvConfig('ui.header-color');
foreach ($this->classes as $class) {
$classes[] = $class;
}
return implode(' ', $classes);
}
private function getConsole() {
if ($this->disableConsole) {
return null;
}
return $this->getRequest()->getApplicationConfiguration()->getConsole();
}
private function getConsoleConfig() {
$user = $this->getRequest()->getUser();
$headers = array();
if (DarkConsoleXHProfPluginAPI::isProfilerStarted()) {
$headers[DarkConsoleXHProfPluginAPI::getProfilerHeader()] = 'page';
}
if (DarkConsoleServicesPlugin::isQueryAnalyzerRequested()) {
$headers[DarkConsoleServicesPlugin::getQueryAnalyzerHeader()] = true;
}
return array(
// NOTE: We use a generic label here to prevent input reflection
// and mitigate compression attacks like BREACH. See discussion in
// T3684.
'uri' => pht('Main Request'),
'selected' => $user ? $user->getConsoleTab() : null,
'visible' => $user ? (int)$user->getConsoleVisible() : true,
'headers' => $headers,
);
}
private function getHighSecurityWarningConfig() {
$user = $this->getRequest()->getUser();
$show = false;
if ($user->hasSession()) {
$hisec = ($user->getSession()->getHighSecurityUntil() - time());
if ($hisec > 0) {
$show = true;
}
}
return array(
'show' => $show,
'uri' => '/auth/session/downgrade/',
'message' => pht(
'Your session is in high security mode. When you '.
'finish using it, click here to leave.'),
);
}
private function renderFooter() {
if (!$this->getShowChrome()) {
return null;
}
if (!$this->getShowFooter()) {
return null;
}
$items = PhabricatorEnv::getEnvConfig('ui.footer-items');
if (!$items) {
return null;
}
$foot = array();
foreach ($items as $item) {
$name = idx($item, 'name', pht('Unnamed Footer Item'));
$href = idx($item, 'href');
Improve granluarity and defaults of `security.allow-outbound-http` Summary: Ref T6755. This is a partial fix, but: - Allow netblocks to be blacklisted instead of making the feature all-or-nothing. - Default to disallow requests to all reserved private/local/special IP blocks. This should generally be a "safe" setting. - Explain the risks better. - Improve the errors rasied by Macro when failing. - Removed `security.allow-outbound-http`, as it is superseded by this setting and is somewhat misleading. - We still make outbound HTTP requests to OAuth. - We still make outbound HTTP requests for repositories. From a technical perspective: - Separate URIs that are safe to link to or redirect to (basically, not "javascript://") from URIs that are safe to fetch (nothing in a private block). - Add the default blacklist. - Be more careful with response data in Macro fetching, and don't let the user see it if it isn't ultimately valid. Additionally: - I want to do this check before pulling repositories, but that's enough of a mess that it should go in a separate diff. - The future implementation of T4190 needs to perform the fetch check. Test Plan: - Fetched a valid macro. - Fetched a non-image, verified it didn't result in a viewable file. - Fetched a private-ip-space image, got an error. - Fetched a 404, got a useful-enough error without additional revealing response content (which is usually HTML anyway and not useful). - Fetched a bad protocol, got an error. - Linked to a local resource, a phriction page, a valid remote site, all worked. - Linked to private IP space, which worked fine (we want to let you link and redierect to other private services, just not fetch them). - Added and executed unit tests. Reviewers: btrahan Reviewed By: btrahan Subscribers: epriestley Maniphest Tasks: T6755 Differential Revision: https://secure.phabricator.com/D12136
2015-03-23 18:44:03 +01:00
if (!PhabricatorEnv::isValidURIForLink($href)) {
$href = null;
}
if ($href !== null) {
$tag = 'a';
} else {
$tag = 'span';
}
$foot[] = phutil_tag(
$tag,
array(
'href' => $href,
),
$name);
}
$foot = phutil_implode_html(" \xC2\xB7 ", $foot);
return phutil_tag(
'div',
array(
'class' => 'phabricator-standard-page-footer grouped',
),
$foot);
}
Make mobile navigation work properly by default in more cases Summary: Fixes T5752. This obsoletes a bunch of old patterns and I'll follow up on those with a big "go do a bunch of mechanical code changes" task. Major goals are: - Don't load named queries multiple times on search pages. - Don't require extra code to get standard navigation right on mobile. - Reduce the amount of boilerplate in ListControllers. - Reduce the amount of boilerplate around navigation/menus in all controllers. Specifically, here's what this does: - The StandardPage is now a smarter/more structured object with `setNavigation()` and `setCrumbs()` methods. More rendering decisions are delayed until the last possible moment. - It uses this to automatically add crumb actions to the application menu. - It uses this to automatically reuse one SearchEngine instead of running queries multiple times. - The new preferred way to build responses is `$this->newPage()` (like `$this->newDialog()`), which has structured methods for adding stuff (`setTitle()`, etc). - SearchEngine exposes a new convenience method so you don't have to do all the controller delegation stuff. - Building menus is generally simpler. Test Plan: - Tested paste list, view, edit, comment, raw controllers for functionality, mobile menu, crumbs, navigation menu. - Edited saved queries. - Tested Differential, Maniphest (no changes). - Verified the paste pages don't run any duplicate NamedQuery queries. Reviewers: chad Reviewed By: chad Maniphest Tasks: T5752 Differential Revision: https://secure.phabricator.com/D14382
2015-11-02 21:06:28 +01:00
public function renderForQuicksand() {
parent::willRenderPage();
Quicksand, an ignoble successor to Quickling Summary: Ref T2086. Ref T7014. With the persistent column, there is significant value in retaining chrome state through navigation events, because the user may have a lot of state in the chat window (scroll position, text selection, room juggling, partially entered text, etc). We can do this by capturing navigation events and faking them with Javascript. (This can also improve performance, albeit slightly, and I believe there are better approaches to tackle performance any problems which exist with the chrome in many cases). At Facebook, this system was "Photostream" in photos and then "Quickling" in general, and the technical cost of the system was //staggering//. I am loathe to pursue it again. However: - Browsers are less junky now, and we target a smaller set of browsers. A large part of the technical cost of Quickling was the high complexity of emulating nagivation events in IE, where we needed to navigate a hidden iframe to make history entries. All desktop browsers which we might want to use this system on support the History API (although this prototype does not yet implement it). - Javelin and Phabricator's architecture are much cleaner than Facebook's was. A large part of the technical cost of Quickling was inconsistency, inlined `onclick` handlers, and general lack of coordination and abstraction. We will have //some// of this, but "correctly written" behaviors are mostly immune to it by design, and many of Javelin's architectural decisions were influenced by desire to avoid issues we encountered building this stuff for Facebook. - Some of the primitives which Quickling required (like loading resources over Ajax) have existed in a stable state in our codebase for a year or more, and adoption of these primitives was trivial and uneventful (vs a huge production at Facebook). - My hubris is bolstered by recent success with WebSockets and JX.Scrollbar, both of which I would have assessed as infeasibly complex to develop in this project a few years ago. To these points, the developer cost to prototype Photostream was several weeks; the developer cost to prototype this was a bit less than an hour. It is plausible to me that implementing and maintaining this system really will be hundreds of times less complex than it was at Facebook. Test Plan: My plan for this and D11497 is: - Get them in master. - Some secret key / relatively-hidden preference activates the column. - Quicksand activates //only// when the column is open. - We can use column + quicksand for a long period of time (i.e., over the course of Conpherence v2 development) and hammer out the long tail of issues. - When it derps up, you just hide the column and you're good to go. Reviewers: btrahan, chad Reviewed By: chad Subscribers: epriestley Maniphest Tasks: T2086, T7014 Differential Revision: https://secure.phabricator.com/D11507
2015-01-27 23:52:09 +01:00
$response = $this->renderPageBodyContent();
$response = $this->willSendResponse($response);
Make mobile navigation work properly by default in more cases Summary: Fixes T5752. This obsoletes a bunch of old patterns and I'll follow up on those with a big "go do a bunch of mechanical code changes" task. Major goals are: - Don't load named queries multiple times on search pages. - Don't require extra code to get standard navigation right on mobile. - Reduce the amount of boilerplate in ListControllers. - Reduce the amount of boilerplate around navigation/menus in all controllers. Specifically, here's what this does: - The StandardPage is now a smarter/more structured object with `setNavigation()` and `setCrumbs()` methods. More rendering decisions are delayed until the last possible moment. - It uses this to automatically add crumb actions to the application menu. - It uses this to automatically reuse one SearchEngine instead of running queries multiple times. - The new preferred way to build responses is `$this->newPage()` (like `$this->newDialog()`), which has structured methods for adding stuff (`setTitle()`, etc). - SearchEngine exposes a new convenience method so you don't have to do all the controller delegation stuff. - Building menus is generally simpler. Test Plan: - Tested paste list, view, edit, comment, raw controllers for functionality, mobile menu, crumbs, navigation menu. - Edited saved queries. - Tested Differential, Maniphest (no changes). - Verified the paste pages don't run any duplicate NamedQuery queries. Reviewers: chad Reviewed By: chad Maniphest Tasks: T5752 Differential Revision: https://secure.phabricator.com/D14382
2015-11-02 21:06:28 +01:00
$extra_config = $this->getQuicksandConfig();
Quicksand, an ignoble successor to Quickling Summary: Ref T2086. Ref T7014. With the persistent column, there is significant value in retaining chrome state through navigation events, because the user may have a lot of state in the chat window (scroll position, text selection, room juggling, partially entered text, etc). We can do this by capturing navigation events and faking them with Javascript. (This can also improve performance, albeit slightly, and I believe there are better approaches to tackle performance any problems which exist with the chrome in many cases). At Facebook, this system was "Photostream" in photos and then "Quickling" in general, and the technical cost of the system was //staggering//. I am loathe to pursue it again. However: - Browsers are less junky now, and we target a smaller set of browsers. A large part of the technical cost of Quickling was the high complexity of emulating nagivation events in IE, where we needed to navigate a hidden iframe to make history entries. All desktop browsers which we might want to use this system on support the History API (although this prototype does not yet implement it). - Javelin and Phabricator's architecture are much cleaner than Facebook's was. A large part of the technical cost of Quickling was inconsistency, inlined `onclick` handlers, and general lack of coordination and abstraction. We will have //some// of this, but "correctly written" behaviors are mostly immune to it by design, and many of Javelin's architectural decisions were influenced by desire to avoid issues we encountered building this stuff for Facebook. - Some of the primitives which Quickling required (like loading resources over Ajax) have existed in a stable state in our codebase for a year or more, and adoption of these primitives was trivial and uneventful (vs a huge production at Facebook). - My hubris is bolstered by recent success with WebSockets and JX.Scrollbar, both of which I would have assessed as infeasibly complex to develop in this project a few years ago. To these points, the developer cost to prototype Photostream was several weeks; the developer cost to prototype this was a bit less than an hour. It is plausible to me that implementing and maintaining this system really will be hundreds of times less complex than it was at Facebook. Test Plan: My plan for this and D11497 is: - Get them in master. - Some secret key / relatively-hidden preference activates the column. - Quicksand activates //only// when the column is open. - We can use column + quicksand for a long period of time (i.e., over the course of Conpherence v2 development) and hammer out the long tail of issues. - When it derps up, you just hide the column and you're good to go. Reviewers: btrahan, chad Reviewed By: chad Subscribers: epriestley Maniphest Tasks: T2086, T7014 Differential Revision: https://secure.phabricator.com/D11507
2015-01-27 23:52:09 +01:00
return array(
'content' => hsprintf('%s', $response),
) + $this->buildQuicksandConfig()
+ $extra_config;
}
private function buildQuicksandConfig() {
$viewer = $this->getRequest()->getUser();
$controller = $this->getController();
$dropdown_query = id(new AphlictDropdownDataQuery())
->setViewer($viewer);
$dropdown_query->execute();
$rendered_dropdowns = array();
$applications = array(
'PhabricatorHelpApplication',
);
foreach ($applications as $application_class) {
if (!PhabricatorApplication::isClassInstalledForViewer(
$application_class,
$viewer)) {
continue;
}
$application = PhabricatorApplication::getByClass($application_class);
$rendered_dropdowns[$application_class] =
$application->buildMainMenuExtraNodes(
$viewer,
$controller);
}
$hisec_warning_config = $this->getHighSecurityWarningConfig();
$console_config = null;
$console = $this->getConsole();
if ($console) {
$console_config = $this->getConsoleConfig();
}
$upload_enabled = false;
if ($controller) {
$upload_enabled = $controller->isGlobalDragAndDropUploadEnabled();
}
$application_class = null;
$application_search_icon = null;
$controller = $this->getController();
if ($controller) {
$application = $controller->getCurrentApplication();
if ($application) {
$application_class = get_class($application);
if ($application->getApplicationSearchDocumentTypes()) {
$application_search_icon = $application->getIcon();
}
}
}
return array(
'title' => $this->getTitle(),
'aphlictDropdownData' => array(
$dropdown_query->getNotificationData(),
$dropdown_query->getConpherenceData(),
),
'globalDragAndDrop' => $upload_enabled,
'aphlictDropdowns' => $rendered_dropdowns,
'hisecWarningConfig' => $hisec_warning_config,
'consoleConfig' => $console_config,
'applicationClass' => $application_class,
'applicationSearchIcon' => $application_search_icon,
) + $this->buildAphlictListenConfigData();
}
private function buildAphlictListenConfigData() {
$user = $this->getRequest()->getUser();
$subscriptions = $this->pageObjects;
$subscriptions[] = $user->getPHID();
return array(
'pageObjects' => array_fill_keys($this->pageObjects, true),
'subscriptions' => $subscriptions,
);
Quicksand, an ignoble successor to Quickling Summary: Ref T2086. Ref T7014. With the persistent column, there is significant value in retaining chrome state through navigation events, because the user may have a lot of state in the chat window (scroll position, text selection, room juggling, partially entered text, etc). We can do this by capturing navigation events and faking them with Javascript. (This can also improve performance, albeit slightly, and I believe there are better approaches to tackle performance any problems which exist with the chrome in many cases). At Facebook, this system was "Photostream" in photos and then "Quickling" in general, and the technical cost of the system was //staggering//. I am loathe to pursue it again. However: - Browsers are less junky now, and we target a smaller set of browsers. A large part of the technical cost of Quickling was the high complexity of emulating nagivation events in IE, where we needed to navigate a hidden iframe to make history entries. All desktop browsers which we might want to use this system on support the History API (although this prototype does not yet implement it). - Javelin and Phabricator's architecture are much cleaner than Facebook's was. A large part of the technical cost of Quickling was inconsistency, inlined `onclick` handlers, and general lack of coordination and abstraction. We will have //some// of this, but "correctly written" behaviors are mostly immune to it by design, and many of Javelin's architectural decisions were influenced by desire to avoid issues we encountered building this stuff for Facebook. - Some of the primitives which Quickling required (like loading resources over Ajax) have existed in a stable state in our codebase for a year or more, and adoption of these primitives was trivial and uneventful (vs a huge production at Facebook). - My hubris is bolstered by recent success with WebSockets and JX.Scrollbar, both of which I would have assessed as infeasibly complex to develop in this project a few years ago. To these points, the developer cost to prototype Photostream was several weeks; the developer cost to prototype this was a bit less than an hour. It is plausible to me that implementing and maintaining this system really will be hundreds of times less complex than it was at Facebook. Test Plan: My plan for this and D11497 is: - Get them in master. - Some secret key / relatively-hidden preference activates the column. - Quicksand activates //only// when the column is open. - We can use column + quicksand for a long period of time (i.e., over the course of Conpherence v2 development) and hammer out the long tail of issues. - When it derps up, you just hide the column and you're good to go. Reviewers: btrahan, chad Reviewed By: chad Subscribers: epriestley Maniphest Tasks: T2086, T7014 Differential Revision: https://secure.phabricator.com/D11507
2015-01-27 23:52:09 +01:00
}
private function getQuicksandURIPatternBlacklist() {
$applications = PhabricatorApplication::getAllApplications();
$blacklist = array();
foreach ($applications as $application) {
$blacklist[] = $application->getQuicksandURIPatternBlacklist();
}
return array_mergev($blacklist);
}
private function getUserPreference($key, $default = null) {
$request = $this->getRequest();
if (!$request) {
return $default;
}
$user = $request->getUser();
if (!$user) {
return $default;
}
return $user->loadPreferences()->getPreference($key, $default);
}
Make mobile navigation work properly by default in more cases Summary: Fixes T5752. This obsoletes a bunch of old patterns and I'll follow up on those with a big "go do a bunch of mechanical code changes" task. Major goals are: - Don't load named queries multiple times on search pages. - Don't require extra code to get standard navigation right on mobile. - Reduce the amount of boilerplate in ListControllers. - Reduce the amount of boilerplate around navigation/menus in all controllers. Specifically, here's what this does: - The StandardPage is now a smarter/more structured object with `setNavigation()` and `setCrumbs()` methods. More rendering decisions are delayed until the last possible moment. - It uses this to automatically add crumb actions to the application menu. - It uses this to automatically reuse one SearchEngine instead of running queries multiple times. - The new preferred way to build responses is `$this->newPage()` (like `$this->newDialog()`), which has structured methods for adding stuff (`setTitle()`, etc). - SearchEngine exposes a new convenience method so you don't have to do all the controller delegation stuff. - Building menus is generally simpler. Test Plan: - Tested paste list, view, edit, comment, raw controllers for functionality, mobile menu, crumbs, navigation menu. - Edited saved queries. - Tested Differential, Maniphest (no changes). - Verified the paste pages don't run any duplicate NamedQuery queries. Reviewers: chad Reviewed By: chad Maniphest Tasks: T5752 Differential Revision: https://secure.phabricator.com/D14382
2015-11-02 21:06:28 +01:00
public function produceAphrontResponse() {
$controller = $this->getController();
if (!$this->getApplicationMenu()) {
$application_menu = $controller->buildApplicationMenu();
if ($application_menu) {
$this->setApplicationMenu($application_menu);
}
}
$viewer = $this->getUser();
if ($viewer && $viewer->getPHID()) {
$object_phids = $this->pageObjects;
foreach ($object_phids as $object_phid) {
PhabricatorFeedStoryNotification::updateObjectNotificationViews(
$viewer,
$object_phid);
}
}
if ($this->getRequest()->isQuicksand()) {
$content = $this->renderForQuicksand();
$response = id(new AphrontAjaxResponse())
->setContent($content);
} else {
$content = $this->render();
$response = id(new AphrontWebpageResponse())
->setContent($content);
}
return $response;
}
}