aboutsummaryrefslogtreecommitdiffhomepage
path: root/client/src/app/+admin
diff options
context:
space:
mode:
Diffstat (limited to 'client/src/app/+admin')
-rw-r--r--client/src/app/+admin/admin.module.ts2
-rw-r--r--client/src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html2
-rw-r--r--client/src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.ts4
-rw-r--r--client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.scss6
-rw-r--r--client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.ts8
-rw-r--r--client/src/app/+admin/follows/following-list/follow-modal.component.ts4
-rw-r--r--client/src/app/+admin/moderation/video-block-list/video-block-list.component.ts7
-rw-r--r--client/src/app/+admin/overview/comments/video-comment-list.component.ts2
-rw-r--r--client/src/app/+admin/overview/users/user-edit/user-create.component.ts4
-rw-r--r--client/src/app/+admin/overview/users/user-edit/user-edit.component.html14
-rw-r--r--client/src/app/+admin/overview/users/user-edit/user-edit.component.scss19
-rw-r--r--client/src/app/+admin/overview/users/user-edit/user-edit.ts36
-rw-r--r--client/src/app/+admin/overview/users/user-edit/user-password.component.html4
-rw-r--r--client/src/app/+admin/overview/users/user-edit/user-password.component.ts4
-rw-r--r--client/src/app/+admin/overview/users/user-edit/user-update.component.ts27
-rw-r--r--client/src/app/+admin/overview/users/user-list/user-list.component.html4
-rw-r--r--client/src/app/+admin/overview/users/user-list/user-list.component.scss2
-rw-r--r--client/src/app/+admin/overview/videos/video-list.component.html7
-rw-r--r--client/src/app/+admin/overview/videos/video-list.component.ts9
-rw-r--r--client/src/app/+admin/plugins/plugin-show-installed/plugin-show-installed.component.ts4
-rw-r--r--client/src/app/+admin/shared/index.ts3
-rw-r--r--client/src/app/+admin/shared/shared-admin.module.ts20
-rw-r--r--client/src/app/+admin/shared/user-real-quota-info.component.html4
-rw-r--r--client/src/app/+admin/shared/user-real-quota-info.component.scss2
-rw-r--r--client/src/app/+admin/shared/user-real-quota-info.component.ts44
25 files changed, 175 insertions, 67 deletions
diff --git a/client/src/app/+admin/admin.module.ts b/client/src/app/+admin/admin.module.ts
index 366e29883..f01967ea6 100644
--- a/client/src/app/+admin/admin.module.ts
+++ b/client/src/app/+admin/admin.module.ts
@@ -49,6 +49,7 @@ import {
49 PluginSearchComponent, 49 PluginSearchComponent,
50 PluginShowInstalledComponent 50 PluginShowInstalledComponent
51} from './plugins' 51} from './plugins'
52import { SharedAdminModule } from './shared'
52import { JobService, LogsComponent, LogsService } from './system' 53import { JobService, LogsComponent, LogsService } from './system'
53import { DebugComponent, DebugService } from './system/debug' 54import { DebugComponent, DebugService } from './system/debug'
54import { JobsComponent } from './system/jobs/jobs.component' 55import { JobsComponent } from './system/jobs/jobs.component'
@@ -69,6 +70,7 @@ import { JobsComponent } from './system/jobs/jobs.component'
69 SharedVideoMiniatureModule, 70 SharedVideoMiniatureModule,
70 SharedTablesModule, 71 SharedTablesModule,
71 SharedUsersModule, 72 SharedUsersModule,
73 SharedAdminModule,
72 74
73 TableModule, 75 TableModule,
74 ChartModule 76 ChartModule
diff --git a/client/src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html b/client/src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html
index 929ea3a90..43f1438e0 100644
--- a/client/src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html
+++ b/client/src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html
@@ -218,6 +218,8 @@
218 [clearable]="false" 218 [clearable]="false"
219 ></my-select-custom-value> 219 ></my-select-custom-value>
220 220
221 <my-user-real-quota-info [videoQuota]="getUserVideoQuota()"></my-user-real-quota-info>
222
221 <div *ngIf="formErrors.user.videoQuota" class="form-error">{{ formErrors.user.videoQuota }}</div> 223 <div *ngIf="formErrors.user.videoQuota" class="form-error">{{ formErrors.user.videoQuota }}</div>
222 </div> 224 </div>
223 225
diff --git a/client/src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.ts b/client/src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.ts
index 90ed58c99..2122e67b2 100644
--- a/client/src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.ts
+++ b/client/src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.ts
@@ -60,6 +60,10 @@ export class EditBasicConfigurationComponent implements OnInit, OnChanges {
60 return !!enabled.find((e: string) => e === algorithm) 60 return !!enabled.find((e: string) => e === algorithm)
61 } 61 }
62 62
63 getUserVideoQuota () {
64 return this.form.value['user']['videoQuota']
65 }
66
63 isSignupEnabled () { 67 isSignupEnabled () {
64 return this.form.value['signup']['enabled'] === true 68 return this.form.value['signup']['enabled'] === true
65 } 69 }
diff --git a/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.scss b/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.scss
index dda5d0b5e..764e626ec 100644
--- a/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.scss
+++ b/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.scss
@@ -150,3 +150,9 @@ ngb-tabset:not(.previews) ::ng-deep {
150 padding: 0 .3em; 150 padding: 0 .3em;
151 } 151 }
152} 152}
153
154my-user-real-quota-info {
155 display: block;
156 margin-top: 5px;
157 font-size: 11px;
158}
diff --git a/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.ts b/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.ts
index 545e37857..168f4702c 100644
--- a/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.ts
+++ b/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.ts
@@ -18,15 +18,15 @@ import {
18 MAX_INSTANCE_LIVES_VALIDATOR, 18 MAX_INSTANCE_LIVES_VALIDATOR,
19 MAX_LIVE_DURATION_VALIDATOR, 19 MAX_LIVE_DURATION_VALIDATOR,
20 MAX_USER_LIVES_VALIDATOR, 20 MAX_USER_LIVES_VALIDATOR,
21 MAX_VIDEO_CHANNELS_PER_USER_VALIDATOR,
21 SEARCH_INDEX_URL_VALIDATOR, 22 SEARCH_INDEX_URL_VALIDATOR,
22 SERVICES_TWITTER_USERNAME_VALIDATOR, 23 SERVICES_TWITTER_USERNAME_VALIDATOR,
23 SIGNUP_LIMIT_VALIDATOR, 24 SIGNUP_LIMIT_VALIDATOR,
24 SIGNUP_MINIMUM_AGE_VALIDATOR, 25 SIGNUP_MINIMUM_AGE_VALIDATOR,
25 TRANSCODING_THREADS_VALIDATOR, 26 TRANSCODING_THREADS_VALIDATOR
26 MAX_VIDEO_CHANNELS_PER_USER_VALIDATOR
27} from '@app/shared/form-validators/custom-config-validators' 27} from '@app/shared/form-validators/custom-config-validators'
28import { USER_VIDEO_QUOTA_DAILY_VALIDATOR, USER_VIDEO_QUOTA_VALIDATOR } from '@app/shared/form-validators/user-validators' 28import { USER_VIDEO_QUOTA_DAILY_VALIDATOR, USER_VIDEO_QUOTA_VALIDATOR } from '@app/shared/form-validators/user-validators'
29import { FormReactive, FormValidatorService } from '@app/shared/shared-forms' 29import { FormReactive, FormReactiveService } from '@app/shared/shared-forms'
30import { CustomPageService } from '@app/shared/shared-main/custom-page' 30import { CustomPageService } from '@app/shared/shared-main/custom-page'
31import { CustomConfig, CustomPage, HTMLServerConfig } from '@shared/models' 31import { CustomConfig, CustomPage, HTMLServerConfig } from '@shared/models'
32import { EditConfigurationService } from './edit-configuration.service' 32import { EditConfigurationService } from './edit-configuration.service'
@@ -52,9 +52,9 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit {
52 categoryItems: SelectOptionsItem[] = [] 52 categoryItems: SelectOptionsItem[] = []
53 53
54 constructor ( 54 constructor (
55 protected formReactiveService: FormReactiveService,
55 private router: Router, 56 private router: Router,
56 private route: ActivatedRoute, 57 private route: ActivatedRoute,
57 protected formValidatorService: FormValidatorService,
58 private notifier: Notifier, 58 private notifier: Notifier,
59 private configService: ConfigService, 59 private configService: ConfigService,
60 private customPage: CustomPageService, 60 private customPage: CustomPageService,
diff --git a/client/src/app/+admin/follows/following-list/follow-modal.component.ts b/client/src/app/+admin/follows/following-list/follow-modal.component.ts
index 07cc75d77..8f74e82a6 100644
--- a/client/src/app/+admin/follows/following-list/follow-modal.component.ts
+++ b/client/src/app/+admin/follows/following-list/follow-modal.component.ts
@@ -2,7 +2,7 @@ import { Component, EventEmitter, OnInit, Output, ViewChild } from '@angular/cor
2import { Notifier } from '@app/core' 2import { Notifier } from '@app/core'
3import { prepareIcu } from '@app/helpers' 3import { prepareIcu } from '@app/helpers'
4import { splitAndGetNotEmpty, UNIQUE_HOSTS_OR_HANDLE_VALIDATOR } from '@app/shared/form-validators/host-validators' 4import { splitAndGetNotEmpty, UNIQUE_HOSTS_OR_HANDLE_VALIDATOR } from '@app/shared/form-validators/host-validators'
5import { FormReactive, FormValidatorService } from '@app/shared/shared-forms' 5import { FormReactive, FormReactiveService } from '@app/shared/shared-forms'
6import { InstanceFollowService } from '@app/shared/shared-instance' 6import { InstanceFollowService } from '@app/shared/shared-instance'
7import { NgbModal } from '@ng-bootstrap/ng-bootstrap' 7import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
8import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref' 8import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref'
@@ -22,7 +22,7 @@ export class FollowModalComponent extends FormReactive implements OnInit {
22 private openedModal: NgbModalRef 22 private openedModal: NgbModalRef
23 23
24 constructor ( 24 constructor (
25 protected formValidatorService: FormValidatorService, 25 protected formReactiveService: FormReactiveService,
26 private modalService: NgbModal, 26 private modalService: NgbModal,
27 private followService: InstanceFollowService, 27 private followService: InstanceFollowService,
28 private notifier: Notifier 28 private notifier: Notifier
diff --git a/client/src/app/+admin/moderation/video-block-list/video-block-list.component.ts b/client/src/app/+admin/moderation/video-block-list/video-block-list.component.ts
index 8d67e9beb..efd99e52b 100644
--- a/client/src/app/+admin/moderation/video-block-list/video-block-list.component.ts
+++ b/client/src/app/+admin/moderation/video-block-list/video-block-list.component.ts
@@ -98,7 +98,10 @@ export class VideoBlockListComponent extends RestTable implements OnInit {
98 98
99 this.videoService.removeVideo(videoBlock.video.id) 99 this.videoService.removeVideo(videoBlock.video.id)
100 .subscribe({ 100 .subscribe({
101 next: () => this.notifier.success($localize`Video deleted.`), 101 next: () => {
102 this.notifier.success($localize`Video deleted.`)
103 this.reloadData()
104 },
102 105
103 error: err => this.notifier.error(err.message) 106 error: err => this.notifier.error(err.message)
104 }) 107 })
@@ -124,7 +127,7 @@ export class VideoBlockListComponent extends RestTable implements OnInit {
124 } 127 }
125 128
126 toHtml (text: string) { 129 toHtml (text: string) {
127 return this.markdownRenderer.textMarkdownToHTML(text) 130 return this.markdownRenderer.textMarkdownToHTML({ markdown: text })
128 } 131 }
129 132
130 async unblockVideo (entry: VideoBlacklist) { 133 async unblockVideo (entry: VideoBlacklist) {
diff --git a/client/src/app/+admin/overview/comments/video-comment-list.component.ts b/client/src/app/+admin/overview/comments/video-comment-list.component.ts
index cfe40b92a..c95d2ffeb 100644
--- a/client/src/app/+admin/overview/comments/video-comment-list.component.ts
+++ b/client/src/app/+admin/overview/comments/video-comment-list.component.ts
@@ -115,7 +115,7 @@ export class VideoCommentListComponent extends RestTable implements OnInit {
115 } 115 }
116 116
117 toHtml (text: string) { 117 toHtml (text: string) {
118 return this.markdownRenderer.textMarkdownToHTML(text, true, true) 118 return this.markdownRenderer.textMarkdownToHTML({ markdown: text, withHtml: true, withEmoji: true })
119 } 119 }
120 120
121 isInSelectionMode () { 121 isInSelectionMode () {
diff --git a/client/src/app/+admin/overview/users/user-edit/user-create.component.ts b/client/src/app/+admin/overview/users/user-edit/user-create.component.ts
index 1713e06ce..0627aa887 100644
--- a/client/src/app/+admin/overview/users/user-edit/user-create.component.ts
+++ b/client/src/app/+admin/overview/users/user-edit/user-create.component.ts
@@ -12,7 +12,7 @@ import {
12 USER_VIDEO_QUOTA_DAILY_VALIDATOR, 12 USER_VIDEO_QUOTA_DAILY_VALIDATOR,
13 USER_VIDEO_QUOTA_VALIDATOR 13 USER_VIDEO_QUOTA_VALIDATOR
14} from '@app/shared/form-validators/user-validators' 14} from '@app/shared/form-validators/user-validators'
15import { FormValidatorService } from '@app/shared/shared-forms' 15import { FormReactiveService } from '@app/shared/shared-forms'
16import { UserAdminService } from '@app/shared/shared-users' 16import { UserAdminService } from '@app/shared/shared-users'
17import { UserCreate, UserRole } from '@shared/models' 17import { UserCreate, UserRole } from '@shared/models'
18import { UserEdit } from './user-edit' 18import { UserEdit } from './user-edit'
@@ -27,7 +27,7 @@ export class UserCreateComponent extends UserEdit implements OnInit {
27 27
28 constructor ( 28 constructor (
29 protected serverService: ServerService, 29 protected serverService: ServerService,
30 protected formValidatorService: FormValidatorService, 30 protected formReactiveService: FormReactiveService,
31 protected configService: ConfigService, 31 protected configService: ConfigService,
32 protected screenService: ScreenService, 32 protected screenService: ScreenService,
33 protected auth: AuthService, 33 protected auth: AuthService,
diff --git a/client/src/app/+admin/overview/users/user-edit/user-edit.component.html b/client/src/app/+admin/overview/users/user-edit/user-edit.component.html
index e484ab8b0..e51ccf808 100644
--- a/client/src/app/+admin/overview/users/user-edit/user-edit.component.html
+++ b/client/src/app/+admin/overview/users/user-edit/user-edit.component.html
@@ -152,10 +152,7 @@
152 [clearable]="false" 152 [clearable]="false"
153 ></my-select-custom-value> 153 ></my-select-custom-value>
154 154
155 <div i18n class="transcoding-information" *ngIf="isTranscodingInformationDisplayed()"> 155 <my-user-real-quota-info [videoQuota]="getUserVideoQuota()"></my-user-real-quota-info>
156 Transcoding is enabled. The video quota only takes into account <strong>original</strong> video size. <br />
157 At most, this user could upload ~ {{ computeQuotaWithTranscoding() | bytes: 0 }}.
158 </div>
159 156
160 <div *ngIf="formErrors.videoQuota" class="form-error"> 157 <div *ngIf="formErrors.videoQuota" class="form-error">
161 {{ formErrors.videoQuota }} 158 {{ formErrors.videoQuota }}
@@ -207,7 +204,7 @@
207</div> 204</div>
208 205
209 206
210<div *ngIf="!isCreation() && user && user.pluginAuth === null" class="row mt-4"> <!-- danger zone grid --> 207<div *ngIf="displayDangerZone()" class="row mt-4"> <!-- danger zone grid -->
211 <div class="col-12 col-lg-4 col-xl-3"> 208 <div class="col-12 col-lg-4 col-xl-3">
212 <div class="anchor" id="danger"></div> <!-- danger zone anchor --> 209 <div class="anchor" id="danger"></div> <!-- danger zone anchor -->
213 <div i18n class="account-title account-title-danger">DANGER ZONE</div> 210 <div i18n class="account-title account-title-danger">DANGER ZONE</div>
@@ -216,7 +213,7 @@
216 <div class="col-12 col-lg-8 col-xl-9"> 213 <div class="col-12 col-lg-8 col-xl-9">
217 214
218 <div class="danger-zone"> 215 <div class="danger-zone">
219 <div class="form-group reset-password-email"> 216 <div class="form-group">
220 <label i18n>Send a link to reset the password by email to the user</label> 217 <label i18n>Send a link to reset the password by email to the user</label>
221 <button (click)="resetPassword()" i18n>Ask for new password</button> 218 <button (click)="resetPassword()" i18n>Ask for new password</button>
222 </div> 219 </div>
@@ -225,6 +222,11 @@
225 <label i18n>Manually set the user password</label> 222 <label i18n>Manually set the user password</label>
226 <my-user-password [userId]="user.id"></my-user-password> 223 <my-user-password [userId]="user.id"></my-user-password>
227 </div> 224 </div>
225
226 <div *ngIf="user.twoFactorEnabled" class="form-group">
227 <label i18n>This user has two factor authentication enabled</label>
228 <button (click)="disableTwoFactorAuth()" i18n>Disable two factor authentication</button>
229 </div>
228 </div> 230 </div>
229 231
230 </div> 232 </div>
diff --git a/client/src/app/+admin/overview/users/user-edit/user-edit.component.scss b/client/src/app/+admin/overview/users/user-edit/user-edit.component.scss
index 254286ae3..698628149 100644
--- a/client/src/app/+admin/overview/users/user-edit/user-edit.component.scss
+++ b/client/src/app/+admin/overview/users/user-edit/user-edit.component.scss
@@ -41,23 +41,20 @@ button {
41 margin-top: 10px; 41 margin-top: 10px;
42} 42}
43 43
44.transcoding-information { 44my-user-real-quota-info {
45 display: block;
45 margin-top: 5px; 46 margin-top: 5px;
46 font-size: 11px; 47 font-size: 11px;
47} 48}
48 49
49.danger-zone { 50.danger-zone {
50 .reset-password-email { 51 button {
51 margin-bottom: 30px; 52 @include peertube-button;
52 53 @include danger-button;
53 button { 54 @include disable-outline;
54 @include peertube-button;
55 @include danger-button;
56 @include disable-outline;
57 55
58 display: block; 56 display: block;
59 margin-top: 0; 57 margin-top: 0;
60 }
61 } 58 }
62} 59}
63 60
diff --git a/client/src/app/+admin/overview/users/user-edit/user-edit.ts b/client/src/app/+admin/overview/users/user-edit/user-edit.ts
index 395d07423..1edca7fbf 100644
--- a/client/src/app/+admin/overview/users/user-edit/user-edit.ts
+++ b/client/src/app/+admin/overview/users/user-edit/user-edit.ts
@@ -3,7 +3,7 @@ import { ConfigService } from '@app/+admin/config/shared/config.service'
3import { AuthService, ScreenService, ServerService, User } from '@app/core' 3import { AuthService, ScreenService, ServerService, User } from '@app/core'
4import { FormReactive } from '@app/shared/shared-forms' 4import { FormReactive } from '@app/shared/shared-forms'
5import { USER_ROLE_LABELS } from '@shared/core-utils/users' 5import { USER_ROLE_LABELS } from '@shared/core-utils/users'
6import { HTMLServerConfig, UserAdminFlag, UserRole, VideoResolution } from '@shared/models' 6import { HTMLServerConfig, UserAdminFlag, UserRole } from '@shared/models'
7import { SelectOptionsItem } from '../../../../../types/select-options-item.model' 7import { SelectOptionsItem } from '../../../../../types/select-options-item.model'
8 8
9@Directive() 9@Directive()
@@ -49,7 +49,7 @@ export abstract class UserEdit extends FormReactive implements OnInit {
49 buildRoles () { 49 buildRoles () {
50 const authUser = this.auth.getUser() 50 const authUser = this.auth.getUser()
51 51
52 if (authUser.role === UserRole.ADMINISTRATOR) { 52 if (authUser.role.id === UserRole.ADMINISTRATOR) {
53 this.roles = Object.keys(USER_ROLE_LABELS) 53 this.roles = Object.keys(USER_ROLE_LABELS)
54 .map(key => ({ value: key.toString(), label: USER_ROLE_LABELS[key] })) 54 .map(key => ({ value: key.toString(), label: USER_ROLE_LABELS[key] }))
55 return 55 return
@@ -60,33 +60,27 @@ export abstract class UserEdit extends FormReactive implements OnInit {
60 ] 60 ]
61 } 61 }
62 62
63 isTranscodingInformationDisplayed () { 63 displayDangerZone () {
64 const formVideoQuota = parseInt(this.form.value['videoQuota'], 10) 64 if (this.isCreation()) return false
65 if (!this.user) return false
66 if (this.user.pluginAuth) return false
67 if (this.auth.getUser().id === this.user.id) return false
65 68
66 return this.serverConfig.transcoding.enabledResolutions.length !== 0 && 69 return true
67 formVideoQuota > 0
68 } 70 }
69 71
70 computeQuotaWithTranscoding () { 72 resetPassword () {
71 const transcodingConfig = this.serverConfig.transcoding 73 return
72
73 const resolutions = transcodingConfig.enabledResolutions
74 const higherResolution = VideoResolution.H_4K
75 let multiplier = 0
76
77 for (const resolution of resolutions) {
78 multiplier += resolution / higherResolution
79 }
80
81 if (transcodingConfig.hls.enabled) multiplier *= 2
82
83 return multiplier * parseInt(this.form.value['videoQuota'], 10)
84 } 74 }
85 75
86 resetPassword () { 76 disableTwoFactorAuth () {
87 return 77 return
88 } 78 }
89 79
80 getUserVideoQuota () {
81 return this.form.value['videoQuota']
82 }
83
90 protected buildAdminFlags (formValue: any) { 84 protected buildAdminFlags (formValue: any) {
91 return formValue.byPassAutoBlock ? UserAdminFlag.BYPASS_VIDEO_AUTO_BLACKLIST : UserAdminFlag.NONE 85 return formValue.byPassAutoBlock ? UserAdminFlag.BYPASS_VIDEO_AUTO_BLACKLIST : UserAdminFlag.NONE
92 } 86 }
diff --git a/client/src/app/+admin/overview/users/user-edit/user-password.component.html b/client/src/app/+admin/overview/users/user-edit/user-password.component.html
index 13f57024b..173825957 100644
--- a/client/src/app/+admin/overview/users/user-edit/user-password.component.html
+++ b/client/src/app/+admin/overview/users/user-edit/user-password.component.html
@@ -1,8 +1,10 @@
1<form role="form" (ngSubmit)="formValidated()" [formGroup]="form"> 1<form role="form" (ngSubmit)="formValidated()" [formGroup]="form">
2 2
3 <div class="input-group"> 3 <div class="input-group">
4 <input id="password" [attr.type]="showPassword ? 'text' : 'password'" class="form-control" 4 <input id="password"
5 [attr.type]="showPassword ? 'text' : 'password'" class="form-control"
5 formControlName="password" [ngClass]="{ 'input-error': formErrors['password'] }" 6 formControlName="password" [ngClass]="{ 'input-error': formErrors['password'] }"
7 autocomplete="new-password"
6 > 8 >
7 <button class="btn btn-sm btn-outline-secondary" (click)="togglePasswordVisibility()" type="button"> 9 <button class="btn btn-sm btn-outline-secondary" (click)="togglePasswordVisibility()" type="button">
8 <ng-container *ngIf="!showPassword" i18n>Show</ng-container> 10 <ng-container *ngIf="!showPassword" i18n>Show</ng-container>
diff --git a/client/src/app/+admin/overview/users/user-edit/user-password.component.ts b/client/src/app/+admin/overview/users/user-edit/user-password.component.ts
index 8999d1f00..d6616e077 100644
--- a/client/src/app/+admin/overview/users/user-edit/user-password.component.ts
+++ b/client/src/app/+admin/overview/users/user-edit/user-password.component.ts
@@ -1,7 +1,7 @@
1import { Component, Input, OnInit } from '@angular/core' 1import { Component, Input, OnInit } from '@angular/core'
2import { Notifier } from '@app/core' 2import { Notifier } from '@app/core'
3import { USER_PASSWORD_VALIDATOR } from '@app/shared/form-validators/user-validators' 3import { USER_PASSWORD_VALIDATOR } from '@app/shared/form-validators/user-validators'
4import { FormReactive, FormValidatorService } from '@app/shared/shared-forms' 4import { FormReactive, FormReactiveService } from '@app/shared/shared-forms'
5import { UserAdminService } from '@app/shared/shared-users' 5import { UserAdminService } from '@app/shared/shared-users'
6import { UserUpdate } from '@shared/models' 6import { UserUpdate } from '@shared/models'
7 7
@@ -18,7 +18,7 @@ export class UserPasswordComponent extends FormReactive implements OnInit {
18 @Input() userId: number 18 @Input() userId: number
19 19
20 constructor ( 20 constructor (
21 protected formValidatorService: FormValidatorService, 21 protected formReactiveService: FormReactiveService,
22 private notifier: Notifier, 22 private notifier: Notifier,
23 private userAdminService: UserAdminService 23 private userAdminService: UserAdminService
24 ) { 24 ) {
diff --git a/client/src/app/+admin/overview/users/user-edit/user-update.component.ts b/client/src/app/+admin/overview/users/user-edit/user-update.component.ts
index bab288a67..25d02f000 100644
--- a/client/src/app/+admin/overview/users/user-edit/user-update.component.ts
+++ b/client/src/app/+admin/overview/users/user-edit/user-update.component.ts
@@ -9,8 +9,8 @@ import {
9 USER_VIDEO_QUOTA_DAILY_VALIDATOR, 9 USER_VIDEO_QUOTA_DAILY_VALIDATOR,
10 USER_VIDEO_QUOTA_VALIDATOR 10 USER_VIDEO_QUOTA_VALIDATOR
11} from '@app/shared/form-validators/user-validators' 11} from '@app/shared/form-validators/user-validators'
12import { FormValidatorService } from '@app/shared/shared-forms' 12import { FormReactiveService } from '@app/shared/shared-forms'
13import { UserAdminService } from '@app/shared/shared-users' 13import { TwoFactorService, UserAdminService } from '@app/shared/shared-users'
14import { User as UserType, UserAdminFlag, UserRole, UserUpdate } from '@shared/models' 14import { User as UserType, UserAdminFlag, UserRole, UserUpdate } from '@shared/models'
15import { UserEdit } from './user-edit' 15import { UserEdit } from './user-edit'
16 16
@@ -25,7 +25,7 @@ export class UserUpdateComponent extends UserEdit implements OnInit, OnDestroy {
25 private paramsSub: Subscription 25 private paramsSub: Subscription
26 26
27 constructor ( 27 constructor (
28 protected formValidatorService: FormValidatorService, 28 protected formReactiveService: FormReactiveService,
29 protected serverService: ServerService, 29 protected serverService: ServerService,
30 protected configService: ConfigService, 30 protected configService: ConfigService,
31 protected screenService: ScreenService, 31 protected screenService: ScreenService,
@@ -34,6 +34,7 @@ export class UserUpdateComponent extends UserEdit implements OnInit, OnDestroy {
34 private router: Router, 34 private router: Router,
35 private notifier: Notifier, 35 private notifier: Notifier,
36 private userService: UserService, 36 private userService: UserService,
37 private twoFactorService: TwoFactorService,
37 private userAdminService: UserAdminService 38 private userAdminService: UserAdminService
38 ) { 39 ) {
39 super() 40 super()
@@ -120,10 +121,22 @@ export class UserUpdateComponent extends UserEdit implements OnInit, OnDestroy {
120 this.notifier.success($localize`An email asking for password reset has been sent to ${this.user.username}.`) 121 this.notifier.success($localize`An email asking for password reset has been sent to ${this.user.username}.`)
121 }, 122 },
122 123
123 error: err => { 124 error: err => this.notifier.error(err.message)
124 this.error = err.message 125 })
125 } 126 }
127
128 disableTwoFactorAuth () {
129 this.twoFactorService.disableTwoFactor({ userId: this.user.id })
130 .subscribe({
131 next: () => {
132 this.user.twoFactorEnabled = false
133
134 this.notifier.success($localize`Two factor authentication of ${this.user.username} disabled.`)
135 },
136
137 error: err => this.notifier.error(err.message)
126 }) 138 })
139
127 } 140 }
128 141
129 private onUserFetched (userJson: UserType) { 142 private onUserFetched (userJson: UserType) {
@@ -131,7 +144,7 @@ export class UserUpdateComponent extends UserEdit implements OnInit, OnDestroy {
131 144
132 this.form.patchValue({ 145 this.form.patchValue({
133 email: userJson.email, 146 email: userJson.email,
134 role: userJson.role.toString(), 147 role: userJson.role.id.toString(),
135 videoQuota: userJson.videoQuota, 148 videoQuota: userJson.videoQuota,
136 videoQuotaDaily: userJson.videoQuotaDaily, 149 videoQuotaDaily: userJson.videoQuotaDaily,
137 pluginAuth: userJson.pluginAuth, 150 pluginAuth: userJson.pluginAuth,
diff --git a/client/src/app/+admin/overview/users/user-list/user-list.component.html b/client/src/app/+admin/overview/users/user-list/user-list.component.html
index c7af7dfae..a96ce561c 100644
--- a/client/src/app/+admin/overview/users/user-list/user-list.component.html
+++ b/client/src/app/+admin/overview/users/user-list/user-list.component.html
@@ -106,8 +106,8 @@
106 </td> 106 </td>
107 107
108 <td *ngIf="isSelected('role')"> 108 <td *ngIf="isSelected('role')">
109 <span *ngIf="user.blocked" class="pt-badge badge-banned" i18n-title title="The user was banned">{{ user.roleLabel }}</span> 109 <span *ngIf="user.blocked" class="pt-badge badge-banned" i18n-title title="The user was banned">{{ user.role.label }}</span>
110 <span *ngIf="!user.blocked" class="pt-badge" [ngClass]="getRoleClass(user.role)">{{ user.roleLabel }}</span> 110 <span *ngIf="!user.blocked" class="pt-badge" [ngClass]="getRoleClass(user.role.id)">{{ user.role.label }}</span>
111 </td> 111 </td>
112 112
113 <td *ngIf="isSelected('email')" [title]="user.email"> 113 <td *ngIf="isSelected('email')" [title]="user.email">
diff --git a/client/src/app/+admin/overview/users/user-list/user-list.component.scss b/client/src/app/+admin/overview/users/user-list/user-list.component.scss
index 3c775cac5..23e0d29ee 100644
--- a/client/src/app/+admin/overview/users/user-list/user-list.component.scss
+++ b/client/src/app/+admin/overview/users/user-list/user-list.component.scss
@@ -1,6 +1,6 @@
1@use '_variables' as *; 1@use '_variables' as *;
2@use '_mixins' as *; 2@use '_mixins' as *;
3@use '~bootstrap/scss/functions' as *; 3@use 'bootstrap/scss/functions' as *;
4 4
5.add-button { 5.add-button {
6 @include create-button; 6 @include create-button;
diff --git a/client/src/app/+admin/overview/videos/video-list.component.html b/client/src/app/+admin/overview/videos/video-list.component.html
index 14bbb55e9..a6cd2e257 100644
--- a/client/src/app/+admin/overview/videos/video-list.component.html
+++ b/client/src/app/+admin/overview/videos/video-list.component.html
@@ -85,7 +85,8 @@
85 <td> 85 <td>
86 <span *ngIf="isHLS(video)" class="pt-badge badge-blue">HLS</span> 86 <span *ngIf="isHLS(video)" class="pt-badge badge-blue">HLS</span>
87 <span *ngIf="isWebTorrent(video)" class="pt-badge badge-blue">WebTorrent ({{ video.files.length }})</span> 87 <span *ngIf="isWebTorrent(video)" class="pt-badge badge-blue">WebTorrent ({{ video.files.length }})</span>
88 <span *ngIf="video.isLive" class="pt-badge badge-blue">Live</span> 88 <span i18n *ngIf="video.isLive" class="pt-badge badge-blue">Live</span>
89 <span i18n *ngIf="hasObjectStorage(video)" class="pt-badge badge-purple">Object storage</span>
89 90
90 <span *ngIf="!isImport(video) && !video.isLive && video.isLocal">{{ getFilesSize(video) | bytes: 1 }}</span> 91 <span *ngIf="!isImport(video) && !video.isLive && video.isLocal">{{ getFilesSize(video) | bytes: 1 }}</span>
91 </td> 92 </td>
@@ -106,7 +107,7 @@
106 107
107 <ul> 108 <ul>
108 <li *ngFor="let file of video.files"> 109 <li *ngFor="let file of video.files">
109 {{ file.resolution.label }}: {{ file.size | bytes: 1 }} 110 <a target="_blank" rel="noopener noreferrer" [href]="file.fileUrl">{{ file.resolution.label }}</a>: {{ file.size | bytes: 1 }}
110 111
111 <my-global-icon 112 <my-global-icon
112 *ngIf="canRemoveOneFile(video)" 113 *ngIf="canRemoveOneFile(video)"
@@ -122,7 +123,7 @@
122 123
123 <ul> 124 <ul>
124 <li *ngFor="let file of video.streamingPlaylists[0].files"> 125 <li *ngFor="let file of video.streamingPlaylists[0].files">
125 {{ file.resolution.label }}: {{ file.size | bytes: 1 }} 126 <a target="_blank" rel="noopener noreferrer" [href]="file.fileUrl">{{ file.resolution.label }}</a>: {{ file.size | bytes: 1 }}
126 127
127 <my-global-icon 128 <my-global-icon
128 *ngIf="canRemoveOneFile(video)" 129 *ngIf="canRemoveOneFile(video)"
diff --git a/client/src/app/+admin/overview/videos/video-list.component.ts b/client/src/app/+admin/overview/videos/video-list.component.ts
index cb693ce12..4d3e9873c 100644
--- a/client/src/app/+admin/overview/videos/video-list.component.ts
+++ b/client/src/app/+admin/overview/videos/video-list.component.ts
@@ -8,6 +8,7 @@ import { AdvancedInputFilter } from '@app/shared/shared-forms'
8import { DropdownAction, Video, VideoService } from '@app/shared/shared-main' 8import { DropdownAction, Video, VideoService } from '@app/shared/shared-main'
9import { VideoBlockComponent, VideoBlockService } from '@app/shared/shared-moderation' 9import { VideoBlockComponent, VideoBlockService } from '@app/shared/shared-moderation'
10import { VideoActionsDisplayType } from '@app/shared/shared-video-miniature' 10import { VideoActionsDisplayType } from '@app/shared/shared-video-miniature'
11import { getAllFiles } from '@shared/core-utils'
11import { UserRight, VideoFile, VideoPrivacy, VideoState, VideoStreamingPlaylistType } from '@shared/models' 12import { UserRight, VideoFile, VideoPrivacy, VideoState, VideoStreamingPlaylistType } from '@shared/models'
12import { VideoAdminService } from './video-admin.service' 13import { VideoAdminService } from './video-admin.service'
13 14
@@ -166,6 +167,14 @@ export class VideoListComponent extends RestTable implements OnInit {
166 return video.files.length !== 0 167 return video.files.length !== 0
167 } 168 }
168 169
170 hasObjectStorage (video: Video) {
171 if (!video.isLocal) return false
172
173 const files = getAllFiles(video)
174
175 return files.some(f => !f.fileUrl.startsWith(window.location.origin))
176 }
177
169 canRemoveOneFile (video: Video) { 178 canRemoveOneFile (video: Video) {
170 return video.canRemoveOneFile(this.authUser) 179 return video.canRemoveOneFile(this.authUser)
171 } 180 }
diff --git a/client/src/app/+admin/plugins/plugin-show-installed/plugin-show-installed.component.ts b/client/src/app/+admin/plugins/plugin-show-installed/plugin-show-installed.component.ts
index ec02cfcd9..b1a41567e 100644
--- a/client/src/app/+admin/plugins/plugin-show-installed/plugin-show-installed.component.ts
+++ b/client/src/app/+admin/plugins/plugin-show-installed/plugin-show-installed.component.ts
@@ -4,7 +4,7 @@ import { Component, OnDestroy, OnInit } from '@angular/core'
4import { ActivatedRoute } from '@angular/router' 4import { ActivatedRoute } from '@angular/router'
5import { HooksService, Notifier, PluginService } from '@app/core' 5import { HooksService, Notifier, PluginService } from '@app/core'
6import { BuildFormArgument } from '@app/shared/form-validators' 6import { BuildFormArgument } from '@app/shared/form-validators'
7import { FormReactive, FormValidatorService } from '@app/shared/shared-forms' 7import { FormReactive, FormReactiveService } from '@app/shared/shared-forms'
8import { PeerTubePlugin, RegisterServerSettingOptions } from '@shared/models' 8import { PeerTubePlugin, RegisterServerSettingOptions } from '@shared/models'
9import { PluginApiService } from '../shared/plugin-api.service' 9import { PluginApiService } from '../shared/plugin-api.service'
10 10
@@ -22,7 +22,7 @@ export class PluginShowInstalledComponent extends FormReactive implements OnInit
22 private npmName: string 22 private npmName: string
23 23
24 constructor ( 24 constructor (
25 protected formValidatorService: FormValidatorService, 25 protected formReactiveService: FormReactiveService,
26 private pluginService: PluginService, 26 private pluginService: PluginService,
27 private pluginAPIService: PluginApiService, 27 private pluginAPIService: PluginApiService,
28 private notifier: Notifier, 28 private notifier: Notifier,
diff --git a/client/src/app/+admin/shared/index.ts b/client/src/app/+admin/shared/index.ts
new file mode 100644
index 000000000..9e3834aae
--- /dev/null
+++ b/client/src/app/+admin/shared/index.ts
@@ -0,0 +1,3 @@
1export * from './user-real-quota-info.component'
2
3export * from './shared-admin.module'
diff --git a/client/src/app/+admin/shared/shared-admin.module.ts b/client/src/app/+admin/shared/shared-admin.module.ts
new file mode 100644
index 000000000..bef7d54ef
--- /dev/null
+++ b/client/src/app/+admin/shared/shared-admin.module.ts
@@ -0,0 +1,20 @@
1import { NgModule } from '@angular/core'
2import { SharedMainModule } from '../../shared/shared-main/shared-main.module'
3import { UserRealQuotaInfoComponent } from './user-real-quota-info.component'
4
5@NgModule({
6 imports: [
7 SharedMainModule
8 ],
9
10 declarations: [
11 UserRealQuotaInfoComponent
12 ],
13
14 exports: [
15 UserRealQuotaInfoComponent
16 ],
17
18 providers: []
19})
20export class SharedAdminModule { }
diff --git a/client/src/app/+admin/shared/user-real-quota-info.component.html b/client/src/app/+admin/shared/user-real-quota-info.component.html
new file mode 100644
index 000000000..b975ab17f
--- /dev/null
+++ b/client/src/app/+admin/shared/user-real-quota-info.component.html
@@ -0,0 +1,4 @@
1<div i18n class="transcoding-information" *ngIf="isTranscodingInformationDisplayed()">
2 The video quota only takes into account <strong>original</strong> video size. <br />
3 Since transcoding is enabled, videos size can be at most ~ {{ computeQuotaWithTranscoding() | bytes: 0 }}.
4</div>
diff --git a/client/src/app/+admin/shared/user-real-quota-info.component.scss b/client/src/app/+admin/shared/user-real-quota-info.component.scss
new file mode 100644
index 000000000..40083bed3
--- /dev/null
+++ b/client/src/app/+admin/shared/user-real-quota-info.component.scss
@@ -0,0 +1,2 @@
1@use '_variables' as *;
2@use '_mixins' as *;
diff --git a/client/src/app/+admin/shared/user-real-quota-info.component.ts b/client/src/app/+admin/shared/user-real-quota-info.component.ts
new file mode 100644
index 000000000..069eeba12
--- /dev/null
+++ b/client/src/app/+admin/shared/user-real-quota-info.component.ts
@@ -0,0 +1,44 @@
1import { Component, Input, OnInit } from '@angular/core'
2import { ServerService } from '@app/core'
3import { HTMLServerConfig, VideoResolution } from '@shared/models/index'
4
5@Component({
6 selector: 'my-user-real-quota-info',
7 templateUrl: './user-real-quota-info.component.html',
8 styleUrls: [ './user-real-quota-info.component.scss' ]
9})
10export class UserRealQuotaInfoComponent implements OnInit {
11 @Input() videoQuota: number | string
12
13 private serverConfig: HTMLServerConfig
14
15 constructor (private server: ServerService) { }
16
17 ngOnInit () {
18 this.serverConfig = this.server.getHTMLConfig()
19 }
20
21 isTranscodingInformationDisplayed () {
22 return this.serverConfig.transcoding.enabledResolutions.length !== 0 && this.getQuotaAsNumber() > 0
23 }
24
25 computeQuotaWithTranscoding () {
26 const transcodingConfig = this.serverConfig.transcoding
27
28 const resolutions = transcodingConfig.enabledResolutions
29 const higherResolution = VideoResolution.H_4K
30 let multiplier = 0
31
32 for (const resolution of resolutions) {
33 multiplier += resolution / higherResolution
34 }
35
36 if (transcodingConfig.hls.enabled) multiplier *= 2
37
38 return multiplier * this.getQuotaAsNumber()
39 }
40
41 private getQuotaAsNumber () {
42 return parseInt(this.videoQuota + '', 10)
43 }
44}