diff options
Diffstat (limited to 'client/src/app/+admin')
18 files changed, 209 insertions, 18 deletions
diff --git a/client/src/app/+admin/admin.module.ts b/client/src/app/+admin/admin.module.ts index c06ae1d60..f7f347105 100644 --- a/client/src/app/+admin/admin.module.ts +++ b/client/src/app/+admin/admin.module.ts | |||
@@ -10,7 +10,7 @@ import { FollowingListComponent } from './follows/following-list/following-list. | |||
10 | import { JobsComponent } from './jobs/job.component' | 10 | import { JobsComponent } from './jobs/job.component' |
11 | import { JobsListComponent } from './jobs/jobs-list/jobs-list.component' | 11 | import { JobsListComponent } from './jobs/jobs-list/jobs-list.component' |
12 | import { JobService } from './jobs/shared/job.service' | 12 | import { JobService } from './jobs/shared/job.service' |
13 | import { UserCreateComponent, UserListComponent, UsersComponent, UserUpdateComponent } from './users' | 13 | import { UserCreateComponent, UserListComponent, UsersComponent, UserUpdateComponent, UserPasswordComponent } from './users' |
14 | import { ModerationCommentModalComponent, VideoAbuseListComponent, VideoBlacklistListComponent } from './moderation' | 14 | import { ModerationCommentModalComponent, VideoAbuseListComponent, VideoBlacklistListComponent } from './moderation' |
15 | import { ModerationComponent } from '@app/+admin/moderation/moderation.component' | 15 | import { ModerationComponent } from '@app/+admin/moderation/moderation.component' |
16 | import { RedundancyCheckboxComponent } from '@app/+admin/follows/shared/redundancy-checkbox.component' | 16 | import { RedundancyCheckboxComponent } from '@app/+admin/follows/shared/redundancy-checkbox.component' |
@@ -36,6 +36,7 @@ import { InstanceAccountBlocklistComponent, InstanceServerBlocklistComponent } f | |||
36 | UsersComponent, | 36 | UsersComponent, |
37 | UserCreateComponent, | 37 | UserCreateComponent, |
38 | UserUpdateComponent, | 38 | UserUpdateComponent, |
39 | UserPasswordComponent, | ||
39 | UserListComponent, | 40 | UserListComponent, |
40 | 41 | ||
41 | ModerationComponent, | 42 | ModerationComponent, |
diff --git a/client/src/app/+admin/moderation/moderation.component.scss b/client/src/app/+admin/moderation/moderation.component.scss index 02ccfc8ca..13b019c5b 100644 --- a/client/src/app/+admin/moderation/moderation.component.scss +++ b/client/src/app/+admin/moderation/moderation.component.scss | |||
@@ -10,6 +10,7 @@ | |||
10 | font-weight: $font-semibold; | 10 | font-weight: $font-semibold; |
11 | min-width: 200px; | 11 | min-width: 200px; |
12 | display: inline-block; | 12 | display: inline-block; |
13 | vertical-align: top; | ||
13 | } | 14 | } |
14 | 15 | ||
15 | .moderation-expanded-text { | 16 | .moderation-expanded-text { |
diff --git a/client/src/app/+admin/moderation/video-abuse-list/moderation-comment-modal.component.html b/client/src/app/+admin/moderation/video-abuse-list/moderation-comment-modal.component.html index 3a8424f68..303a788d2 100644 --- a/client/src/app/+admin/moderation/video-abuse-list/moderation-comment-modal.component.html +++ b/client/src/app/+admin/moderation/video-abuse-list/moderation-comment-modal.component.html | |||
@@ -1,7 +1,8 @@ | |||
1 | <ng-template #modal> | 1 | <ng-template #modal> |
2 | <div class="modal-header"> | 2 | <div class="modal-header"> |
3 | <h4 i18n class="modal-title">Moderation comment</h4> | 3 | <h4 i18n class="modal-title">Moderation comment</h4> |
4 | <span class="close" aria-hidden="true" (click)="hideModerationCommentModal()"></span> | 4 | |
5 | <my-global-icon iconName="cross" aria-label="Close" role="button" (click)="hide()"></my-global-icon> | ||
5 | </div> | 6 | </div> |
6 | 7 | ||
7 | <div class="modal-body"> | 8 | <div class="modal-body"> |
@@ -14,12 +15,12 @@ | |||
14 | </div> | 15 | </div> |
15 | </div> | 16 | </div> |
16 | 17 | ||
17 | <div i18n> | 18 | <div class="form-group" i18n> |
18 | This comment can only be seen by you or the other moderators. | 19 | This comment can only be seen by you or the other moderators. |
19 | </div> | 20 | </div> |
20 | 21 | ||
21 | <div class="form-group inputs"> | 22 | <div class="form-group inputs"> |
22 | <span i18n class="action-button action-button-cancel" (click)="hideModerationCommentModal()">Cancel</span> | 23 | <span i18n class="action-button action-button-cancel" (click)="hide()">Cancel</span> |
23 | 24 | ||
24 | <input | 25 | <input |
25 | type="submit" i18n-value value="Update this comment" class="action-button-submit" | 26 | type="submit" i18n-value value="Update this comment" class="action-button-submit" |
@@ -29,4 +30,4 @@ | |||
29 | </form> | 30 | </form> |
30 | </div> | 31 | </div> |
31 | 32 | ||
32 | </ng-template> \ No newline at end of file | 33 | </ng-template> |
diff --git a/client/src/app/+admin/moderation/video-abuse-list/moderation-comment-modal.component.ts b/client/src/app/+admin/moderation/video-abuse-list/moderation-comment-modal.component.ts index bebcb4207..f915978ee 100644 --- a/client/src/app/+admin/moderation/video-abuse-list/moderation-comment-modal.component.ts +++ b/client/src/app/+admin/moderation/video-abuse-list/moderation-comment-modal.component.ts | |||
@@ -45,7 +45,7 @@ export class ModerationCommentModalComponent extends FormReactive implements OnI | |||
45 | }) | 45 | }) |
46 | } | 46 | } |
47 | 47 | ||
48 | hideModerationCommentModal () { | 48 | hide () { |
49 | this.abuseToComment = undefined | 49 | this.abuseToComment = undefined |
50 | this.openedModal.close() | 50 | this.openedModal.close() |
51 | this.form.reset() | 51 | this.form.reset() |
@@ -60,7 +60,7 @@ export class ModerationCommentModalComponent extends FormReactive implements OnI | |||
60 | this.notifier.success(this.i18n('Comment updated.')) | 60 | this.notifier.success(this.i18n('Comment updated.')) |
61 | 61 | ||
62 | this.commentUpdated.emit(moderationComment) | 62 | this.commentUpdated.emit(moderationComment) |
63 | this.hideModerationCommentModal() | 63 | this.hide() |
64 | }, | 64 | }, |
65 | 65 | ||
66 | err => this.notifier.error(err.message) | 66 | err => this.notifier.error(err.message) |
diff --git a/client/src/app/+admin/moderation/video-abuse-list/video-abuse-list.component.html b/client/src/app/+admin/moderation/video-abuse-list/video-abuse-list.component.html index e862d5162..05b549de6 100644 --- a/client/src/app/+admin/moderation/video-abuse-list/video-abuse-list.component.html +++ b/client/src/app/+admin/moderation/video-abuse-list/video-abuse-list.component.html | |||
@@ -51,11 +51,11 @@ | |||
51 | <td class="moderation-expanded" colspan="6"> | 51 | <td class="moderation-expanded" colspan="6"> |
52 | <div> | 52 | <div> |
53 | <span i18n class="moderation-expanded-label">Reason:</span> | 53 | <span i18n class="moderation-expanded-label">Reason:</span> |
54 | <span class="moderation-expanded-text">{{ videoAbuse.reason }}</span> | 54 | <span class="moderation-expanded-text" [innerHTML]="toHtml(videoAbuse.reason)"></span> |
55 | </div> | 55 | </div> |
56 | <div *ngIf="videoAbuse.moderationComment"> | 56 | <div *ngIf="videoAbuse.moderationComment"> |
57 | <span i18n class="moderation-expanded-label">Moderation comment:</span> | 57 | <span i18n class="moderation-expanded-label">Moderation comment:</span> |
58 | <span class="moderation-expanded-text">{{ videoAbuse.moderationComment }}</span> | 58 | <span class="moderation-expanded-text" [innerHTML]="toHtml(videoAbuse.moderationComment)"></span> |
59 | </div> | 59 | </div> |
60 | </td> | 60 | </td> |
61 | </tr> | 61 | </tr> |
diff --git a/client/src/app/+admin/moderation/video-abuse-list/video-abuse-list.component.ts b/client/src/app/+admin/moderation/video-abuse-list/video-abuse-list.component.ts index f64234b74..00c871659 100644 --- a/client/src/app/+admin/moderation/video-abuse-list/video-abuse-list.component.ts +++ b/client/src/app/+admin/moderation/video-abuse-list/video-abuse-list.component.ts | |||
@@ -9,6 +9,7 @@ import { DropdownAction } from '../../../shared/buttons/action-dropdown.componen | |||
9 | import { ConfirmService } from '../../../core/index' | 9 | import { ConfirmService } from '../../../core/index' |
10 | import { ModerationCommentModalComponent } from './moderation-comment-modal.component' | 10 | import { ModerationCommentModalComponent } from './moderation-comment-modal.component' |
11 | import { Video } from '../../../shared/video/video.model' | 11 | import { Video } from '../../../shared/video/video.model' |
12 | import { MarkdownService } from '@app/shared/renderer' | ||
12 | 13 | ||
13 | @Component({ | 14 | @Component({ |
14 | selector: 'my-video-abuse-list', | 15 | selector: 'my-video-abuse-list', |
@@ -30,7 +31,8 @@ export class VideoAbuseListComponent extends RestTable implements OnInit { | |||
30 | private notifier: Notifier, | 31 | private notifier: Notifier, |
31 | private videoAbuseService: VideoAbuseService, | 32 | private videoAbuseService: VideoAbuseService, |
32 | private confirmService: ConfirmService, | 33 | private confirmService: ConfirmService, |
33 | private i18n: I18n | 34 | private i18n: I18n, |
35 | private markdownRenderer: MarkdownService | ||
34 | ) { | 36 | ) { |
35 | super() | 37 | super() |
36 | 38 | ||
@@ -108,6 +110,10 @@ export class VideoAbuseListComponent extends RestTable implements OnInit { | |||
108 | 110 | ||
109 | } | 111 | } |
110 | 112 | ||
113 | toHtml (text: string) { | ||
114 | return this.markdownRenderer.textMarkdownToHTML(text) | ||
115 | } | ||
116 | |||
111 | protected loadData () { | 117 | protected loadData () { |
112 | return this.videoAbuseService.getVideoAbuses(this.pagination, this.sort) | 118 | return this.videoAbuseService.getVideoAbuses(this.pagination, this.sort) |
113 | .subscribe( | 119 | .subscribe( |
diff --git a/client/src/app/+admin/moderation/video-blacklist-list/video-blacklist-list.component.html b/client/src/app/+admin/moderation/video-blacklist-list/video-blacklist-list.component.html index 7cef787d2..247f441c1 100644 --- a/client/src/app/+admin/moderation/video-blacklist-list/video-blacklist-list.component.html +++ b/client/src/app/+admin/moderation/video-blacklist-list/video-blacklist-list.component.html | |||
@@ -7,6 +7,7 @@ | |||
7 | <th style="width: 40px"></th> | 7 | <th style="width: 40px"></th> |
8 | <th i18n pSortableColumn="name">Video name <p-sortIcon field="name"></p-sortIcon></th> | 8 | <th i18n pSortableColumn="name">Video name <p-sortIcon field="name"></p-sortIcon></th> |
9 | <th i18n>Sensitive</th> | 9 | <th i18n>Sensitive</th> |
10 | <th i18n>Unfederated</th> | ||
10 | <th i18n pSortableColumn="createdAt">Date <p-sortIcon field="createdAt"></p-sortIcon></th> | 11 | <th i18n pSortableColumn="createdAt">Date <p-sortIcon field="createdAt"></p-sortIcon></th> |
11 | <th style="width: 120px;"></th> | 12 | <th style="width: 120px;"></th> |
12 | </tr> | 13 | </tr> |
@@ -26,7 +27,8 @@ | |||
26 | </a> | 27 | </a> |
27 | </td> | 28 | </td> |
28 | 29 | ||
29 | <td>{{ videoBlacklist.video.nsfw }}</td> | 30 | <td>{{ booleanToText(videoBlacklist.video.nsfw) }}</td> |
31 | <td>{{ booleanToText(videoBlacklist.unfederated) }}</td> | ||
30 | <td>{{ videoBlacklist.createdAt }}</td> | 32 | <td>{{ videoBlacklist.createdAt }}</td> |
31 | 33 | ||
32 | <td class="action-cell"> | 34 | <td class="action-cell"> |
@@ -37,9 +39,9 @@ | |||
37 | 39 | ||
38 | <ng-template pTemplate="rowexpansion" let-videoBlacklist> | 40 | <ng-template pTemplate="rowexpansion" let-videoBlacklist> |
39 | <tr> | 41 | <tr> |
40 | <td class="moderation-expanded" colspan="5"> | 42 | <td class="moderation-expanded" colspan="6"> |
41 | <span i18n class="moderation-expanded-label">Blacklist reason:</span> | 43 | <span i18n class="moderation-expanded-label">Blacklist reason:</span> |
42 | <span class="moderation-expanded-text">{{ videoBlacklist.reason }}</span> | 44 | <span class="moderation-expanded-text" [innerHTML]="toHtml(videoBlacklist.reason)"></span> |
43 | </td> | 45 | </td> |
44 | </tr> | 46 | </tr> |
45 | </ng-template> | 47 | </ng-template> |
diff --git a/client/src/app/+admin/moderation/video-blacklist-list/video-blacklist-list.component.ts b/client/src/app/+admin/moderation/video-blacklist-list/video-blacklist-list.component.ts index a02e84f67..b27bbbfef 100644 --- a/client/src/app/+admin/moderation/video-blacklist-list/video-blacklist-list.component.ts +++ b/client/src/app/+admin/moderation/video-blacklist-list/video-blacklist-list.component.ts | |||
@@ -7,6 +7,7 @@ import { VideoBlacklist } from '../../../../../../shared' | |||
7 | import { I18n } from '@ngx-translate/i18n-polyfill' | 7 | import { I18n } from '@ngx-translate/i18n-polyfill' |
8 | import { DropdownAction } from '../../../shared/buttons/action-dropdown.component' | 8 | import { DropdownAction } from '../../../shared/buttons/action-dropdown.component' |
9 | import { Video } from '../../../shared/video/video.model' | 9 | import { Video } from '../../../shared/video/video.model' |
10 | import { MarkdownService } from '@app/shared/renderer' | ||
10 | 11 | ||
11 | @Component({ | 12 | @Component({ |
12 | selector: 'my-video-blacklist-list', | 13 | selector: 'my-video-blacklist-list', |
@@ -26,6 +27,7 @@ export class VideoBlacklistListComponent extends RestTable implements OnInit { | |||
26 | private notifier: Notifier, | 27 | private notifier: Notifier, |
27 | private confirmService: ConfirmService, | 28 | private confirmService: ConfirmService, |
28 | private videoBlacklistService: VideoBlacklistService, | 29 | private videoBlacklistService: VideoBlacklistService, |
30 | private markdownRenderer: MarkdownService, | ||
29 | private i18n: I18n | 31 | private i18n: I18n |
30 | ) { | 32 | ) { |
31 | super() | 33 | super() |
@@ -46,6 +48,16 @@ export class VideoBlacklistListComponent extends RestTable implements OnInit { | |||
46 | return Video.buildClientUrl(videoBlacklist.video.uuid) | 48 | return Video.buildClientUrl(videoBlacklist.video.uuid) |
47 | } | 49 | } |
48 | 50 | ||
51 | booleanToText (value: boolean) { | ||
52 | if (value === true) return this.i18n('yes') | ||
53 | |||
54 | return this.i18n('no') | ||
55 | } | ||
56 | |||
57 | toHtml (text: string) { | ||
58 | return this.markdownRenderer.textMarkdownToHTML(text) | ||
59 | } | ||
60 | |||
49 | async removeVideoFromBlacklist (entry: VideoBlacklist) { | 61 | async removeVideoFromBlacklist (entry: VideoBlacklist) { |
50 | const confirmMessage = this.i18n( | 62 | const confirmMessage = this.i18n( |
51 | 'Do you really want to remove this video from the blacklist? It will be available again in the videos list.' | 63 | 'Do you really want to remove this video from the blacklist? It will be available again in the videos list.' |
diff --git a/client/src/app/+admin/users/user-edit/index.ts b/client/src/app/+admin/users/user-edit/index.ts index fd80a02e0..ec734ef92 100644 --- a/client/src/app/+admin/users/user-edit/index.ts +++ b/client/src/app/+admin/users/user-edit/index.ts | |||
@@ -1,2 +1,3 @@ | |||
1 | export * from './user-create.component' | 1 | export * from './user-create.component' |
2 | export * from './user-update.component' | 2 | export * from './user-update.component' |
3 | export * from './user-password.component' | ||
diff --git a/client/src/app/+admin/users/user-edit/user-edit.component.html b/client/src/app/+admin/users/user-edit/user-edit.component.html index 56cf7d17d..c6566da24 100644 --- a/client/src/app/+admin/users/user-edit/user-edit.component.html +++ b/client/src/app/+admin/users/user-edit/user-edit.component.html | |||
@@ -81,3 +81,17 @@ | |||
81 | 81 | ||
82 | <input type="submit" value="{{ getFormButtonTitle() }}" [disabled]="!form.valid"> | 82 | <input type="submit" value="{{ getFormButtonTitle() }}" [disabled]="!form.valid"> |
83 | </form> | 83 | </form> |
84 | |||
85 | <div *ngIf="!isCreation()" class="danger-zone"> | ||
86 | <div class="account-title" i18n>Danger Zone</div> | ||
87 | |||
88 | <div class="form-group reset-password-email"> | ||
89 | <label i18n>Send a link to reset the password by email to the user</label> | ||
90 | <button (click)="resetPassword()" i18n>Ask for new password</button> | ||
91 | </div> | ||
92 | |||
93 | <div class="form-group"> | ||
94 | <label i18n>Manually set the user password</label> | ||
95 | <my-user-password [userId]="userId"></my-user-password> | ||
96 | </div> | ||
97 | </div> | ||
diff --git a/client/src/app/+admin/users/user-edit/user-edit.component.scss b/client/src/app/+admin/users/user-edit/user-edit.component.scss index 6675f65cc..c1cc4ca45 100644 --- a/client/src/app/+admin/users/user-edit/user-edit.component.scss +++ b/client/src/app/+admin/users/user-edit/user-edit.component.scss | |||
@@ -14,7 +14,7 @@ input:not([type=submit]) { | |||
14 | @include peertube-select-container(340px); | 14 | @include peertube-select-container(340px); |
15 | } | 15 | } |
16 | 16 | ||
17 | input[type=submit] { | 17 | input[type=submit], button { |
18 | @include peertube-button; | 18 | @include peertube-button; |
19 | @include orange-button; | 19 | @include orange-button; |
20 | 20 | ||
@@ -25,3 +25,23 @@ input[type=submit] { | |||
25 | margin-top: 5px; | 25 | margin-top: 5px; |
26 | font-size: 11px; | 26 | font-size: 11px; |
27 | } | 27 | } |
28 | |||
29 | .account-title { | ||
30 | @include in-content-small-title; | ||
31 | |||
32 | margin-top: 55px; | ||
33 | margin-bottom: 30px; | ||
34 | } | ||
35 | |||
36 | .danger-zone { | ||
37 | .reset-password-email { | ||
38 | margin-bottom: 30px; | ||
39 | padding-bottom: 30px; | ||
40 | border-bottom: 1px solid rgba(0, 0, 0, 0.1); | ||
41 | |||
42 | button { | ||
43 | display: block; | ||
44 | margin-top: 0; | ||
45 | } | ||
46 | } | ||
47 | } | ||
diff --git a/client/src/app/+admin/users/user-edit/user-edit.ts b/client/src/app/+admin/users/user-edit/user-edit.ts index 0b3511e8e..649b35b0c 100644 --- a/client/src/app/+admin/users/user-edit/user-edit.ts +++ b/client/src/app/+admin/users/user-edit/user-edit.ts | |||
@@ -8,6 +8,7 @@ export abstract class UserEdit extends FormReactive { | |||
8 | videoQuotaDailyOptions: { value: string, label: string }[] = [] | 8 | videoQuotaDailyOptions: { value: string, label: string }[] = [] |
9 | roles = Object.keys(USER_ROLE_LABELS).map(key => ({ value: key.toString(), label: USER_ROLE_LABELS[key] })) | 9 | roles = Object.keys(USER_ROLE_LABELS).map(key => ({ value: key.toString(), label: USER_ROLE_LABELS[key] })) |
10 | username: string | 10 | username: string |
11 | userId: number | ||
11 | 12 | ||
12 | protected abstract serverService: ServerService | 13 | protected abstract serverService: ServerService |
13 | protected abstract configService: ConfigService | 14 | protected abstract configService: ConfigService |
@@ -22,7 +23,9 @@ export abstract class UserEdit extends FormReactive { | |||
22 | } | 23 | } |
23 | 24 | ||
24 | computeQuotaWithTranscoding () { | 25 | computeQuotaWithTranscoding () { |
25 | const resolutions = this.serverService.getConfig().transcoding.enabledResolutions | 26 | const transcodingConfig = this.serverService.getConfig().transcoding |
27 | |||
28 | const resolutions = transcodingConfig.enabledResolutions | ||
26 | const higherResolution = VideoResolution.H_1080P | 29 | const higherResolution = VideoResolution.H_1080P |
27 | let multiplier = 0 | 30 | let multiplier = 0 |
28 | 31 | ||
@@ -30,9 +33,15 @@ export abstract class UserEdit extends FormReactive { | |||
30 | multiplier += resolution / higherResolution | 33 | multiplier += resolution / higherResolution |
31 | } | 34 | } |
32 | 35 | ||
36 | if (transcodingConfig.hls.enabled) multiplier *= 2 | ||
37 | |||
33 | return multiplier * parseInt(this.form.value['videoQuota'], 10) | 38 | return multiplier * parseInt(this.form.value['videoQuota'], 10) |
34 | } | 39 | } |
35 | 40 | ||
41 | resetPassword () { | ||
42 | return | ||
43 | } | ||
44 | |||
36 | protected buildQuotaOptions () { | 45 | protected buildQuotaOptions () { |
37 | // These are used by a HTML select, so convert key into strings | 46 | // These are used by a HTML select, so convert key into strings |
38 | this.videoQuotaOptions = this.configService | 47 | this.videoQuotaOptions = this.configService |
diff --git a/client/src/app/+admin/users/user-edit/user-password.component.html b/client/src/app/+admin/users/user-edit/user-password.component.html new file mode 100644 index 000000000..a1e1f6216 --- /dev/null +++ b/client/src/app/+admin/users/user-edit/user-password.component.html | |||
@@ -0,0 +1,21 @@ | |||
1 | <form role="form" (ngSubmit)="formValidated()" [formGroup]="form"> | ||
2 | <div class="form-group"> | ||
3 | |||
4 | <div class="input-group"> | ||
5 | <input id="password" [attr.type]="showPassword ? 'text' : 'password'" | ||
6 | formControlName="password" [ngClass]="{ 'input-error': formErrors['password'] }" | ||
7 | > | ||
8 | <div class="input-group-append"> | ||
9 | <button class="btn btn-sm btn-outline-secondary" (click)="togglePasswordVisibility()" type="button"> | ||
10 | <ng-container *ngIf="!showPassword" i18n>Show</ng-container> | ||
11 | <ng-container *ngIf="!!showPassword" i18n>Hide</ng-container> | ||
12 | </button> | ||
13 | </div> | ||
14 | </div> | ||
15 | <div *ngIf="formErrors.password" class="form-error"> | ||
16 | {{ formErrors.password }} | ||
17 | </div> | ||
18 | </div> | ||
19 | |||
20 | <input type="submit" value="{{ getFormButtonTitle() }}" [disabled]="!form.valid"> | ||
21 | </form> | ||
diff --git a/client/src/app/+admin/users/user-edit/user-password.component.scss b/client/src/app/+admin/users/user-edit/user-password.component.scss new file mode 100644 index 000000000..217d585af --- /dev/null +++ b/client/src/app/+admin/users/user-edit/user-password.component.scss | |||
@@ -0,0 +1,22 @@ | |||
1 | @import '_variables'; | ||
2 | @import '_mixins'; | ||
3 | |||
4 | input:not([type=submit]):not([type=checkbox]) { | ||
5 | @include peertube-input-text(340px); | ||
6 | |||
7 | display: block; | ||
8 | border-top-right-radius: 0; | ||
9 | border-bottom-right-radius: 0; | ||
10 | border-right: none; | ||
11 | } | ||
12 | |||
13 | input[type=submit] { | ||
14 | @include peertube-button; | ||
15 | @include orange-button; | ||
16 | |||
17 | margin-top: 10px; | ||
18 | } | ||
19 | |||
20 | .input-group-append { | ||
21 | height: 30px; | ||
22 | } | ||
diff --git a/client/src/app/+admin/users/user-edit/user-password.component.ts b/client/src/app/+admin/users/user-edit/user-password.component.ts new file mode 100644 index 000000000..5b3040440 --- /dev/null +++ b/client/src/app/+admin/users/user-edit/user-password.component.ts | |||
@@ -0,0 +1,64 @@ | |||
1 | import { Component, Input, OnInit } from '@angular/core' | ||
2 | import { ActivatedRoute, Router } from '@angular/router' | ||
3 | import { UserService } from '@app/shared/users/user.service' | ||
4 | import { Notifier } from '../../../core' | ||
5 | import { User, UserUpdate } from '../../../../../../shared' | ||
6 | import { I18n } from '@ngx-translate/i18n-polyfill' | ||
7 | import { FormValidatorService } from '@app/shared/forms/form-validators/form-validator.service' | ||
8 | import { UserValidatorsService } from '@app/shared/forms/form-validators/user-validators.service' | ||
9 | import { FormReactive } from '../../../shared' | ||
10 | |||
11 | @Component({ | ||
12 | selector: 'my-user-password', | ||
13 | templateUrl: './user-password.component.html', | ||
14 | styleUrls: [ './user-password.component.scss' ] | ||
15 | }) | ||
16 | export class UserPasswordComponent extends FormReactive implements OnInit { | ||
17 | error: string | ||
18 | username: string | ||
19 | showPassword = false | ||
20 | |||
21 | @Input() userId: number | ||
22 | |||
23 | constructor ( | ||
24 | protected formValidatorService: FormValidatorService, | ||
25 | private userValidatorsService: UserValidatorsService, | ||
26 | private route: ActivatedRoute, | ||
27 | private router: Router, | ||
28 | private notifier: Notifier, | ||
29 | private userService: UserService, | ||
30 | private i18n: I18n | ||
31 | ) { | ||
32 | super() | ||
33 | } | ||
34 | |||
35 | ngOnInit () { | ||
36 | this.buildForm({ | ||
37 | password: this.userValidatorsService.USER_PASSWORD | ||
38 | }) | ||
39 | } | ||
40 | |||
41 | formValidated () { | ||
42 | this.error = undefined | ||
43 | |||
44 | const userUpdate: UserUpdate = this.form.value | ||
45 | |||
46 | this.userService.updateUser(this.userId, userUpdate).subscribe( | ||
47 | () => { | ||
48 | this.notifier.success( | ||
49 | this.i18n('Password changed for user {{username}}.', { username: this.username }) | ||
50 | ) | ||
51 | }, | ||
52 | |||
53 | err => this.error = err.message | ||
54 | ) | ||
55 | } | ||
56 | |||
57 | togglePasswordVisibility () { | ||
58 | this.showPassword = !this.showPassword | ||
59 | } | ||
60 | |||
61 | getFormButtonTitle () { | ||
62 | return this.i18n('Update user password') | ||
63 | } | ||
64 | } | ||
diff --git a/client/src/app/+admin/users/user-edit/user-update.component.ts b/client/src/app/+admin/users/user-edit/user-update.component.ts index 61e641823..94ef87b08 100644 --- a/client/src/app/+admin/users/user-edit/user-update.component.ts +++ b/client/src/app/+admin/users/user-edit/user-update.component.ts | |||
@@ -19,6 +19,7 @@ import { UserService } from '@app/shared' | |||
19 | export class UserUpdateComponent extends UserEdit implements OnInit, OnDestroy { | 19 | export class UserUpdateComponent extends UserEdit implements OnInit, OnDestroy { |
20 | error: string | 20 | error: string |
21 | userId: number | 21 | userId: number |
22 | userEmail: string | ||
22 | username: string | 23 | username: string |
23 | 24 | ||
24 | private paramsSub: Subscription | 25 | private paramsSub: Subscription |
@@ -89,9 +90,22 @@ export class UserUpdateComponent extends UserEdit implements OnInit, OnDestroy { | |||
89 | return this.i18n('Update user') | 90 | return this.i18n('Update user') |
90 | } | 91 | } |
91 | 92 | ||
93 | resetPassword () { | ||
94 | this.userService.askResetPassword(this.userEmail).subscribe( | ||
95 | () => { | ||
96 | this.notifier.success( | ||
97 | this.i18n('An email asking for password reset has been sent to {{username}}.', { username: this.username }) | ||
98 | ) | ||
99 | }, | ||
100 | |||
101 | err => this.error = err.message | ||
102 | ) | ||
103 | } | ||
104 | |||
92 | private onUserFetched (userJson: User) { | 105 | private onUserFetched (userJson: User) { |
93 | this.userId = userJson.id | 106 | this.userId = userJson.id |
94 | this.username = userJson.username | 107 | this.username = userJson.username |
108 | this.userEmail = userJson.email | ||
95 | 109 | ||
96 | this.form.patchValue({ | 110 | this.form.patchValue({ |
97 | email: userJson.email, | 111 | email: userJson.email, |
diff --git a/client/src/app/+admin/users/user-list/user-list.component.html b/client/src/app/+admin/users/user-list/user-list.component.html index 556ab3c5d..69a4616a3 100644 --- a/client/src/app/+admin/users/user-list/user-list.component.html +++ b/client/src/app/+admin/users/user-list/user-list.component.html | |||
@@ -2,7 +2,7 @@ | |||
2 | <div i18n class="form-sub-title">Users list</div> | 2 | <div i18n class="form-sub-title">Users list</div> |
3 | 3 | ||
4 | <a class="add-button" routerLink="/admin/users/create"> | 4 | <a class="add-button" routerLink="/admin/users/create"> |
5 | <span class="icon icon-add"></span> | 5 | <my-global-icon iconName="add"></my-global-icon> |
6 | <ng-container i18n>Create user</ng-container> | 6 | <ng-container i18n>Create user</ng-container> |
7 | </a> | 7 | </a> |
8 | </div> | 8 | </div> |
@@ -65,7 +65,9 @@ | |||
65 | <span i18n *ngIf="user.blocked" class="banned-info">(banned)</span> | 65 | <span i18n *ngIf="user.blocked" class="banned-info">(banned)</span> |
66 | </a> | 66 | </a> |
67 | </td> | 67 | </td> |
68 | |||
68 | <td *ngIf="!requiresEmailVerification || user.blocked; else emailWithVerificationStatus">{{ user.email }}</td> | 69 | <td *ngIf="!requiresEmailVerification || user.blocked; else emailWithVerificationStatus">{{ user.email }}</td> |
70 | |||
69 | <ng-template #emailWithVerificationStatus> | 71 | <ng-template #emailWithVerificationStatus> |
70 | <td *ngIf="user.emailVerified === false; else emailVerifiedNotFalse" i18n-title title="User's email must be verified to login"> | 72 | <td *ngIf="user.emailVerified === false; else emailVerifiedNotFalse" i18n-title title="User's email must be verified to login"> |
71 | <em>? {{ user.email }}</em> | 73 | <em>? {{ user.email }}</em> |
@@ -76,6 +78,7 @@ | |||
76 | </td> | 78 | </td> |
77 | </ng-template> | 79 | </ng-template> |
78 | </ng-template> | 80 | </ng-template> |
81 | |||
79 | <td>{{ user.videoQuotaUsed }} / {{ user.videoQuota }}</td> | 82 | <td>{{ user.videoQuotaUsed }} / {{ user.videoQuota }}</td> |
80 | <td>{{ user.roleLabel }}</td> | 83 | <td>{{ user.roleLabel }}</td> |
81 | <td>{{ user.createdAt }}</td> | 84 | <td>{{ user.createdAt }}</td> |
diff --git a/client/src/app/+admin/users/user-list/user-list.component.scss b/client/src/app/+admin/users/user-list/user-list.component.scss index f235769f0..5274be01c 100644 --- a/client/src/app/+admin/users/user-list/user-list.component.scss +++ b/client/src/app/+admin/users/user-list/user-list.component.scss | |||
@@ -2,7 +2,7 @@ | |||
2 | @import '_mixins'; | 2 | @import '_mixins'; |
3 | 3 | ||
4 | .add-button { | 4 | .add-button { |
5 | @include create-button('../../../../assets/images/global/add.svg'); | 5 | @include create-button; |
6 | } | 6 | } |
7 | 7 | ||
8 | tr.banned { | 8 | tr.banned { |
@@ -23,4 +23,4 @@ tr.banned { | |||
23 | input { | 23 | input { |
24 | @include peertube-input-text(250px); | 24 | @include peertube-input-text(250px); |
25 | } | 25 | } |
26 | } \ No newline at end of file | 26 | } |