Support broadcast messages
[oweals/peertube.git] / client / src / app / +admin / config / edit-custom-config / edit-custom-config.component.html
1 <form role="form" [formGroup]="form">
2
3   <div ngbNav #nav="ngbNav" class="nav-tabs">
4
5     <ng-container ngbNavItem="instance-information">
6       <a ngbNavLink i18n>Instance information</a>
7
8       <ng-template ngbNavContent>
9
10         <ng-container formGroupName="instance">
11
12           <div class="form-row mt-5"> <!-- instance grid -->
13             <div class="form-group col-12 col-lg-4 col-xl-3">
14               <div i18n class="inner-form-title">INSTANCE</div>
15             </div>
16
17             <div class="form-group form-group-right col-12 col-lg-8 col-xl-9">
18
19               <div class="form-group">
20                 <label i18n for="instanceName">Name</label>
21                 <input
22                   type="text" id="instanceName" class="form-control"
23                   formControlName="name" [ngClass]="{ 'input-error': formErrors.instance.name }"
24                 >
25                 <div *ngIf="formErrors.instance.name" class="form-error">{{ formErrors.instance.name }}</div>
26               </div>
27
28               <div class="form-group">
29                 <label i18n for="instanceShortDescription">Short description</label>
30                 <textarea
31                   id="instanceShortDescription" formControlName="shortDescription" class="form-control small"
32                   [ngClass]="{ 'input-error': formErrors['instance.shortDescription'] }"
33                 ></textarea>
34                 <div *ngIf="formErrors.instance.shortDescription" class="form-error">{{ formErrors.instance.shortDescription }}</div>
35               </div>
36
37               <div class="form-group">
38                 <label i18n for="instanceDescription">Description</label><my-help helpType="markdownText"></my-help>
39                 <my-markdown-textarea
40                   name="instanceDescription" formControlName="description" textareaMaxWidth="500px"
41                   [classes]="{ 'input-error': formErrors['instance.description'] }"
42                 ></my-markdown-textarea>
43                 <div *ngIf="formErrors.instance.description" class="form-error">{{ formErrors.instance.description }}</div>
44               </div>
45
46               <div class="form-group">
47                 <label i18n for="instanceCategories">Main instance categories</label>
48
49                 <div>
50                   <p-multiSelect
51                     inputId="instanceCategories" [options]="categoryItems" formControlName="categories" [showToggleAll]="false"
52                     [defaultLabel]="getDefaultCategoryLabel()" [selectedItemsLabel]="getSelectedCategoryLabel()"
53                     emptyFilterMessage="No results found" i18n-emptyFilterMessage
54                   ></p-multiSelect>
55                 </div>
56               </div>
57
58               <div class="form-group">
59                 <label i18n for="instanceLanguages">Main languages you/your moderators speak</label>
60
61                 <div>
62                   <p-multiSelect
63                     inputId="instanceLanguages" [options]="languageItems" formControlName="languages" [showToggleAll]="false"
64                     [defaultLabel]="getDefaultLanguageLabel()" [selectedItemsLabel]="getSelectedLanguageLabel()"
65                     emptyFilterMessage="No results found" i18n-emptyFilterMessage
66                   ></p-multiSelect>
67                 </div>
68               </div>
69
70             </div>
71           </div>
72
73           <div class="form-row mt-4"> <!-- moderation & nsfw grid -->
74             <div class="form-group col-12 col-lg-4 col-xl-3">
75               <div i18n class="inner-form-title">MODERATION & NSFW</div>
76               <div i18n class="inner-for-description">
77                 Manage <a routerLink="/admin/users">users</a> to build a moderation team.
78               </div>
79             </div>
80
81             <div class="form-group form-group-right col-12 col-lg-8 col-xl-9">
82
83               <div class="form-group">
84                 <my-peertube-checkbox inputName="instanceIsNSFW" formControlName="isNSFW">
85                   <ng-template ptTemplate="label">
86                     <ng-container i18n>This instance is dedicated to sensitive or NSFW content</ng-container>
87                   </ng-template>
88
89                   <ng-template ptTemplate="help">
90                     <ng-container i18n>
91                       Enabling it will allow other administrators to know that you are mainly federating sensitive content.<br /><br />
92                       Moreover, the NSFW checkbox on video upload will be automatically checked by default.
93                     </ng-container>
94                   </ng-template>
95                 </my-peertube-checkbox>
96               </div>
97
98               <div class="form-group">
99                 <label i18n for="instanceDefaultNSFWPolicy">Policy on videos containing sensitive content</label>
100
101                 <my-help>
102                   <ng-template ptTemplate="customHtml">
103                     <ng-container i18n>
104                       With <strong>Do not list</strong> or <strong>Blur thumbnails</strong>, a confirmation will be requested to watch the video.
105                     </ng-container>
106                   </ng-template>
107                 </my-help>
108
109                 <div class="peertube-select-container">
110                   <select id="instanceDefaultNSFWPolicy" formControlName="defaultNSFWPolicy" class="form-control">
111                     <option i18n value="undefined" disabled>Policy for sensitive videos</option>
112                     <option i18n value="do_not_list">Do not list</option>
113                     <option i18n value="blur">Blur thumbnails</option>
114                     <option i18n value="display">Display</option>
115                   </select>
116                 </div>
117                 <div *ngIf="formErrors.instance.defaultNSFWPolicy" class="form-error">{{ formErrors.instance.defaultNSFWPolicy }}</div>
118               </div>
119
120               <div class="form-group">
121                 <label i18n for="instanceTerms">Terms</label><my-help helpType="markdownText"></my-help>
122                 <my-markdown-textarea
123                   name="instanceTerms" formControlName="terms" textareaMaxWidth="500px"
124                   [ngClass]="{ 'input-error': formErrors['instance.terms'] }"
125                 ></my-markdown-textarea>
126                 <div *ngIf="formErrors.instance.terms" class="form-error">{{ formErrors.instance.terms }}</div>
127               </div>
128
129               <div class="form-group">
130                 <label i18n for="instanceCodeOfConduct">Code of conduct</label><my-help helpType="markdownText"></my-help>
131                 <my-markdown-textarea
132                   name="instanceCodeOfConduct" formControlName="codeOfConduct" textareaMaxWidth="500px"
133                   [ngClass]="{ 'input-error': formErrors['instance.codeOfConduct'] }"
134                 ></my-markdown-textarea>
135                 <div *ngIf="formErrors.instance.codeOfConduct" class="form-error">{{ formErrors.instance.codeOfConduct }}</div>
136               </div>
137
138               <div class="form-group">
139                 <label i18n for="instanceModerationInformation">Moderation information</label><my-help helpType="markdownText"></my-help>
140                 <div i18n class="label-small-info">Who moderates the instance? What is the policy regarding NSFW videos? Political videos? etc</div>
141
142                 <my-markdown-textarea
143                   name="instanceModerationInformation" formControlName="moderationInformation" textareaMaxWidth="500px"
144                   [ngClass]="{ 'input-error': formErrors['instance.moderationInformation'] }"
145                 ></my-markdown-textarea>
146                 <div *ngIf="formErrors.instance.moderationInformation" class="form-error">{{ formErrors.instance.moderationInformation }}</div>
147               </div>
148
149             </div>
150           </div>
151
152           <div class="form-row mt-4"> <!-- you and your instance grid -->
153             <div class="form-group col-12 col-lg-4 col-xl-3">
154               <div i18n class="inner-form-title">YOU AND YOUR INSTANCE</div>
155             </div>
156
157             <div class="form-group form-group-right col-12 col-lg-8 col-xl-9">
158
159               <div class="form-group">
160                 <label i18n for="instanceAdministrator">Who is behind the instance?</label>
161                 <div i18n class="label-small-info">A single person? A non-profit? A company?</div>
162
163                 <my-markdown-textarea
164                   name="instanceAdministrator" formControlName="administrator" textareaMaxWidth="500px" textareaHeight="75px"
165                   [classes]="{ 'input-error': formErrors['instance.administrator'] }"
166                 ></my-markdown-textarea>
167
168                 <div *ngIf="formErrors.instance.administrator" class="form-error">{{ formErrors.instance.administrator }}</div>
169               </div>
170
171               <div class="form-group">
172                 <label i18n for="instanceCreationReason">Why did you create this instance?</label>
173                 <div i18n class="label-small-info">To share your personal videos? To open registrations and allow people to upload what they want?</div>
174
175                 <textarea
176                   id="instanceCreationReason" formControlName="creationReason" class="small" class="form-control"
177                   [ngClass]="{ 'input-error': formErrors['instance.creationReason'] }"
178                 ></textarea>
179                 <div *ngIf="formErrors.instance.creationReason" class="form-error">{{ formErrors.instance.creationReason }}</div>
180               </div>
181
182               <div class="form-group">
183                 <label i18n for="instanceMaintenanceLifetime">How long do you plan to maintain this instance?</label>
184                 <div i18n class="label-small-info">It's important to know for users who want to register on your instance</div>
185
186                 <textarea
187                   id="instanceMaintenanceLifetime" formControlName="maintenanceLifetime" class="form-control small"
188                   [ngClass]="{ 'input-error': formErrors['instance.maintenanceLifetime'] }"
189                 ></textarea>
190                 <div *ngIf="formErrors.instance.maintenanceLifetime" class="form-error">{{ formErrors.instance.maintenanceLifetime }}</div>
191               </div>
192
193               <div class="form-group">
194                 <label i18n for="instanceBusinessModel">How will you finance the PeerTube server?</label>
195                 <div i18n class="label-small-info">With your own funds? With user donations? Advertising?</div>
196
197                 <textarea
198                   id="instanceBusinessModel" formControlName="businessModel" class="form-control small"
199                   [ngClass]="{ 'input-error': formErrors['instance.businessModel'] }"
200                 ></textarea>
201                 <div *ngIf="formErrors.instance.businessModel" class="form-error">{{ formErrors.instance.businessModel }}</div>
202               </div>
203
204             </div>
205           </div>
206
207           <div class="form-row mt-4"> <!-- other information grid -->
208             <div class="form-group col-12 col-lg-4 col-xl-3">
209               <div i18n class="inner-form-title">OTHER INFORMATION</div>
210             </div>
211
212             <div class="form-group form-group-right col-12 col-lg-8 col-xl-9">
213
214               <div class="form-group">
215                 <label i18n for="instanceHardwareInformation">What server/hardware does the instance run on?</label>
216                 <div i18n class="label-small-info">i.e. 2vCore 2GB RAM, a direct the link to the server you rent, etc.</div>
217
218                 <my-markdown-textarea
219                   name="instanceHardwareInformation" formControlName="hardwareInformation" textareaMaxWidth="500px" textareaHeight="75px"
220                   [classes]="{ 'input-error': formErrors['instance.hardwareInformation'] }"
221                 ></my-markdown-textarea>
222
223                 <div *ngIf="formErrors.instance.hardwareInformation" class="form-error">{{ formErrors.instance.hardwareInformation }}</div>
224               </div>
225
226             </div>
227           </div>
228
229         </ng-container>
230       </ng-template>
231     </ng-container>
232
233     <ng-container ngbNavItem="basic-configuration">
234       <a ngbNavLink i18n>Basic configuration</a>
235
236       <ng-template ngbNavContent>
237
238         <div class="form-row mt-5"> <!-- appearance grid -->
239           <div class="form-group col-12 col-lg-4 col-xl-3">
240             <div i18n class="inner-form-title">APPEARANCE</div>
241             <div i18n class="inner-for-description">
242               Use <a routerLink="/admin/plugins">plugins & themes</a> for more involved changes, or <a routerLink="/admin/config/edit-custom" fragment="customizations" (click)="gotoAnchor()">add slight customizations</a>.
243             </div>
244           </div>
245
246           <div class="form-group form-group-right col-12 col-lg-8 col-xl-9">
247
248             <ng-container formGroupName="theme">
249               <div class="form-group">
250                 <label i18n for="themeDefault">Theme</label>
251
252                 <div class="peertube-select-container">
253                   <select formControlName="default" id="themeDefault" class="form-control">
254                     <option i18n value="default">default</option>
255
256                     <option *ngFor="let theme of availableThemes" [value]="theme">{{ theme }}</option>
257                   </select>
258                 </div>
259               </div>
260             </ng-container>
261
262             <div class="form-group" formGroupName="instance">
263               <label i18n for="instanceDefaultClientRoute">Landing page</label>
264               <div class="peertube-select-container">
265                 <select id="instanceDefaultClientRoute" formControlName="defaultClientRoute" class="form-control">
266                   <option i18n value="/videos/overview">Discover videos</option>
267                   <option i18n value="/videos/trending">Trending videos</option>
268                   <option i18n value="/videos/most-liked">Most liked videos</option>
269                   <option i18n value="/videos/recently-added">Recently added videos</option>
270                   <option i18n value="/videos/local">Local videos</option>
271                 </select>
272               </div>
273               <div *ngIf="formErrors.instance.defaultClientRoute" class="form-error">{{ formErrors.instance.defaultClientRoute }}</div>
274             </div>
275
276           </div>
277         </div>
278
279         <div class="form-row mt-4"> <!-- broadcast grid -->
280           <div class="form-group col-12 col-lg-4 col-xl-3">
281             <div i18n class="inner-form-title">BROADCAST MESSAGE</div>
282             <div i18n class="inner-for-description">
283               Display a message on your instance
284             </div>
285           </div>
286
287           <div class="form-group form-group-right col-12 col-lg-8 col-xl-9">
288
289             <ng-container formGroupName="broadcastMessage">
290
291               <div class="form-group">
292                 <my-peertube-checkbox
293                   inputName="broadcastMessageEnabled" formControlName="enabled"
294                   i18n-labelText labelText="Enable broadcast message"
295                 ></my-peertube-checkbox>
296               </div>
297
298               <div class="form-group">
299                 <my-peertube-checkbox
300                   inputName="broadcastMessageDismissable" formControlName="dismissable"
301                   i18n-labelText labelText="Allow users to dismiss the broadcast message "
302                 ></my-peertube-checkbox>
303               </div>
304
305               <div class="form-group">
306                 <label i18n for="broadcastMessageLevel">Broadcast message level</label>
307                 <div class="peertube-select-container">
308                   <select id="broadcastMessageLevel" formControlName="level" class="form-control">
309                     <option value="info">info</option>
310                     <option value="warning">warning</option>
311                     <option value="error">error</option>
312                   </select>
313                 </div>
314                 <div *ngIf="formErrors.broadcastMessage.level" class="form-error">{{ formErrors.broadcastMessage.level }}</div>
315               </div>
316
317               <div class="form-group">
318                 <label i18n for="broadcastMessageMessage">Message</label><my-help helpType="markdownText"></my-help>
319                 <my-markdown-textarea
320                   name="broadcastMessageMessage" formControlName="message" textareaMaxWidth="500px"
321                   [classes]="{ 'input-error': formErrors['broadcastMessage.message'] }"
322                 ></my-markdown-textarea>
323                 <div *ngIf="formErrors.broadcastMessage.message" class="form-error">{{ formErrors.broadcastMessage.message }}</div>
324               </div>
325
326             </ng-container>
327
328           </div>
329         </div>
330
331         <div class="form-row mt-4"> <!-- new users grid -->
332           <div class="form-group col-12 col-lg-4 col-xl-3">
333             <div i18n class="inner-form-title">NEW USERS</div>
334             <div i18n class="inner-for-description">
335               Manage <a routerLink="/admin/users">users</a> to set their quota individually.
336             </div>
337           </div>
338
339           <div class="form-group form-group-right col-12 col-lg-8 col-xl-9">
340
341             <ng-container formGroupName="signup">
342               <div class="form-group">
343                 <my-peertube-checkbox
344                   inputName="signupEnabled" formControlName="enabled"
345                   i18n-labelText labelText="Signup enabled"
346                 >
347                   <ng-container ngProjectAs="description">
348                     <span i18n>⚠️ This functionality requires a lot of attention and extra moderation.</span>
349                   </ng-container>
350                   <ng-container ngProjectAs="extra">
351                     <my-peertube-checkbox [ngClass]="{ 'disabled-checkbox-extra': !isSignupEnabled() }"
352                       inputName="signupRequiresEmailVerification" formControlName="requiresEmailVerification"
353                       i18n-labelText labelText="Signup requires email verification"
354                     ></my-peertube-checkbox>
355
356                     <div [ngClass]="{ 'disabled-checkbox-extra': !isSignupEnabled() }" class="mt-3">
357                       <label i18n for="signupLimit">Signup limit</label>
358                       <input
359                         type="number" min="-1" id="signupLimit" class="form-control"
360                         formControlName="limit" [ngClass]="{ 'input-error': formErrors['signup.limit'] }"
361                       >
362                       <div *ngIf="formErrors.signup.limit" class="form-error">{{ formErrors.signup.limit }}</div>
363                       <small *ngIf="form.value['signup']['limit'] === -1" class="text-muted">Signup won't be limited to a fixed number of users.</small>
364                     </div>
365                   </ng-container>
366                 </my-peertube-checkbox>
367               </div>
368             </ng-container>
369
370             <ng-container formGroupName="user">
371               <div class="form-group">
372                 <label i18n for="userVideoQuota">Default video quota per user</label>
373                 <div class="peertube-select-container">
374                   <select id="userVideoQuota" formControlName="videoQuota" class="form-control">
375                     <option *ngFor="let videoQuotaOption of videoQuotaOptions" [value]="videoQuotaOption.value" [disabled]="videoQuotaOption.disabled">
376                       {{ videoQuotaOption.label }}
377                     </option>
378                   </select>
379                 </div>
380                 <div *ngIf="formErrors.user.videoQuota" class="form-error">{{ formErrors.user.videoQuota }}</div>
381               </div>
382
383               <div class="form-group">
384                 <label i18n for="userVideoQuotaDaily">Default daily upload limit per user</label>
385                 <div class="peertube-select-container">
386                   <select id="userVideoQuotaDaily" formControlName="videoQuotaDaily" class="form-control">
387                     <option *ngFor="let videoQuotaDailyOption of videoQuotaDailyOptions" [value]="videoQuotaDailyOption.value" [disabled]="videoQuotaDailyOption.disabled">
388                       {{ videoQuotaDailyOption.label }}
389                     </option>
390                   </select>
391                 </div>
392                 <div *ngIf="formErrors.user.videoQuotaDaily" class="form-error">{{ formErrors.user.videoQuotaDaily }}</div>
393               </div>
394             </ng-container>
395
396           </div>
397         </div>
398
399         <div class="form-row mt-4"> <!-- new videos grid -->
400           <div class="form-group col-12 col-lg-4 col-xl-3">
401             <div i18n class="inner-form-title">NEW VIDEOS</div>
402           </div>
403
404           <div class="form-group form-group-right col-12 col-lg-8 col-xl-9">
405
406             <ng-container formGroupName="import">
407               <ng-container formGroupName="videos">
408
409                 <div class="form-group" formGroupName="http">
410                   <my-peertube-checkbox
411                     inputName="importVideosHttpEnabled" formControlName="enabled"
412                     i18n-labelText labelText="Allow import with HTTP URL (i.e. YouTube)"
413                   ></my-peertube-checkbox>
414                 </div>
415
416                 <div class="form-group" formGroupName="torrent">
417                   <my-peertube-checkbox
418                     inputName="importVideosTorrentEnabled" formControlName="enabled"
419                     i18n-labelText labelText="Allow import with a torrent file or a magnet URI"
420                   ></my-peertube-checkbox>
421                 </div>
422
423               </ng-container>
424             </ng-container>
425
426             <ng-container formGroupName="autoBlacklist">
427               <ng-container formGroupName="videos">
428                 <ng-container formGroupName="ofUsers">
429
430                   <div class="form-group">
431                     <my-peertube-checkbox
432                       inputName="autoBlacklistVideosOfUsersEnabled" formControlName="enabled"
433                       i18n-labelText labelText="Blacklist new videos automatically"
434                     >
435                     <ng-container ngProjectAs="description">
436                       <span i18n>Unless a user is marked as trusted, their videos will stay private until a moderator reviews them.</span>
437                     </ng-container>
438                   </my-peertube-checkbox>
439                   </div>
440
441                 </ng-container>
442               </ng-container>
443             </ng-container>
444
445           </div>
446         </div>
447
448         <div class="form-row mt-4"> <!-- federation grid -->
449           <div class="form-group col-12 col-lg-4 col-xl-3">
450             <div i18n class="inner-form-title">FEDERATION</div>
451             <div i18n class="inner-form-description">
452               Manage <a routerLink="/admin/follows">relations</a> with other instances.
453             </div>
454           </div>
455
456           <div class="form-group form-group-right col-12 col-lg-8 col-xl-9">
457
458             <ng-container formGroupName="followers">
459               <ng-container formGroupName="instance">
460
461                 <div class="form-group">
462                   <my-peertube-checkbox
463                     inputName="followersInstanceEnabled" formControlName="enabled"
464                     i18n-labelText labelText="Other instances can follow yours"
465                   ></my-peertube-checkbox>
466                 </div>
467
468                 <div class="form-group">
469                   <my-peertube-checkbox
470                     inputName="followersInstanceManualApproval" formControlName="manualApproval"
471                     i18n-labelText labelText="Manually approve new instance followers"
472                   ></my-peertube-checkbox>
473                 </div>
474               </ng-container>
475             </ng-container>
476
477             <ng-container formGroupName="followings">
478               <ng-container formGroupName="instance">
479
480                 <ng-container formGroupName="autoFollowBack">
481                   <div class="form-group">
482                     <my-peertube-checkbox
483                       inputName="followingsInstanceAutoFollowBackEnabled" formControlName="enabled"
484                       i18n-labelText labelText="Automatically follow back instances"
485                     >
486                       <ng-container ngProjectAs="description">
487                         <span i18n>⚠️ This functionality requires a lot of attention and extra moderation.</span>
488                       </ng-container>
489                     </my-peertube-checkbox>
490                   </div>
491                 </ng-container>
492
493                 <ng-container formGroupName="autoFollowIndex">
494                   <div class="form-group">
495                     <my-peertube-checkbox
496                       inputName="followingsInstanceAutoFollowIndexEnabled" formControlName="enabled"
497                       i18n-labelText labelText="Automatically follow instances of a public index"
498                     >
499                       <ng-container ngProjectAs="description">
500                         <p i18n>⚠️ This functionality requires a lot of attention and extra moderation.</p>
501
502                         <span i18n>
503                           You should only follow indexes you trust, or <a href="https://framagit.org/framasoft/peertube/instances-peertube#peertube-auto-follow">host your own</a>.
504                         </span>
505                       </ng-container>
506
507                       <ng-container ngProjectAs="extra">
508                         <div [ngClass]="{ 'disabled-checkbox-extra': !isAutoFollowIndexEnabled() }">
509                           <label i18n for="followingsInstanceAutoFollowIndexUrl">Index URL</label>
510                           <input
511                             type="text" id="followingsInstanceAutoFollowIndexUrl" class="form-control"
512                             formControlName="indexUrl" [ngClass]="{ 'input-error': formErrors['followings.instance.autoFollowIndex.indexUrl'] }"
513                           >
514                           <div *ngIf="formErrors.followings.instance.autoFollowIndex.indexUrl" class="form-error">{{ formErrors.followings.instance.autoFollowIndex.indexUrl }}</div>
515                         </div>
516                       </ng-container>
517                     </my-peertube-checkbox>
518                   </div>
519
520                 </ng-container>
521               </ng-container>
522             </ng-container>
523
524           </div>
525         </div>
526
527         <div class="form-row mt-4"> <!-- administrators grid -->
528           <div class="form-group col-12 col-lg-4 col-xl-3">
529             <div i18n class="inner-form-title">ADMINISTRATORS</div>
530           </div>
531
532           <div class="form-group form-group-right col-12 col-lg-8 col-xl-9">
533
534             <div class="form-group" formGroupName="admin">
535               <label i18n for="adminEmail">Admin email</label>
536               <input
537                 type="text" id="adminEmail" class="form-control"
538                 formControlName="email" [ngClass]="{ 'input-error': formErrors['admin.email'] }"
539               >
540               <div *ngIf="formErrors.admin.email" class="form-error">{{ formErrors.admin.email }}</div>
541             </div>
542
543             <div class="form-group" formGroupName="contactForm">
544               <my-peertube-checkbox
545                 inputName="enableContactForm" formControlName="enabled"
546                 i18n-labelText labelText="Enable contact form"
547               ></my-peertube-checkbox>
548             </div>
549
550           </div>
551         </div>
552
553       </ng-template>
554     </ng-container>
555
556     <ng-container ngbNavItem="services">
557       <a ngbNavLink i18n>Services</a>
558
559       <ng-template ngbNavContent>
560
561         <div class="form-row mt-5"> <!-- twitter grid -->
562           <div class="form-group col-12 col-lg-4 col-xl-3">
563             <div i18n class="inner-form-title">TWITTER</div>
564             <div i18n class="inner-form-description">
565               Optional. If any, provide the Twitter account representing your instance to improve link previews.
566             </div>
567           </div>
568
569           <div class="form-group form-group-right col-12 col-lg-8 col-xl-9">
570
571             <ng-container formGroupName="services">
572               <ng-container formGroupName="twitter">
573
574                 <div class="form-group">
575                   <label i18n for="signupLimit">Your Twitter username</label>
576
577                   <input
578                     type="text" id="servicesTwitterUsername" class="form-control"
579                     formControlName="username" [ngClass]="{ 'input-error': formErrors['services.twitter.username'] }"
580                   >
581                   <div *ngIf="formErrors.services.twitter.username" class="form-error">{{ formErrors.services.twitter.username }}</div>
582                 </div>
583
584                 <div class="form-group">
585                   <my-peertube-checkbox inputName="servicesTwitterWhitelisted" formControlName="whitelisted">
586                     <ng-template ptTemplate="label">
587                       <ng-container i18n>Instance whitelisted by Twitter</ng-container>
588                     </ng-template>
589
590                     <ng-template ptTemplate="help">
591                       <ng-container i18n>
592                         If your instance is whitelisted by Twitter, a video player will be embedded in the Twitter feed on PeerTube video share.<br />
593                         If the instance is not whitelisted, we use an image link card that will redirect on your PeerTube instance.<br /><br />
594                         Check this checkbox, save the configuration and test with a video URL of your instance (https://example.com/videos/watch/blabla) on
595                         <a target='_blank' rel='noopener noreferrer' href='https://cards-dev.twitter.com/validator'>https://cards-dev.twitter.com/validator</a>
596                         to see if you instance is whitelisted.
597                       </ng-container>
598                     </ng-template>
599                   </my-peertube-checkbox>
600                 </div>
601
602               </ng-container>
603             </ng-container>
604
605           </div>
606         </div>
607       </ng-template>
608     </ng-container>
609
610     <ng-container ngbNavItem="advanced-configuration">
611       <a ngbNavLink i18n>Advanced configuration</a>
612
613       <ng-template ngbNavContent>
614
615         <div class="form-row mt-5"> <!-- transcoding grid -->
616           <div class="form-group col-12 col-lg-4 col-xl-3">
617             <div i18n class="inner-form-title">TRANSCODING</div>
618             <div i18n class="inner-form-description">
619               Process uploaded videos so that they are in a streamable form that any device can play. Though costly in
620               resources, this is a critical part of PeerTube, so tread carefully.
621             </div>
622           </div>
623
624           <div class="form-group form-group-right col-12 col-lg-8 col-xl-9">
625
626             <ng-container formGroupName="transcoding">
627
628               <div class="form-group">
629                 <my-peertube-checkbox inputName="transcodingEnabled" formControlName="enabled">
630                   <ng-template ptTemplate="label">
631                     <ng-container i18n>Transcoding enabled</ng-container>
632                   </ng-template>
633
634                   <ng-template ptTemplate="help">
635                     <ng-container i18n>If you disable transcoding, many videos from your users will not work!</ng-container>
636                   </ng-template>
637
638                   <ng-container ngProjectAs="extra">
639
640                     <div class="form-group" [ngClass]="{ 'disabled-checkbox-extra': !isTranscodingEnabled() }">
641                       <my-peertube-checkbox
642                         inputName="transcodingAllowAdditionalExtensions" formControlName="allowAdditionalExtensions"
643                         i18n-labelText labelText="Allow additional extensions"
644                       >
645                         <ng-container ngProjectAs="description">
646                           <span i18n>Allows users to upload .mkv, .mov, .avi and .flv videos.</span>
647                         </ng-container>
648                       </my-peertube-checkbox>
649                     </div>
650
651                     <div class="form-group" [ngClass]="{ 'disabled-checkbox-extra': !isTranscodingEnabled() }">
652                       <my-peertube-checkbox
653                         inputName="transcodingAllowAudioFiles" formControlName="allowAudioFiles"
654                         i18n-labelText labelText="Allow audio files upload"
655                       >
656                         <ng-container ngProjectAs="description">
657                           <span i18n>Allows users to upload audio files that will be merged with the preview file on upload.</span>
658                         </ng-container>
659                       </my-peertube-checkbox>
660                     </div>
661
662                     <ng-container formGroupName="webtorrent">
663                       <div class="form-group" [ngClass]="{ 'disabled-checkbox-extra': !isTranscodingEnabled() }">
664                         <my-peertube-checkbox
665                           inputName="transcodingWebTorrentEnabled" formControlName="enabled"
666                           i18n-labelText labelText="WebTorrent support enabled"
667                         >
668                           <ng-template ptTemplate="help">
669                             <ng-container i18n>
670                               <strong>Experimental, we suggest you to not disable webtorrent support for now</strong>
671
672                               <p>If you also enabled HLS support, it will multiply videos storage by 2</p>
673
674                               <br />
675
676                               <strong>If disabled, breaks federation with PeerTube instances < 2.1</strong>
677                             </ng-container>
678                           </ng-template>
679                         </my-peertube-checkbox>
680                       </div>
681                     </ng-container>
682
683                     <ng-container formGroupName="hls">
684                       <div class="form-group" [ngClass]="{ 'disabled-checkbox-extra': !isTranscodingEnabled() }">
685                         <my-peertube-checkbox
686                           inputName="transcodingHlsEnabled" formControlName="enabled"
687                           i18n-labelText labelText="HLS support enabled"
688                         >
689                           <ng-template ptTemplate="help">
690                             <ng-container i18n>
691                               <strong>Requires ffmpeg >= 4.1</strong>
692
693                               <p>Generate HLS playlists and fragmented MP4 files resulting in a better playback than with the current default player:</p>
694                               <ul>
695                                 <li>Resolution change is smoother</li>
696                                 <li>Faster playback in particular with long videos</li>
697                                 <li>More stable playback (less bugs/infinite loading)</li>
698                               </ul>
699
700                               <p>If you also enabled WebTorrent support, it will multiply videos storage by 2</p>
701                             </ng-container>
702                           </ng-template>
703                         </my-peertube-checkbox>
704                       </div>
705                     </ng-container>
706
707                   </ng-container>
708                 </my-peertube-checkbox>
709               </div>
710
711               <div class="form-group" [ngClass]="{ 'disabled-checkbox-extra': !isTranscodingEnabled() }">
712                 <label i18n for="transcodingThreads">Transcoding threads</label>
713                 <div class="peertube-select-container">
714                   <select id="transcodingThreads" formControlName="threads" class="form-control">
715                     <option *ngFor="let transcodingThreadOption of transcodingThreadOptions" [value]="transcodingThreadOption.value">
716                       {{ transcodingThreadOption.label }}
717                     </option>
718                   </select>
719                 </div>
720                 <div *ngIf="formErrors.transcoding.threads" class="form-error">{{ formErrors.transcoding.threads }}</div>
721               </div>
722
723               <div class="form-group" [ngClass]="{ 'disabled-checkbox-extra': !isTranscodingEnabled() }">
724
725                 <label i18n for="transcodingThreads">Resolutions to generate</label>
726
727                 <div class="ml-2 mt-2 d-flex flex-column">
728                   <ng-container formGroupName="resolutions">
729                     <div class="form-group" *ngFor="let resolution of resolutions">
730                       <my-peertube-checkbox
731                         [inputName]="getResolutionKey(resolution.id)" [formControlName]="resolution.id"
732                         labelText="{{resolution.label}}"
733                       >
734                         <ng-template *ngIf="resolution.description" ptTemplate="help">
735                           <div [innerHTML]="resolution.description"></div>
736                         </ng-template>
737                       </my-peertube-checkbox>
738                     </div>
739                   </ng-container>
740                 </div>
741
742               </div>
743
744             </ng-container>
745
746           </div>
747         </div>
748
749         <div class="form-row mt-4"> <!-- cache grid -->
750           <div class="form-group col-12 col-lg-4 col-xl-3">
751             <div i18n class="inner-form-title">CACHE</div>
752             <div i18n class="inner-form-description">
753               Some files are not federated, and fetched when necessary. Define their caching policies.
754             </div>
755           </div>
756
757           <div class="form-group form-group-right col-12 col-lg-8 col-xl-9">
758
759             <ng-container formGroupName="cache">
760               <div class="form-group" formGroupName="previews">
761                 <label i18n for="cachePreviewsSize">Number of previews to keep in cache</label>
762                 <input
763                   type="number" min="0" id="cachePreviewsSize" class="form-control"
764                   formControlName="size" [ngClass]="{ 'input-error': formErrors['cache.previews.size'] }"
765                 >
766                 <div *ngIf="formErrors.cache.previews.size" class="form-error">{{ formErrors.cache.previews.size }}</div>
767               </div>
768
769               <div class="form-group" formGroupName="captions">
770                 <label i18n for="cacheCaptionsSize">Number of video captions to keep in cache</label>
771                 <input
772                   type="number" min="0" id="cacheCaptionsSize" class="form-control"
773                   formControlName="size" [ngClass]="{ 'input-error': formErrors['cache.captions.size'] }"
774                 >
775                 <div *ngIf="formErrors.cache.captions.size" class="form-error">{{ formErrors.cache.captions.size }}</div>
776               </div>
777             </ng-container>
778
779           </div>
780         </div>
781
782         <div class="form-row mt-4"> <!-- cache grid -->
783           <div class="form-group col-12 col-lg-4 col-xl-3">
784             <div class="anchor" id="customizations"></div> <!-- customizations anchor -->
785             <div i18n class="inner-form-title">CUSTOMIZATIONS</div>
786             <div i18n class="inner-form-description">
787               Slight modifications to your PeerTube instance for when creating a plugin or theme is overkill.
788             </div>
789           </div>
790
791           <div class="form-group form-group-right col-12 col-lg-8 col-xl-9">
792
793             <ng-container formGroupName="instance">
794               <ng-container formGroupName="customizations">
795                 <div class="form-group">
796                   <label i18n for="customizationJavascript">JavaScript</label>
797                   <my-help>
798                     <ng-template ptTemplate="customHtml">
799                       <ng-container i18n>
800                         Write JavaScript code directly.<br />Example: <pre>console.log('my instance is amazing');</pre>
801                       </ng-container>
802                     </ng-template>
803                   </my-help>
804
805                   <textarea
806                     id="customizationJavascript" formControlName="javascript" class="form-control"
807                     [ngClass]="{ 'input-error': formErrors['instance.customizations.javascript'] }"
808                   ></textarea>
809
810                   <div *ngIf="formErrors.instance.customizations.javascript" class="form-error">{{ formErrors.instance.customizations.javascript }}</div>
811                 </div>
812
813                 <div class="form-group">
814                   <label for="customizationCSS">CSS</label>
815
816                   <my-help>
817                     <ng-template ptTemplate="customHtml">
818                       <ng-container i18n>
819                         Write CSS code directly. Example:<br /><br />
820     <pre>
821     #custom-css {{ '{' }}
822       color: red;
823     {{ '}' }}
824     </pre>
825                         Prepend with <em>#custom-css</em> to override styles. Example:<br /><br />
826     <pre>
827     #custom-css .logged-in-email {{ '{' }}
828       color: red;
829     {{ '}' }}
830     </pre>
831                       </ng-container>
832                     </ng-template>
833                   </my-help>
834
835                   <textarea
836                     id="customizationCSS" formControlName="css" class="form-control"
837                     [ngClass]="{ 'input-error': formErrors['instance.customizations.css'] }"
838                   ></textarea>
839                   <div *ngIf="formErrors.instance.customizations.css" class="form-error">{{ formErrors.instance.customizations.css }}</div>
840                 </div>
841               </ng-container>
842             </ng-container>
843
844           </div>
845         </div>
846
847       </ng-template>
848     </ng-container>
849   </div>
850
851   <div [ngbNavOutlet]="nav"></div>
852
853   <div class="form-row mt-4"> <!-- submit placement block -->
854     <div class="col-md-7 col-xl-5"></div>
855     <div class="col-md-5 col-xl-5">
856       <span class="form-error submit-error" i18n *ngIf="!form.valid">It seems like the configuration is invalid. Please search for potential errors in the different tabs.</span>
857
858       <input (click)="formValidated()" type="submit" i18n-value value="Update configuration" [disabled]="!form.valid">
859     </div>
860   </div>
861 </form>