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 {
})
}
- if (this.hasUsersRight()) this.menuEntries.push({ label: this.i18n('Users'), routerLink: '/admin/users' })
+ if (this.hasUsersRight()) {
+ this.menuEntries.push({ label: this.i18n('Users'), routerLink: '/admin/users' })
+ }
+
if (this.hasServerFollowRight()) this.menuEntries.push(federationItems)
if (this.hasAbusesRight() || this.hasVideoBlocklistRight()) this.menuEntries.push(moderationItems)
- if (this.hasConfigRight()) this.menuEntries.push({ label: this.i18n('Configuration'), routerLink: '/admin/config' })
- if (this.hasPluginsRight()) this.menuEntries.push({ label: this.i18n('Plugins/Themes'), routerLink: '/admin/plugins' })
- if (this.hasJobsRight() || this.hasLogsRight() || this.hasDebugRight()) this.menuEntries.push({ label: this.i18n('System'), routerLink: '/admin/system' })
+
+ if (this.hasConfigRight()) {
+ this.menuEntries.push({ label: this.i18n('Configuration'), routerLink: '/admin/config' })
+ }
+
+ if (this.hasPluginsRight()) {
+ this.menuEntries.push({ label: this.i18n('Plugins/Themes'), routerLink: '/admin/plugins' })
+ }
+
+ if (this.hasJobsRight() || this.hasLogsRight() || this.hasDebugRight()) {
+ this.menuEntries.push({ label: this.i18n('System'), routerLink: '/admin/system' })
+ }
}
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 @@
import { Component, OnInit } from '@angular/core'
-import { ServerService } from '@app/core'
+import { AuthService, ServerService, AuthUser } from '@app/core'
import { I18n } from '@ngx-translate/i18n-polyfill'
import { ServerConfig } from '@shared/models'
import { TopMenuDropdownParam } from '../shared/shared-main/misc/top-menu-dropdown.component'
@@ -11,11 +11,13 @@ import { TopMenuDropdownParam } from '../shared/shared-main/misc/top-menu-dropdo
})
export class MyAccountComponent implements OnInit {
menuEntries: TopMenuDropdownParam[] = []
+ user: AuthUser
private serverConfig: ServerConfig
constructor (
private serverService: ServerService,
+ private authService: AuthService,
private i18n: I18n
) { }
@@ -24,6 +26,20 @@ export class MyAccountComponent implements OnInit {
this.serverService.getConfig()
.subscribe(config => this.serverConfig = config)
+ this.user = this.authService.getUser()
+
+ this.authService.userInformationLoaded.subscribe(
+ () => this.buildMenu()
+ )
+ }
+
+ isVideoImportEnabled () {
+ const importConfig = this.serverConfig.import.videos
+
+ return importConfig.http.enabled || importConfig.torrent.enabled
+ }
+
+ private buildMenu () {
const libraryEntries: TopMenuDropdownParam = {
label: this.i18n('My library'),
children: [
@@ -35,7 +51,8 @@ export class MyAccountComponent implements OnInit {
{
label: this.i18n('My videos'),
routerLink: '/my-account/videos',
- iconName: 'videos'
+ iconName: 'videos',
+ isDisplayed: () => this.user.canSeeVideosLink
},
{
label: this.i18n('My playlists'),
@@ -45,7 +62,7 @@ export class MyAccountComponent implements OnInit {
{
label: this.i18n('My subscriptions'),
routerLink: '/my-account/subscriptions',
- iconName: 'subscriptions'
+ iconName: 'inbox-full'
},
{
label: this.i18n('My history'),
@@ -59,7 +76,8 @@ export class MyAccountComponent implements OnInit {
libraryEntries.children.push({
label: 'My imports',
routerLink: '/my-account/video-imports',
- iconName: 'cloud-download'
+ iconName: 'cloud-download',
+ isDisplayed: () => this.user.canSeeVideosLink
})
}
@@ -97,11 +115,4 @@ export class MyAccountComponent implements OnInit {
miscEntries
]
}
-
- isVideoImportEnabled () {
- const importConfig = this.serverConfig.import.videos
-
- return importConfig.http.enabled || importConfig.torrent.enabled
- }
-
}
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 @@
-
+
+
+
Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.
+
Read instance rules for help
+
+

+
+
+
We recommend you to not use the root user to publish your videos, since it's the super-admin account of your instance.
@@ -45,4 +53,4 @@
-
+
\ 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;
$border-color: #EAEAEA;
$nav-link-height: 40px;
+.no-upload {
+ height: 100%;
+ width: 100%;
+ text-align: center;
+
+ .about-link {
+ @include peertube-button-link;
+ @include orange-button;
+
+ height: fit-content;
+ margin-top: 10px;
+ }
+
+ img {
+ margin-top: 10px;
+ margin-bottom: 75px;
+ width: 220px;
+ height: auto;
+ }
+
+ @media screen and (max-height: 600px) {
+ img {
+ margin-top: 5px;
+ width: 160px;
+ }
+ }
+}
+
.margin-content {
padding-top: 20px;
}
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 @@
import { Component, HostListener, OnInit, ViewChild } from '@angular/core'
-import { AuthService, CanComponentDeactivate, ServerService, User } from '@app/core'
+import { AuthService, AuthUser, CanComponentDeactivate, ServerService } from '@app/core'
import { ServerConfig } from '@shared/models'
import { VideoImportTorrentComponent } from './video-add-components/video-import-torrent.component'
import { VideoImportUrlComponent } from './video-add-components/video-import-url.component'
@@ -15,7 +15,7 @@ export class VideoAddComponent implements OnInit, CanComponentDeactivate {
@ViewChild('videoImportUrl') videoImportUrl: VideoImportUrlComponent
@ViewChild('videoImportTorrent') videoImportTorrent: VideoImportTorrentComponent
- user: User = null
+ user: AuthUser = null
secondStepType: 'upload' | 'import-url' | 'import-torrent'
videoName: string
@@ -37,6 +37,8 @@ export class VideoAddComponent implements OnInit, CanComponentDeactivate {
this.serverService.getConfig()
.subscribe(config => this.serverConfig = config)
+
+ this.user = this.auth.getUser()
}
onFirstStepDone (type: 'upload' | 'import-url' | 'import-torrent', videoName: string) {
@@ -80,6 +82,6 @@ export class VideoAddComponent implements OnInit, CanComponentDeactivate {
}
isRootUser () {
- return this.auth.getUser().username === 'root'
+ return this.user.username === 'root'
}
}
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 @@
+import { Observable, of } from 'rxjs'
+import { map } from 'rxjs/operators'
import { User } from '@app/core/users/user.model'
import { peertubeLocalStorage } from '@app/helpers/peertube-web-storage'
import {
@@ -7,7 +9,8 @@ import {
NSFWPolicyType,
User as ServerUserModel,
UserRight,
- UserRole
+ UserRole,
+ UserVideoQuota
} from '@shared/models'
export type TokenOptions = {
@@ -74,6 +77,8 @@ export class AuthUser extends User implements ServerMyUserModel {
tokens: Tokens
specialPlaylists: MyUserSpecialPlaylist[]
+ canSeeVideosLink = true
+
static load () {
const usernameLocalStorage = peertubeLocalStorage.getItem(this.KEYS.USERNAME)
if (usernameLocalStorage) {
@@ -150,4 +155,26 @@ export class AuthUser extends User implements ServerMyUserModel {
peertubeLocalStorage.setItem(AuthUser.KEYS.AUTO_PLAY_VIDEO, JSON.stringify(this.autoPlayVideo))
this.tokens.save()
}
+
+ computeCanSeeVideosLink (quotaObservable: Observable): Observable {
+ if (!this.isUploadDisabled()) {
+ this.canSeeVideosLink = true
+ return of(this.canSeeVideosLink)
+ }
+
+ // Check if the user has videos
+ return quotaObservable.pipe(
+ map(({ videoQuotaUsed }) => {
+ if (videoQuotaUsed !== 0) {
+ // User already uploaded videos, so it can see the link
+ this.canSeeVideosLink = true
+ } else {
+ // No videos, no upload so the user don't need to see the videos link
+ this.canSeeVideosLink = false
+ }
+
+ return this.canSeeVideosLink
+ })
+ )
+ }
}
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 {
updateAccountAvatar (newAccountAvatar: Avatar) {
this.account.updateAvatar(newAccountAvatar)
}
+
+ isUploadDisabled () {
+ return this.videoQuota === 0 || this.videoQuotaDaily === 0
+ }
}
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 @@
MY LIBRARY
-
+
Videos
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 @@
import { HotkeysService } from 'angular2-hotkeys'
+import * as debug from 'debug'
+import { switchMap } from 'rxjs/operators'
import { Component, OnInit, ViewChild } from '@angular/core'
-import { AuthService, AuthStatus, RedirectService, ScreenService, ServerService, User, UserService } from '@app/core'
+import { AuthService, AuthStatus, AuthUser, RedirectService, ScreenService, ServerService, UserService } from '@app/core'
import { LanguageChooserComponent } from '@app/menu/language-chooser.component'
import { QuickSettingsModalComponent } from '@app/modal/quick-settings-modal.component'
import { I18n } from '@ngx-translate/i18n-polyfill'
import { ServerConfig, UserRight, VideoConstant } from '@shared/models'
+const logger = debug('peertube:menu:MenuComponent')
+
@Component({
selector: 'my-menu',
templateUrl: './menu.component.html',
- styleUrls: [ './menu.component.scss' ]
+ styleUrls: ['./menu.component.scss']
})
export class MenuComponent implements OnInit {
@ViewChild('languageChooserModal', { static: true }) languageChooserModal: LanguageChooserComponent
@ViewChild('quickSettingsModal', { static: true }) quickSettingsModal: QuickSettingsModalComponent
- user: User
+ user: AuthUser
isLoggedIn: boolean
userHasAdminAccess = false
@@ -25,7 +29,7 @@ export class MenuComponent implements OnInit {
private languages: VideoConstant
[] = []
private serverConfig: ServerConfig
- private routesPerRight: { [ role in UserRight ]?: string } = {
+ private routesPerRight: { [role in UserRight]?: string } = {
[UserRight.MANAGE_USERS]: '/admin/users',
[UserRight.MANAGE_SERVER_FOLLOW]: '/admin/friends',
[UserRight.MANAGE_ABUSES]: '/admin/moderation/abuses',
@@ -62,21 +66,30 @@ export class MenuComponent implements OnInit {
.subscribe(config => this.serverConfig = config)
this.isLoggedIn = this.authService.isLoggedIn()
- if (this.isLoggedIn === true) this.user = this.authService.getUser()
- this.computeIsUserHasAdminAccess()
+ if (this.isLoggedIn === true) {
+ this.user = this.authService.getUser()
+ this.computeVideosLink()
+ }
+
+ this.computeAdminAccess()
this.authService.loginChangedSource.subscribe(
status => {
if (status === AuthStatus.LoggedIn) {
this.isLoggedIn = true
this.user = this.authService.getUser()
- this.computeIsUserHasAdminAccess()
- console.log('Logged in.')
+
+ this.computeAdminAccess()
+ this.computeVideosLink()
+
+ logger('Logged in.')
} else if (status === AuthStatus.LoggedOut) {
this.isLoggedIn = false
this.user = undefined
- this.computeIsUserHasAdminAccess()
- console.log('Logged out.')
+
+ this.computeAdminAccess()
+
+ logger('Logged out.')
} else {
console.error('Unknown auth status: ' + status)
}
@@ -84,15 +97,15 @@ export class MenuComponent implements OnInit {
)
this.hotkeysService.cheatSheetToggle
- .subscribe(isOpen => this.helpVisible = isOpen)
+ .subscribe(isOpen => this.helpVisible = isOpen)
this.serverService.getVideoLanguages()
- .subscribe(languages => {
- this.languages = languages
+ .subscribe(languages => {
+ this.languages = languages
- this.authService.userInformationLoaded
- .subscribe(() => this.buildUserLanguages())
- })
+ this.authService.userInformationLoaded
+ .subscribe(() => this.buildUserLanguages())
+ })
}
get language () {
@@ -116,7 +129,7 @@ export class MenuComponent implements OnInit {
isRegistrationAllowed () {
return this.serverConfig.signup.allowed &&
- this.serverConfig.signup.allowedForCurrentIP
+ this.serverConfig.signup.allowedForCurrentIP
}
getFirstAdminRightAvailable () {
@@ -172,7 +185,7 @@ export class MenuComponent implements OnInit {
this.user.webTorrentEnabled = !this.user.webTorrentEnabled
this.userService.updateMyProfile({ webTorrentEnabled: this.user.webTorrentEnabled })
- .subscribe(() => this.authService.refreshUserInformation())
+ .subscribe(() => this.authService.refreshUserInformation())
}
langForLocale (localeId: string) {
@@ -188,18 +201,28 @@ export class MenuComponent implements OnInit {
}
if (!this.user.videoLanguages) {
- this.videoLanguages = [ this.i18n('any language') ]
+ this.videoLanguages = [this.i18n('any language')]
return
}
this.videoLanguages = this.user.videoLanguages
- .map(locale => this.langForLocale(locale))
- .map(value => value === undefined ? '?' : value)
+ .map(locale => this.langForLocale(locale))
+ .map(value => value === undefined ? '?' : value)
}
- private computeIsUserHasAdminAccess () {
+ private computeAdminAccess () {
const right = this.getFirstAdminRightAvailable()
this.userHasAdminAccess = right !== undefined
}
+
+ private computeVideosLink () {
+ this.authService.userInformationLoaded
+ .pipe(
+ switchMap(() => this.user.computeCanSeeVideosLink(this.userService.getMyVideoQuotaUsed()))
+ ).subscribe(res => {
+ if (res === true) logger('User can see videos link.')
+ else logger('User cannot see videos link.')
+ })
+ }
}
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 @@