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/config/edit-custom-config/edit-custom-config.component.html493
-rw-r--r--client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.ts210
-rw-r--r--client/src/app/+admin/follows/followers-list/followers-list.component.ts8
-rw-r--r--client/src/app/+admin/follows/following-add/following-add.component.ts8
-rw-r--r--client/src/app/+admin/follows/following-list/following-list.component.ts13
-rw-r--r--client/src/app/+admin/follows/shared/redundancy-checkbox.component.ts21
-rw-r--r--client/src/app/+admin/jobs/jobs-list/jobs-list.component.ts6
-rw-r--r--client/src/app/+admin/moderation/instance-blocklist/instance-account-blocklist.component.ts11
-rw-r--r--client/src/app/+admin/moderation/instance-blocklist/instance-server-blocklist.component.ts11
-rw-r--r--client/src/app/+admin/moderation/moderation.component.scss1
-rw-r--r--client/src/app/+admin/moderation/video-abuse-list/moderation-comment-modal.component.html9
-rw-r--r--client/src/app/+admin/moderation/video-abuse-list/moderation-comment-modal.component.ts27
-rw-r--r--client/src/app/+admin/moderation/video-abuse-list/video-abuse-list.component.html8
-rw-r--r--client/src/app/+admin/moderation/video-abuse-list/video-abuse-list.component.ts23
-rw-r--r--client/src/app/+admin/moderation/video-blacklist-list/video-blacklist-list.component.html10
-rw-r--r--client/src/app/+admin/moderation/video-blacklist-list/video-blacklist-list.component.ts25
-rw-r--r--client/src/app/+admin/users/user-edit/user-create.component.ts10
-rw-r--r--client/src/app/+admin/users/user-edit/user-update.component.ts9
-rw-r--r--client/src/app/+admin/users/user-list/user-list.component.html5
-rw-r--r--client/src/app/+admin/users/user-list/user-list.component.scss4
-rw-r--r--client/src/app/+admin/users/user-list/user-list.component.ts42
21 files changed, 451 insertions, 503 deletions
diff --git a/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.html b/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.html
index fd4d3d9c9..52eb00d93 100644
--- a/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.html
+++ b/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.html
@@ -7,169 +7,169 @@
7 7
8 <div i18n class="inner-form-title">Instance</div> 8 <div i18n class="inner-form-title">Instance</div>
9 9
10 <div class="form-group"> 10 <ng-container formGroupName="instance">
11 <label i18n for="instanceName">Name</label> 11 <div class="form-group">
12 <input 12 <label i18n for="instanceName">Name</label>
13 type="text" id="instanceName" 13 <input
14 formControlName="instanceName" [ngClass]="{ 'input-error': formErrors['instanceName'] }" 14 type="text" id="instanceName"
15 > 15 formControlName="name" [ngClass]="{ 'input-error': formErrors.instance.name }"
16 <div *ngIf="formErrors.instanceName" class="form-error"> 16 >
17 {{ formErrors.instanceName }} 17 <div *ngIf="formErrors.instance.name" class="form-error">{{ formErrors.instance.name }}</div>
18 </div> 18 </div>
19 </div>
20 19
21 <div class="form-group"> 20 <div class="form-group">
22 <label i18n for="instanceShortDescription">Short description</label> 21 <label i18n for="instanceShortDescription">Short description</label>
23 <textarea 22 <textarea
24 id="instanceShortDescription" formControlName="instanceShortDescription" 23 id="instanceShortDescription" formControlName="shortDescription"
25 [ngClass]="{ 'input-error': formErrors['instanceShortDescription'] }" 24 [ngClass]="{ 'input-error': formErrors['instance.shortDescription'] }"
26 ></textarea> 25 ></textarea>
27 <div *ngIf="formErrors.instanceShortDescription" class="form-error"> 26 <div *ngIf="formErrors.instance.shortDescription" class="form-error">{{ formErrors.instance.shortDescription }}</div>
28 {{ formErrors.instanceShortDescription }}
29 </div> 27 </div>
30 </div>
31 28
32 <div class="form-group"> 29 <div class="form-group">
33 <label i18n for="instanceDescription">Description</label><my-help helpType="markdownText"></my-help> 30 <label i18n for="instanceDescription">Description</label><my-help helpType="markdownText"></my-help>
34 <my-markdown-textarea 31 <my-markdown-textarea
35 id="instanceDescription" formControlName="instanceDescription" textareaWidth="500px" [previewColumn]="true" 32 id="instanceDescription" formControlName="description" textareaWidth="500px" [previewColumn]="true"
36 [classes]="{ 'input-error': formErrors['instanceDescription'] }" 33 [classes]="{ 'input-error': formErrors['instance.description'] }"
37 ></my-markdown-textarea> 34 ></my-markdown-textarea>
38 <div *ngIf="formErrors.instanceDescription" class="form-error"> 35 <div *ngIf="formErrors.instance.description" class="form-error">{{ formErrors.instance.description }}</div>
39 {{ formErrors.instanceDescription }}
40 </div> 36 </div>
41 </div>
42 37
43 <div class="form-group"> 38 <div class="form-group">
44 <label i18n for="instanceTerms">Terms</label><my-help helpType="markdownText"></my-help> 39 <label i18n for="instanceTerms">Terms</label><my-help helpType="markdownText"></my-help>
45 <my-markdown-textarea 40 <my-markdown-textarea
46 id="instanceTerms" formControlName="instanceTerms" textareaWidth="500px" [previewColumn]="true" 41 id="instanceTerms" formControlName="terms" textareaWidth="500px" [previewColumn]="true"
47 [ngClass]="{ 'input-error': formErrors['instanceTerms'] }" 42 [ngClass]="{ 'input-error': formErrors['instance.terms'] }"
48 ></my-markdown-textarea> 43 ></my-markdown-textarea>
49 <div *ngIf="formErrors.instanceTerms" class="form-error"> 44 <div *ngIf="formErrors.instance.terms" class="form-error">{{ formErrors.instance.terms }}</div>
50 {{ formErrors.instanceTerms }}
51 </div> 45 </div>
52 </div>
53 46
54 <div class="form-group"> 47 <div class="form-group">
55 <label i18n for="instanceDefaultClientRoute">Default client route</label> 48 <label i18n for="instanceDefaultClientRoute">Default client route</label>
56 <div class="peertube-select-container"> 49 <div class="peertube-select-container">
57 <select id="instanceDefaultClientRoute" formControlName="instanceDefaultClientRoute"> 50 <select id="instanceDefaultClientRoute" formControlName="defaultClientRoute">
58 <option i18n value="/videos/overview">Videos Overview</option> 51 <option i18n value="/videos/overview">Videos Overview</option>
59 <option i18n value="/videos/trending">Videos Trending</option> 52 <option i18n value="/videos/trending">Videos Trending</option>
60 <option i18n value="/videos/recently-added">Videos Recently Added</option> 53 <option i18n value="/videos/recently-added">Videos Recently Added</option>
61 <option i18n value="/videos/local">Local videos</option> 54 <option i18n value="/videos/local">Local videos</option>
62 </select> 55 </select>
56 </div>
57 <div *ngIf="formErrors.instance.defaultClientRoute" class="form-error">{{ formErrors.instance.defaultClientRoute }}</div>
63 </div> 58 </div>
64 <div *ngIf="formErrors.instanceDefaultClientRoute" class="form-error"> 59
65 {{ formErrors.instanceDefaultClientRoute }} 60 <div class="form-group">
61 <label i18n for="instanceDefaultNSFWPolicy">Policy on videos containing sensitive content</label>
62 <my-help
63 helpType="custom" i18n-customHtml
64 customHtml="With <strong>Do not list</strong> or <strong>Blur thumbnails</strong>, a confirmation will be requested to watch the video."
65 ></my-help>
66
67 <div class="peertube-select-container">
68 <select id="instanceDefaultNSFWPolicy" formControlName="defaultNSFWPolicy">
69 <option i18n value="do_not_list">Do not list</option>
70 <option i18n value="blur">Blur thumbnails</option>
71 <option i18n value="display">Display</option>
72 </select>
73 </div>
74 <div *ngIf="formErrors.instance.defaultNSFWPolicy" class="form-error">{{ formErrors.instance.defaultNSFWPolicy }}</div>
66 </div> 75 </div>
67 </div> 76 </ng-container>
68 77
69 <div class="form-group"> 78 <div i18n class="inner-form-title">Signup</div>
70 <label i18n for="instanceDefaultNSFWPolicy">Policy on videos containing sensitive content</label>
71 <my-help
72 helpType="custom" i18n-customHtml
73 customHtml="With <strong>Do not list</strong> or <strong>Blur thumbnails</strong>, a confirmation will be requested to watch the video."
74 ></my-help>
75 79
76 <div class="peertube-select-container"> 80 <ng-container formGroupName="signup">
77 <select id="instanceDefaultNSFWPolicy" formControlName="instanceDefaultNSFWPolicy"> 81 <div class="form-group">
78 <option i18n value="do_not_list">Do not list</option> 82 <my-peertube-checkbox
79 <option i18n value="blur">Blur thumbnails</option> 83 inputName="signupEnabled" formControlName="enabled"
80 <option i18n value="display">Display</option> 84 i18n-labelText labelText="Signup enabled"
81 </select> 85 ></my-peertube-checkbox>
82 </div> 86 </div>
83 <div *ngIf="formErrors.instanceDefaultNSFWPolicy" class="form-error"> 87
84 {{ formErrors.instanceDefaultNSFWPolicy }} 88 <div class="form-group">
89 <my-peertube-checkbox *ngIf="isSignupEnabled()"
90 inputName="signupRequiresEmailVerification" formControlName="requiresEmailVerification"
91 i18n-labelText labelText="Signup requires email verification"
92 ></my-peertube-checkbox>
85 </div> 93 </div>
86 </div>
87 94
88 <div i18n class="inner-form-title">Signup</div> 95 <div *ngIf="isSignupEnabled()" class="form-group">
96 <label i18n for="signupLimit">Signup limit</label>
97 <input
98 type="text" id="signupLimit"
99 formControlName="limit" [ngClass]="{ 'input-error': formErrors['signup.limit'] }"
100 >
101 <div *ngIf="formErrors.signup.limit" class="form-error">{{ formErrors.signup.limit }}</div>
102 </div>
103 </ng-container>
89 104
90 <div class="form-group"> 105 <div i18n class="inner-form-title">Users</div>
91 <my-peertube-checkbox
92 inputName="signupEnabled" formControlName="signupEnabled"
93 i18n-labelText labelText="Signup enabled"
94 ></my-peertube-checkbox>
95 </div>
96 106
97 <div class="form-group"> 107 <ng-container formGroupName="user">
98 <my-peertube-checkbox *ngIf="isSignupEnabled()" 108 <div class="form-group">
99 inputName="signupRequiresEmailVerification" formControlName="signupRequiresEmailVerification" 109 <label i18n for="userVideoQuota">User default video quota</label>
100 i18n-labelText labelText="Signup requires email verification" 110 <div class="peertube-select-container">
101 ></my-peertube-checkbox> 111 <select id="userVideoQuota" formControlName="videoQuota">
102 </div> 112 <option *ngFor="let videoQuotaOption of videoQuotaOptions" [value]="videoQuotaOption.value">
113 {{ videoQuotaOption.label }}
114 </option>
115 </select>
116 </div>
117 <div *ngIf="formErrors.user.videoQuota" class="form-error">{{ formErrors.user.videoQuota }}</div>
118 </div>
103 119
104 <div *ngIf="isSignupEnabled()" class="form-group"> 120 <div class="form-group">
105 <label i18n for="signupLimit">Signup limit</label> 121 <label i18n for="userVideoQuotaDaily">User default daily upload limit</label>
106 <input 122 <div class="peertube-select-container">
107 type="text" id="signupLimit" 123 <select id="userVideoQuotaDaily" formControlName="videoQuotaDaily">
108 formControlName="signupLimit" [ngClass]="{ 'input-error': formErrors['signupLimit'] }" 124 <option *ngFor="let videoQuotaDailyOption of videoQuotaDailyOptions" [value]="videoQuotaDailyOption.value">
109 > 125 {{ videoQuotaDailyOption.label }}
110 <div *ngIf="formErrors.signupLimit" class="form-error"> 126 </option>
111 {{ formErrors.signupLimit }} 127 </select>
128 </div>
129 <div *ngIf="formErrors.user.videoQuotaDaily" class="form-error">{{ formErrors.user.videoQuotaDaily }}</div>
112 </div> 130 </div>
113 </div> 131 </ng-container>
114 132
115 <div i18n class="inner-form-title">Import</div> 133 <div i18n class="inner-form-title">Import</div>
116 134
117 <div class="form-group"> 135 <ng-container formGroupName="import">
118 <my-peertube-checkbox 136 <ng-container formGroupName="videos">
119 inputName="importVideosHttpEnabled" formControlName="importVideosHttpEnabled"
120 i18n-labelText labelText="Video import with HTTP URL (i.e. YouTube) enabled"
121 ></my-peertube-checkbox>
122 </div>
123 137
124 <div class="form-group"> 138 <div class="form-group" formGroupName="http">
125 <my-peertube-checkbox 139 <my-peertube-checkbox
126 inputName="importVideosTorrentEnabled" formControlName="importVideosTorrentEnabled" 140 inputName="importVideosHttpEnabled" formControlName="enabled"
127 i18n-labelText labelText="Video import with a torrent file or a magnet URI enabled" 141 i18n-labelText labelText="Video import with HTTP URL (i.e. YouTube) enabled"
128 ></my-peertube-checkbox> 142 ></my-peertube-checkbox>
129 </div> 143 </div>
144
145 <div class="form-group" formGroupName="torrent">
146 <my-peertube-checkbox
147 inputName="importVideosTorrentEnabled" formControlName="enabled"
148 i18n-labelText labelText="Video import with a torrent file or a magnet URI enabled"
149 ></my-peertube-checkbox>
150 </div>
151
152 </ng-container>
153 </ng-container>
130 154
131 <div i18n class="inner-form-title">Administrator</div> 155 <div i18n class="inner-form-title">Administrator</div>
132 156
133 <div class="form-group"> 157 <div class="form-group" formGroupName="admin">
134 <label i18n for="adminEmail">Admin email</label> 158 <label i18n for="adminEmail">Admin email</label>
135 <input 159 <input
136 type="text" id="adminEmail" 160 type="text" id="adminEmail"
137 formControlName="adminEmail" [ngClass]="{ 'input-error': formErrors['adminEmail'] }" 161 formControlName="email" [ngClass]="{ 'input-error': formErrors['admin.email'] }"
138 > 162 >
139 <div *ngIf="formErrors.adminEmail" class="form-error"> 163 <div *ngIf="formErrors.admin.email" class="form-error">{{ formErrors.admin.email }}</div>
140 {{ formErrors.adminEmail }}
141 </div>
142 </div> 164 </div>
143 165
144 <div i18n class="inner-form-title">Users</div> 166 <div class="form-group" formGroupName="contactForm">
145 167 <my-peertube-checkbox
146 <div class="form-group"> 168 inputName="enableContactForm" formControlName="enabled"
147 <label i18n for="userVideoQuota">User default video quota</label> 169 i18n-labelText labelText="Enable contact form"
148 <div class="peertube-select-container"> 170 ></my-peertube-checkbox>
149 <select id="userVideoQuota" formControlName="userVideoQuota">
150 <option *ngFor="let videoQuotaOption of videoQuotaOptions" [value]="videoQuotaOption.value">
151 {{ videoQuotaOption.label }}
152 </option>
153 </select>
154 </div>
155 <div *ngIf="formErrors.userVideoQuota" class="form-error">
156 {{ formErrors.userVideoQuota }}
157 </div>
158 </div> 171 </div>
159 172
160 <div class="form-group">
161 <label i18n for="userVideoQuotaDaily">User default daily upload limit</label>
162 <div class="peertube-select-container">
163 <select id="userVideoQuotaDaily" formControlName="userVideoQuotaDaily">
164 <option *ngFor="let videoQuotaDailyOption of videoQuotaDailyOptions" [value]="videoQuotaDailyOption.value">
165 {{ videoQuotaDailyOption.label }}
166 </option>
167 </select>
168 </div>
169 <div *ngIf="formErrors.userVideoQuotaDaily" class="form-error">
170 {{ formErrors.userVideoQuotaDaily }}
171 </div>
172 </div>
173 </ng-template> 173 </ng-template>
174 </ngb-tab> 174 </ngb-tab>
175 175
@@ -177,30 +177,35 @@
177 <ng-template ngbTabContent> 177 <ng-template ngbTabContent>
178 <div i18n class="inner-form-title">Twitter</div> 178 <div i18n class="inner-form-title">Twitter</div>
179 179
180 <div class="form-group"> 180 <ng-container formGroupName="services">
181 <label i18n for="signupLimit">Your Twitter username</label> 181 <ng-container formGroupName="twitter">
182 <my-help 182
183 helpType="custom" i18n-customHtml 183 <div class="form-group">
184 customHtml="Indicates the Twitter account for the website or platform on which the content was published." 184 <label i18n for="signupLimit">Your Twitter username</label>
185 ></my-help> 185 <my-help
186 <input 186 helpType="custom" i18n-customHtml
187 type="text" id="servicesTwitterUsername" 187 customHtml="Indicates the Twitter account for the website or platform on which the content was published."
188 formControlName="servicesTwitterUsername" [ngClass]="{ 'input-error': formErrors['servicesTwitterUsername'] }" 188 ></my-help>
189 > 189 <input
190 <div *ngIf="formErrors.servicesTwitterUsername" class="form-error"> 190 type="text" id="servicesTwitterUsername"
191 {{ formErrors.servicesTwitterUsername }} 191 formControlName="username" [ngClass]="{ 'input-error': formErrors['services.twitter.username'] }"
192 </div> 192 >
193 </div> 193 <div *ngIf="formErrors.services.twitter.username" class="form-error">{{ formErrors.services.twitter.username }}</div>
194 </div>
195
196 <div class="form-group">
197 <my-peertube-checkbox
198 inputName="servicesTwitterWhitelisted" formControlName="whitelisted"
199 i18n-labelText labelText="Instance whitelisted by Twitter"
200 i18n-helpHtml helpHtml="If your instance is whitelisted by Twitter, a video player will be embedded in the Twitter feed on PeerTube video share.<br />
201 If the instance is not whitelisted, we use an image link card that will redirect on your PeerTube instance.<br /><br />
202 Check this checkbox, save the configuration and test with a video URL of your instance (https://example.com/videos/watch/blabla) on <a target='_blank' rel='noopener noreferrer' href='https://cards-dev.twitter.com/validator'>https://cards-dev.twitter.com/validator</a> to see if you instance is whitelisted."
203 ></my-peertube-checkbox>
204 </div>
205
206 </ng-container>
207 </ng-container>
194 208
195 <div class="form-group">
196 <my-peertube-checkbox
197 inputName="servicesTwitterWhitelisted" formControlName="servicesTwitterWhitelisted"
198 i18n-labelText labelText="Instance whitelisted by Twitter"
199 i18n-helpHtml helpHtml="If your instance is whitelisted by Twitter, a video player will be embedded in the Twitter feed on PeerTube video share.<br />
200 If the instance is not whitelisted, we use an image link card that will redirect on your PeerTube instance.<br /><br />
201 Check this checkbox, save the configuration and test with a video URL of your instance (https://example.com/videos/watch/blabla) on <a target='_blank' rel='noopener noreferrer' href='https://cards-dev.twitter.com/validator'>https://cards-dev.twitter.com/validator</a> to see if you instance is whitelisted."
202 ></my-peertube-checkbox>
203 </div>
204 </ng-template> 209 </ng-template>
205 </ngb-tab> 210 </ngb-tab>
206 211
@@ -209,37 +214,48 @@
209 214
210 <div i18n class="inner-form-title">Transcoding</div> 215 <div i18n class="inner-form-title">Transcoding</div>
211 216
212 <div class="form-group"> 217 <ng-container formGroupName="transcoding">
213 <my-peertube-checkbox 218 <div class="form-group">
214 inputName="transcodingEnabled" formControlName="transcodingEnabled" 219 <my-peertube-checkbox
215 i18n-labelText labelText="Transcoding enabled" 220 inputName="transcodingEnabled" formControlName="enabled"
216 i18n-helpHtml helpHtml="If you disable transcoding, many videos from your users will not work!" 221 i18n-labelText labelText="Transcoding enabled"
217 ></my-peertube-checkbox> 222 i18n-helpHtml helpHtml="If you disable transcoding, many videos from your users will not work!"
218 </div> 223 ></my-peertube-checkbox>
224 </div>
219 225
220 <ng-template [ngIf]="isTranscodingEnabled()"> 226 <ng-container *ngIf="isTranscodingEnabled()">
221 227
222 <div class="form-group"> 228 <div class="form-group">
223 <label i18n for="transcodingThreads">Transcoding threads</label> 229 <my-peertube-checkbox
224 <div class="peertube-select-container"> 230 inputName="transcodingAllowAdditionalExtensions" formControlName="allowAdditionalExtensions"
225 <select id="transcodingThreads" formControlName="transcodingThreads"> 231 i18n-labelText labelText="Allow additional extensions"
226 <option *ngFor="let transcodingThreadOption of transcodingThreadOptions" [value]="transcodingThreadOption.value"> 232 i18n-helpHtml helpHtml="Allow your users to upload .mkv, .mov, .avi, .flv videos"
227 {{ transcodingThreadOption.label }} 233 ></my-peertube-checkbox>
228 </option>
229 </select>
230 </div> 234 </div>
231 <div *ngIf="formErrors.transcodingThreads" class="form-error"> 235
232 {{ formErrors.transcodingThreads }} 236 <div class="form-group">
237 <label i18n for="transcodingThreads">Transcoding threads</label>
238 <div class="peertube-select-container">
239 <select id="transcodingThreads" formControlName="threads">
240 <option *ngFor="let transcodingThreadOption of transcodingThreadOptions" [value]="transcodingThreadOption.value">
241 {{ transcodingThreadOption.label }}
242 </option>
243 </select>
244 </div>
245 <div *ngIf="formErrors.transcoding.threads" class="form-error">{{ formErrors.transcoding.threads }}</div>
233 </div> 246 </div>
234 </div>
235 247
236 <div class="form-group" *ngFor="let resolution of resolutions"> 248 <ng-container formGroupName="resolutions">
237 <my-peertube-checkbox 249 <div class="form-group" *ngFor="let resolution of resolutions">
238 [inputName]="getResolutionKey(resolution)" [formControlName]="getResolutionKey(resolution)" 250 <my-peertube-checkbox
239 i18n-labelText labelText="Resolution {{resolution}} enabled" 251 [inputName]="getResolutionKey(resolution)" [formControlName]="resolution"
240 ></my-peertube-checkbox> 252 i18n-labelText labelText="Resolution {{resolution}} enabled"
241 </div> 253 ></my-peertube-checkbox>
242 </ng-template> 254 </div>
255 </ng-container>
256
257 </ng-container>
258 </ng-container>
243 259
244 <div i18n class="inner-form-title"> 260 <div i18n class="inner-form-title">
245 Cache 261 Cache
@@ -250,74 +266,73 @@
250 ></my-help> 266 ></my-help>
251 </div> 267 </div>
252 268
253 <div class="form-group"> 269 <ng-container formGroupName="cache">
254 <label i18n for="cachePreviewsSize">Previews cache size</label> 270 <div class="form-group" formGroupName="previews">
255 <input 271 <label i18n for="cachePreviewsSize">Previews cache size</label>
256 type="text" id="cachePreviewsSize" 272 <input
257 formControlName="cachePreviewsSize" [ngClass]="{ 'input-error': formErrors['cachePreviewsSize'] }" 273 type="text" id="cachePreviewsSize"
258 > 274 formControlName="size" [ngClass]="{ 'input-error': formErrors['cache.previews.size'] }"
259 <div *ngIf="formErrors.cachePreviewsSize" class="form-error"> 275 >
260 {{ formErrors.cachePreviewsSize }} 276 <div *ngIf="formErrors.cache.previews.size" class="form-error">{{ formErrors.cache.previews.size }}</div>
261 </div> 277 </div>
262 </div>
263 278
264 <div class="form-group"> 279 <div class="form-group" formGroupName="captions">
265 <label i18n for="cachePreviewsSize">Video captions cache size</label> 280 <label i18n for="cacheCaptionsSize">Video captions cache size</label>
266 <input 281 <input
267 type="text" id="cacheCaptionsSize" 282 type="text" id="cacheCaptionsSize"
268 formControlName="cacheCaptionsSize" [ngClass]="{ 'input-error': formErrors['cacheCaptionsSize'] }" 283 formControlName="size" [ngClass]="{ 'input-error': formErrors['cache.captions.size'] }"
269 > 284 >
270 <div *ngIf="formErrors.cacheCaptionsSize" class="form-error"> 285 <div *ngIf="formErrors.cache.captions.size" class="form-error">{{ formErrors.cache.captions.size }}</div>
271 {{ formErrors.cacheCaptionsSize }}
272 </div> 286 </div>
273 </div> 287 </ng-container>
274 288
275 <div i18n class="inner-form-title">Customizations</div> 289 <div i18n class="inner-form-title">Customizations</div>
276 290
277 <div class="form-group"> 291 <ng-container formGroupName="instance">
278 <label i18n for="customizationJavascript">JavaScript</label> 292 <ng-container formGroupName="customizations">
279 <my-help 293 <div class="form-group">
280 helpType="custom" i18n-customHtml 294 <label i18n for="customizationJavascript">JavaScript</label>
281 customHtml="Write directly JavaScript code.<br />Example: <pre>console.log('my instance is amazing');</pre>" 295 <my-help
282 ></my-help> 296 helpType="custom" i18n-customHtml
283 <textarea 297 customHtml="Write directly JavaScript code.<br />Example: <pre>console.log('my instance is amazing');</pre>"
284 id="customizationJavascript" formControlName="customizationJavascript" 298 ></my-help>
285 [ngClass]="{ 'input-error': formErrors['customizationJavascript'] }" 299 <textarea
286 ></textarea> 300 id="customizationJavascript" formControlName="javascript"
287 <div *ngIf="formErrors.customizationJavascript" class="form-error"> 301 [ngClass]="{ 'input-error': formErrors['instance.customizations.javascript'] }"
288 {{ formErrors.customizationJavascript }} 302 ></textarea>
289 </div> 303 <div *ngIf="formErrors.instance.customizations.javascript" class="form-error">{{ formErrors.instance.customizations.javascript }}</div>
290 </div> 304 </div>
305
306 <div class="form-group">
307 <label for="customizationCSS">CSS</label>
308 <my-help
309 helpType="custom"
310 i18n-customHtml
311 customHtml="
312 Write directly CSS code. Example:<br />
313 <pre>
314 body {{ '{' }}
315 background-color: red;
316 {{ '}' }}
317 </pre>
318
319 Prepend with <em>#custom-css</em> to override styles. Example:
320 <pre>
321 #custom-css .logged-in-email {{ '{' }}
322 color: red;
323 {{ '}' }}
324 </pre>
325 "
326 ></my-help>
327 <textarea
328 id="customizationCSS" formControlName="css"
329 [ngClass]="{ 'input-error': formErrors['instance.customizations.css'] }"
330 ></textarea>
331 <div *ngIf="formErrors.instance.customizations.css" class="form-error">{{ formErrors.instance.customizations.css }}</div>
332 </div>
333 </ng-container>
334 </ng-container>
291 335
292 <div class="form-group">
293 <label for="customizationCSS">CSS</label>
294 <my-help
295 helpType="custom"
296 i18n-customHtml
297 customHtml="
298 Write directly CSS code. Example:<br />
299 <pre>
300 body {{ '{' }}
301 background-color: red;
302 {{ '}' }}
303 </pre>
304
305 Prepend with <em>#custom-css</em> to override styles. Example:
306 <pre>
307 #custom-css .logged-in-email {{ '{' }}
308 color: red;
309 {{ '}' }}
310 </pre>
311 "
312 ></my-help>
313 <textarea
314 id="customizationCSS" formControlName="customizationCSS"
315 [ngClass]="{ 'input-error': formErrors['customizationCSS'] }"
316 ></textarea>
317 <div *ngIf="formErrors.customizationCSS" class="form-error">
318 {{ formErrors.customizationCSS }}
319 </div>
320 </div>
321 </ng-template> 336 </ng-template>
322 </ngb-tab> 337 </ngb-tab>
323 </ngb-tabset> 338 </ngb-tabset>
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 f48b6fc1a..654a076b0 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
@@ -2,7 +2,7 @@ import { Component, OnInit } from '@angular/core'
2import { ConfigService } from '@app/+admin/config/shared/config.service' 2import { ConfigService } from '@app/+admin/config/shared/config.service'
3import { ServerService } from '@app/core/server/server.service' 3import { ServerService } from '@app/core/server/server.service'
4import { CustomConfigValidatorsService, FormReactive, UserValidatorsService } from '@app/shared' 4import { CustomConfigValidatorsService, FormReactive, UserValidatorsService } from '@app/shared'
5import { NotificationsService } from 'angular2-notifications' 5import { Notifier } from '@app/core'
6import { CustomConfig } from '../../../../../../shared/models/server/custom-config.model' 6import { CustomConfig } from '../../../../../../shared/models/server/custom-config.model'
7import { I18n } from '@ngx-translate/i18n-polyfill' 7import { I18n } from '@ngx-translate/i18n-polyfill'
8import { BuildFormDefaultValues, FormValidatorService } from '@app/shared/forms/form-validators/form-validator.service' 8import { BuildFormDefaultValues, FormValidatorService } from '@app/shared/forms/form-validators/form-validator.service'
@@ -18,14 +18,11 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit {
18 resolutions: string[] = [] 18 resolutions: string[] = []
19 transcodingThreadOptions: { label: string, value: number }[] = [] 19 transcodingThreadOptions: { label: string, value: number }[] = []
20 20
21 private oldCustomJavascript: string
22 private oldCustomCSS: string
23
24 constructor ( 21 constructor (
25 protected formValidatorService: FormValidatorService, 22 protected formValidatorService: FormValidatorService,
26 private customConfigValidatorsService: CustomConfigValidatorsService, 23 private customConfigValidatorsService: CustomConfigValidatorsService,
27 private userValidatorsService: UserValidatorsService, 24 private userValidatorsService: UserValidatorsService,
28 private notificationsService: NotificationsService, 25 private notifier: Notifier,
29 private configService: ConfigService, 26 private configService: ConfigService,
30 private serverService: ServerService, 27 private serverService: ServerService,
31 private i18n: I18n 28 private i18n: I18n
@@ -58,40 +55,78 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit {
58 } 55 }
59 56
60 getResolutionKey (resolution: string) { 57 getResolutionKey (resolution: string) {
61 return 'transcodingResolution' + resolution 58 return 'transcoding.resolutions.' + resolution
62 } 59 }
63 60
64 ngOnInit () { 61 ngOnInit () {
65 const formGroupData: { [key: string]: any } = { 62 const formGroupData: { [key in keyof CustomConfig ]: any } = {
66 instanceName: this.customConfigValidatorsService.INSTANCE_NAME, 63 instance: {
67 instanceShortDescription: this.customConfigValidatorsService.INSTANCE_SHORT_DESCRIPTION, 64 name: this.customConfigValidatorsService.INSTANCE_NAME,
68 instanceDescription: null, 65 shortDescription: this.customConfigValidatorsService.INSTANCE_SHORT_DESCRIPTION,
69 instanceTerms: null, 66 description: null,
70 instanceDefaultClientRoute: null, 67 terms: null,
71 instanceDefaultNSFWPolicy: null, 68 defaultClientRoute: null,
72 servicesTwitterUsername: this.customConfigValidatorsService.SERVICES_TWITTER_USERNAME, 69 defaultNSFWPolicy: null,
73 servicesTwitterWhitelisted: null, 70 customizations: {
74 cachePreviewsSize: this.customConfigValidatorsService.CACHE_PREVIEWS_SIZE, 71 javascript: null,
75 cacheCaptionsSize: this.customConfigValidatorsService.CACHE_CAPTIONS_SIZE, 72 css: null
76 signupEnabled: null, 73 }
77 signupLimit: this.customConfigValidatorsService.SIGNUP_LIMIT, 74 },
78 signupRequiresEmailVerification: null, 75 services: {
79 importVideosHttpEnabled: null, 76 twitter: {
80 importVideosTorrentEnabled: null, 77 username: this.customConfigValidatorsService.SERVICES_TWITTER_USERNAME,
81 adminEmail: this.customConfigValidatorsService.ADMIN_EMAIL, 78 whitelisted: null
82 userVideoQuota: this.userValidatorsService.USER_VIDEO_QUOTA, 79 }
83 userVideoQuotaDaily: this.userValidatorsService.USER_VIDEO_QUOTA_DAILY, 80 },
84 transcodingThreads: this.customConfigValidatorsService.TRANSCODING_THREADS, 81 cache: {
85 transcodingEnabled: null, 82 previews: {
86 customizationJavascript: null, 83 size: this.customConfigValidatorsService.CACHE_PREVIEWS_SIZE
87 customizationCSS: null 84 },
85 captions: {
86 size: this.customConfigValidatorsService.CACHE_CAPTIONS_SIZE
87 }
88 },
89 signup: {
90 enabled: null,
91 limit: this.customConfigValidatorsService.SIGNUP_LIMIT,
92 requiresEmailVerification: null
93 },
94 import: {
95 videos: {
96 http: {
97 enabled: null
98 },
99 torrent: {
100 enabled: null
101 }
102 }
103 },
104 admin: {
105 email: this.customConfigValidatorsService.ADMIN_EMAIL
106 },
107 contactForm: {
108 enabled: null
109 },
110 user: {
111 videoQuota: this.userValidatorsService.USER_VIDEO_QUOTA,
112 videoQuotaDaily: this.userValidatorsService.USER_VIDEO_QUOTA_DAILY
113 },
114 transcoding: {
115 enabled: null,
116 threads: this.customConfigValidatorsService.TRANSCODING_THREADS,
117 allowAdditionalExtensions: null,
118 resolutions: {}
119 }
88 } 120 }
89 121
90 const defaultValues: BuildFormDefaultValues = {} 122 const defaultValues = {
123 transcoding: {
124 resolutions: {}
125 }
126 }
91 for (const resolution of this.resolutions) { 127 for (const resolution of this.resolutions) {
92 const key = this.getResolutionKey(resolution) 128 defaultValues.transcoding.resolutions[resolution] = 'false'
93 defaultValues[key] = 'false' 129 formGroupData.transcoding.resolutions[resolution] = null
94 formGroupData[key] = null
95 } 130 }
96 131
97 this.buildForm(formGroupData) 132 this.buildForm(formGroupData)
@@ -101,90 +136,25 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit {
101 res => { 136 res => {
102 this.customConfig = res 137 this.customConfig = res
103 138
104 this.oldCustomCSS = this.customConfig.instance.customizations.css
105 this.oldCustomJavascript = this.customConfig.instance.customizations.javascript
106
107 this.updateForm() 139 this.updateForm()
108 // Force form validation 140 // Force form validation
109 this.forceCheck() 141 this.forceCheck()
110 }, 142 },
111 143
112 err => this.notificationsService.error(this.i18n('Error'), err.message) 144 err => this.notifier.error(err.message)
113 ) 145 )
114 } 146 }
115 147
116 isTranscodingEnabled () { 148 isTranscodingEnabled () {
117 return this.form.value['transcodingEnabled'] === true 149 return this.form.value['transcoding']['enabled'] === true
118 } 150 }
119 151
120 isSignupEnabled () { 152 isSignupEnabled () {
121 return this.form.value['signupEnabled'] === true 153 return this.form.value['signup']['enabled'] === true
122 } 154 }
123 155
124 async formValidated () { 156 async formValidated () {
125 const data: CustomConfig = { 157 this.configService.updateCustomConfig(this.form.value)
126 instance: {
127 name: this.form.value['instanceName'],
128 shortDescription: this.form.value['instanceShortDescription'],
129 description: this.form.value['instanceDescription'],
130 terms: this.form.value['instanceTerms'],
131 defaultClientRoute: this.form.value['instanceDefaultClientRoute'],
132 defaultNSFWPolicy: this.form.value['instanceDefaultNSFWPolicy'],
133 customizations: {
134 javascript: this.form.value['customizationJavascript'],
135 css: this.form.value['customizationCSS']
136 }
137 },
138 services: {
139 twitter: {
140 username: this.form.value['servicesTwitterUsername'],
141 whitelisted: this.form.value['servicesTwitterWhitelisted']
142 }
143 },
144 cache: {
145 previews: {
146 size: this.form.value['cachePreviewsSize']
147 },
148 captions: {
149 size: this.form.value['cacheCaptionsSize']
150 }
151 },
152 signup: {
153 enabled: this.form.value['signupEnabled'],
154 limit: this.form.value['signupLimit'],
155 requiresEmailVerification: this.form.value['signupRequiresEmailVerification']
156 },
157 admin: {
158 email: this.form.value['adminEmail']
159 },
160 user: {
161 videoQuota: this.form.value['userVideoQuota'],
162 videoQuotaDaily: this.form.value['userVideoQuotaDaily']
163 },
164 transcoding: {
165 enabled: this.form.value['transcodingEnabled'],
166 threads: this.form.value['transcodingThreads'],
167 resolutions: {
168 '240p': this.form.value[this.getResolutionKey('240p')],
169 '360p': this.form.value[this.getResolutionKey('360p')],
170 '480p': this.form.value[this.getResolutionKey('480p')],
171 '720p': this.form.value[this.getResolutionKey('720p')],
172 '1080p': this.form.value[this.getResolutionKey('1080p')]
173 }
174 },
175 import: {
176 videos: {
177 http: {
178 enabled: this.form.value['importVideosHttpEnabled']
179 },
180 torrent: {
181 enabled: this.form.value['importVideosTorrentEnabled']
182 }
183 }
184 }
185 }
186
187 this.configService.updateCustomConfig(data)
188 .subscribe( 158 .subscribe(
189 res => { 159 res => {
190 this.customConfig = res 160 this.customConfig = res
@@ -194,45 +164,15 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit {
194 164
195 this.updateForm() 165 this.updateForm()
196 166
197 this.notificationsService.success(this.i18n('Success'), this.i18n('Configuration updated.')) 167 this.notifier.success(this.i18n('Configuration updated.'))
198 }, 168 },
199 169
200 err => this.notificationsService.error(this.i18n('Error'), err.message) 170 err => this.notifier.error(err.message)
201 ) 171 )
202 } 172 }
203 173
204 private updateForm () { 174 private updateForm () {
205 const data: { [key: string]: any } = { 175 this.form.patchValue(this.customConfig)
206 instanceName: this.customConfig.instance.name,
207 instanceShortDescription: this.customConfig.instance.shortDescription,
208 instanceDescription: this.customConfig.instance.description,
209 instanceTerms: this.customConfig.instance.terms,
210 instanceDefaultClientRoute: this.customConfig.instance.defaultClientRoute,
211 instanceDefaultNSFWPolicy: this.customConfig.instance.defaultNSFWPolicy,
212 servicesTwitterUsername: this.customConfig.services.twitter.username,
213 servicesTwitterWhitelisted: this.customConfig.services.twitter.whitelisted,
214 cachePreviewsSize: this.customConfig.cache.previews.size,
215 cacheCaptionsSize: this.customConfig.cache.captions.size,
216 signupEnabled: this.customConfig.signup.enabled,
217 signupLimit: this.customConfig.signup.limit,
218 signupRequiresEmailVerification: this.customConfig.signup.requiresEmailVerification,
219 adminEmail: this.customConfig.admin.email,
220 userVideoQuota: this.customConfig.user.videoQuota,
221 userVideoQuotaDaily: this.customConfig.user.videoQuotaDaily,
222 transcodingThreads: this.customConfig.transcoding.threads,
223 transcodingEnabled: this.customConfig.transcoding.enabled,
224 customizationJavascript: this.customConfig.instance.customizations.javascript,
225 customizationCSS: this.customConfig.instance.customizations.css,
226 importVideosHttpEnabled: this.customConfig.import.videos.http.enabled,
227 importVideosTorrentEnabled: this.customConfig.import.videos.torrent.enabled
228 }
229
230 for (const resolution of this.resolutions) {
231 const key = this.getResolutionKey(resolution)
232 data[key] = this.customConfig.transcoding.resolutions[resolution]
233 }
234
235 this.form.patchValue(data)
236 } 176 }
237 177
238} 178}
diff --git a/client/src/app/+admin/follows/followers-list/followers-list.component.ts b/client/src/app/+admin/follows/followers-list/followers-list.component.ts
index 4a25b7ff3..9a8848bfb 100644
--- a/client/src/app/+admin/follows/followers-list/followers-list.component.ts
+++ b/client/src/app/+admin/follows/followers-list/followers-list.component.ts
@@ -1,6 +1,6 @@
1import { Component, OnInit } from '@angular/core' 1import { Component, OnInit } from '@angular/core'
2 2
3import { NotificationsService } from 'angular2-notifications' 3import { Notifier } from '@app/core'
4import { SortMeta } from 'primeng/primeng' 4import { SortMeta } from 'primeng/primeng'
5import { ActorFollow } from '../../../../../../shared/models/actors/follow.model' 5import { ActorFollow } from '../../../../../../shared/models/actors/follow.model'
6import { RestPagination, RestTable } from '../../../shared' 6import { RestPagination, RestTable } from '../../../shared'
@@ -20,7 +20,7 @@ export class FollowersListComponent extends RestTable implements OnInit {
20 pagination: RestPagination = { count: this.rowsPerPage, start: 0 } 20 pagination: RestPagination = { count: this.rowsPerPage, start: 0 }
21 21
22 constructor ( 22 constructor (
23 private notificationsService: NotificationsService, 23 private notifier: Notifier,
24 private followService: FollowService, 24 private followService: FollowService,
25 private i18n: I18n 25 private i18n: I18n
26 ) { 26 ) {
@@ -32,14 +32,14 @@ export class FollowersListComponent extends RestTable implements OnInit {
32 } 32 }
33 33
34 protected loadData () { 34 protected loadData () {
35 this.followService.getFollowers(this.pagination, this.sort) 35 this.followService.getFollowers(this.pagination, this.sort, this.search)
36 .subscribe( 36 .subscribe(
37 resultList => { 37 resultList => {
38 this.followers = resultList.data 38 this.followers = resultList.data
39 this.totalRecords = resultList.total 39 this.totalRecords = resultList.total
40 }, 40 },
41 41
42 err => this.notificationsService.error(this.i18n('Error'), err.message) 42 err => this.notifier.error(err.message)
43 ) 43 )
44 } 44 }
45} 45}
diff --git a/client/src/app/+admin/follows/following-add/following-add.component.ts b/client/src/app/+admin/follows/following-add/following-add.component.ts
index bd9cc022b..2bb249746 100644
--- a/client/src/app/+admin/follows/following-add/following-add.component.ts
+++ b/client/src/app/+admin/follows/following-add/following-add.component.ts
@@ -1,6 +1,6 @@
1import { Component } from '@angular/core' 1import { Component } from '@angular/core'
2import { Router } from '@angular/router' 2import { Router } from '@angular/router'
3import { NotificationsService } from 'angular2-notifications' 3import { Notifier } from '@app/core'
4import { ConfirmService } from '../../../core' 4import { ConfirmService } from '../../../core'
5import { validateHost } from '../../../shared' 5import { validateHost } from '../../../shared'
6import { FollowService } from '../shared' 6import { FollowService } from '../shared'
@@ -18,7 +18,7 @@ export class FollowingAddComponent {
18 18
19 constructor ( 19 constructor (
20 private router: Router, 20 private router: Router,
21 private notificationsService: NotificationsService, 21 private notifier: Notifier,
22 private confirmService: ConfirmService, 22 private confirmService: ConfirmService,
23 private followService: FollowService, 23 private followService: FollowService,
24 private i18n: I18n 24 private i18n: I18n
@@ -64,12 +64,12 @@ export class FollowingAddComponent {
64 64
65 this.followService.follow(hosts).subscribe( 65 this.followService.follow(hosts).subscribe(
66 () => { 66 () => {
67 this.notificationsService.success(this.i18n('Success'), this.i18n('Follow request(s) sent!')) 67 this.notifier.success(this.i18n('Follow request(s) sent!'))
68 68
69 setTimeout(() => this.router.navigate([ '/admin/follows/following-list' ]), 500) 69 setTimeout(() => this.router.navigate([ '/admin/follows/following-list' ]), 500)
70 }, 70 },
71 71
72 err => this.notificationsService.error(this.i18n('Error'), err.message) 72 err => this.notifier.error(err.message)
73 ) 73 )
74 } 74 }
75 75
diff --git a/client/src/app/+admin/follows/following-list/following-list.component.ts b/client/src/app/+admin/follows/following-list/following-list.component.ts
index 9b7029f75..4517a721e 100644
--- a/client/src/app/+admin/follows/following-list/following-list.component.ts
+++ b/client/src/app/+admin/follows/following-list/following-list.component.ts
@@ -1,5 +1,5 @@
1import { Component, OnInit } from '@angular/core' 1import { Component, OnInit } from '@angular/core'
2import { NotificationsService } from 'angular2-notifications' 2import { Notifier } from '@app/core'
3import { SortMeta } from 'primeng/primeng' 3import { SortMeta } from 'primeng/primeng'
4import { ActorFollow } from '../../../../../../shared/models/actors/follow.model' 4import { ActorFollow } from '../../../../../../shared/models/actors/follow.model'
5import { ConfirmService } from '../../../core/confirm/confirm.service' 5import { ConfirmService } from '../../../core/confirm/confirm.service'
@@ -20,7 +20,7 @@ export class FollowingListComponent extends RestTable implements OnInit {
20 pagination: RestPagination = { count: this.rowsPerPage, start: 0 } 20 pagination: RestPagination = { count: this.rowsPerPage, start: 0 }
21 21
22 constructor ( 22 constructor (
23 private notificationsService: NotificationsService, 23 private notifier: Notifier,
24 private confirmService: ConfirmService, 24 private confirmService: ConfirmService,
25 private followService: FollowService, 25 private followService: FollowService,
26 private i18n: I18n 26 private i18n: I18n
@@ -41,14 +41,11 @@ export class FollowingListComponent extends RestTable implements OnInit {
41 41
42 this.followService.unfollow(follow).subscribe( 42 this.followService.unfollow(follow).subscribe(
43 () => { 43 () => {
44 this.notificationsService.success( 44 this.notifier.success(this.i18n('You are not following {{host}} anymore.', { host: follow.following.host }))
45 this.i18n('Success'),
46 this.i18n('You are not following {{host}} anymore.', { host: follow.following.host })
47 )
48 this.loadData() 45 this.loadData()
49 }, 46 },
50 47
51 err => this.notificationsService.error(this.i18n('Error'), err.message) 48 err => this.notifier.error(err.message)
52 ) 49 )
53 } 50 }
54 51
@@ -60,7 +57,7 @@ export class FollowingListComponent extends RestTable implements OnInit {
60 this.totalRecords = resultList.total 57 this.totalRecords = resultList.total
61 }, 58 },
62 59
63 err => this.notificationsService.error(this.i18n('Error'), err.message) 60 err => this.notifier.error(err.message)
64 ) 61 )
65 } 62 }
66} 63}
diff --git a/client/src/app/+admin/follows/shared/redundancy-checkbox.component.ts b/client/src/app/+admin/follows/shared/redundancy-checkbox.component.ts
index 6d77a0eb4..fa1da26bf 100644
--- a/client/src/app/+admin/follows/shared/redundancy-checkbox.component.ts
+++ b/client/src/app/+admin/follows/shared/redundancy-checkbox.component.ts
@@ -1,5 +1,5 @@
1import { Component, Input } from '@angular/core' 1import { Component, Input } from '@angular/core'
2import { NotificationsService } from 'angular2-notifications' 2import { Notifier } from '@app/core'
3import { I18n } from '@ngx-translate/i18n-polyfill' 3import { I18n } from '@ngx-translate/i18n-polyfill'
4import { RedundancyService } from '@app/+admin/follows/shared/redundancy.service' 4import { RedundancyService } from '@app/+admin/follows/shared/redundancy.service'
5 5
@@ -13,24 +13,21 @@ export class RedundancyCheckboxComponent {
13 @Input() host: string 13 @Input() host: string
14 14
15 constructor ( 15 constructor (
16 private notificationsService: NotificationsService, 16 private notifier: Notifier,
17 private redundancyService: RedundancyService, 17 private redundancyService: RedundancyService,
18 private i18n: I18n 18 private i18n: I18n
19 ) { } 19 ) { }
20 20
21 updateRedundancyState () { 21 updateRedundancyState () {
22 this.redundancyService.updateRedundancy(this.host, this.redundancyAllowed) 22 this.redundancyService.updateRedundancy(this.host, this.redundancyAllowed)
23 .subscribe( 23 .subscribe(
24 () => { 24 () => {
25 const stateLabel = this.redundancyAllowed ? this.i18n('enabled') : this.i18n('disabled') 25 const stateLabel = this.redundancyAllowed ? this.i18n('enabled') : this.i18n('disabled')
26 26
27 this.notificationsService.success( 27 this.notifier.success(this.i18n('Redundancy for {{host}} is {{stateLabel}}', { host: this.host, stateLabel }))
28 this.i18n('Success'), 28 },
29 this.i18n('Redundancy for {{host}} is {{stateLabel}}', { host: this.host, stateLabel })
30 )
31 },
32 29
33 err => this.notificationsService.error(this.i18n('Error'), err.message) 30 err => this.notifier.error(err.message)
34 ) 31 )
35 } 32 }
36} 33}
diff --git a/client/src/app/+admin/jobs/jobs-list/jobs-list.component.ts b/client/src/app/+admin/jobs/jobs-list/jobs-list.component.ts
index 44778ab56..b265e1dd6 100644
--- a/client/src/app/+admin/jobs/jobs-list/jobs-list.component.ts
+++ b/client/src/app/+admin/jobs/jobs-list/jobs-list.component.ts
@@ -1,6 +1,6 @@
1import { Component, OnInit } from '@angular/core' 1import { Component, OnInit } from '@angular/core'
2import { peertubeLocalStorage } from '@app/shared/misc/peertube-local-storage' 2import { peertubeLocalStorage } from '@app/shared/misc/peertube-local-storage'
3import { NotificationsService } from 'angular2-notifications' 3import { Notifier } from '@app/core'
4import { SortMeta } from 'primeng/primeng' 4import { SortMeta } from 'primeng/primeng'
5import { Job } from '../../../../../../shared/index' 5import { Job } from '../../../../../../shared/index'
6import { JobState } from '../../../../../../shared/models' 6import { JobState } from '../../../../../../shared/models'
@@ -25,7 +25,7 @@ export class JobsListComponent extends RestTable implements OnInit {
25 pagination: RestPagination = { count: this.rowsPerPage, start: 0 } 25 pagination: RestPagination = { count: this.rowsPerPage, start: 0 }
26 26
27 constructor ( 27 constructor (
28 private notificationsService: NotificationsService, 28 private notifier: Notifier,
29 private jobsService: JobService, 29 private jobsService: JobService,
30 private i18n: I18n 30 private i18n: I18n
31 ) { 31 ) {
@@ -53,7 +53,7 @@ export class JobsListComponent extends RestTable implements OnInit {
53 this.totalRecords = resultList.total 53 this.totalRecords = resultList.total
54 }, 54 },
55 55
56 err => this.notificationsService.error(this.i18n('Error'), err.message) 56 err => this.notifier.error(err.message)
57 ) 57 )
58 } 58 }
59 59
diff --git a/client/src/app/+admin/moderation/instance-blocklist/instance-account-blocklist.component.ts b/client/src/app/+admin/moderation/instance-blocklist/instance-account-blocklist.component.ts
index 3f243aee4..032bf745a 100644
--- a/client/src/app/+admin/moderation/instance-blocklist/instance-account-blocklist.component.ts
+++ b/client/src/app/+admin/moderation/instance-blocklist/instance-account-blocklist.component.ts
@@ -1,9 +1,9 @@
1import { Component, OnInit } from '@angular/core' 1import { Component, OnInit } from '@angular/core'
2import { NotificationsService } from 'angular2-notifications' 2import { Notifier } from '@app/core'
3import { I18n } from '@ngx-translate/i18n-polyfill' 3import { I18n } from '@ngx-translate/i18n-polyfill'
4import { RestPagination, RestTable } from '@app/shared' 4import { RestPagination, RestTable } from '@app/shared'
5import { SortMeta } from 'primeng/components/common/sortmeta' 5import { SortMeta } from 'primeng/components/common/sortmeta'
6import { BlocklistService, AccountBlock } from '@app/shared/blocklist' 6import { AccountBlock, BlocklistService } from '@app/shared/blocklist'
7 7
8@Component({ 8@Component({
9 selector: 'my-instance-account-blocklist', 9 selector: 'my-instance-account-blocklist',
@@ -18,7 +18,7 @@ export class InstanceAccountBlocklistComponent extends RestTable implements OnIn
18 pagination: RestPagination = { count: this.rowsPerPage, start: 0 } 18 pagination: RestPagination = { count: this.rowsPerPage, start: 0 }
19 19
20 constructor ( 20 constructor (
21 private notificationsService: NotificationsService, 21 private notifier: Notifier,
22 private blocklistService: BlocklistService, 22 private blocklistService: BlocklistService,
23 private i18n: I18n 23 private i18n: I18n
24 ) { 24 ) {
@@ -35,8 +35,7 @@ export class InstanceAccountBlocklistComponent extends RestTable implements OnIn
35 this.blocklistService.unblockAccountByInstance(blockedAccount) 35 this.blocklistService.unblockAccountByInstance(blockedAccount)
36 .subscribe( 36 .subscribe(
37 () => { 37 () => {
38 this.notificationsService.success( 38 this.notifier.success(
39 this.i18n('Success'),
40 this.i18n('Account {{nameWithHost}} unmuted by your instance.', { nameWithHost: blockedAccount.nameWithHost }) 39 this.i18n('Account {{nameWithHost}} unmuted by your instance.', { nameWithHost: blockedAccount.nameWithHost })
41 ) 40 )
42 41
@@ -53,7 +52,7 @@ export class InstanceAccountBlocklistComponent extends RestTable implements OnIn
53 this.totalRecords = resultList.total 52 this.totalRecords = resultList.total
54 }, 53 },
55 54
56 err => this.notificationsService.error(this.i18n('Error'), err.message) 55 err => this.notifier.error(err.message)
57 ) 56 )
58 } 57 }
59} 58}
diff --git a/client/src/app/+admin/moderation/instance-blocklist/instance-server-blocklist.component.ts b/client/src/app/+admin/moderation/instance-blocklist/instance-server-blocklist.component.ts
index 130009dc7..db3dfcd1c 100644
--- a/client/src/app/+admin/moderation/instance-blocklist/instance-server-blocklist.component.ts
+++ b/client/src/app/+admin/moderation/instance-blocklist/instance-server-blocklist.component.ts
@@ -1,5 +1,5 @@
1import { Component, OnInit } from '@angular/core' 1import { Component, OnInit } from '@angular/core'
2import { NotificationsService } from 'angular2-notifications' 2import { Notifier } from '@app/core'
3import { I18n } from '@ngx-translate/i18n-polyfill' 3import { I18n } from '@ngx-translate/i18n-polyfill'
4import { RestPagination, RestTable } from '@app/shared' 4import { RestPagination, RestTable } from '@app/shared'
5import { SortMeta } from 'primeng/components/common/sortmeta' 5import { SortMeta } from 'primeng/components/common/sortmeta'
@@ -19,7 +19,7 @@ export class InstanceServerBlocklistComponent extends RestTable implements OnIni
19 pagination: RestPagination = { count: this.rowsPerPage, start: 0 } 19 pagination: RestPagination = { count: this.rowsPerPage, start: 0 }
20 20
21 constructor ( 21 constructor (
22 private notificationsService: NotificationsService, 22 private notifier: Notifier,
23 private blocklistService: BlocklistService, 23 private blocklistService: BlocklistService,
24 private i18n: I18n 24 private i18n: I18n
25 ) { 25 ) {
@@ -36,10 +36,7 @@ export class InstanceServerBlocklistComponent extends RestTable implements OnIni
36 this.blocklistService.unblockServerByInstance(host) 36 this.blocklistService.unblockServerByInstance(host)
37 .subscribe( 37 .subscribe(
38 () => { 38 () => {
39 this.notificationsService.success( 39 this.notifier.success(this.i18n('Instance {{host}} unmuted by your instance.', { host }))
40 this.i18n('Success'),
41 this.i18n('Instance {{host}} unmuted by your instance.', { host })
42 )
43 40
44 this.loadData() 41 this.loadData()
45 } 42 }
@@ -54,7 +51,7 @@ export class InstanceServerBlocklistComponent extends RestTable implements OnIni
54 this.totalRecords = resultList.total 51 this.totalRecords = resultList.total
55 }, 52 },
56 53
57 err => this.notificationsService.error(this.i18n('Error'), err.message) 54 err => this.notifier.error(err.message)
58 ) 55 )
59 } 56 }
60} 57}
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 34ab384d1..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
@@ -1,5 +1,5 @@
1import { Component, EventEmitter, OnInit, Output, ViewChild } from '@angular/core' 1import { Component, EventEmitter, OnInit, Output, ViewChild } from '@angular/core'
2import { NotificationsService } from 'angular2-notifications' 2import { Notifier } from '@app/core'
3import { FormReactive, VideoAbuseService, VideoAbuseValidatorsService } from '../../../shared' 3import { FormReactive, VideoAbuseService, VideoAbuseValidatorsService } from '../../../shared'
4import { I18n } from '@ngx-translate/i18n-polyfill' 4import { I18n } from '@ngx-translate/i18n-polyfill'
5import { NgbModal } from '@ng-bootstrap/ng-bootstrap' 5import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
@@ -22,7 +22,7 @@ export class ModerationCommentModalComponent extends FormReactive implements OnI
22 constructor ( 22 constructor (
23 protected formValidatorService: FormValidatorService, 23 protected formValidatorService: FormValidatorService,
24 private modalService: NgbModal, 24 private modalService: NgbModal,
25 private notificationsService: NotificationsService, 25 private notifier: Notifier,
26 private videoAbuseService: VideoAbuseService, 26 private videoAbuseService: VideoAbuseService,
27 private videoAbuseValidatorsService: VideoAbuseValidatorsService, 27 private videoAbuseValidatorsService: VideoAbuseValidatorsService,
28 private i18n: I18n 28 private i18n: I18n
@@ -45,29 +45,26 @@ 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()
52 } 52 }
53 53
54 async banUser () { 54 async banUser () {
55 const moderationComment: string = this.form.value['moderationComment'] 55 const moderationComment: string = this.form.value[ 'moderationComment' ]
56 56
57 this.videoAbuseService.updateVideoAbuse(this.abuseToComment, { moderationComment }) 57 this.videoAbuseService.updateVideoAbuse(this.abuseToComment, { moderationComment })
58 .subscribe( 58 .subscribe(
59 () => { 59 () => {
60 this.notificationsService.success( 60 this.notifier.success(this.i18n('Comment updated.'))
61 this.i18n('Success'),
62 this.i18n('Comment updated.')
63 )
64 61
65 this.commentUpdated.emit(moderationComment) 62 this.commentUpdated.emit(moderationComment)
66 this.hideModerationCommentModal() 63 this.hide()
67 }, 64 },
68 65
69 err => this.notificationsService.error(this.i18n('Error'), err.message) 66 err => this.notifier.error(err.message)
70 ) 67 )
71 } 68 }
72 69
73} 70}
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 0374b70ef..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
@@ -41,7 +41,7 @@
41 </td> 41 </td>
42 42
43 <td class="action-cell"> 43 <td class="action-cell">
44 <my-action-dropdown i18n-label label="Actions" [actions]="videoAbuseActions" [entry]="videoAbuse"></my-action-dropdown> 44 <my-action-dropdown placement="bottom-right" i18n-label label="Actions" [actions]="videoAbuseActions" [entry]="videoAbuse"></my-action-dropdown>
45 </td> 45 </td>
46 </tr> 46 </tr>
47 </ng-template> 47 </ng-template>
@@ -51,15 +51,15 @@
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>
62 </ng-template> 62 </ng-template>
63</p-table> 63</p-table>
64 64
65<my-moderation-comment-modal #moderationCommentModal (commentUpdated)="onModerationCommentUpdated()"></my-moderation-comment-modal> \ No newline at end of file 65<my-moderation-comment-modal #moderationCommentModal (commentUpdated)="onModerationCommentUpdated()"></my-moderation-comment-modal>
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 7a219c846..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
@@ -1,6 +1,6 @@
1import { Component, OnInit, ViewChild } from '@angular/core' 1import { Component, OnInit, ViewChild } from '@angular/core'
2import { Account } from '../../../shared/account/account.model' 2import { Account } from '../../../shared/account/account.model'
3import { NotificationsService } from 'angular2-notifications' 3import { Notifier } from '@app/core'
4import { SortMeta } from 'primeng/components/common/sortmeta' 4import { SortMeta } from 'primeng/components/common/sortmeta'
5import { VideoAbuse, VideoAbuseState } from '../../../../../../shared' 5import { VideoAbuse, VideoAbuseState } from '../../../../../../shared'
6import { RestPagination, RestTable, VideoAbuseService } from '../../../shared' 6import { RestPagination, RestTable, VideoAbuseService } from '../../../shared'
@@ -9,6 +9,7 @@ import { DropdownAction } from '../../../shared/buttons/action-dropdown.componen
9import { ConfirmService } from '../../../core/index' 9import { ConfirmService } from '../../../core/index'
10import { ModerationCommentModalComponent } from './moderation-comment-modal.component' 10import { ModerationCommentModalComponent } from './moderation-comment-modal.component'
11import { Video } from '../../../shared/video/video.model' 11import { Video } from '../../../shared/video/video.model'
12import { MarkdownService } from '@app/shared/renderer'
12 13
13@Component({ 14@Component({
14 selector: 'my-video-abuse-list', 15 selector: 'my-video-abuse-list',
@@ -27,10 +28,11 @@ export class VideoAbuseListComponent extends RestTable implements OnInit {
27 videoAbuseActions: DropdownAction<VideoAbuse>[] = [] 28 videoAbuseActions: DropdownAction<VideoAbuse>[] = []
28 29
29 constructor ( 30 constructor (
30 private notificationsService: NotificationsService, 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
@@ -90,14 +92,11 @@ export class VideoAbuseListComponent extends RestTable implements OnInit {
90 92
91 this.videoAbuseService.removeVideoAbuse(videoAbuse).subscribe( 93 this.videoAbuseService.removeVideoAbuse(videoAbuse).subscribe(
92 () => { 94 () => {
93 this.notificationsService.success( 95 this.notifier.success(this.i18n('Abuse deleted.'))
94 this.i18n('Success'),
95 this.i18n('Abuse deleted.')
96 )
97 this.loadData() 96 this.loadData()
98 }, 97 },
99 98
100 err => this.notificationsService.error(this.i18n('Error'), err.message) 99 err => this.notifier.error(err.message)
101 ) 100 )
102 } 101 }
103 102
@@ -106,11 +105,15 @@ export class VideoAbuseListComponent extends RestTable implements OnInit {
106 .subscribe( 105 .subscribe(
107 () => this.loadData(), 106 () => this.loadData(),
108 107
109 err => this.notificationsService.error(this.i18n('Error'), err.message) 108 err => this.notifier.error(err.message)
110 ) 109 )
111 110
112 } 111 }
113 112
113 toHtml (text: string) {
114 return this.markdownRenderer.textMarkdownToHTML(text)
115 }
116
114 protected loadData () { 117 protected loadData () {
115 return this.videoAbuseService.getVideoAbuses(this.pagination, this.sort) 118 return this.videoAbuseService.getVideoAbuses(this.pagination, this.sort)
116 .subscribe( 119 .subscribe(
@@ -119,7 +122,7 @@ export class VideoAbuseListComponent extends RestTable implements OnInit {
119 this.totalRecords = resultList.total 122 this.totalRecords = resultList.total
120 }, 123 },
121 124
122 err => this.notificationsService.error(this.i18n('Error'), err.message) 125 err => this.notifier.error(err.message)
123 ) 126 )
124 } 127 }
125} 128}
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 ff4543b97..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,20 +27,21 @@
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">
33 <my-action-dropdown i18n-label label="Actions" [actions]="videoBlacklistActions" [entry]="videoBlacklist"></my-action-dropdown> 35 <my-action-dropdown i18n-label placement="bottom-right" label="Actions" [actions]="videoBlacklistActions" [entry]="videoBlacklist"></my-action-dropdown>
34 </td> 36 </td>
35 </tr> 37 </tr>
36 </ng-template> 38 </ng-template>
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 e491edaca..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
@@ -1,12 +1,13 @@
1import { Component, OnInit } from '@angular/core' 1import { Component, OnInit } from '@angular/core'
2import { SortMeta } from 'primeng/components/common/sortmeta' 2import { SortMeta } from 'primeng/components/common/sortmeta'
3import { NotificationsService } from 'angular2-notifications' 3import { Notifier } from '@app/core'
4import { ConfirmService } from '../../../core' 4import { ConfirmService } from '../../../core'
5import { RestPagination, RestTable, VideoBlacklistService } from '../../../shared' 5import { RestPagination, RestTable, VideoBlacklistService } from '../../../shared'
6import { VideoBlacklist } from '../../../../../../shared' 6import { VideoBlacklist } from '../../../../../../shared'
7import { I18n } from '@ngx-translate/i18n-polyfill' 7import { I18n } from '@ngx-translate/i18n-polyfill'
8import { DropdownAction } from '../../../shared/buttons/action-dropdown.component' 8import { DropdownAction } from '../../../shared/buttons/action-dropdown.component'
9import { Video } from '../../../shared/video/video.model' 9import { Video } from '../../../shared/video/video.model'
10import { MarkdownService } from '@app/shared/renderer'
10 11
11@Component({ 12@Component({
12 selector: 'my-video-blacklist-list', 13 selector: 'my-video-blacklist-list',
@@ -23,9 +24,10 @@ export class VideoBlacklistListComponent extends RestTable implements OnInit {
23 videoBlacklistActions: DropdownAction<VideoBlacklist>[] = [] 24 videoBlacklistActions: DropdownAction<VideoBlacklist>[] = []
24 25
25 constructor ( 26 constructor (
26 private notificationsService: NotificationsService, 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.'
@@ -56,14 +68,11 @@ export class VideoBlacklistListComponent extends RestTable implements OnInit {
56 68
57 this.videoBlacklistService.removeVideoFromBlacklist(entry.video.id).subscribe( 69 this.videoBlacklistService.removeVideoFromBlacklist(entry.video.id).subscribe(
58 () => { 70 () => {
59 this.notificationsService.success( 71 this.notifier.success(this.i18n('Video {{name}} removed from the blacklist.', { name: entry.video.name }))
60 this.i18n('Success'),
61 this.i18n('Video {{name}} removed from the blacklist.', { name: entry.video.name })
62 )
63 this.loadData() 72 this.loadData()
64 }, 73 },
65 74
66 err => this.notificationsService.error(this.i18n('Error'), err.message) 75 err => this.notifier.error(err.message)
67 ) 76 )
68 } 77 }
69 78
@@ -75,7 +84,7 @@ export class VideoBlacklistListComponent extends RestTable implements OnInit {
75 this.totalRecords = resultList.total 84 this.totalRecords = resultList.total
76 }, 85 },
77 86
78 err => this.notificationsService.error(this.i18n('Error'), err.message) 87 err => this.notifier.error(err.message)
79 ) 88 )
80 } 89 }
81} 90}
diff --git a/client/src/app/+admin/users/user-edit/user-create.component.ts b/client/src/app/+admin/users/user-edit/user-create.component.ts
index dd8e4efd5..137ecfcbd 100644
--- a/client/src/app/+admin/users/user-edit/user-create.component.ts
+++ b/client/src/app/+admin/users/user-edit/user-create.component.ts
@@ -1,7 +1,6 @@
1import { Component, OnInit } from '@angular/core' 1import { Component, OnInit } from '@angular/core'
2import { Router } from '@angular/router' 2import { Router } from '@angular/router'
3import { NotificationsService } from 'angular2-notifications' 3import { Notifier, ServerService } from '@app/core'
4import { ServerService } from '../../../core'
5import { UserCreate, UserRole } from '../../../../../../shared' 4import { UserCreate, UserRole } from '../../../../../../shared'
6import { UserEdit } from './user-edit' 5import { UserEdit } from './user-edit'
7import { I18n } from '@ngx-translate/i18n-polyfill' 6import { I18n } from '@ngx-translate/i18n-polyfill'
@@ -24,7 +23,7 @@ export class UserCreateComponent extends UserEdit implements OnInit {
24 protected configService: ConfigService, 23 protected configService: ConfigService,
25 private userValidatorsService: UserValidatorsService, 24 private userValidatorsService: UserValidatorsService,
26 private router: Router, 25 private router: Router,
27 private notificationsService: NotificationsService, 26 private notifier: Notifier,
28 private userService: UserService, 27 private userService: UserService,
29 private i18n: I18n 28 private i18n: I18n
30 ) { 29 ) {
@@ -60,10 +59,7 @@ export class UserCreateComponent extends UserEdit implements OnInit {
60 59
61 this.userService.addUser(userCreate).subscribe( 60 this.userService.addUser(userCreate).subscribe(
62 () => { 61 () => {
63 this.notificationsService.success( 62 this.notifier.success(this.i18n('User {{username}} created.', { username: userCreate.username }))
64 this.i18n('Success'),
65 this.i18n('User {{username}} created.', { username: userCreate.username })
66 )
67 this.router.navigate([ '/admin/users/list' ]) 63 this.router.navigate([ '/admin/users/list' ])
68 }, 64 },
69 65
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 cd3885a99..61e641823 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
@@ -1,7 +1,7 @@
1import { Component, OnDestroy, OnInit } from '@angular/core' 1import { Component, OnDestroy, OnInit } from '@angular/core'
2import { ActivatedRoute, Router } from '@angular/router' 2import { ActivatedRoute, Router } from '@angular/router'
3import { Subscription } from 'rxjs' 3import { Subscription } from 'rxjs'
4import { NotificationsService } from 'angular2-notifications' 4import { Notifier } from '@app/core'
5import { ServerService } from '../../../core' 5import { ServerService } from '../../../core'
6import { UserEdit } from './user-edit' 6import { UserEdit } from './user-edit'
7import { User, UserUpdate } from '../../../../../../shared' 7import { User, UserUpdate } from '../../../../../../shared'
@@ -30,7 +30,7 @@ export class UserUpdateComponent extends UserEdit implements OnInit, OnDestroy {
30 private userValidatorsService: UserValidatorsService, 30 private userValidatorsService: UserValidatorsService,
31 private route: ActivatedRoute, 31 private route: ActivatedRoute,
32 private router: Router, 32 private router: Router,
33 private notificationsService: NotificationsService, 33 private notifier: Notifier,
34 private userService: UserService, 34 private userService: UserService,
35 private i18n: I18n 35 private i18n: I18n
36 ) { 36 ) {
@@ -73,10 +73,7 @@ export class UserUpdateComponent extends UserEdit implements OnInit, OnDestroy {
73 73
74 this.userService.updateUser(this.userId, userUpdate).subscribe( 74 this.userService.updateUser(this.userId, userUpdate).subscribe(
75 () => { 75 () => {
76 this.notificationsService.success( 76 this.notifier.success(this.i18n('User {{username}} updated.', { username: this.username }))
77 this.i18n('Success'),
78 this.i18n('User {{username}} updated.', { username: this.username })
79 )
80 this.router.navigate([ '/admin/users/list' ]) 77 this.router.navigate([ '/admin/users/list' ])
81 }, 78 },
82 79
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
8tr.banned { 8tr.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}
diff --git a/client/src/app/+admin/users/user-list/user-list.component.ts b/client/src/app/+admin/users/user-list/user-list.component.ts
index fb085c133..66ab796f9 100644
--- a/client/src/app/+admin/users/user-list/user-list.component.ts
+++ b/client/src/app/+admin/users/user-list/user-list.component.ts
@@ -1,5 +1,5 @@
1import { Component, OnInit, ViewChild } from '@angular/core' 1import { Component, OnInit, ViewChild } from '@angular/core'
2import { NotificationsService } from 'angular2-notifications' 2import { Notifier } from '@app/core'
3import { SortMeta } from 'primeng/components/common/sortmeta' 3import { SortMeta } from 'primeng/components/common/sortmeta'
4import { ConfirmService, ServerService } from '../../../core' 4import { ConfirmService, ServerService } from '../../../core'
5import { RestPagination, RestTable, UserService } from '../../../shared' 5import { RestPagination, RestTable, UserService } from '../../../shared'
@@ -26,7 +26,7 @@ export class UserListComponent extends RestTable implements OnInit {
26 bulkUserActions: DropdownAction<User[]>[] = [] 26 bulkUserActions: DropdownAction<User[]>[] = []
27 27
28 constructor ( 28 constructor (
29 private notificationsService: NotificationsService, 29 private notifier: Notifier,
30 private confirmService: ConfirmService, 30 private confirmService: ConfirmService,
31 private serverService: ServerService, 31 private serverService: ServerService,
32 private userService: UserService, 32 private userService: UserService,
@@ -68,7 +68,7 @@ export class UserListComponent extends RestTable implements OnInit {
68 openBanUserModal (users: User[]) { 68 openBanUserModal (users: User[]) {
69 for (const user of users) { 69 for (const user of users) {
70 if (user.username === 'root') { 70 if (user.username === 'root') {
71 this.notificationsService.error(this.i18n('Error'), this.i18n('You cannot ban root.')) 71 this.notifier.error(this.i18n('You cannot ban root.'))
72 return 72 return
73 } 73 }
74 } 74 }
@@ -91,18 +91,18 @@ export class UserListComponent extends RestTable implements OnInit {
91 () => { 91 () => {
92 const message = this.i18n('{{num}} users unbanned.', { num: users.length }) 92 const message = this.i18n('{{num}} users unbanned.', { num: users.length })
93 93
94 this.notificationsService.success(this.i18n('Success'), message) 94 this.notifier.success(message)
95 this.loadData() 95 this.loadData()
96 }, 96 },
97 97
98 err => this.notificationsService.error(this.i18n('Error'), err.message) 98 err => this.notifier.error(err.message)
99 ) 99 )
100 } 100 }
101 101
102 async removeUsers (users: User[]) { 102 async removeUsers (users: User[]) {
103 for (const user of users) { 103 for (const user of users) {
104 if (user.username === 'root') { 104 if (user.username === 'root') {
105 this.notificationsService.error(this.i18n('Error'), this.i18n('You cannot delete root.')) 105 this.notifier.error(this.i18n('You cannot delete root.'))
106 return 106 return
107 } 107 }
108 } 108 }
@@ -113,28 +113,22 @@ export class UserListComponent extends RestTable implements OnInit {
113 113
114 this.userService.removeUser(users).subscribe( 114 this.userService.removeUser(users).subscribe(
115 () => { 115 () => {
116 this.notificationsService.success( 116 this.notifier.success(this.i18n('{{num}} users deleted.', { num: users.length }))
117 this.i18n('Success'),
118 this.i18n('{{num}} users deleted.', { num: users.length })
119 )
120 this.loadData() 117 this.loadData()
121 }, 118 },
122 119
123 err => this.notificationsService.error(this.i18n('Error'), err.message) 120 err => this.notifier.error(err.message)
124 ) 121 )
125 } 122 }
126 123
127 async setEmailsAsVerified (users: User[]) { 124 async setEmailsAsVerified (users: User[]) {
128 this.userService.updateUsers(users, { emailVerified: true }).subscribe( 125 this.userService.updateUsers(users, { emailVerified: true }).subscribe(
129 () => { 126 () => {
130 this.notificationsService.success( 127 this.notifier.success(this.i18n('{{num}} users email set as verified.', { num: users.length }))
131 this.i18n('Success'),
132 this.i18n('{{num}} users email set as verified.', { num: users.length })
133 )
134 this.loadData() 128 this.loadData()
135 }, 129 },
136 130
137 err => this.notificationsService.error(this.i18n('Error'), err.message) 131 err => this.notifier.error(err.message)
138 ) 132 )
139 } 133 }
140 134
@@ -146,13 +140,13 @@ export class UserListComponent extends RestTable implements OnInit {
146 this.selectedUsers = [] 140 this.selectedUsers = []
147 141
148 this.userService.getUsers(this.pagination, this.sort, this.search) 142 this.userService.getUsers(this.pagination, this.sort, this.search)
149 .subscribe( 143 .subscribe(
150 resultList => { 144 resultList => {
151 this.users = resultList.data 145 this.users = resultList.data
152 this.totalRecords = resultList.total 146 this.totalRecords = resultList.total
153 }, 147 },
154 148
155 err => this.notificationsService.error(this.i18n('Error'), err.message) 149 err => this.notifier.error(err.message)
156 ) 150 )
157 } 151 }
158} 152}