diff options
10 files changed, 107 insertions, 53 deletions
diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 4477da332..68cf88a75 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md | |||
@@ -1,6 +1,6 @@ | |||
1 | <!-- If you have a question, please read the FAQ.md first --> | 1 | <!-- If you have a question, please read FAQ.md first --> |
2 | <!-- If you report a security issue, please refrain from filling an issue and refer to SECURITY.md for the disclosure procedure. --> | 2 | <!-- If you report a security issue, please refrain from filling an issue, refer to SECURITY.md and follow the disclosure procedure it describes. --> |
3 | <!-- If you report a bug, please fill the form --> | 3 | <!-- If you report a bug, please fill the following form --> |
4 | 4 | ||
5 | **What happened?** | 5 | **What happened?** |
6 | 6 | ||
@@ -16,9 +16,9 @@ | |||
16 | 16 | ||
17 | 17 | ||
18 | **Additional information** | 18 | **Additional information** |
19 | * PeerTube version or URL: | 19 | * PeerTube version and URL: |
20 | * Browser name/version: | 20 | * Browser name and version: |
21 | * NodeJS version: | 21 | * NodeJS version: |
22 | 22 | ||
23 | * Link to browser console log if useful: | 23 | * Link to browser console log if useful: |
24 | * Link to server log if useful (journalctl or /var/www/peertube/storage/logs/): | 24 | * Link to server log if useful (`journalctl` or `/var/www/peertube/storage/logs/`): |
diff --git a/client/src/app/+admin/moderation/instance-blocklist/instance-account-blocklist.component.html b/client/src/app/+admin/moderation/instance-blocklist/instance-account-blocklist.component.html index f461ef31b..128f4962d 100644 --- a/client/src/app/+admin/moderation/instance-blocklist/instance-account-blocklist.component.html +++ b/client/src/app/+admin/moderation/instance-blocklist/instance-account-blocklist.component.html | |||
@@ -20,7 +20,7 @@ | |||
20 | <ng-template pTemplate="header"> | 20 | <ng-template pTemplate="header"> |
21 | <tr> | 21 | <tr> |
22 | <th style="width: 150px;">Action</th> <!-- column for action buttons --> | 22 | <th style="width: 150px;">Action</th> <!-- column for action buttons --> |
23 | <th style="width: 100%;" i18n>Account</th> | 23 | <th style="width: calc(100% - 300px);" i18n>Account</th> |
24 | <th style="width: 150px;" i18n pSortableColumn="createdAt">Muted at <p-sortIcon field="createdAt"></p-sortIcon></th> | 24 | <th style="width: 150px;" i18n pSortableColumn="createdAt">Muted at <p-sortIcon field="createdAt"></p-sortIcon></th> |
25 | </tr> | 25 | </tr> |
26 | </ng-template> | 26 | </ng-template> |
diff --git a/client/src/app/+admin/system/jobs/jobs.component.html b/client/src/app/+admin/system/jobs/jobs.component.html index 185fae220..e06156a9e 100644 --- a/client/src/app/+admin/system/jobs/jobs.component.html +++ b/client/src/app/+admin/system/jobs/jobs.component.html | |||
@@ -10,45 +10,48 @@ | |||
10 | 10 | ||
11 | <div class="select-filter-block"> | 11 | <div class="select-filter-block"> |
12 | <label for="jobState" i18n>Job state</label> | 12 | <label for="jobState" i18n>Job state</label> |
13 | <div class="peertube-select-container"> | 13 | <ng-select |
14 | <select id="jobState" name="jobState" [(ngModel)]="jobState" (ngModelChange)="onJobStateOrTypeChanged()" class="form-control"> | 14 | class="select-job-state" |
15 | <option *ngFor="let state of jobStates" [value]="state">{{ state }}</option> | 15 | [(ngModel)]="jobState" |
16 | </select> | 16 | (ngModelChange)="onJobStateOrTypeChanged()" |
17 | </div> | 17 | [clearable]="false" |
18 | [searchable]="false" | ||
19 | > | ||
20 | <ng-option *ngFor="let state of jobStates" [value]="state"> | ||
21 | <span class="badge" [ngClass]="getJobStateClass(state)">{{ state }}</span> | ||
22 | </ng-option> | ||
23 | </ng-select> | ||
18 | </div> | 24 | </div> |
19 | </div> | 25 | </div> |
20 | 26 | ||
21 | <p-table | 27 | <p-table |
22 | [value]="jobs" [lazy]="true" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" dataKey="uniqId" | 28 | [value]="jobs" [lazy]="true" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [rowsPerPageOptions]="rowsPerPageOptions" |
23 | [sortField]="sort.field" [sortOrder]="sort.order" (onLazyLoad)="loadLazy($event)" [first]="pagination.start" | 29 | [sortField]="sort.field" [sortOrder]="sort.order" (onLazyLoad)="loadLazy($event)" dataKey="uniqId" [first]="pagination.start" |
24 | [tableStyle]="{'table-layout':'auto'}" (onPage)="onPage($event)" [expandedRowKeys]="expandedRows" | 30 | [tableStyle]="{'table-layout':'auto'}" (onPage)="onPage($event)" |
31 | [showCurrentPageReport]="true" i18n-currentPageReportTemplate | ||
32 | currentPageReportTemplate="Showing {{'{first}'}} to {{'{last}'}} of {{'{totalRecords}'}} jobs" | ||
33 | (onPage)="onPage($event)" [expandedRowKeys]="expandedRows" | ||
25 | > | 34 | > |
26 | <ng-template pTemplate="header"> | 35 | <ng-template pTemplate="header"> |
27 | <tr> | 36 | <tr> |
28 | <th style="width: 40px"></th> | 37 | <th style="width: 40px"></th> |
29 | <th style="width: 100%" class="job-id" i18n>ID</th> | 38 | <th style="width: calc(100% - 390px)" class="job-id" i18n>ID</th> |
30 | <th style="width: 200px" class="job-type" i18n>Type</th> | 39 | <th style="width: 200px" class="job-type" i18n>Type</th> |
31 | <th style="width: 150px" class="job-date" i18n pSortableColumn="createdAt">Created <p-sortIcon field="createdAt"></p-sortIcon></th> | 40 | <th style="width: 150px" class="job-date" i18n pSortableColumn="createdAt">Created <p-sortIcon field="createdAt"></p-sortIcon></th> |
32 | <th style="width: 150px" class="job-state" i18n>State</th> | ||
33 | </tr> | 41 | </tr> |
34 | </ng-template> | 42 | </ng-template> |
35 | 43 | ||
36 | <ng-template pTemplate="body" let-expanded="expanded" let-job> | 44 | <ng-template pTemplate="body" let-expanded="expanded" let-job> |
37 | <tr> | 45 | <tr> |
38 | <td class="expand-cell"> | 46 | <td class="expand-cell" [pRowToggler]="job" i18n-ngbTooltip ngbTooltip="More information" placement="top-left" container="body"> |
39 | <span class="expander" [pRowToggler]="job" i18n-ngbTooltip ngbTooltip="More information" placement="top-left" container="body"> | 47 | <span class="expander"> |
40 | <i [ngClass]="expanded ? 'glyphicon glyphicon-menu-down' : 'glyphicon glyphicon-menu-right'"></i> | 48 | <i [ngClass]="expanded ? 'glyphicon glyphicon-menu-down' : 'glyphicon glyphicon-menu-right'"></i> |
41 | </span> | 49 | </span> |
42 | </td> | 50 | </td> |
43 | 51 | ||
44 | <td class="job-id" [title]="job.id">{{ job.id }}</td> | 52 | <td class="job-id" [pRowToggler]="job" [title]="job.id">{{ job.id }}</td> |
45 | <td class="job-type">{{ job.type }}</td> | 53 | <td class="job-type" [pRowToggler]="job">{{ job.type }}</td> |
46 | <td class="job-date">{{ job.createdAt | date: 'short' }}</td> | 54 | <td class="job-date" [pRowToggler]="job">{{ job.createdAt | date: 'short' }}</td> |
47 | <td class="job-state" *ngIf="job.state === 'delayed'" class="text-muted"><span class="glyphicon glyphicon-repeat"></span> <span i18n>Delayed</span></td> | ||
48 | <td class="job-state" *ngIf="job.state === 'waiting'" class="text-warning"><span class="glyphicon glyphicon-hourglass"></span> <span i18n>Will start soon...</span></td> | ||
49 | <td class="job-state" *ngIf="job.state === 'active'" class="text-warning"><span class="glyphicon glyphicon-cog"></span> <span i18n>Running...</span></td> | ||
50 | <td class="job-state" *ngIf="job.state === 'completed'" class="text-success"><span class="glyphicon glyphicon-ok"></span> <span i18n>Finished</span></td> | ||
51 | <td class="job-state" *ngIf="job.state === 'failed'" class="text-danger"><span class="glyphicon glyphicon-remove"></span> <span i18n>Failed</span></td> | ||
52 | </tr> | 55 | </tr> |
53 | </ng-template> | 56 | </ng-template> |
54 | 57 | ||
@@ -74,5 +77,18 @@ | |||
74 | </td> | 77 | </td> |
75 | </tr> | 78 | </tr> |
76 | </ng-template> | 79 | </ng-template> |
80 | |||
81 | <ng-template pTemplate="emptymessage"> | ||
82 | <tr> | ||
83 | <td colspan="4"> | ||
84 | <div class="no-results"> | ||
85 | <div class="d-block"> | ||
86 | <ng-container *ngIf="jobType === 'all'" i18n>No <span class="badge" [ngClass]="getJobStateClass(jobState)">{{ jobState }}</span> jobs found.</ng-container> | ||
87 | <ng-container *ngIf="jobType !== 'all'" i18n>No <code>{{ jobType }}</code> jobs found that are <span class="badge" [ngClass]="getJobStateClass(jobState)">{{ jobState }}</span>.</ng-container> | ||
88 | </div> | ||
89 | </div> | ||
90 | </td> | ||
91 | </tr> | ||
92 | </ng-template> | ||
77 | </p-table> | 93 | </p-table> |
78 | 94 | ||
diff --git a/client/src/app/+admin/system/jobs/jobs.component.scss b/client/src/app/+admin/system/jobs/jobs.component.scss index c33e14292..784ec4572 100644 --- a/client/src/app/+admin/system/jobs/jobs.component.scss +++ b/client/src/app/+admin/system/jobs/jobs.component.scss | |||
@@ -1,6 +1,10 @@ | |||
1 | @import '_variables'; | 1 | @import '_variables'; |
2 | @import '_mixins'; | 2 | @import '_mixins'; |
3 | 3 | ||
4 | .select-job-state { | ||
5 | min-width: 120px; | ||
6 | } | ||
7 | |||
4 | .job-id { | 8 | .job-id { |
5 | max-width: 30vw !important; | 9 | max-width: 30vw !important; |
6 | } | 10 | } |
@@ -13,10 +17,6 @@ | |||
13 | width: 170px !important; | 17 | width: 170px !important; |
14 | } | 18 | } |
15 | 19 | ||
16 | .job-state { | ||
17 | max-width: 60px; | ||
18 | } | ||
19 | |||
20 | .admin-sub-header { | 20 | .admin-sub-header { |
21 | flex-direction: row !important; | 21 | flex-direction: row !important; |
22 | justify-content: flex-end; | 22 | justify-content: flex-end; |
@@ -47,3 +47,7 @@ pre { | |||
47 | .job-error { | 47 | .job-error { |
48 | color: red; | 48 | color: red; |
49 | } | 49 | } |
50 | |||
51 | .badge { | ||
52 | @include table-badge; | ||
53 | } | ||
diff --git a/client/src/app/+admin/system/jobs/jobs.component.ts b/client/src/app/+admin/system/jobs/jobs.component.ts index 96e0f25b0..25d75aed2 100644 --- a/client/src/app/+admin/system/jobs/jobs.component.ts +++ b/client/src/app/+admin/system/jobs/jobs.component.ts | |||
@@ -56,6 +56,21 @@ export class JobsComponent extends RestTable implements OnInit { | |||
56 | return 'JobsComponent' | 56 | return 'JobsComponent' |
57 | } | 57 | } |
58 | 58 | ||
59 | getJobStateClass (state: JobStateClient) { | ||
60 | switch (state) { | ||
61 | case 'active': | ||
62 | return 'badge-blue' | ||
63 | case 'completed': | ||
64 | return 'badge-green' | ||
65 | case 'delayed': | ||
66 | return 'badge-brown' | ||
67 | case 'failed': | ||
68 | return 'badge-red' | ||
69 | case 'waiting': | ||
70 | return 'badge-yellow' | ||
71 | } | ||
72 | } | ||
73 | |||
59 | onJobStateOrTypeChanged () { | 74 | onJobStateOrTypeChanged () { |
60 | this.pagination.start = 0 | 75 | this.pagination.start = 0 |
61 | 76 | ||
diff --git a/client/src/app/+admin/system/logs/logs.component.html b/client/src/app/+admin/system/logs/logs.component.html index ae1b0c601..e92e11c2a 100644 --- a/client/src/app/+admin/system/logs/logs.component.html +++ b/client/src/app/+admin/system/logs/logs.component.html | |||
@@ -5,17 +5,30 @@ | |||
5 | </select> | 5 | </select> |
6 | </div> | 6 | </div> |
7 | 7 | ||
8 | <div class="peertube-select-container"> | 8 | <ng-select |
9 | <select [(ngModel)]="startDate" (ngModelChange)="refresh()" class="form-control"> | 9 | [(ngModel)]="startDate" |
10 | <option *ngFor="let timeChoice of timeChoices" [value]="timeChoice.id">{{ timeChoice.label }}</option> | 10 | (ngModelChange)="refresh()" |
11 | </select> | 11 | [clearable]="false" |
12 | </div> | 12 | [searchable]="false" |
13 | 13 | > | |
14 | <div class="peertube-select-container" *ngIf="!isAuditLog()"> | 14 | <ng-option *ngFor="let time of timeChoices" [value]="time.id"> |
15 | <select [(ngModel)]="level" (ngModelChange)="refresh()" class="form-control"> | 15 | {{ time.label }} ({{ time.id | date: time.dateFormat }} - <span i18n>now</span>) |
16 | <option *ngFor="let levelChoice of levelChoices" [value]="levelChoice.id">{{ levelChoice.label }}</option> | 16 | </ng-option> |
17 | </select> | 17 | </ng-select> |
18 | </div> | 18 | |
19 | <ng-select | ||
20 | [(ngModel)]="level" | ||
21 | (ngModelChange)="refresh()" | ||
22 | [clearable]="false" | ||
23 | [searchable]="false" | ||
24 | > | ||
25 | <ng-option *ngFor="let levelChoice of levelChoices" [value]="levelChoice.id"> | ||
26 | <ng-container *ngIf="levelChoice.id === 'debug'"><span style="font-size:80%;color:lightgray;vertical-align:text-top;">⬤</span> {{ levelChoice.label }}</ng-container> | ||
27 | <ng-container *ngIf="levelChoice.id === 'info'"><span style="font-size:80%;color:lightskyblue;vertical-align:text-top;">⬤</span> {{ levelChoice.label }}</ng-container> | ||
28 | <ng-container *ngIf="levelChoice.id === 'warn'"><span style="font-size:80%;color:orange;vertical-align:text-top;">⬤</span> {{ levelChoice.label }}</ng-container> | ||
29 | <ng-container *ngIf="levelChoice.id === 'error'"><span style="font-size:80%;color:red;vertical-align:text-top;">⬤</span> {{ levelChoice.label }}</ng-container> | ||
30 | </ng-option> | ||
31 | </ng-select> | ||
19 | 32 | ||
20 | <my-button i18n-label label="Refresh" icon="refresh" (click)="refresh()"></my-button> | 33 | <my-button i18n-label label="Refresh" icon="refresh" (click)="refresh()"></my-button> |
21 | </div> | 34 | </div> |
diff --git a/client/src/app/+admin/system/logs/logs.component.scss b/client/src/app/+admin/system/logs/logs.component.scss index 6b92f4d0b..587a9795c 100644 --- a/client/src/app/+admin/system/logs/logs.component.scss +++ b/client/src/app/+admin/system/logs/logs.component.scss | |||
@@ -52,7 +52,8 @@ | |||
52 | } | 52 | } |
53 | 53 | ||
54 | my-button, | 54 | my-button, |
55 | .peertube-select-container { | 55 | .peertube-select-container, |
56 | ng-select { | ||
56 | margin-left: 10px; | 57 | margin-left: 10px; |
57 | } | 58 | } |
58 | } | 59 | } |
@@ -62,6 +63,7 @@ | |||
62 | flex-direction: column; | 63 | flex-direction: column; |
63 | 64 | ||
64 | .peertube-select-container, | 65 | .peertube-select-container, |
66 | ng-select, | ||
65 | my-button { | 67 | my-button { |
66 | width: 100% !important; | 68 | width: 100% !important; |
67 | margin-left: 0px !important; | 69 | margin-left: 0px !important; |
@@ -80,6 +82,7 @@ | |||
80 | flex-direction: column; | 82 | flex-direction: column; |
81 | 83 | ||
82 | .peertube-select-container, | 84 | .peertube-select-container, |
85 | ng-select, | ||
83 | my-button { | 86 | my-button { |
84 | width: 100% !important; | 87 | width: 100% !important; |
85 | margin-left: 0px !important; | 88 | margin-left: 0px !important; |
diff --git a/client/src/app/+admin/system/logs/logs.component.ts b/client/src/app/+admin/system/logs/logs.component.ts index c9c9dc3d1..62b8bc0b9 100644 --- a/client/src/app/+admin/system/logs/logs.component.ts +++ b/client/src/app/+admin/system/logs/logs.component.ts | |||
@@ -14,7 +14,7 @@ export class LogsComponent implements OnInit { | |||
14 | loading = false | 14 | loading = false |
15 | 15 | ||
16 | logs: LogRow[] = [] | 16 | logs: LogRow[] = [] |
17 | timeChoices: { id: string, label: string }[] = [] | 17 | timeChoices: { id: string, label: string, dateFormat: string }[] = [] |
18 | levelChoices: { id: LogLevel, label: string }[] = [] | 18 | levelChoices: { id: LogLevel, label: string }[] = [] |
19 | logTypeChoices: { id: 'audit' | 'standard', label: string }[] = [] | 19 | logTypeChoices: { id: 'audit' | 'standard', label: string }[] = [] |
20 | 20 | ||
@@ -76,15 +76,18 @@ export class LogsComponent implements OnInit { | |||
76 | this.timeChoices = [ | 76 | this.timeChoices = [ |
77 | { | 77 | { |
78 | id: lastWeek.toISOString(), | 78 | id: lastWeek.toISOString(), |
79 | label: $localize`Last week` | 79 | label: $localize`Last week`, |
80 | dateFormat: 'shortDate' | ||
80 | }, | 81 | }, |
81 | { | 82 | { |
82 | id: lastDay.toISOString(), | 83 | id: lastDay.toISOString(), |
83 | label: $localize`Last day` | 84 | label: $localize`Last day`, |
85 | dateFormat: 'short' | ||
84 | }, | 86 | }, |
85 | { | 87 | { |
86 | id: lastHour.toISOString(), | 88 | id: lastHour.toISOString(), |
87 | label: $localize`Last hour` | 89 | label: $localize`Last hour`, |
90 | dateFormat: 'mediumTime' | ||
88 | } | 91 | } |
89 | ] | 92 | ] |
90 | 93 | ||
@@ -95,19 +98,19 @@ export class LogsComponent implements OnInit { | |||
95 | this.levelChoices = [ | 98 | this.levelChoices = [ |
96 | { | 99 | { |
97 | id: 'debug', | 100 | id: 'debug', |
98 | label: $localize`Debug` | 101 | label: $localize`debug` |
99 | }, | 102 | }, |
100 | { | 103 | { |
101 | id: 'info', | 104 | id: 'info', |
102 | label: $localize`Info` | 105 | label: $localize`info` |
103 | }, | 106 | }, |
104 | { | 107 | { |
105 | id: 'warn', | 108 | id: 'warn', |
106 | label: $localize`Warning` | 109 | label: $localize`warning` |
107 | }, | 110 | }, |
108 | { | 111 | { |
109 | id: 'error', | 112 | id: 'error', |
110 | label: $localize`Error` | 113 | label: $localize`error` |
111 | } | 114 | } |
112 | ] | 115 | ] |
113 | 116 | ||
diff --git a/client/src/app/shared/shared-moderation/account-blocklist.component.html b/client/src/app/shared/shared-moderation/account-blocklist.component.html index ad7b6d163..7eca6411e 100644 --- a/client/src/app/shared/shared-moderation/account-blocklist.component.html +++ b/client/src/app/shared/shared-moderation/account-blocklist.component.html | |||
@@ -25,7 +25,7 @@ | |||
25 | <ng-template pTemplate="header"> | 25 | <ng-template pTemplate="header"> |
26 | <tr> | 26 | <tr> |
27 | <th style="width: 150px;">Action</th> <!-- column for action buttons --> | 27 | <th style="width: 150px;">Action</th> <!-- column for action buttons --> |
28 | <th style="width: 100%;" i18n>Account</th> | 28 | <th style="width: calc(100% - 300px);" i18n>Account</th> |
29 | <th style="width: 150px;" i18n pSortableColumn="createdAt">Muted at <p-sortIcon field="createdAt"></p-sortIcon></th> | 29 | <th style="width: 150px;" i18n pSortableColumn="createdAt">Muted at <p-sortIcon field="createdAt"></p-sortIcon></th> |
30 | </tr> | 30 | </tr> |
31 | </ng-template> | 31 | </ng-template> |
diff --git a/client/src/app/shared/shared-moderation/server-blocklist.component.html b/client/src/app/shared/shared-moderation/server-blocklist.component.html index 598e1a42c..225d49c1b 100644 --- a/client/src/app/shared/shared-moderation/server-blocklist.component.html +++ b/client/src/app/shared/shared-moderation/server-blocklist.component.html | |||
@@ -29,7 +29,7 @@ | |||
29 | <ng-template pTemplate="header"> | 29 | <ng-template pTemplate="header"> |
30 | <tr> | 30 | <tr> |
31 | <th style="width: 150px;">Action</th> <!-- column for action buttons --> | 31 | <th style="width: 150px;">Action</th> <!-- column for action buttons --> |
32 | <th style="width: 100%;" i18n>Instance</th> | 32 | <th style="width: calc(100% - 300px);" i18n>Instance</th> |
33 | <th style="width: 150px;" i18n pSortableColumn="createdAt">Muted at <p-sortIcon field="createdAt"></p-sortIcon></th> | 33 | <th style="width: 150px;" i18n pSortableColumn="createdAt">Muted at <p-sortIcon field="createdAt"></p-sortIcon></th> |
34 | </tr> | 34 | </tr> |
35 | </ng-template> | 35 | </ng-template> |