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