Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/phabricator
Path: blob/master/src/view/page/menu/PhabricatorMainMenuView.php
12242 views
1
<?php
2
3
final class PhabricatorMainMenuView extends AphrontView {
4
5
private $controller;
6
private $applicationMenu;
7
8
public function setApplicationMenu(PHUIListView $application_menu) {
9
$this->applicationMenu = $application_menu;
10
return $this;
11
}
12
13
public function getApplicationMenu() {
14
return $this->applicationMenu;
15
}
16
17
public function setController(PhabricatorController $controller) {
18
$this->controller = $controller;
19
return $this;
20
}
21
22
public function getController() {
23
return $this->controller;
24
}
25
26
private static function getFavicons() {
27
$refs = array();
28
29
$refs['favicon'] = id(new PhabricatorFaviconRef())
30
->setWidth(64)
31
->setHeight(64);
32
33
$refs['message_favicon'] = id(new PhabricatorFaviconRef())
34
->setWidth(64)
35
->setHeight(64)
36
->setEmblems(
37
array(
38
'dot-pink',
39
null,
40
null,
41
null,
42
));
43
44
id(new PhabricatorFaviconRefQuery())
45
->withRefs($refs)
46
->execute();
47
48
return mpull($refs, 'getURI');
49
}
50
51
public function render() {
52
$viewer = $this->getViewer();
53
54
require_celerity_resource('phabricator-main-menu-view');
55
56
$header_id = celerity_generate_unique_node_id();
57
$menu_bar = array();
58
$alerts = array();
59
$search_button = '';
60
$app_button = '';
61
$aural = null;
62
63
$is_full = $this->isFullSession($viewer);
64
65
if ($is_full) {
66
list($menu, $dropdowns, $aural) = $this->renderNotificationMenu();
67
if (array_filter($menu)) {
68
$alerts[] = $menu;
69
}
70
$menu_bar = array_merge($menu_bar, $dropdowns);
71
$app_button = $this->renderApplicationMenuButton();
72
$search_button = $this->renderSearchMenuButton($header_id);
73
} else if (!$viewer->isLoggedIn()) {
74
$app_button = $this->renderApplicationMenuButton();
75
if (PhabricatorEnv::getEnvConfig('policy.allow-public')) {
76
$search_button = $this->renderSearchMenuButton($header_id);
77
}
78
}
79
80
if ($search_button) {
81
$search_menu = $this->renderPhabricatorSearchMenu();
82
} else {
83
$search_menu = null;
84
}
85
86
if ($alerts) {
87
$alerts = javelin_tag(
88
'div',
89
array(
90
'class' => 'phabricator-main-menu-alerts',
91
'aural' => false,
92
),
93
$alerts);
94
}
95
96
if ($aural) {
97
$aural = javelin_tag(
98
'span',
99
array(
100
'aural' => true,
101
),
102
phutil_implode_html(' ', $aural));
103
}
104
105
$extensions = PhabricatorMainMenuBarExtension::getAllEnabledExtensions();
106
foreach ($extensions as $extension) {
107
$extension
108
->setViewer($viewer)
109
->setIsFullSession($is_full);
110
111
$controller = $this->getController();
112
if ($controller) {
113
$extension->setController($controller);
114
$application = $controller->getCurrentApplication();
115
if ($application) {
116
$extension->setApplication($application);
117
}
118
}
119
}
120
121
if (!$is_full) {
122
foreach ($extensions as $key => $extension) {
123
if ($extension->shouldRequireFullSession()) {
124
unset($extensions[$key]);
125
}
126
}
127
}
128
129
foreach ($extensions as $key => $extension) {
130
if (!$extension->isExtensionEnabledForViewer($extension->getViewer())) {
131
unset($extensions[$key]);
132
}
133
}
134
135
$menus = array();
136
foreach ($extensions as $extension) {
137
foreach ($extension->buildMainMenus() as $menu) {
138
$menus[] = $menu;
139
}
140
}
141
142
// Because we display these with "float: right", reverse their order before
143
// rendering them into the document so that the extension order and display
144
// order are the same.
145
$menus = array_reverse($menus);
146
147
foreach ($menus as $menu) {
148
$menu_bar[] = $menu;
149
}
150
151
$classes = array();
152
$classes[] = 'phabricator-main-menu';
153
$classes[] = 'phabricator-main-menu-background';
154
155
return phutil_tag(
156
'div',
157
array(
158
'class' => implode(' ', $classes),
159
'id' => $header_id,
160
),
161
array(
162
$app_button,
163
$search_button,
164
$this->renderPhabricatorLogo(),
165
$alerts,
166
$aural,
167
$search_menu,
168
$menu_bar,
169
));
170
}
171
172
private function renderSearch() {
173
$viewer = $this->getViewer();
174
175
$result = null;
176
177
$keyboard_config = array(
178
'helpURI' => '/help/keyboardshortcut/',
179
);
180
181
if ($viewer->isLoggedIn()) {
182
$show_search = $viewer->isUserActivated();
183
} else {
184
$show_search = PhabricatorEnv::getEnvConfig('policy.allow-public');
185
}
186
187
if ($show_search) {
188
$search = new PhabricatorMainMenuSearchView();
189
$search->setViewer($viewer);
190
191
$application = null;
192
$controller = $this->getController();
193
if ($controller) {
194
$application = $controller->getCurrentApplication();
195
}
196
if ($application) {
197
$search->setApplication($application);
198
}
199
200
$result = $search;
201
$keyboard_config['searchID'] = $search->getID();
202
}
203
204
$keyboard_config['pht'] = array(
205
'/' => pht('Give keyboard focus to the search box.'),
206
'?' => pht('Show keyboard shortcut help for the current page.'),
207
);
208
209
Javelin::initBehavior(
210
'phabricator-keyboard-shortcuts',
211
$keyboard_config);
212
213
if ($result) {
214
$result = id(new PHUIListItemView())
215
->addClass('phabricator-main-menu-search')
216
->appendChild($result);
217
}
218
219
return $result;
220
}
221
222
public function renderApplicationMenuButton() {
223
$dropdown = $this->renderApplicationMenu();
224
if (!$dropdown) {
225
return null;
226
}
227
228
return id(new PHUIButtonView())
229
->setTag('a')
230
->setHref('#')
231
->setIcon('fa-bars')
232
->addClass('phabricator-core-user-menu')
233
->addClass('phabricator-core-user-mobile-menu')
234
->setNoCSS(true)
235
->setDropdownMenu($dropdown)
236
->setAuralLabel(pht('Page Menu'));
237
}
238
239
private function renderApplicationMenu() {
240
$viewer = $this->getViewer();
241
$view = $this->getApplicationMenu();
242
if ($view) {
243
$items = $view->getItems();
244
$view = id(new PhabricatorActionListView())
245
->setViewer($viewer);
246
foreach ($items as $item) {
247
$view->addAction(
248
id(new PhabricatorActionView())
249
->setName($item->getName())
250
->setHref($item->getHref())
251
->setType($item->getType()));
252
}
253
}
254
return $view;
255
}
256
257
public function renderSearchMenuButton($header_id) {
258
$button_id = celerity_generate_unique_node_id();
259
return javelin_tag(
260
'a',
261
array(
262
'class' => 'phabricator-main-menu-search-button '.
263
'phabricator-expand-application-menu',
264
'sigil' => 'jx-toggle-class',
265
'meta' => array(
266
'map' => array(
267
$header_id => 'phabricator-search-menu-expanded',
268
$button_id => 'menu-icon-selected',
269
),
270
),
271
),
272
phutil_tag(
273
'span',
274
array(
275
'class' => 'phabricator-menu-button-icon phui-icon-view '.
276
'phui-font-fa fa-search',
277
'id' => $button_id,
278
),
279
''));
280
}
281
282
private function renderPhabricatorSearchMenu() {
283
284
$view = new PHUIListView();
285
$view->addClass('phabricator-search-menu');
286
287
$search = $this->renderSearch();
288
if ($search) {
289
$view->addMenuItem($search);
290
}
291
292
return $view;
293
}
294
295
private function renderPhabricatorLogo() {
296
$custom_header = PhabricatorCustomLogoConfigType::getLogoImagePHID();
297
298
$logo_style = array();
299
if ($custom_header) {
300
$cache = PhabricatorCaches::getImmutableCache();
301
$cache_key_logo = 'ui.custom-header.logo-phid.v3.'.$custom_header;
302
303
$logo_uri = $cache->getKey($cache_key_logo);
304
if (!$logo_uri) {
305
// NOTE: If the file policy has been changed to be restrictive, we'll
306
// miss here and just show the default logo. The cache will fill later
307
// when someone who can see the file loads the page. This might be a
308
// little spooky, see T11982.
309
$files = id(new PhabricatorFileQuery())
310
->setViewer($this->getViewer())
311
->withPHIDs(array($custom_header))
312
->execute();
313
$file = head($files);
314
if ($file) {
315
$logo_uri = $file->getViewURI();
316
$cache->setKey($cache_key_logo, $logo_uri);
317
}
318
}
319
320
if ($logo_uri) {
321
$logo_style[] = 'background-size: 40px 40px;';
322
$logo_style[] = 'background-position: 0 0;';
323
$logo_style[] = 'background-image: url('.$logo_uri.')';
324
}
325
}
326
327
$logo_node = phutil_tag(
328
'span',
329
array(
330
'class' => 'phabricator-main-menu-eye',
331
'style' => implode(' ', $logo_style),
332
));
333
334
335
$wordmark_text = PhabricatorCustomLogoConfigType::getLogoWordmark();
336
if ($wordmark_text === null || !strlen($wordmark_text)) {
337
$wordmark_text = PlatformSymbols::getPlatformServerName();
338
}
339
340
$wordmark_node = phutil_tag(
341
'span',
342
array(
343
'class' => 'phabricator-wordmark',
344
),
345
$wordmark_text);
346
347
return phutil_tag(
348
'a',
349
array(
350
'class' => 'phabricator-main-menu-brand',
351
'href' => '/',
352
),
353
array(
354
javelin_tag(
355
'span',
356
array(
357
'aural' => true,
358
),
359
pht('Home')),
360
$logo_node,
361
$wordmark_node,
362
));
363
}
364
365
private function renderNotificationMenu() {
366
$viewer = $this->getViewer();
367
368
require_celerity_resource('phabricator-notification-css');
369
require_celerity_resource('phabricator-notification-menu-css');
370
371
$container_classes = array('alert-notifications');
372
$aural = array();
373
374
$dropdown_query = id(new AphlictDropdownDataQuery())
375
->setViewer($viewer);
376
$dropdown_data = $dropdown_query->execute();
377
378
$message_tag = '';
379
$message_notification_dropdown = '';
380
$conpherence_app = 'PhabricatorConpherenceApplication';
381
$conpherence_data = $dropdown_data[$conpherence_app];
382
if ($conpherence_data['isInstalled']) {
383
$message_id = celerity_generate_unique_node_id();
384
$message_count_id = celerity_generate_unique_node_id();
385
$message_dropdown_id = celerity_generate_unique_node_id();
386
387
$message_count_number = $conpherence_data['rawCount'];
388
389
if ($message_count_number) {
390
$aural[] = phutil_tag(
391
'a',
392
array(
393
'href' => '/conpherence/',
394
),
395
pht(
396
'%s unread messages.',
397
new PhutilNumber($message_count_number)));
398
} else {
399
$aural[] = pht('No messages.');
400
}
401
402
$message_count_tag = phutil_tag(
403
'span',
404
array(
405
'id' => $message_count_id,
406
'class' => 'phabricator-main-menu-message-count',
407
),
408
$conpherence_data['count']);
409
410
$message_icon_tag = javelin_tag(
411
'span',
412
array(
413
'class' => 'phabricator-main-menu-message-icon phui-icon-view '.
414
'phui-font-fa fa-comments',
415
'sigil' => 'menu-icon',
416
),
417
'');
418
419
if ($message_count_number) {
420
$container_classes[] = 'message-unread';
421
}
422
423
$message_tag = phutil_tag(
424
'a',
425
array(
426
'href' => '/conpherence/',
427
'class' => implode(' ', $container_classes),
428
'id' => $message_id,
429
),
430
array(
431
$message_icon_tag,
432
$message_count_tag,
433
));
434
435
Javelin::initBehavior(
436
'aphlict-dropdown',
437
array(
438
'bubbleID' => $message_id,
439
'countID' => $message_count_id,
440
'dropdownID' => $message_dropdown_id,
441
'loadingText' => pht('Loading...'),
442
'uri' => '/conpherence/panel/',
443
'countType' => $conpherence_data['countType'],
444
'countNumber' => $message_count_number,
445
'unreadClass' => 'message-unread',
446
) + self::getFavicons());
447
448
$message_notification_dropdown = javelin_tag(
449
'div',
450
array(
451
'id' => $message_dropdown_id,
452
'class' => 'phabricator-notification-menu',
453
'sigil' => 'phabricator-notification-menu',
454
'style' => 'display: none;',
455
),
456
'');
457
}
458
459
$bubble_tag = '';
460
$notification_dropdown = '';
461
$notification_app = 'PhabricatorNotificationsApplication';
462
$notification_data = $dropdown_data[$notification_app];
463
if ($notification_data['isInstalled']) {
464
$count_id = celerity_generate_unique_node_id();
465
$dropdown_id = celerity_generate_unique_node_id();
466
$bubble_id = celerity_generate_unique_node_id();
467
468
$count_number = $notification_data['rawCount'];
469
470
if ($count_number) {
471
$aural[] = phutil_tag(
472
'a',
473
array(
474
'href' => '/notification/',
475
),
476
pht(
477
'%s unread notifications.',
478
new PhutilNumber($count_number)));
479
} else {
480
$aural[] = pht('No notifications.');
481
}
482
483
$count_tag = phutil_tag(
484
'span',
485
array(
486
'id' => $count_id,
487
'class' => 'phabricator-main-menu-alert-count',
488
),
489
$notification_data['count']);
490
491
$icon_tag = javelin_tag(
492
'span',
493
array(
494
'class' => 'phabricator-main-menu-alert-icon phui-icon-view '.
495
'phui-font-fa fa-bell',
496
'sigil' => 'menu-icon',
497
),
498
'');
499
500
if ($count_number) {
501
$container_classes[] = 'alert-unread';
502
}
503
504
$bubble_tag = phutil_tag(
505
'a',
506
array(
507
'href' => '/notification/',
508
'class' => implode(' ', $container_classes),
509
'id' => $bubble_id,
510
),
511
array($icon_tag, $count_tag));
512
513
Javelin::initBehavior(
514
'aphlict-dropdown',
515
array(
516
'bubbleID' => $bubble_id,
517
'countID' => $count_id,
518
'dropdownID' => $dropdown_id,
519
'loadingText' => pht('Loading...'),
520
'uri' => '/notification/panel/',
521
'countType' => $notification_data['countType'],
522
'countNumber' => $count_number,
523
'unreadClass' => 'alert-unread',
524
) + self::getFavicons());
525
526
$notification_dropdown = javelin_tag(
527
'div',
528
array(
529
'id' => $dropdown_id,
530
'class' => 'phabricator-notification-menu',
531
'sigil' => 'phabricator-notification-menu',
532
'style' => 'display: none;',
533
),
534
'');
535
}
536
537
// Admin Level Urgent Notification Channel
538
$setup_tag = '';
539
$setup_notification_dropdown = '';
540
if ($viewer && $viewer->getIsAdmin()) {
541
$open = PhabricatorSetupCheck::getOpenSetupIssueKeys();
542
if ($open) {
543
$setup_id = celerity_generate_unique_node_id();
544
$setup_count_id = celerity_generate_unique_node_id();
545
$setup_dropdown_id = celerity_generate_unique_node_id();
546
547
$setup_count_number = count($open);
548
549
if ($setup_count_number) {
550
$aural[] = phutil_tag(
551
'a',
552
array(
553
'href' => '/config/issue/',
554
),
555
pht(
556
'%s unresolved issues.',
557
new PhutilNumber($setup_count_number)));
558
} else {
559
$aural[] = pht('No issues.');
560
}
561
562
$setup_count_tag = phutil_tag(
563
'span',
564
array(
565
'id' => $setup_count_id,
566
'class' => 'phabricator-main-menu-setup-count',
567
),
568
$setup_count_number);
569
570
$setup_icon_tag = javelin_tag(
571
'span',
572
array(
573
'class' => 'phabricator-main-menu-setup-icon phui-icon-view '.
574
'phui-font-fa fa-exclamation-circle',
575
'sigil' => 'menu-icon',
576
),
577
'');
578
579
if ($setup_count_number) {
580
$container_classes[] = 'setup-unread';
581
}
582
583
$setup_tag = phutil_tag(
584
'a',
585
array(
586
'href' => '/config/issue/',
587
'class' => implode(' ', $container_classes),
588
'id' => $setup_id,
589
),
590
array(
591
$setup_icon_tag,
592
$setup_count_tag,
593
));
594
595
Javelin::initBehavior(
596
'aphlict-dropdown',
597
array(
598
'bubbleID' => $setup_id,
599
'countID' => $setup_count_id,
600
'dropdownID' => $setup_dropdown_id,
601
'loadingText' => pht('Loading...'),
602
'uri' => '/config/issue/panel/',
603
'countType' => null,
604
'countNumber' => null,
605
'unreadClass' => 'setup-unread',
606
) + self::getFavicons());
607
608
$setup_notification_dropdown = javelin_tag(
609
'div',
610
array(
611
'id' => $setup_dropdown_id,
612
'class' => 'phabricator-notification-menu',
613
'sigil' => 'phabricator-notification-menu',
614
'style' => 'display: none;',
615
),
616
'');
617
}
618
}
619
620
$user_dropdown = null;
621
$user_tag = null;
622
if ($viewer->isLoggedIn()) {
623
if (!$viewer->getIsEmailVerified()) {
624
$bubble_id = celerity_generate_unique_node_id();
625
$count_id = celerity_generate_unique_node_id();
626
$dropdown_id = celerity_generate_unique_node_id();
627
628
$settings_uri = id(new PhabricatorEmailAddressesSettingsPanel())
629
->setViewer($viewer)
630
->setUser($viewer)
631
->getPanelURI();
632
633
$user_icon = javelin_tag(
634
'span',
635
array(
636
'class' => 'phabricator-main-menu-setup-icon phui-icon-view '.
637
'phui-font-fa fa-user',
638
'sigil' => 'menu-icon',
639
));
640
641
$user_count = javelin_tag(
642
'span',
643
array(
644
'class' => 'phabricator-main-menu-setup-count',
645
'id' => $count_id,
646
),
647
1);
648
649
$user_tag = phutil_tag(
650
'a',
651
array(
652
'href' => $settings_uri,
653
'class' => 'setup-unread',
654
'id' => $bubble_id,
655
),
656
array(
657
$user_icon,
658
$user_count,
659
));
660
661
Javelin::initBehavior(
662
'aphlict-dropdown',
663
array(
664
'bubbleID' => $bubble_id,
665
'countID' => $count_id,
666
'dropdownID' => $dropdown_id,
667
'loadingText' => pht('Loading...'),
668
'uri' => '/settings/issue/',
669
'unreadClass' => 'setup-unread',
670
));
671
672
$user_dropdown = javelin_tag(
673
'div',
674
array(
675
'id' => $dropdown_id,
676
'class' => 'phabricator-notification-menu',
677
'sigil' => 'phabricator-notification-menu',
678
'style' => 'display: none;',
679
));
680
}
681
}
682
683
$dropdowns = array(
684
$notification_dropdown,
685
$message_notification_dropdown,
686
$setup_notification_dropdown,
687
$user_dropdown,
688
);
689
690
return array(
691
array(
692
$bubble_tag,
693
$message_tag,
694
$setup_tag,
695
$user_tag,
696
),
697
$dropdowns,
698
$aural,
699
);
700
}
701
702
private function isFullSession(PhabricatorUser $viewer) {
703
if (!$viewer->isLoggedIn()) {
704
return false;
705
}
706
707
if (!$viewer->isUserActivated()) {
708
return false;
709
}
710
711
if (!$viewer->hasSession()) {
712
return false;
713
}
714
715
$session = $viewer->getSession();
716
if ($session->getIsPartial()) {
717
return false;
718
}
719
720
if (!$session->getSignedLegalpadDocuments()) {
721
return false;
722
}
723
724
$mfa_key = 'security.require-multi-factor-auth';
725
$need_mfa = PhabricatorEnv::getEnvConfig($mfa_key);
726
if ($need_mfa) {
727
$have_mfa = $viewer->getIsEnrolledInMultiFactor();
728
if (!$have_mfa) {
729
return false;
730
}
731
}
732
733
return true;
734
}
735
736
}
737
738