@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at recaptime-dev/main 932 lines 25 kB view raw
1<?php 2 3/** 4 * This is a standard Phabricator page with menus, Javelin, DarkConsole, and 5 * basic styles. 6 */ 7final class PhabricatorStandardPageView extends PhabricatorBarePageView 8 implements AphrontResponseProducerInterface { 9 10 private $baseURI; 11 private $applicationName; 12 private $glyph; 13 private $menuContent; 14 private $showChrome = true; 15 private $classes = array(); 16 private $disableConsole; 17 private $pageObjects = array(); 18 private $applicationMenu; 19 private $showFooter = true; 20 private $showDurableColumn = true; 21 private $quicksandConfig = array(); 22 private $tabs; 23 private $crumbs; 24 private $navigation; 25 private $footer; 26 private $headItems = array(); 27 28 public function setShowFooter($show_footer) { 29 $this->showFooter = $show_footer; 30 return $this; 31 } 32 33 public function getShowFooter() { 34 return $this->showFooter; 35 } 36 37 public function setApplicationName($application_name) { 38 $this->applicationName = $application_name; 39 return $this; 40 } 41 42 public function setDisableConsole($disable) { 43 $this->disableConsole = $disable; 44 return $this; 45 } 46 47 public function getApplicationName() { 48 return $this->applicationName; 49 } 50 51 public function setBaseURI($base_uri) { 52 $this->baseURI = $base_uri; 53 return $this; 54 } 55 56 public function getBaseURI() { 57 return $this->baseURI; 58 } 59 60 public function setShowChrome($show_chrome) { 61 $this->showChrome = $show_chrome; 62 return $this; 63 } 64 65 public function getShowChrome() { 66 return $this->showChrome; 67 } 68 69 public function addClass($class) { 70 $this->classes[] = $class; 71 return $this; 72 } 73 74 public function setPageObjectPHIDs(array $phids) { 75 $this->pageObjects = $phids; 76 return $this; 77 } 78 79 public function setShowDurableColumn($show) { 80 $this->showDurableColumn = $show; 81 return $this; 82 } 83 84 public function getShowDurableColumn() { 85 $request = $this->getRequest(); 86 if (!$request) { 87 return false; 88 } 89 90 $viewer = $request->getUser(); 91 if (!$viewer->isLoggedIn()) { 92 return false; 93 } 94 95 $conpherence_installed = PhabricatorApplication::isClassInstalledForViewer( 96 PhabricatorConpherenceApplication::class, 97 $viewer); 98 if (!$conpherence_installed) { 99 return false; 100 } 101 102 if ($this->isQuicksandBlacklistURI()) { 103 return false; 104 } 105 106 return true; 107 } 108 109 private function isQuicksandBlacklistURI() { 110 $request = $this->getRequest(); 111 if (!$request) { 112 return false; 113 } 114 115 $patterns = $this->getQuicksandURIPatternBlacklist(); 116 $path = $request->getRequestURI()->getPath(); 117 foreach ($patterns as $pattern) { 118 if (preg_match('(^'.$pattern.'$)', $path)) { 119 return true; 120 } 121 } 122 return false; 123 } 124 125 public function getDurableColumnVisible() { 126 $column_key = PhabricatorConpherenceColumnVisibleSetting::SETTINGKEY; 127 return (bool)$this->getUserPreference($column_key, false); 128 } 129 130 public function getDurableColumnMinimize() { 131 $column_key = PhabricatorConpherenceColumnMinimizeSetting::SETTINGKEY; 132 return (bool)$this->getUserPreference($column_key, false); 133 } 134 135 public function addQuicksandConfig(array $config) { 136 $this->quicksandConfig = $config + $this->quicksandConfig; 137 return $this; 138 } 139 140 public function getQuicksandConfig() { 141 return $this->quicksandConfig; 142 } 143 144 public function setCrumbs(PHUICrumbsView $crumbs) { 145 $this->crumbs = $crumbs; 146 return $this; 147 } 148 149 public function getCrumbs() { 150 return $this->crumbs; 151 } 152 153 public function setTabs(PHUIListView $tabs) { 154 $tabs->setType(PHUIListView::TABBAR_LIST); 155 $tabs->addClass('phabricator-standard-page-tabs'); 156 $this->tabs = $tabs; 157 return $this; 158 } 159 160 public function getTabs() { 161 return $this->tabs; 162 } 163 164 public function setNavigation(AphrontSideNavFilterView $navigation) { 165 $this->navigation = $navigation; 166 return $this; 167 } 168 169 public function getNavigation() { 170 return $this->navigation; 171 } 172 173 public function getTitle() { 174 $glyph_key = PhabricatorTitleGlyphsSetting::SETTINGKEY; 175 $glyph_on = PhabricatorTitleGlyphsSetting::VALUE_TITLE_GLYPHS; 176 $glyph_setting = $this->getUserPreference($glyph_key, $glyph_on); 177 178 $use_glyph = ($glyph_setting == $glyph_on); 179 180 $title = parent::getTitle(); 181 182 $prefix = null; 183 if ($use_glyph) { 184 $prefix = $this->getGlyph(); 185 } else { 186 $application_name = $this->getApplicationName(); 187 if (strlen($application_name)) { 188 $prefix = '['.$application_name.']'; 189 } 190 } 191 192 if (phutil_nonempty_string($prefix)) { 193 $title = $prefix.' '.$title; 194 } 195 196 return $title; 197 } 198 199 200 protected function willRenderPage() { 201 $footer = $this->renderFooter(); 202 203 // NOTE: A cleaner solution would be to let body layout elements implement 204 // some kind of "LayoutInterface" so content can be embedded inside frames, 205 // but there's only really one use case for this for now. 206 $children = $this->renderChildren(); 207 if ($children) { 208 $layout = head($children); 209 if ($layout instanceof PHUIFormationView) { 210 $layout->setFooter($footer); 211 $footer = null; 212 } 213 } 214 215 $this->footer = $footer; 216 217 parent::willRenderPage(); 218 219 $request = $this->getRequest(); 220 if (!$request) { 221 throw new Exception( 222 pht( 223 'You must set the %s to render a %s.', 224 'Request', 225 self::class)); 226 } 227 228 $console = $this->getConsole(); 229 230 require_celerity_resource('phabricator-core-css'); 231 require_celerity_resource('phabricator-zindex-css'); 232 require_celerity_resource('phui-button-css'); 233 require_celerity_resource('phui-spacing-css'); 234 require_celerity_resource('phui-form-css'); 235 require_celerity_resource('phabricator-standard-page-view'); 236 require_celerity_resource('conpherence-durable-column-view'); 237 require_celerity_resource('font-lato'); 238 239 Javelin::initBehavior('workflow', array()); 240 241 $user = $request->getUser(); 242 if ($user) { 243 if ($user->isUserActivated()) { 244 // Only bother user about timezone offset if they have not set UTC 245 if ($user->getTimezoneIdentifier() !== 'UTC') { 246 $offset = $user->getTimeZoneOffset(); 247 248 $ignore_key = PhabricatorTimezoneIgnoreOffsetSetting::SETTINGKEY; 249 $ignore = $user->getUserSetting($ignore_key); 250 251 Javelin::initBehavior( 252 'detect-timezone', 253 array( 254 'offset' => $offset, 255 'uri' => '/settings/timezone/', 256 'message' => pht( 257 'Your browser timezone setting differs from the timezone '. 258 'setting in your profile, click to reconcile.'), 259 'ignoreKey' => $ignore_key, 260 'ignore' => $ignore, 261 )); 262 } 263 264 if ($user->getIsAdmin()) { 265 $server_https = $request->isHTTPS(); 266 $server_protocol = $server_https ? 'HTTPS' : 'HTTP'; 267 $client_protocol = $server_https ? 'HTTP' : 'HTTPS'; 268 269 $doc_name = 'Configuring a Preamble Script'; 270 $doc_href = PhabricatorEnv::getDoclink($doc_name); 271 272 Javelin::initBehavior( 273 'setup-check-https', 274 array( 275 'server_https' => $server_https, 276 'doc_name' => pht('See Documentation'), 277 'doc_href' => $doc_href, 278 'message' => pht( 279 'This server thinks you are using %s, but your '. 280 'client is convinced that it is using %s. This is a serious '. 281 'misconfiguration with subtle, but significant, consequences.', 282 $server_protocol, $client_protocol), 283 )); 284 } 285 } 286 287 Javelin::initBehavior('lightbox-attachments'); 288 } 289 290 Javelin::initBehavior('aphront-form-disable-on-submit'); 291 Javelin::initBehavior('toggle-class', array()); 292 Javelin::initBehavior('history-install'); 293 Javelin::initBehavior('phabricator-gesture'); 294 295 $current_token = null; 296 if ($user) { 297 $current_token = $user->getCSRFToken(); 298 } 299 300 Javelin::initBehavior( 301 'refresh-csrf', 302 array( 303 'tokenName' => AphrontRequest::getCSRFTokenName(), 304 'header' => AphrontRequest::getCSRFHeaderName(), 305 'viaHeader' => AphrontRequest::getViaHeaderName(), 306 'current' => $current_token, 307 )); 308 309 Javelin::initBehavior('device'); 310 311 Javelin::initBehavior( 312 'high-security-warning', 313 $this->getHighSecurityWarningConfig()); 314 315 if (PhabricatorEnv::isReadOnly()) { 316 Javelin::initBehavior( 317 'read-only-warning', 318 array( 319 'message' => PhabricatorEnv::getReadOnlyMessage(), 320 'uri' => PhabricatorEnv::getReadOnlyURI(), 321 )); 322 } 323 324 // If we aren't showing the page chrome, skip rendering DarkConsole and the 325 // main menu, since they won't be visible on the page. 326 if (!$this->getShowChrome()) { 327 return; 328 } 329 330 if ($console) { 331 require_celerity_resource('aphront-dark-console-css'); 332 333 $headers = array(); 334 if (DarkConsoleXHProfPluginAPI::isProfilerStarted()) { 335 $headers[DarkConsoleXHProfPluginAPI::getProfilerHeader()] = 'page'; 336 } 337 if (DarkConsoleServicesPlugin::isQueryAnalyzerRequested()) { 338 $headers[DarkConsoleServicesPlugin::getQueryAnalyzerHeader()] = true; 339 } 340 341 Javelin::initBehavior( 342 'dark-console', 343 $this->getConsoleConfig()); 344 } 345 346 if ($user) { 347 $viewer = $user; 348 } else { 349 $viewer = new PhabricatorUser(); 350 } 351 352 $menu = id(new PhabricatorMainMenuView()) 353 ->setViewer($viewer); 354 355 if ($this->getController()) { 356 $menu->setController($this->getController()); 357 } 358 359 $application_menu = $this->applicationMenu; 360 if ($application_menu) { 361 if ($application_menu instanceof PHUIApplicationMenuView) { 362 $crumbs = $this->getCrumbs(); 363 if ($crumbs) { 364 $application_menu->setCrumbs($crumbs); 365 } 366 367 $application_menu = $application_menu->buildListView(); 368 } 369 370 $menu->setApplicationMenu($application_menu); 371 } 372 373 374 $this->menuContent = $menu->render(); 375 } 376 377 378 /** 379 * Insert a HTML element into <head> of the page to render. 380 * 381 * @param PhutilSafeHTML $html HTML header to add 382 */ 383 public function addHeadItem($html) { 384 if ($html instanceof PhutilSafeHTML) { 385 $this->headItems[] = $html; 386 } 387 } 388 389 protected function getHead() { 390 $monospaced = null; 391 392 $request = $this->getRequest(); 393 if ($request) { 394 $user = $request->getUser(); 395 if ($user) { 396 $monospaced = $user->getUserSetting( 397 PhabricatorMonospacedFontSetting::SETTINGKEY); 398 } 399 } 400 401 $response = CelerityAPI::getStaticResourceResponse(); 402 403 $font_css = null; 404 if (!empty($monospaced)) { 405 // We can't print this normally because escaping quotation marks will 406 // break the CSS. Instead, filter it strictly and then mark it as safe. 407 $monospaced = new PhutilSafeHTML( 408 PhabricatorMonospacedFontSetting::filterMonospacedCSSRule( 409 $monospaced)); 410 411 $font_css = hsprintf( 412 '<style type="text/css">'. 413 '.PhabricatorMonospaced, '. 414 '.phabricator-remarkup .remarkup-code-block .remarkup-code, '. 415 '.phabricator-remarkup .remarkup-monospaced '. 416 '{ font: %s !important; } '. 417 '</style>', 418 $monospaced); 419 } 420 421 return hsprintf( 422 '%s%s%s%s', 423 parent::getHead(), 424 $font_css, 425 phutil_implode_html('', $this->headItems), 426 $response->renderSingleResource('javelin-magical-init', 'phabricator')); 427 } 428 429 public function setGlyph($glyph) { 430 $this->glyph = $glyph; 431 return $this; 432 } 433 434 public function getGlyph() { 435 return $this->glyph; 436 } 437 438 protected function willSendResponse($response) { 439 $request = $this->getRequest(); 440 $response = parent::willSendResponse($response); 441 442 $console = $request->getApplicationConfiguration()->getConsole(); 443 444 if ($console) { 445 $response = PhutilSafeHTML::applyFunction( 446 'str_replace', 447 hsprintf('<darkconsole />'), 448 $console->render($request), 449 $response); 450 } 451 452 return $response; 453 } 454 455 protected function getBody() { 456 $user = null; 457 $request = $this->getRequest(); 458 if ($request) { 459 $user = $request->getUser(); 460 } 461 462 $header_chrome = null; 463 if ($this->getShowChrome()) { 464 $header_chrome = $this->menuContent; 465 } 466 467 $classes = array(); 468 $classes[] = 'main-page-frame'; 469 $developer_warning = null; 470 if (PhabricatorEnv::getEnvConfig('phabricator.developer-mode') && 471 DarkConsoleErrorLogPluginAPI::getErrors()) { 472 $developer_warning = phutil_tag_div( 473 'aphront-developer-error-callout', 474 pht( 475 'This page raised PHP errors. Find them in DarkConsole '. 476 'or the error log.')); 477 } 478 479 $main_page = phutil_tag( 480 'div', 481 array( 482 'id' => 'phabricator-standard-page', 483 'class' => 'phabricator-standard-page', 484 ), 485 array( 486 $developer_warning, 487 $header_chrome, 488 phutil_tag( 489 'div', 490 array( 491 'id' => 'phabricator-standard-page-body', 492 'class' => 'phabricator-standard-page-body', 493 ), 494 $this->renderPageBodyContent()), 495 )); 496 497 $durable_column = null; 498 if ($this->getShowDurableColumn()) { 499 $is_visible = $this->getDurableColumnVisible(); 500 $is_minimize = $this->getDurableColumnMinimize(); 501 $durable_column = id(new ConpherenceDurableColumnView()) 502 ->setSelectedConpherence(null) 503 ->setViewer($user) 504 ->setQuicksandConfig($this->buildQuicksandConfig()) 505 ->setVisible($is_visible) 506 ->setMinimize($is_minimize) 507 ->setInitialLoad(true); 508 if ($is_minimize) { 509 $this->classes[] = 'minimize-column'; 510 } 511 } 512 513 Javelin::initBehavior('quicksand-blacklist', array( 514 'patterns' => $this->getQuicksandURIPatternBlacklist(), 515 )); 516 517 return phutil_tag( 518 'div', 519 array( 520 'class' => implode(' ', $classes), 521 'id' => 'main-page-frame', 522 ), 523 array( 524 $main_page, 525 $durable_column, 526 )); 527 } 528 529 private function renderPageBodyContent() { 530 $console = $this->getConsole(); 531 532 $body = parent::getBody(); 533 534 $nav = $this->getNavigation(); 535 $tabs = $this->getTabs(); 536 if ($nav) { 537 $crumbs = $this->getCrumbs(); 538 if ($crumbs) { 539 $nav->setCrumbs($crumbs); 540 } 541 $nav->appendChild($body); 542 $nav->appendFooter($this->footer); 543 $content = phutil_implode_html('', array($nav->render())); 544 } else { 545 $content = array(); 546 547 $crumbs = $this->getCrumbs(); 548 if ($crumbs) { 549 if ($this->getTabs()) { 550 $crumbs->setBorder(true); 551 } 552 $content[] = $crumbs; 553 } 554 555 $tabs = $this->getTabs(); 556 if ($tabs) { 557 $content[] = $tabs; 558 } 559 560 $content[] = $body; 561 $content[] = $this->footer; 562 563 $content = phutil_implode_html('', $content); 564 } 565 566 return array( 567 ($console ? hsprintf('<darkconsole />') : null), 568 $content, 569 ); 570 } 571 572 protected function getTail() { 573 $request = $this->getRequest(); 574 $user = $request->getUser(); 575 576 $tail = array( 577 parent::getTail(), 578 ); 579 580 $response = CelerityAPI::getStaticResourceResponse(); 581 582 if ($request->isHTTPS()) { 583 $with_protocol = 'https'; 584 } else { 585 $with_protocol = 'http'; 586 } 587 588 $servers = PhabricatorNotificationServerRef::getEnabledClientServers( 589 $with_protocol); 590 591 if ($servers) { 592 if ($user && $user->isLoggedIn()) { 593 // TODO: We could tell the browser about all the servers and let it 594 // do random reconnects to improve reliability. 595 shuffle($servers); 596 $server = head($servers); 597 598 $client_uri = $server->getWebsocketURI(); 599 600 Javelin::initBehavior( 601 'aphlict-listen', 602 array( 603 'websocketURI' => (string)$client_uri, 604 ) + $this->buildAphlictListenConfigData()); 605 606 CelerityAPI::getStaticResourceResponse() 607 ->addContentSecurityPolicyURI('connect-src', $client_uri); 608 } 609 } 610 611 $tail[] = $response->renderHTMLFooter($this->getFrameable()); 612 613 return $tail; 614 } 615 616 protected function getBodyClasses() { 617 $classes = array(); 618 619 if (!$this->getShowChrome()) { 620 $classes[] = 'phabricator-chromeless-page'; 621 } 622 623 $agent = AphrontRequest::getHTTPHeader('User-Agent'); 624 625 // Try to guess the device resolution based on UA strings to avoid a flash 626 // of incorrectly-styled content. 627 $device_guess = 'device-desktop'; 628 if (phutil_nonempty_string($agent)) { 629 if (preg_match('@iPhone|iPod|Android.*(Chrome/[.0-9]* Mobile|'. 630 'Mobile.*Firefox/[.0-9]*)@', $agent)) { 631 $device_guess = 'device-phone device'; 632 } else if (preg_match('@iPad|(Android.*Chrome/)@', $agent)) { 633 $device_guess = 'device-tablet device'; 634 } 635 } 636 637 $classes[] = $device_guess; 638 639 if (phutil_nonempty_string($agent)) { 640 if (preg_match('@Windows@', $agent)) { 641 $classes[] = 'platform-windows'; 642 } else if (preg_match('@Macintosh@', $agent)) { 643 $classes[] = 'platform-mac'; 644 } else if (preg_match('@X11@', $agent)) { 645 $classes[] = 'platform-linux'; 646 } 647 } 648 649 if ($this->getRequest()->getStr('__print__')) { 650 $classes[] = 'printable'; 651 } 652 653 if ($this->getRequest()->getStr('__aural__')) { 654 $classes[] = 'audible'; 655 } 656 657 $classes[] = 'phui-theme-'.PhabricatorEnv::getEnvConfig('ui.header-color'); 658 foreach ($this->classes as $class) { 659 $classes[] = $class; 660 } 661 662 return implode(' ', $classes); 663 } 664 665 private function getConsole() { 666 if ($this->disableConsole) { 667 return null; 668 } 669 return $this->getRequest()->getApplicationConfiguration()->getConsole(); 670 } 671 672 private function getConsoleConfig() { 673 $user = $this->getRequest()->getUser(); 674 675 $headers = array(); 676 if (DarkConsoleXHProfPluginAPI::isProfilerStarted()) { 677 $headers[DarkConsoleXHProfPluginAPI::getProfilerHeader()] = 'page'; 678 } 679 if (DarkConsoleServicesPlugin::isQueryAnalyzerRequested()) { 680 $headers[DarkConsoleServicesPlugin::getQueryAnalyzerHeader()] = true; 681 } 682 683 if ($user) { 684 $setting_tab = PhabricatorDarkConsoleTabSetting::SETTINGKEY; 685 $setting_visible = PhabricatorDarkConsoleVisibleSetting::SETTINGKEY; 686 $tab = $user->getUserSetting($setting_tab); 687 $visible = $user->getUserSetting($setting_visible); 688 } else { 689 $tab = null; 690 $visible = true; 691 } 692 693 return array( 694 // NOTE: We use a generic label here to prevent input reflection 695 // and mitigate compression attacks like BREACH. See discussion in 696 // T3684. 697 'uri' => pht('Main Request'), 698 'selected' => $tab, 699 'visible' => $visible, 700 'headers' => $headers, 701 ); 702 } 703 704 private function getHighSecurityWarningConfig() { 705 $user = $this->getRequest()->getUser(); 706 707 $show = false; 708 if ($user->hasSession()) { 709 $hisec = ($user->getSession()->getHighSecurityUntil() - time()); 710 if ($hisec > 0) { 711 $show = true; 712 } 713 } 714 715 return array( 716 'show' => $show, 717 'uri' => '/auth/session/downgrade/', 718 'message' => pht( 719 'Your session is in high security mode. When you '. 720 'finish using it, click here to leave.'), 721 ); 722 } 723 724 private function renderFooter() { 725 if (!$this->getShowChrome()) { 726 return null; 727 } 728 729 if (!$this->getShowFooter()) { 730 return null; 731 } 732 733 $items = PhabricatorEnv::getEnvConfig('ui.footer-items'); 734 if (!$items) { 735 return null; 736 } 737 738 $foot = array(); 739 foreach ($items as $item) { 740 $name = idx($item, 'name', pht('Unnamed Footer Item')); 741 742 $href = idx($item, 'href'); 743 if (!PhabricatorEnv::isValidURIForLink($href)) { 744 $href = null; 745 } 746 747 if ($href !== null) { 748 $tag = 'a'; 749 } else { 750 $tag = 'span'; 751 } 752 753 $foot[] = phutil_tag( 754 $tag, 755 array( 756 'href' => $href, 757 ), 758 $name); 759 } 760 $foot = phutil_implode_html(" \xC2\xB7 ", $foot); 761 762 return phutil_tag( 763 'div', 764 array( 765 'class' => 'phabricator-standard-page-footer grouped', 766 ), 767 $foot); 768 } 769 770 public function renderForQuicksand() { 771 parent::willRenderPage(); 772 $response = $this->renderPageBodyContent(); 773 $response = $this->willSendResponse($response); 774 775 $extra_config = $this->getQuicksandConfig(); 776 777 return array( 778 'content' => hsprintf('%s', $response), 779 ) + $this->buildQuicksandConfig() 780 + $extra_config; 781 } 782 783 private function buildQuicksandConfig() { 784 $viewer = $this->getRequest()->getUser(); 785 $controller = $this->getController(); 786 787 $dropdown_query = id(new AphlictDropdownDataQuery()) 788 ->setViewer($viewer); 789 $dropdown_query->execute(); 790 791 $hisec_warning_config = $this->getHighSecurityWarningConfig(); 792 793 $console_config = null; 794 $console = $this->getConsole(); 795 if ($console) { 796 $console_config = $this->getConsoleConfig(); 797 } 798 799 $upload_enabled = false; 800 if ($controller) { 801 $upload_enabled = $controller->isGlobalDragAndDropUploadEnabled(); 802 } 803 804 $application_class = null; 805 $application_search_icon = null; 806 $application_help = null; 807 $controller = $this->getController(); 808 if ($controller) { 809 $application = $controller->getCurrentApplication(); 810 if ($application) { 811 $application_class = get_class($application); 812 if ($application->getApplicationSearchDocumentTypes()) { 813 $application_search_icon = $application->getIcon(); 814 } 815 816 $help_items = $application->getHelpMenuItems($viewer); 817 if ($help_items) { 818 $help_list = id(new PhabricatorActionListView()) 819 ->setViewer($viewer); 820 foreach ($help_items as $help_item) { 821 $help_list->addAction($help_item); 822 } 823 $application_help = $help_list->getDropdownMenuMetadata(); 824 } 825 } 826 } 827 828 return array( 829 'title' => $this->getTitle(), 830 'bodyClasses' => $this->getBodyClasses(), 831 'aphlictDropdownData' => array( 832 $dropdown_query->getNotificationData(), 833 $dropdown_query->getConpherenceData(), 834 ), 835 'globalDragAndDrop' => $upload_enabled, 836 'hisecWarningConfig' => $hisec_warning_config, 837 'consoleConfig' => $console_config, 838 'applicationClass' => $application_class, 839 'applicationSearchIcon' => $application_search_icon, 840 'helpItems' => $application_help, 841 ) + $this->buildAphlictListenConfigData(); 842 } 843 844 private function buildAphlictListenConfigData() { 845 $user = $this->getRequest()->getUser(); 846 $subscriptions = $this->pageObjects; 847 $subscriptions[] = $user->getPHID(); 848 849 return array( 850 'pageObjects' => array_fill_keys($this->pageObjects, true), 851 'subscriptions' => $subscriptions, 852 ); 853 } 854 855 private function getQuicksandURIPatternBlacklist() { 856 $applications = PhabricatorApplication::getAllApplications(); 857 858 $blacklist = array(); 859 foreach ($applications as $application) { 860 $blacklist[] = $application->getQuicksandURIPatternBlacklist(); 861 } 862 863 // See T4340. Currently, Phortune and Auth both require pulling in external 864 // Javascript (for Stripe card management and Recaptcha, respectively). 865 // This can put us in a position where the user loads a page with a 866 // restrictive Content-Security-Policy, then uses Quicksand to navigate to 867 // a page which needs to load external scripts. For now, just blacklist 868 // these entire applications since we aren't giving up anything 869 // significant by doing so. 870 871 $blacklist[] = array( 872 '/phortune/.*', 873 '/auth/.*', 874 ); 875 876 return array_mergev($blacklist); 877 } 878 879 private function getUserPreference($key, $default = null) { 880 $request = $this->getRequest(); 881 if (!$request) { 882 return $default; 883 } 884 885 $user = $request->getUser(); 886 if (!$user) { 887 return $default; 888 } 889 890 return $user->getUserSetting($key); 891 } 892 893 public function produceAphrontResponse() { 894 $controller = $this->getController(); 895 896 $viewer = $this->getUser(); 897 if ($viewer && $viewer->getPHID()) { 898 $object_phids = $this->pageObjects; 899 foreach ($object_phids as $object_phid) { 900 PhabricatorFeedStoryNotification::updateObjectNotificationViews( 901 $viewer, 902 $object_phid); 903 } 904 } 905 906 if ($this->getRequest()->isQuicksand()) { 907 $content = $this->renderForQuicksand(); 908 $response = id(new AphrontAjaxResponse()) 909 ->setContent($content); 910 } else { 911 // See T13247. Try to find some navigational menu items to create a 912 // mobile navigation menu from. 913 $application_menu = $controller->buildApplicationMenu(); 914 if (!$application_menu) { 915 $navigation = $this->getNavigation(); 916 if ($navigation) { 917 $application_menu = $navigation->getMenu(); 918 } 919 } 920 $this->applicationMenu = $application_menu; 921 922 $content = $this->render(); 923 924 $response = id(new AphrontWebpageResponse()) 925 ->setContent($content) 926 ->setFrameable($this->getFrameable()); 927 } 928 929 return $response; 930 } 931 932}