aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorKim <1877318+kimsible@users.noreply.github.com>2020-07-28 15:18:38 +0200
committerGitHub <noreply@github.com>2020-07-28 15:18:38 +0200
commitdfe3f7b72ef46401206f6f461077a7984a0c72f0 (patch)
tree775a747f2dd4bc098afc2ec254792e2e8e8cbbb4
parent0579dee3b29e301838387f53b91b58bff2ffb19a (diff)
downloadPeerTube-dfe3f7b72ef46401206f6f461077a7984a0c72f0.tar.gz
PeerTube-dfe3f7b72ef46401206f6f461077a7984a0c72f0.tar.zst
PeerTube-dfe3f7b72ef46401206f6f461077a7984a0c72f0.zip
Add alert and hide upload view when no upload is possible (#2966)
* Add alert and hide upload view when no upload is possible * Add about instance link to alert * Hide videos and imports links when no upload is possible * Correct curly spacing lint * Put logic canUpload to User model + add isHidden param to to-menu-dropdown * Use canSeeVideoLinks from user model * Rename and change logic canUpload to isUploadDisabled * Use isDisplayed() method intead of isHidden value * Refactor client and check videos count using quota Co-authored-by: kimsible <kimsible@users.noreply.github.com> Co-authored-by: Chocobozzz <me@florianbigard.com>
-rw-r--r--client/src/app/+admin/admin.component.ts20
-rw-r--r--client/src/app/+my-account/my-account.component.ts33
-rw-r--r--client/src/app/+videos/+video-edit/video-add.component.html12
-rw-r--r--client/src/app/+videos/+video-edit/video-add.component.scss28
-rw-r--r--client/src/app/+videos/+video-edit/video-add.component.ts8
-rw-r--r--client/src/app/core/auth/auth-user.model.ts29
-rw-r--r--client/src/app/core/users/user.model.ts4
-rw-r--r--client/src/app/menu/menu.component.html2
-rw-r--r--client/src/app/menu/menu.component.ts67
-rw-r--r--client/src/app/shared/shared-main/misc/top-menu-dropdown.component.html30
-rw-r--r--client/src/app/shared/shared-main/misc/top-menu-dropdown.component.ts10
11 files changed, 186 insertions, 57 deletions
diff --git a/client/src/app/+admin/admin.component.ts b/client/src/app/+admin/admin.component.ts
index 319d50cda..7fc83351b 100644
--- a/client/src/app/+admin/admin.component.ts
+++ b/client/src/app/+admin/admin.component.ts
@@ -74,12 +74,24 @@ export class AdminComponent implements OnInit {
74 }) 74 })
75 } 75 }
76 76
77 if (this.hasUsersRight()) this.menuEntries.push({ label: this.i18n('Users'), routerLink: '/admin/users' }) 77 if (this.hasUsersRight()) {
78 this.menuEntries.push({ label: this.i18n('Users'), routerLink: '/admin/users' })
79 }
80
78 if (this.hasServerFollowRight()) this.menuEntries.push(federationItems) 81 if (this.hasServerFollowRight()) this.menuEntries.push(federationItems)
79 if (this.hasAbusesRight() || this.hasVideoBlocklistRight()) this.menuEntries.push(moderationItems) 82 if (this.hasAbusesRight() || this.hasVideoBlocklistRight()) this.menuEntries.push(moderationItems)
80 if (this.hasConfigRight()) this.menuEntries.push({ label: this.i18n('Configuration'), routerLink: '/admin/config' }) 83
81 if (this.hasPluginsRight()) this.menuEntries.push({ label: this.i18n('Plugins/Themes'), routerLink: '/admin/plugins' }) 84 if (this.hasConfigRight()) {
82 if (this.hasJobsRight() || this.hasLogsRight() || this.hasDebugRight()) this.menuEntries.push({ label: this.i18n('System'), routerLink: '/admin/system' }) 85 this.menuEntries.push({ label: this.i18n('Configuration'), routerLink: '/admin/config' })
86 }
87
88 if (this.hasPluginsRight()) {
89 this.menuEntries.push({ label: this.i18n('Plugins/Themes'), routerLink: '/admin/plugins' })
90 }
91
92 if (this.hasJobsRight() || this.hasLogsRight() || this.hasDebugRight()) {
93 this.menuEntries.push({ label: this.i18n('System'), routerLink: '/admin/system' })
94 }
83 } 95 }
84 96
85 hasUsersRight () { 97 hasUsersRight () {
diff --git a/client/src/app/+my-account/my-account.component.ts b/client/src/app/+my-account/my-account.component.ts
index 07cab37fc..5b2238f5a 100644
--- a/client/src/app/+my-account/my-account.component.ts
+++ b/client/src/app/+my-account/my-account.component.ts
@@ -1,5 +1,5 @@
1import { Component, OnInit } from '@angular/core' 1import { Component, OnInit } from '@angular/core'
2import { ServerService } from '@app/core' 2import { AuthService, ServerService, AuthUser } from '@app/core'
3import { I18n } from '@ngx-translate/i18n-polyfill' 3import { I18n } from '@ngx-translate/i18n-polyfill'
4import { ServerConfig } from '@shared/models' 4import { ServerConfig } from '@shared/models'
5import { TopMenuDropdownParam } from '../shared/shared-main/misc/top-menu-dropdown.component' 5import { TopMenuDropdownParam } from '../shared/shared-main/misc/top-menu-dropdown.component'
@@ -11,11 +11,13 @@ import { TopMenuDropdownParam } from '../shared/shared-main/misc/top-menu-dropdo
11}) 11})
12export class MyAccountComponent implements OnInit { 12export class MyAccountComponent implements OnInit {
13 menuEntries: TopMenuDropdownParam[] = [] 13 menuEntries: TopMenuDropdownParam[] = []
14 user: AuthUser
14 15
15 private serverConfig: ServerConfig 16 private serverConfig: ServerConfig
16 17
17 constructor ( 18 constructor (
18 private serverService: ServerService, 19 private serverService: ServerService,
20 private authService: AuthService,
19 private i18n: I18n 21 private i18n: I18n
20 ) { } 22 ) { }
21 23
@@ -24,6 +26,20 @@ export class MyAccountComponent implements OnInit {
24 this.serverService.getConfig() 26 this.serverService.getConfig()
25 .subscribe(config => this.serverConfig = config) 27 .subscribe(config => this.serverConfig = config)
26 28
29 this.user = this.authService.getUser()
30
31 this.authService.userInformationLoaded.subscribe(
32 () => this.buildMenu()
33 )
34 }
35
36 isVideoImportEnabled () {
37 const importConfig = this.serverConfig.import.videos
38
39 return importConfig.http.enabled || importConfig.torrent.enabled
40 }
41
42 private buildMenu () {
27 const libraryEntries: TopMenuDropdownParam = { 43 const libraryEntries: TopMenuDropdownParam = {
28 label: this.i18n('My library'), 44 label: this.i18n('My library'),
29 children: [ 45 children: [
@@ -35,7 +51,8 @@ export class MyAccountComponent implements OnInit {
35 { 51 {
36 label: this.i18n('My videos'), 52 label: this.i18n('My videos'),
37 routerLink: '/my-account/videos', 53 routerLink: '/my-account/videos',
38 iconName: 'videos' 54 iconName: 'videos',
55 isDisplayed: () => this.user.canSeeVideosLink
39 }, 56 },
40 { 57 {
41 label: this.i18n('My playlists'), 58 label: this.i18n('My playlists'),
@@ -45,7 +62,7 @@ export class MyAccountComponent implements OnInit {
45 { 62 {
46 label: this.i18n('My subscriptions'), 63 label: this.i18n('My subscriptions'),
47 routerLink: '/my-account/subscriptions', 64 routerLink: '/my-account/subscriptions',
48 iconName: 'subscriptions' 65 iconName: 'inbox-full'
49 }, 66 },
50 { 67 {
51 label: this.i18n('My history'), 68 label: this.i18n('My history'),
@@ -59,7 +76,8 @@ export class MyAccountComponent implements OnInit {
59 libraryEntries.children.push({ 76 libraryEntries.children.push({
60 label: 'My imports', 77 label: 'My imports',
61 routerLink: '/my-account/video-imports', 78 routerLink: '/my-account/video-imports',
62 iconName: 'cloud-download' 79 iconName: 'cloud-download',
80 isDisplayed: () => this.user.canSeeVideosLink
63 }) 81 })
64 } 82 }
65 83
@@ -97,11 +115,4 @@ export class MyAccountComponent implements OnInit {
97 miscEntries 115 miscEntries
98 ] 116 ]
99 } 117 }
100
101 isVideoImportEnabled () {
102 const importConfig = this.serverConfig.import.videos
103
104 return importConfig.http.enabled || importConfig.torrent.enabled
105 }
106
107} 118}
diff --git a/client/src/app/+videos/+video-edit/video-add.component.html b/client/src/app/+videos/+video-edit/video-add.component.html
index 5690ac37f..14d41f95b 100644
--- a/client/src/app/+videos/+video-edit/video-add.component.html
+++ b/client/src/app/+videos/+video-edit/video-add.component.html
@@ -1,4 +1,12 @@
1<div class="margin-content"> 1<div *ngIf="user.isUploadDisabled()" class="no-upload">
2 <div class="alert alert-warning">
3 <div i18n>Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</div>
4 <a i18n routerLink="/about/instance" class="about-link">Read instance rules for help</a>
5 </div>
6 <img src="/client/assets/images/mascot/defeated.svg" alt="defeated mascot">
7</div>
8
9<div *ngIf="!user.isUploadDisabled()" class="margin-content">
2 <div class="alert alert-warning" *ngIf="isRootUser()" i18n> 10 <div class="alert alert-warning" *ngIf="isRootUser()" i18n>
3 We recommend you to not use the <strong>root</strong> user to publish your videos, since it's the super-admin account of your instance. 11 We recommend you to not use the <strong>root</strong> user to publish your videos, since it's the super-admin account of your instance.
4 <br /> 12 <br />
@@ -45,4 +53,4 @@
45 </div> 53 </div>
46 54
47 <div [ngbNavOutlet]="nav"></div> 55 <div [ngbNavOutlet]="nav"></div>
48</div> 56</div> \ No newline at end of file
diff --git a/client/src/app/+videos/+video-edit/video-add.component.scss b/client/src/app/+videos/+video-edit/video-add.component.scss
index f9977bda0..5db9e823d 100644
--- a/client/src/app/+videos/+video-edit/video-add.component.scss
+++ b/client/src/app/+videos/+video-edit/video-add.component.scss
@@ -6,6 +6,34 @@ $border-type: solid;
6$border-color: #EAEAEA; 6$border-color: #EAEAEA;
7$nav-link-height: 40px; 7$nav-link-height: 40px;
8 8
9.no-upload {
10 height: 100%;
11 width: 100%;
12 text-align: center;
13
14 .about-link {
15 @include peertube-button-link;
16 @include orange-button;
17
18 height: fit-content;
19 margin-top: 10px;
20 }
21
22 img {
23 margin-top: 10px;
24 margin-bottom: 75px;
25 width: 220px;
26 height: auto;
27 }
28
29 @media screen and (max-height: 600px) {
30 img {
31 margin-top: 5px;
32 width: 160px;
33 }
34 }
35}
36
9.margin-content { 37.margin-content {
10 padding-top: 20px; 38 padding-top: 20px;
11} 39}
diff --git a/client/src/app/+videos/+video-edit/video-add.component.ts b/client/src/app/+videos/+video-edit/video-add.component.ts
index 016791d59..94e85efc1 100644
--- a/client/src/app/+videos/+video-edit/video-add.component.ts
+++ b/client/src/app/+videos/+video-edit/video-add.component.ts
@@ -1,5 +1,5 @@
1import { Component, HostListener, OnInit, ViewChild } from '@angular/core' 1import { Component, HostListener, OnInit, ViewChild } from '@angular/core'
2import { AuthService, CanComponentDeactivate, ServerService, User } from '@app/core' 2import { AuthService, AuthUser, CanComponentDeactivate, ServerService } from '@app/core'
3import { ServerConfig } from '@shared/models' 3import { ServerConfig } from '@shared/models'
4import { VideoImportTorrentComponent } from './video-add-components/video-import-torrent.component' 4import { VideoImportTorrentComponent } from './video-add-components/video-import-torrent.component'
5import { VideoImportUrlComponent } from './video-add-components/video-import-url.component' 5import { VideoImportUrlComponent } from './video-add-components/video-import-url.component'
@@ -15,7 +15,7 @@ export class VideoAddComponent implements OnInit, CanComponentDeactivate {
15 @ViewChild('videoImportUrl') videoImportUrl: VideoImportUrlComponent 15 @ViewChild('videoImportUrl') videoImportUrl: VideoImportUrlComponent
16 @ViewChild('videoImportTorrent') videoImportTorrent: VideoImportTorrentComponent 16 @ViewChild('videoImportTorrent') videoImportTorrent: VideoImportTorrentComponent
17 17
18 user: User = null 18 user: AuthUser = null
19 19
20 secondStepType: 'upload' | 'import-url' | 'import-torrent' 20 secondStepType: 'upload' | 'import-url' | 'import-torrent'
21 videoName: string 21 videoName: string
@@ -37,6 +37,8 @@ export class VideoAddComponent implements OnInit, CanComponentDeactivate {
37 37
38 this.serverService.getConfig() 38 this.serverService.getConfig()
39 .subscribe(config => this.serverConfig = config) 39 .subscribe(config => this.serverConfig = config)
40
41 this.user = this.auth.getUser()
40 } 42 }
41 43
42 onFirstStepDone (type: 'upload' | 'import-url' | 'import-torrent', videoName: string) { 44 onFirstStepDone (type: 'upload' | 'import-url' | 'import-torrent', videoName: string) {
@@ -80,6 +82,6 @@ export class VideoAddComponent implements OnInit, CanComponentDeactivate {
80 } 82 }
81 83
82 isRootUser () { 84 isRootUser () {
83 return this.auth.getUser().username === 'root' 85 return this.user.username === 'root'
84 } 86 }
85} 87}
diff --git a/client/src/app/core/auth/auth-user.model.ts b/client/src/app/core/auth/auth-user.model.ts
index 4e7801550..88b730938 100644
--- a/client/src/app/core/auth/auth-user.model.ts
+++ b/client/src/app/core/auth/auth-user.model.ts
@@ -1,3 +1,5 @@
1import { Observable, of } from 'rxjs'
2import { map } from 'rxjs/operators'
1import { User } from '@app/core/users/user.model' 3import { User } from '@app/core/users/user.model'
2import { peertubeLocalStorage } from '@app/helpers/peertube-web-storage' 4import { peertubeLocalStorage } from '@app/helpers/peertube-web-storage'
3import { 5import {
@@ -7,7 +9,8 @@ import {
7 NSFWPolicyType, 9 NSFWPolicyType,
8 User as ServerUserModel, 10 User as ServerUserModel,
9 UserRight, 11 UserRight,
10 UserRole 12 UserRole,
13 UserVideoQuota
11} from '@shared/models' 14} from '@shared/models'
12 15
13export type TokenOptions = { 16export type TokenOptions = {
@@ -74,6 +77,8 @@ export class AuthUser extends User implements ServerMyUserModel {
74 tokens: Tokens 77 tokens: Tokens
75 specialPlaylists: MyUserSpecialPlaylist[] 78 specialPlaylists: MyUserSpecialPlaylist[]
76 79
80 canSeeVideosLink = true
81
77 static load () { 82 static load () {
78 const usernameLocalStorage = peertubeLocalStorage.getItem(this.KEYS.USERNAME) 83 const usernameLocalStorage = peertubeLocalStorage.getItem(this.KEYS.USERNAME)
79 if (usernameLocalStorage) { 84 if (usernameLocalStorage) {
@@ -150,4 +155,26 @@ export class AuthUser extends User implements ServerMyUserModel {
150 peertubeLocalStorage.setItem(AuthUser.KEYS.AUTO_PLAY_VIDEO, JSON.stringify(this.autoPlayVideo)) 155 peertubeLocalStorage.setItem(AuthUser.KEYS.AUTO_PLAY_VIDEO, JSON.stringify(this.autoPlayVideo))
151 this.tokens.save() 156 this.tokens.save()
152 } 157 }
158
159 computeCanSeeVideosLink (quotaObservable: Observable<UserVideoQuota>): Observable<boolean> {
160 if (!this.isUploadDisabled()) {
161 this.canSeeVideosLink = true
162 return of(this.canSeeVideosLink)
163 }
164
165 // Check if the user has videos
166 return quotaObservable.pipe(
167 map(({ videoQuotaUsed }) => {
168 if (videoQuotaUsed !== 0) {
169 // User already uploaded videos, so it can see the link
170 this.canSeeVideosLink = true
171 } else {
172 // No videos, no upload so the user don't need to see the videos link
173 this.canSeeVideosLink = false
174 }
175
176 return this.canSeeVideosLink
177 })
178 )
179 }
153} 180}
diff --git a/client/src/app/core/users/user.model.ts b/client/src/app/core/users/user.model.ts
index 31b9c2152..6a56786d9 100644
--- a/client/src/app/core/users/user.model.ts
+++ b/client/src/app/core/users/user.model.ts
@@ -149,4 +149,8 @@ export class User implements UserServerModel {
149 updateAccountAvatar (newAccountAvatar: Avatar) { 149 updateAccountAvatar (newAccountAvatar: Avatar) {
150 this.account.updateAvatar(newAccountAvatar) 150 this.account.updateAvatar(newAccountAvatar)
151 } 151 }
152
153 isUploadDisabled () {
154 return this.videoQuota === 0 || this.videoQuotaDaily === 0
155 }
152} 156}
diff --git a/client/src/app/menu/menu.component.html b/client/src/app/menu/menu.component.html
index 8faa37ed6..71fb2c154 100644
--- a/client/src/app/menu/menu.component.html
+++ b/client/src/app/menu/menu.component.html
@@ -81,7 +81,7 @@
81 <div *ngIf="isLoggedIn" class="panel-block"> 81 <div *ngIf="isLoggedIn" class="panel-block">
82 <div i18n class="block-title">MY LIBRARY</div> 82 <div i18n class="block-title">MY LIBRARY</div>
83 83
84 <a routerLink="/my-account/videos" routerLinkActive="active"> 84 <a *ngIf="user.canSeeVideosLink" routerLink="/my-account/videos" routerLinkActive="active">
85 <my-global-icon iconName="videos" aria-hidden="true"></my-global-icon> 85 <my-global-icon iconName="videos" aria-hidden="true"></my-global-icon>
86 <ng-container i18n>Videos</ng-container> 86 <ng-container i18n>Videos</ng-container>
87 </a> 87 </a>
diff --git a/client/src/app/menu/menu.component.ts b/client/src/app/menu/menu.component.ts
index 0ea251f1c..2c55b9a84 100644
--- a/client/src/app/menu/menu.component.ts
+++ b/client/src/app/menu/menu.component.ts
@@ -1,21 +1,25 @@
1import { HotkeysService } from 'angular2-hotkeys' 1import { HotkeysService } from 'angular2-hotkeys'
2import * as debug from 'debug'
3import { switchMap } from 'rxjs/operators'
2import { Component, OnInit, ViewChild } from '@angular/core' 4import { Component, OnInit, ViewChild } from '@angular/core'
3import { AuthService, AuthStatus, RedirectService, ScreenService, ServerService, User, UserService } from '@app/core' 5import { AuthService, AuthStatus, AuthUser, RedirectService, ScreenService, ServerService, UserService } from '@app/core'
4import { LanguageChooserComponent } from '@app/menu/language-chooser.component' 6import { LanguageChooserComponent } from '@app/menu/language-chooser.component'
5import { QuickSettingsModalComponent } from '@app/modal/quick-settings-modal.component' 7import { QuickSettingsModalComponent } from '@app/modal/quick-settings-modal.component'
6import { I18n } from '@ngx-translate/i18n-polyfill' 8import { I18n } from '@ngx-translate/i18n-polyfill'
7import { ServerConfig, UserRight, VideoConstant } from '@shared/models' 9import { ServerConfig, UserRight, VideoConstant } from '@shared/models'
8 10
11const logger = debug('peertube:menu:MenuComponent')
12
9@Component({ 13@Component({
10 selector: 'my-menu', 14 selector: 'my-menu',
11 templateUrl: './menu.component.html', 15 templateUrl: './menu.component.html',
12 styleUrls: [ './menu.component.scss' ] 16 styleUrls: ['./menu.component.scss']
13}) 17})
14export class MenuComponent implements OnInit { 18export class MenuComponent implements OnInit {
15 @ViewChild('languageChooserModal', { static: true }) languageChooserModal: LanguageChooserComponent 19 @ViewChild('languageChooserModal', { static: true }) languageChooserModal: LanguageChooserComponent
16 @ViewChild('quickSettingsModal', { static: true }) quickSettingsModal: QuickSettingsModalComponent 20 @ViewChild('quickSettingsModal', { static: true }) quickSettingsModal: QuickSettingsModalComponent
17 21
18 user: User 22 user: AuthUser
19 isLoggedIn: boolean 23 isLoggedIn: boolean
20 24
21 userHasAdminAccess = false 25 userHasAdminAccess = false
@@ -25,7 +29,7 @@ export class MenuComponent implements OnInit {
25 29
26 private languages: VideoConstant<string>[] = [] 30 private languages: VideoConstant<string>[] = []
27 private serverConfig: ServerConfig 31 private serverConfig: ServerConfig
28 private routesPerRight: { [ role in UserRight ]?: string } = { 32 private routesPerRight: { [role in UserRight]?: string } = {
29 [UserRight.MANAGE_USERS]: '/admin/users', 33 [UserRight.MANAGE_USERS]: '/admin/users',
30 [UserRight.MANAGE_SERVER_FOLLOW]: '/admin/friends', 34 [UserRight.MANAGE_SERVER_FOLLOW]: '/admin/friends',
31 [UserRight.MANAGE_ABUSES]: '/admin/moderation/abuses', 35 [UserRight.MANAGE_ABUSES]: '/admin/moderation/abuses',
@@ -62,21 +66,30 @@ export class MenuComponent implements OnInit {
62 .subscribe(config => this.serverConfig = config) 66 .subscribe(config => this.serverConfig = config)
63 67
64 this.isLoggedIn = this.authService.isLoggedIn() 68 this.isLoggedIn = this.authService.isLoggedIn()
65 if (this.isLoggedIn === true) this.user = this.authService.getUser() 69 if (this.isLoggedIn === true) {
66 this.computeIsUserHasAdminAccess() 70 this.user = this.authService.getUser()
71 this.computeVideosLink()
72 }
73
74 this.computeAdminAccess()
67 75
68 this.authService.loginChangedSource.subscribe( 76 this.authService.loginChangedSource.subscribe(
69 status => { 77 status => {
70 if (status === AuthStatus.LoggedIn) { 78 if (status === AuthStatus.LoggedIn) {
71 this.isLoggedIn = true 79 this.isLoggedIn = true
72 this.user = this.authService.getUser() 80 this.user = this.authService.getUser()
73 this.computeIsUserHasAdminAccess() 81
74 console.log('Logged in.') 82 this.computeAdminAccess()
83 this.computeVideosLink()
84
85 logger('Logged in.')
75 } else if (status === AuthStatus.LoggedOut) { 86 } else if (status === AuthStatus.LoggedOut) {
76 this.isLoggedIn = false 87 this.isLoggedIn = false
77 this.user = undefined 88 this.user = undefined
78 this.computeIsUserHasAdminAccess() 89
79 console.log('Logged out.') 90 this.computeAdminAccess()
91
92 logger('Logged out.')
80 } else { 93 } else {
81 console.error('Unknown auth status: ' + status) 94 console.error('Unknown auth status: ' + status)
82 } 95 }
@@ -84,15 +97,15 @@ export class MenuComponent implements OnInit {
84 ) 97 )
85 98
86 this.hotkeysService.cheatSheetToggle 99 this.hotkeysService.cheatSheetToggle
87 .subscribe(isOpen => this.helpVisible = isOpen) 100 .subscribe(isOpen => this.helpVisible = isOpen)
88 101
89 this.serverService.getVideoLanguages() 102 this.serverService.getVideoLanguages()
90 .subscribe(languages => { 103 .subscribe(languages => {
91 this.languages = languages 104 this.languages = languages
92 105
93 this.authService.userInformationLoaded 106 this.authService.userInformationLoaded
94 .subscribe(() => this.buildUserLanguages()) 107 .subscribe(() => this.buildUserLanguages())
95 }) 108 })
96 } 109 }
97 110
98 get language () { 111 get language () {
@@ -116,7 +129,7 @@ export class MenuComponent implements OnInit {
116 129
117 isRegistrationAllowed () { 130 isRegistrationAllowed () {
118 return this.serverConfig.signup.allowed && 131 return this.serverConfig.signup.allowed &&
119 this.serverConfig.signup.allowedForCurrentIP 132 this.serverConfig.signup.allowedForCurrentIP
120 } 133 }
121 134
122 getFirstAdminRightAvailable () { 135 getFirstAdminRightAvailable () {
@@ -172,7 +185,7 @@ export class MenuComponent implements OnInit {
172 this.user.webTorrentEnabled = !this.user.webTorrentEnabled 185 this.user.webTorrentEnabled = !this.user.webTorrentEnabled
173 186
174 this.userService.updateMyProfile({ webTorrentEnabled: this.user.webTorrentEnabled }) 187 this.userService.updateMyProfile({ webTorrentEnabled: this.user.webTorrentEnabled })
175 .subscribe(() => this.authService.refreshUserInformation()) 188 .subscribe(() => this.authService.refreshUserInformation())
176 } 189 }
177 190
178 langForLocale (localeId: string) { 191 langForLocale (localeId: string) {
@@ -188,18 +201,28 @@ export class MenuComponent implements OnInit {
188 } 201 }
189 202
190 if (!this.user.videoLanguages) { 203 if (!this.user.videoLanguages) {
191 this.videoLanguages = [ this.i18n('any language') ] 204 this.videoLanguages = [this.i18n('any language')]
192 return 205 return
193 } 206 }
194 207
195 this.videoLanguages = this.user.videoLanguages 208 this.videoLanguages = this.user.videoLanguages
196 .map(locale => this.langForLocale(locale)) 209 .map(locale => this.langForLocale(locale))
197 .map(value => value === undefined ? '?' : value) 210 .map(value => value === undefined ? '?' : value)
198 } 211 }
199 212
200 private computeIsUserHasAdminAccess () { 213 private computeAdminAccess () {
201 const right = this.getFirstAdminRightAvailable() 214 const right = this.getFirstAdminRightAvailable()
202 215
203 this.userHasAdminAccess = right !== undefined 216 this.userHasAdminAccess = right !== undefined
204 } 217 }
218
219 private computeVideosLink () {
220 this.authService.userInformationLoaded
221 .pipe(
222 switchMap(() => this.user.computeCanSeeVideosLink(this.userService.getMyVideoQuotaUsed()))
223 ).subscribe(res => {
224 if (res === true) logger('User can see videos link.')
225 else logger('User cannot see videos link.')
226 })
227 }
205} 228}
diff --git a/client/src/app/shared/shared-main/misc/top-menu-dropdown.component.html b/client/src/app/shared/shared-main/misc/top-menu-dropdown.component.html
index c737b40c7..530b9e376 100644
--- a/client/src/app/shared/shared-main/misc/top-menu-dropdown.component.html
+++ b/client/src/app/shared/shared-main/misc/top-menu-dropdown.component.html
@@ -1,9 +1,9 @@
1<div class="sub-menu" [ngClass]="{ 'no-scroll': isModalOpened }"> 1<div class="sub-menu" [ngClass]="{ 'no-scroll': isModalOpened }">
2 <ng-container *ngFor="let menuEntry of menuEntries; index as id"> 2 <ng-container *ngFor="let menuEntry of menuEntries; index as id">
3 3
4 <a *ngIf="menuEntry.routerLink" [routerLink]="menuEntry.routerLink" routerLinkActive="active" class="title-page title-page-settings">{{ menuEntry.label }}</a> 4 <a *ngIf="menuEntry.routerLink && isDisplayed(menuEntry)" [routerLink]="menuEntry.routerLink" routerLinkActive="active" class="title-page title-page-settings">{{ menuEntry.label }}</a>
5 5
6 <div *ngIf="!menuEntry.routerLink" ngbDropdown class="parent-entry" 6 <div *ngIf="!menuEntry.routerLink && isDisplayed(menuEntry)" ngbDropdown class="parent-entry"
7 #dropdown="ngbDropdown" autoClose="outside"> 7 #dropdown="ngbDropdown" autoClose="outside">
8 <span 8 <span
9 *ngIf="isInSmallView" 9 *ngIf="isInSmallView"
@@ -25,11 +25,15 @@
25 </span> 25 </span>
26 26
27 <div ngbDropdownMenu> 27 <div ngbDropdownMenu>
28 <a *ngFor="let menuChild of menuEntry.children" class="dropdown-item" [ngClass]="{ icon: hasIcons, active: suffixLabels[menuEntry.label] === menuChild.label }" [routerLink]="menuChild.routerLink"> 28 <ng-container *ngFor="let menuChild of menuEntry.children">
29 <my-global-icon *ngIf="menuChild.iconName" [iconName]="menuChild.iconName" aria-hidden="true"></my-global-icon> 29 <a *ngIf="isDisplayed(menuChild)" class="dropdown-item"
30 [ngClass]="{ icon: hasIcons, active: suffixLabels[menuEntry.label] === menuChild.label }"
31 [routerLink]="menuChild.routerLink">
32 <my-global-icon *ngIf="menuChild.iconName" [iconName]="menuChild.iconName" aria-hidden="true"></my-global-icon>
30 33
31 {{ menuChild.label }} 34 {{ menuChild.label }}
32 </a> 35 </a>
36 </ng-container>
33 </div> 37 </div>
34 </div> 38 </div>
35 </ng-container> 39 </ng-container>
@@ -39,13 +43,15 @@
39 <div class="modal-body"> 43 <div class="modal-body">
40 <ng-container *ngFor="let menuEntry of menuEntries; index as id"> 44 <ng-container *ngFor="let menuEntry of menuEntries; index as id">
41 <div [ngClass]="{ hidden: id !== currentMenuEntryIndex }"> 45 <div [ngClass]="{ hidden: id !== currentMenuEntryIndex }">
42 <a *ngFor="let menuChild of menuEntry.children" 46 <ng-container *ngFor="let menuChild of menuEntry.children">
43 [ngClass]="{ icon: hasIcons }" 47 <a *ngIf="isDisplayed(menuChild)"
44 [routerLink]="menuChild.routerLink" routerLinkActive="active" (click)="dismissOtherModals()"> 48 [ngClass]="{ icon: hasIcons }"
45 <my-global-icon *ngIf="menuChild.iconName" [iconName]="menuChild.iconName" aria-hidden="true"></my-global-icon> 49 [routerLink]="menuChild.routerLink" routerLinkActive="active" (click)="dismissOtherModals()">
50 <my-global-icon *ngIf="menuChild.iconName" [iconName]="menuChild.iconName" aria-hidden="true"></my-global-icon>
46 51
47 {{ menuChild.label }} 52 {{ menuChild.label }}
48 </a> 53 </a>
54 </ng-container>
49 </div> 55 </div>
50 </ng-container> 56 </ng-container>
51 </div> 57 </div>
diff --git a/client/src/app/shared/shared-main/misc/top-menu-dropdown.component.ts b/client/src/app/shared/shared-main/misc/top-menu-dropdown.component.ts
index 66f8f7e55..c3cd22307 100644
--- a/client/src/app/shared/shared-main/misc/top-menu-dropdown.component.ts
+++ b/client/src/app/shared/shared-main/misc/top-menu-dropdown.component.ts
@@ -9,12 +9,14 @@ import { NgbDropdown, NgbModal } from '@ng-bootstrap/ng-bootstrap'
9export type TopMenuDropdownParam = { 9export type TopMenuDropdownParam = {
10 label: string 10 label: string
11 routerLink?: string 11 routerLink?: string
12 isDisplayed?: () => boolean // Default: () => true
12 13
13 children?: { 14 children?: {
14 label: string 15 label: string
15 routerLink: string 16 routerLink: string
16
17 iconName?: GlobalIconName 17 iconName?: GlobalIconName
18
19 isDisplayed?: () => boolean // Default: () => true
18 }[] 20 }[]
19} 21}
20 22
@@ -92,6 +94,12 @@ export class TopMenuDropdownComponent implements OnInit, OnDestroy {
92 this.modalService.dismissAll() 94 this.modalService.dismissAll()
93 } 95 }
94 96
97 isDisplayed (obj: { isDisplayed?: () => boolean }) {
98 if (typeof obj.isDisplayed !== 'function') return true
99
100 return obj.isDisplayed()
101 }
102
95 private updateChildLabels (path: string) { 103 private updateChildLabels (path: string) {
96 this.suffixLabels = {} 104 this.suffixLabels = {}
97 105