1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-12-04 20:52:43 +01:00
phorge-phorge/src/view/page/standard/PhabricatorStandardPageView.php

374 lines
9.7 KiB
PHP
Raw Normal View History

<?php
/*
* Copyright 2011 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
2011-01-23 02:48:55 +01:00
class PhabricatorStandardPageView extends AphrontPageView {
private $baseURI;
private $applicationName;
private $tabs = array();
private $selectedTab;
private $glyph;
private $bodyContent;
2011-01-26 22:21:12 +01:00
private $request;
private $isAdminInterface;
private $showChrome = true;
private $isFrameable = false;
private $disableConsole;
public function setIsAdminInterface($is_admin_interface) {
$this->isAdminInterface = $is_admin_interface;
return $this;
}
public function getIsAdminInterface() {
return $this->isAdminInterface;
}
2011-01-26 22:21:12 +01:00
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 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();
}
2011-01-25 20:31:40 +01:00
protected function willRenderPage() {
2011-02-02 22:48:52 +01:00
if (!$this->getRequest()) {
throw new Exception(
"You must set the Request to render a PhabricatorStandardPageView.");
}
$console = $this->getConsole();
2011-02-02 22:48:52 +01:00
require_celerity_resource('phabricator-core-css');
2011-01-25 20:31:40 +01:00
require_celerity_resource('phabricator-core-buttons-css');
require_celerity_resource('phabricator-standard-page-view');
2011-02-05 20:45:13 +01:00
Javelin::initBehavior('workflow', array());
Fix conservative CSRF token cycling limit Summary: We currently cycle CSRF tokens every hour and check for the last two valid ones. This means that a form could go stale in as little as an hour, and is certainly stale after two. When a stale form is submitted, you basically get a terrible heisen-state where some of your data might persist if you're lucky but more likely it all just vanishes. The .js file below outlines some more details. This is a pretty terrible UX and we don't need to be as conservative about CSRF validation as we're being. Remedy this problem by: - Accepting the last 6 CSRF tokens instead of the last 1 (i.e., pages are valid for at least 6 hours, and for as long as 7). - Using JS to refresh the CSRF token every 55 minutes (i.e., pages connected to the internet are valid indefinitely). - Showing the user an explicit message about what went wrong when CSRF validation fails so the experience is less bewildering. They should now only be able to submit with a bad CSRF token if: - They load a page, disconnect from the internet for 7 hours, reconnect, and submit the form within 55 minutes; or - They are actually the victim of a CSRF attack. We could eventually fix the first one by tracking reconnects, which might be "free" once the notification server gets built. It will probably never be an issue in practice. Test Plan: - Reduced CSRF cycle frequency to 2 seconds, submitted a form after 15 seconds, got the CSRF exception. - Reduced csrf-refresh cycle frequency to 3 seconds, submitted a form after 15 seconds, got a clean form post. - Added debugging code the the csrf refresh to make sure it was doing sensible things (pulling different tokens, finding all the inputs). Reviewed By: aran Reviewers: tuomaspelkonen, jungejason, aran CC: aran, epriestley Differential Revision: 660
2011-07-13 23:05:18 +02:00
Javelin::initBehavior('refresh-csrf', array());
Javelin::initBehavior(
'phabricator-keyboard-shortcuts',
array(
'helpURI' => '/help/keyboardshortcut/',
));
2011-02-05 20:45:13 +01:00
2011-02-02 22:48:52 +01:00
if ($console) {
require_celerity_resource('aphront-dark-console-css');
Javelin::initBehavior(
'dark-console',
array(
'uri' => '/~/',
));
// 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
}
$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 =
'<script type="text/javascript">'.
$framebust.
'window.__DEV__=1;'.
'</script>'.
$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 .=
'<style type="text/css">'.
'.PhabricatorMonospaced { font: '.
$monospaced.
' !important; }'.
'</style>';
}
}
}
return $head;
}
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) {
$console = $this->getRequest()->getApplicationConfiguration()->getConsole();
if ($console) {
$response = str_replace(
'<darkconsole />',
$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)
2011-01-25 20:31:40 +01:00
? 'phabricator-selected-tab'
: null,
),
$tab_markup);
$tabs[] = $tab_markup;
}
$tabs = implode('', $tabs);
2011-01-26 22:21:12 +01:00
$login_stuff = null;
$request = $this->getRequest();
$user = null;
2011-01-26 02:17:19 +01:00
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()) {
2011-01-31 03:52:29 +01:00
$login_stuff =
phutil_render_tag(
'a',
array(
'href' => '/p/'.$user->getUsername().'/',
),
phutil_escape_html($user->getUsername())).
2011-01-31 03:52:29 +01:00
' &middot; '.
'<a href="/settings/">Settings</a>'.
' &middot; '.
phabricator_render_form(
$user,
array(
'action' => '/search/',
'method' => 'post',
'style' => 'display: inline',
),
'<input type="text" name="query" />'.
'<button>Search</button>');
2011-01-26 02:17:19 +01:00
}
2011-01-26 22:21:12 +01:00
}
2011-02-05 20:45:13 +01:00
$foot_links = array();
2011-02-05 20:45:13 +01:00
$version = PhabricatorEnv::getEnvConfig('phabricator.version');
$foot_links[] = phutil_escape_html('Phabricator '.$version);
2011-02-05 20:45:13 +01:00
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',
),
'<button class="link">Logout</button>');
}
2011-02-05 20:45:13 +01:00
$foot_links = implode(' &middot; ', $foot_links);
$admin_class = null;
if ($this->getIsAdminInterface()) {
$admin_class = 'phabricator-admin-page-view';
}
$header_chrome = null;
$footer_chrome = null;
if ($this->getShowChrome()) {
$header_chrome =
'<table class="phabricator-standard-header">'.
'<tr>'.
'<td class="phabricator-logo"><a href="/"> </a></td>'.
'<td>'.
'<table class="phabricator-primary-navigation">'.
'<tr>'.
'<th>'.
phutil_render_tag(
'a',
array(
'href' => $this->getBaseURI(),
'class' => 'phabricator-head-appname',
),
phutil_escape_html($this->getApplicationName())).
'</th>'.
$tabs.
'</tr>'.
'</table>'.
'</td>'.
'<td class="phabricator-login-details">'.
$login_stuff.
'</td>'.
'</tr>'.
'</table>';
$footer_chrome =
'<div class="phabricator-page-foot">'.
$foot_links.
'</div>';
}
return
($console ? '<darkconsole />' : null).
'<div class="phabricator-standard-page '.$admin_class.'">'.
$header_chrome.
$this->bodyContent.
'<div style="clear: both;"></div>'.
2011-02-05 20:45:13 +01:00
'</div>'.
$footer_chrome;
}
protected function getTail() {
$response = CelerityAPI::getStaticResourceResponse();
return
$response->renderResourcesOfType('js').
2011-01-25 20:57:47 +01:00
$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();
}
}