aboutsummaryrefslogtreecommitdiffhomepage
path: root/client/src/app/+admin
diff options
context:
space:
mode:
Diffstat (limited to 'client/src/app/+admin')
-rw-r--r--client/src/app/+admin/admin.module.ts2
-rw-r--r--client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.html513
-rw-r--r--client/src/app/+admin/follows/following-add/following-add.component.html2
-rw-r--r--client/src/app/+admin/follows/follows.component.html12
-rw-r--r--client/src/app/+admin/follows/follows.component.scss6
-rw-r--r--client/src/app/+admin/follows/follows.component.ts8
6 files changed, 271 insertions, 272 deletions
diff --git a/client/src/app/+admin/admin.module.ts b/client/src/app/+admin/admin.module.ts
index 8d50b8715..d7ae2f7f0 100644
--- a/client/src/app/+admin/admin.module.ts
+++ b/client/src/app/+admin/admin.module.ts
@@ -1,7 +1,6 @@
1import { NgModule } from '@angular/core' 1import { NgModule } from '@angular/core'
2import { ConfigComponent, EditCustomConfigComponent } from '@app/+admin/config' 2import { ConfigComponent, EditCustomConfigComponent } from '@app/+admin/config'
3import { ConfigService } from '@app/+admin/config/shared/config.service' 3import { ConfigService } from '@app/+admin/config/shared/config.service'
4import { TabsModule } from 'ngx-bootstrap/tabs'
5import { TableModule } from 'primeng/table' 4import { TableModule } from 'primeng/table'
6import { SharedModule } from '../shared' 5import { SharedModule } from '../shared'
7import { AdminRoutingModule } from './admin-routing.module' 6import { AdminRoutingModule } from './admin-routing.module'
@@ -18,7 +17,6 @@ import { VideoBlacklistComponent, VideoBlacklistListComponent } from './video-bl
18@NgModule({ 17@NgModule({
19 imports: [ 18 imports: [
20 AdminRoutingModule, 19 AdminRoutingModule,
21 TabsModule.forRoot(),
22 TableModule, 20 TableModule,
23 SharedModule 21 SharedModule
24 ], 22 ],
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 0a032df12..49b89cef4 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
@@ -1,290 +1,295 @@
1<form role="form" [formGroup]="form"> 1<form role="form" [formGroup]="form">
2 2
3 <tabset class="root-tabset bootstrap"> 3 <ngb-tabset class="root-tabset bootstrap">
4 4
5 <tab i18n-heading heading="Basic configuration"> 5 <ngb-tab i18n-title title="Basic configuration">
6 <ng-template ngbTabContent>
6 7
7 <div i18n class="inner-form-title">Instance</div> 8 <div i18n class="inner-form-title">Instance</div>
8 9
9 <div class="form-group"> 10 <div class="form-group">
10 <label i18n for="instanceName">Name</label> 11 <label i18n for="instanceName">Name</label>
11 <input 12 <input
12 type="text" id="instanceName" 13 type="text" id="instanceName"
13 formControlName="instanceName" [ngClass]="{ 'input-error': formErrors['instanceName'] }" 14 formControlName="instanceName" [ngClass]="{ 'input-error': formErrors['instanceName'] }"
14 > 15 >
15 <div *ngIf="formErrors.instanceName" class="form-error"> 16 <div *ngIf="formErrors.instanceName" class="form-error">
16 {{ formErrors.instanceName }} 17 {{ formErrors.instanceName }}
17 </div> 18 </div>
18 </div>
19
20 <div class="form-group">
21 <label i18n for="instanceShortDescription">Short description</label>
22 <textarea
23 id="instanceShortDescription" formControlName="instanceShortDescription"
24 [ngClass]="{ 'input-error': formErrors['instanceShortDescription'] }"
25 ></textarea>
26 <div *ngIf="formErrors.instanceShortDescription" class="form-error">
27 {{ formErrors.instanceShortDescription }}
28 </div>
29 </div>
30
31 <div class="form-group">
32 <label i18n for="instanceDescription">Description</label><my-help helpType="markdownText"></my-help>
33 <my-markdown-textarea
34 id="instanceDescription" formControlName="instanceDescription" textareaWidth="500px" [previewColumn]="true"
35 [classes]="{ 'input-error': formErrors['instanceDescription'] }"
36 ></my-markdown-textarea>
37 <div *ngIf="formErrors.instanceDescription" class="form-error">
38 {{ formErrors.instanceDescription }}
39 </div>
40 </div>
41
42 <div class="form-group">
43 <label i18n for="instanceTerms">Terms</label><my-help helpType="markdownText"></my-help>
44 <my-markdown-textarea
45 id="instanceTerms" formControlName="instanceTerms" textareaWidth="500px" [previewColumn]="true"
46 [ngClass]="{ 'input-error': formErrors['instanceTerms'] }"
47 ></my-markdown-textarea>
48 <div *ngIf="formErrors.instanceTerms" class="form-error">
49 {{ formErrors.instanceTerms }}
50 </div>
51 </div>
52
53 <div class="form-group">
54 <label i18n for="instanceDefaultClientRoute">Default client route</label>
55 <div class="peertube-select-container">
56 <select id="instanceDefaultClientRoute" formControlName="instanceDefaultClientRoute">
57 <option i18n value="/videos/trending">Videos Trending</option>
58 <option i18n value="/videos/recently-added">Videos Recently Added</option>
59 <option i18n value="/videos/local">Local videos</option>
60 </select>
61 </div>
62 <div *ngIf="formErrors.instanceDefaultClientRoute" class="form-error">
63 {{ formErrors.instanceDefaultClientRoute }}
64 </div>
65 </div>
66
67 <div class="form-group">
68 <label i18n for="instanceDefaultNSFWPolicy">Policy on videos containing sensitive content</label>
69 <my-help
70 helpType="custom" i18n-customHtml
71 customHtml="With <strong>Do not list</strong> or <strong>Blur thumbnails</strong>, a confirmation will be requested to watch the video."
72 ></my-help>
73
74 <div class="peertube-select-container">
75 <select id="instanceDefaultNSFWPolicy" formControlName="instanceDefaultNSFWPolicy">
76 <option i18n value="do_not_list">Do not list</option>
77 <option i18n value="blur">Blur thumbnails</option>
78 <option i18n value="display">Display</option>
79 </select>
80 </div> 19 </div>
81 <div *ngIf="formErrors.instanceDefaultNSFWPolicy" class="form-error"> 20
82 {{ formErrors.instanceDefaultNSFWPolicy }} 21 <div class="form-group">
22 <label i18n for="instanceShortDescription">Short description</label>
23 <textarea
24 id="instanceShortDescription" formControlName="instanceShortDescription"
25 [ngClass]="{ 'input-error': formErrors['instanceShortDescription'] }"
26 ></textarea>
27 <div *ngIf="formErrors.instanceShortDescription" class="form-error">
28 {{ formErrors.instanceShortDescription }}
29 </div>
83 </div> 30 </div>
84 </div> 31
85 32 <div class="form-group">
86 <div i18n class="inner-form-title">Signup</div> 33 <label i18n for="instanceDescription">Description</label><my-help helpType="markdownText"></my-help>
87 34 <my-markdown-textarea
88 <my-peertube-checkbox 35 id="instanceDescription" formControlName="instanceDescription" textareaWidth="500px" [previewColumn]="true"
89 inputName="signupEnabled" formControlName="signupEnabled" 36 [classes]="{ 'input-error': formErrors['instanceDescription'] }"
90 i18n-labelText labelText="Signup enabled" 37 ></my-markdown-textarea>
91 ></my-peertube-checkbox> 38 <div *ngIf="formErrors.instanceDescription" class="form-error">
92 39 {{ formErrors.instanceDescription }}
93 <div *ngIf="isSignupEnabled()" class="form-group"> 40 </div>
94 <label i18n for="signupLimit">Signup limit</label>
95 <input
96 type="text" id="signupLimit"
97 formControlName="signupLimit" [ngClass]="{ 'input-error': formErrors['signupLimit'] }"
98 >
99 <div *ngIf="formErrors.signupLimit" class="form-error">
100 {{ formErrors.signupLimit }}
101 </div> 41 </div>
102 </div> 42
103 43 <div class="form-group">
104 <div i18n class="inner-form-title">Import</div> 44 <label i18n for="instanceTerms">Terms</label><my-help helpType="markdownText"></my-help>
105 45 <my-markdown-textarea
106 <my-peertube-checkbox 46 id="instanceTerms" formControlName="instanceTerms" textareaWidth="500px" [previewColumn]="true"
107 inputName="importVideosHttpEnabled" formControlName="importVideosHttpEnabled" 47 [ngClass]="{ 'input-error': formErrors['instanceTerms'] }"
108 i18n-labelText labelText="Video import with HTTP enabled" 48 ></my-markdown-textarea>
109 ></my-peertube-checkbox> 49 <div *ngIf="formErrors.instanceTerms" class="form-error">
110 50 {{ formErrors.instanceTerms }}
111 <my-peertube-checkbox 51 </div>
112 inputName="importVideosTorrentEnabled" formControlName="importVideosTorrentEnabled"
113 i18n-labelText labelText="Video import with a torrent file or a magnet URI enabled"
114 ></my-peertube-checkbox>
115
116 <div i18n class="inner-form-title">Administrator</div>
117
118 <div class="form-group">
119 <label i18n for="adminEmail">Admin email</label>
120 <input
121 type="text" id="adminEmail"
122 formControlName="adminEmail" [ngClass]="{ 'input-error': formErrors['adminEmail'] }"
123 >
124 <div *ngIf="formErrors.adminEmail" class="form-error">
125 {{ formErrors.adminEmail }}
126 </div> 52 </div>
127 </div> 53
128 54 <div class="form-group">
129 <div i18n class="inner-form-title">Users</div> 55 <label i18n for="instanceDefaultClientRoute">Default client route</label>
130 56 <div class="peertube-select-container">
131 <div class="form-group"> 57 <select id="instanceDefaultClientRoute" formControlName="instanceDefaultClientRoute">
132 <label i18n for="userVideoQuota">User default video quota</label> 58 <option i18n value="/videos/trending">Videos Trending</option>
133 <div class="peertube-select-container"> 59 <option i18n value="/videos/recently-added">Videos Recently Added</option>
134 <select id="userVideoQuota" formControlName="userVideoQuota"> 60 <option i18n value="/videos/local">Local videos</option>
135 <option *ngFor="let videoQuotaOption of videoQuotaOptions" [value]="videoQuotaOption.value"> 61 </select>
136 {{ videoQuotaOption.label }} 62 </div>
137 </option> 63 <div *ngIf="formErrors.instanceDefaultClientRoute" class="form-error">
138 </select> 64 {{ formErrors.instanceDefaultClientRoute }}
65 </div>
139 </div> 66 </div>
140 <div *ngIf="formErrors.userVideoQuota" class="form-error"> 67
141 {{ formErrors.userVideoQuota }} 68 <div class="form-group">
69 <label i18n for="instanceDefaultNSFWPolicy">Policy on videos containing sensitive content</label>
70 <my-help
71 helpType="custom" i18n-customHtml
72 customHtml="With <strong>Do not list</strong> or <strong>Blur thumbnails</strong>, a confirmation will be requested to watch the video."
73 ></my-help>
74
75 <div class="peertube-select-container">
76 <select id="instanceDefaultNSFWPolicy" formControlName="instanceDefaultNSFWPolicy">
77 <option i18n value="do_not_list">Do not list</option>
78 <option i18n value="blur">Blur thumbnails</option>
79 <option i18n value="display">Display</option>
80 </select>
81 </div>
82 <div *ngIf="formErrors.instanceDefaultNSFWPolicy" class="form-error">
83 {{ formErrors.instanceDefaultNSFWPolicy }}
84 </div>
142 </div> 85 </div>
143 </div> 86
144 </tab> 87 <div i18n class="inner-form-title">Signup</div>
145 88
146 <tab i18n-heading heading="Services"> 89 <my-peertube-checkbox
147 90 inputName="signupEnabled" formControlName="signupEnabled"
148 <div i18n class="inner-form-title">Twitter</div> 91 i18n-labelText labelText="Signup enabled"
149 92 ></my-peertube-checkbox>
150 <div class="form-group"> 93
151 <label i18n for="signupLimit">Your Twitter username</label> 94 <div *ngIf="isSignupEnabled()" class="form-group">
152 <my-help 95 <label i18n for="signupLimit">Signup limit</label>
153 helpType="custom" i18n-customHtml 96 <input
154 customHtml="Indicates the Twitter account for the website or platform on which the content was published." 97 type="text" id="signupLimit"
155 ></my-help> 98 formControlName="signupLimit" [ngClass]="{ 'input-error': formErrors['signupLimit'] }"
156 <input 99 >
157 type="text" id="servicesTwitterUsername" 100 <div *ngIf="formErrors.signupLimit" class="form-error">
158 formControlName="servicesTwitterUsername" [ngClass]="{ 'input-error': formErrors['servicesTwitterUsername'] }" 101 {{ formErrors.signupLimit }}
159 > 102 </div>
160 <div *ngIf="formErrors.servicesTwitterUsername" class="form-error">
161 {{ formErrors.servicesTwitterUsername }}
162 </div> 103 </div>
163 </div>
164 104
165 <my-peertube-checkbox 105 <div i18n class="inner-form-title">Import</div>
166 inputName="servicesTwitterWhitelisted" formControlName="servicesTwitterWhitelisted" 106
167 i18n-labelText labelText="Instance whitelisted by Twitter" 107 <my-peertube-checkbox
168 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 /> 108 inputName="importVideosHttpEnabled" formControlName="importVideosHttpEnabled"
169If the instance is not whitelisted, we use an image link card that will redirect on your PeerTube instance.<br /><br /> 109 i18n-labelText labelText="Video import with HTTP enabled"
170Check 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." 110 ></my-peertube-checkbox>
171 ></my-peertube-checkbox>
172 </tab>
173 111
174 <tab i18n-heading heading="Advanced configuration"> 112 <my-peertube-checkbox
113 inputName="importVideosTorrentEnabled" formControlName="importVideosTorrentEnabled"
114 i18n-labelText labelText="Video import with a torrent file or a magnet URI enabled"
115 ></my-peertube-checkbox>
175 116
176 <div i18n class="inner-form-title">Transcoding</div> 117 <div i18n class="inner-form-title">Administrator</div>
177 118
178 <my-peertube-checkbox 119 <div class="form-group">
179 inputName="transcodingEnabled" formControlName="transcodingEnabled" 120 <label i18n for="adminEmail">Admin email</label>
180 i18n-labelText labelText="Transcoding enabled" 121 <input
181 i18n-helpHtml helpHtml="If you disable transcoding, many videos from your users will not work!" 122 type="text" id="adminEmail"
182 ></my-peertube-checkbox> 123 formControlName="adminEmail" [ngClass]="{ 'input-error': formErrors['adminEmail'] }"
124 >
125 <div *ngIf="formErrors.adminEmail" class="form-error">
126 {{ formErrors.adminEmail }}
127 </div>
128 </div>
183 129
184 <ng-template [ngIf]="isTranscodingEnabled()"> 130 <div i18n class="inner-form-title">Users</div>
185 131
186 <div class="form-group"> 132 <div class="form-group">
187 <label i18n for="transcodingThreads">Transcoding threads</label> 133 <label i18n for="userVideoQuota">User default video quota</label>
188 <div class="peertube-select-container"> 134 <div class="peertube-select-container">
189 <select id="transcodingThreads" formControlName="transcodingThreads"> 135 <select id="userVideoQuota" formControlName="userVideoQuota">
190 <option *ngFor="let transcodingThreadOption of transcodingThreadOptions" [value]="transcodingThreadOption.value"> 136 <option *ngFor="let videoQuotaOption of videoQuotaOptions" [value]="videoQuotaOption.value">
191 {{ transcodingThreadOption.label }} 137 {{ videoQuotaOption.label }}
192 </option> 138 </option>
193 </select> 139 </select>
194 </div> 140 </div>
195 <div *ngIf="formErrors.transcodingThreads" class="form-error"> 141 <div *ngIf="formErrors.userVideoQuota" class="form-error">
196 {{ formErrors.transcodingThreads }} 142 {{ formErrors.userVideoQuota }}
197 </div> 143 </div>
198 </div> 144 </div>
145 </ng-template>
146 </ngb-tab>
199 147
200 <div class="form-group" *ngFor="let resolution of resolutions"> 148 <ngb-tab i18n-title title="Services">
201 <my-peertube-checkbox 149 <ng-template ngbTabContent>
202 [inputName]="getResolutionKey(resolution)" [formControlName]="getResolutionKey(resolution)" 150 <div i18n class="inner-form-title">Twitter</div>
203 i18n-labelText labelText="Resolution {{resolution}} enabled"
204 ></my-peertube-checkbox>
205 151
152 <div class="form-group">
153 <label i18n for="signupLimit">Your Twitter username</label>
154 <my-help
155 helpType="custom" i18n-customHtml
156 customHtml="Indicates the Twitter account for the website or platform on which the content was published."
157 ></my-help>
158 <input
159 type="text" id="servicesTwitterUsername"
160 formControlName="servicesTwitterUsername" [ngClass]="{ 'input-error': formErrors['servicesTwitterUsername'] }"
161 >
162 <div *ngIf="formErrors.servicesTwitterUsername" class="form-error">
163 {{ formErrors.servicesTwitterUsername }}
164 </div>
206 </div> 165 </div>
207 </ng-template>
208 166
209 <div i18n class="inner-form-title"> 167 <my-peertube-checkbox
210 Cache 168 inputName="servicesTwitterWhitelisted" formControlName="servicesTwitterWhitelisted"
211 169 i18n-labelText labelText="Instance whitelisted by Twitter"
212 <my-help 170 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 />
213 helpType="custom" i18n-customHtml 171 If the instance is not whitelisted, we use an image link card that will redirect on your PeerTube instance.<br /><br />
214 customHtml="Some files are not federated (previews, captions). We fetch them directly from the origin instance and cache them." 172 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."
215 ></my-help> 173 ></my-peertube-checkbox>
216 </div> 174 </ng-template>
217 175 </ngb-tab>
218 <div class="form-group"> 176
219 <label i18n for="cachePreviewsSize">Previews cache size</label> 177 <ngb-tab i18n-title title="Advanced configuration">
220 <input 178 <ng-template ngbTabContent>
221 type="text" id="cachePreviewsSize" 179
222 formControlName="cachePreviewsSize" [ngClass]="{ 'input-error': formErrors['cachePreviewsSize'] }" 180 <div i18n class="inner-form-title">Transcoding</div>
223 > 181
224 <div *ngIf="formErrors.cachePreviewsSize" class="form-error"> 182 <my-peertube-checkbox
225 {{ formErrors.cachePreviewsSize }} 183 inputName="transcodingEnabled" formControlName="transcodingEnabled"
184 i18n-labelText labelText="Transcoding enabled"
185 i18n-helpHtml helpHtml="If you disable transcoding, many videos from your users will not work!"
186 ></my-peertube-checkbox>
187
188 <ng-template [ngIf]="isTranscodingEnabled()">
189
190 <div class="form-group">
191 <label i18n for="transcodingThreads">Transcoding threads</label>
192 <div class="peertube-select-container">
193 <select id="transcodingThreads" formControlName="transcodingThreads">
194 <option *ngFor="let transcodingThreadOption of transcodingThreadOptions" [value]="transcodingThreadOption.value">
195 {{ transcodingThreadOption.label }}
196 </option>
197 </select>
198 </div>
199 <div *ngIf="formErrors.transcodingThreads" class="form-error">
200 {{ formErrors.transcodingThreads }}
201 </div>
202 </div>
203
204 <div class="form-group" *ngFor="let resolution of resolutions">
205 <my-peertube-checkbox
206 [inputName]="getResolutionKey(resolution)" [formControlName]="getResolutionKey(resolution)"
207 i18n-labelText labelText="Resolution {{resolution}} enabled"
208 ></my-peertube-checkbox>
209
210 </div>
211 </ng-template>
212
213 <div i18n class="inner-form-title">
214 Cache
215
216 <my-help
217 helpType="custom" i18n-customHtml
218 customHtml="Some files are not federated (previews, captions). We fetch them directly from the origin instance and cache them."
219 ></my-help>
226 </div> 220 </div>
227 </div> 221
228 222 <div class="form-group">
229 <div class="form-group"> 223 <label i18n for="cachePreviewsSize">Previews cache size</label>
230 <label i18n for="cachePreviewsSize">Video captions cache size</label> 224 <input
231 <input 225 type="text" id="cachePreviewsSize"
232 type="text" id="cacheCaptionsSize" 226 formControlName="cachePreviewsSize" [ngClass]="{ 'input-error': formErrors['cachePreviewsSize'] }"
233 formControlName="cacheCaptionsSize" [ngClass]="{ 'input-error': formErrors['cacheCaptionsSize'] }" 227 >
234 > 228 <div *ngIf="formErrors.cachePreviewsSize" class="form-error">
235 <div *ngIf="formErrors.cacheCaptionsSize" class="form-error"> 229 {{ formErrors.cachePreviewsSize }}
236 {{ formErrors.cacheCaptionsSize }} 230 </div>
237 </div> 231 </div>
238 </div> 232
239 233 <div class="form-group">
240 <div i18n class="inner-form-title">Customizations</div> 234 <label i18n for="cachePreviewsSize">Video captions cache size</label>
241 235 <input
242 <div class="form-group"> 236 type="text" id="cacheCaptionsSize"
243 <label i18n for="customizationJavascript">JavaScript</label> 237 formControlName="cacheCaptionsSize" [ngClass]="{ 'input-error': formErrors['cacheCaptionsSize'] }"
244 <my-help 238 >
245 helpType="custom" i18n-customHtml 239 <div *ngIf="formErrors.cacheCaptionsSize" class="form-error">
246 customHtml="Write directly JavaScript code.<br />Example: <pre>console.log('my instance is amazing');</pre>" 240 {{ formErrors.cacheCaptionsSize }}
247 ></my-help> 241 </div>
248 <textarea
249 id="customizationJavascript" formControlName="customizationJavascript"
250 [ngClass]="{ 'input-error': formErrors['customizationJavascript'] }"
251 ></textarea>
252 <div *ngIf="formErrors.customizationJavascript" class="form-error">
253 {{ formErrors.customizationJavascript }}
254 </div> 242 </div>
255 </div> 243
256 244 <div i18n class="inner-form-title">Customizations</div>
257 <div class="form-group"> 245
258 <label for="customizationCSS">CSS</label> 246 <div class="form-group">
259 <my-help 247 <label i18n for="customizationJavascript">JavaScript</label>
260 helpType="custom" 248 <my-help
261 i18n-customHtml 249 helpType="custom" i18n-customHtml
262 customHtml=" 250 customHtml="Write directly JavaScript code.<br />Example: <pre>console.log('my instance is amazing');</pre>"
263 Write directly CSS code. Example:<br /> 251 ></my-help>
264 <pre> 252 <textarea
265 body {{ '{' }} 253 id="customizationJavascript" formControlName="customizationJavascript"
266 background-color: red; 254 [ngClass]="{ 'input-error': formErrors['customizationJavascript'] }"
267 {{ '}' }} 255 ></textarea>
268 </pre> 256 <div *ngIf="formErrors.customizationJavascript" class="form-error">
269 257 {{ formErrors.customizationJavascript }}
270 Prepend with <em>#custom-css</em> to override styles. Example: 258 </div>
271 <pre>
272 #custom-css .logged-in-email {{ '{' }}
273 color: red;
274 {{ '}' }}
275 </pre>
276 "
277 ></my-help>
278 <textarea
279 id="customizationCSS" formControlName="customizationCSS"
280 [ngClass]="{ 'input-error': formErrors['customizationCSS'] }"
281 ></textarea>
282 <div *ngIf="formErrors.customizationCSS" class="form-error">
283 {{ formErrors.customizationCSS }}
284 </div> 259 </div>
285 </div> 260
286 </tab> 261 <div class="form-group">
287 </tabset> 262 <label for="customizationCSS">CSS</label>
263 <my-help
264 helpType="custom"
265 i18n-customHtml
266 customHtml="
267 Write directly CSS code. Example:<br />
268 <pre>
269 body {{ '{' }}
270 background-color: red;
271 {{ '}' }}
272 </pre>
273
274 Prepend with <em>#custom-css</em> to override styles. Example:
275 <pre>
276 #custom-css .logged-in-email {{ '{' }}
277 color: red;
278 {{ '}' }}
279 </pre>
280 "
281 ></my-help>
282 <textarea
283 id="customizationCSS" formControlName="customizationCSS"
284 [ngClass]="{ 'input-error': formErrors['customizationCSS'] }"
285 ></textarea>
286 <div *ngIf="formErrors.customizationCSS" class="form-error">
287 {{ formErrors.customizationCSS }}
288 </div>
289 </div>
290 </ng-template>
291 </ngb-tab>
292 </ngb-tabset>
288 293
289 <input (click)="formValidated()" type="submit" i18n-value value="Update configuration" [disabled]="!form.valid"> 294 <input (click)="formValidated()" type="submit" i18n-value value="Update configuration" [disabled]="!form.valid">
290 <span class="form-error" i18n *ngIf="!form.valid">It seems the configuration is invalid. Please search potential errors in the different tabs.</span> 295 <span class="form-error" i18n *ngIf="!form.valid">It seems the configuration is invalid. Please search potential errors in the different tabs.</span>
diff --git a/client/src/app/+admin/follows/following-add/following-add.component.html b/client/src/app/+admin/follows/following-add/following-add.component.html
index 72635048c..e08decb3f 100644
--- a/client/src/app/+admin/follows/following-add/following-add.component.html
+++ b/client/src/app/+admin/follows/following-add/following-add.component.html
@@ -18,5 +18,5 @@
18 It seems that you are not on a HTTPS server. Your webserver needs to have TLS activated in order to follow servers. 18 It seems that you are not on a HTTPS server. Your webserver needs to have TLS activated in order to follow servers.
19 </div> 19 </div>
20 20
21 <input type="submit" i18n-value value="Add following" [disabled]="hostsError || !hostsString" class="btn btn-default"> 21 <input type="submit" i18n-value value="Add following" [disabled]="hostsError || !hostsString" class="btn btn-secondary">
22</form> 22</form>
diff --git a/client/src/app/+admin/follows/follows.component.html b/client/src/app/+admin/follows/follows.component.html
index a8258bf70..8eabb3392 100644
--- a/client/src/app/+admin/follows/follows.component.html
+++ b/client/src/app/+admin/follows/follows.component.html
@@ -1,13 +1,15 @@
1<div class="admin-sub-header"> 1<div class="admin-sub-header">
2 <div i18n class="form-sub-title">Manage follows</div> 2 <div i18n class="form-sub-title">Manage follows</div>
3 3
4 <tabset #followsMenuTabs> 4 <ngb-tabset #followsMenuTabs type="pills">
5 <tab *ngFor="let link of links"> 5
6 <ng-template tabHeading> 6 <ngb-tab *ngFor="let link of links">
7 <ng-template ngbTabTitle>
7 <a class="tab-link" [routerLink]="link.path">{{ link.title }}</a> 8 <a class="tab-link" [routerLink]="link.path">{{ link.title }}</a>
8 </ng-template> 9 </ng-template>
9 </tab> 10 </ngb-tab>
10 </tabset> 11
12 </ngb-tabset>
11</div> 13</div>
12 14
13<router-outlet></router-outlet> 15<router-outlet></router-outlet>
diff --git a/client/src/app/+admin/follows/follows.component.scss b/client/src/app/+admin/follows/follows.component.scss
index 08b3737f8..766d7853b 100644
--- a/client/src/app/+admin/follows/follows.component.scss
+++ b/client/src/app/+admin/follows/follows.component.scss
@@ -2,9 +2,3 @@
2 flex-grow: 0; 2 flex-grow: 0;
3 margin-right: 30px; 3 margin-right: 30px;
4} 4}
5
6/deep/ .tab-content {
7 height: 0;
8 min-height: 0;
9 padding: 0;
10}
diff --git a/client/src/app/+admin/follows/follows.component.ts b/client/src/app/+admin/follows/follows.component.ts
index f7af9826c..b6f7715b3 100644
--- a/client/src/app/+admin/follows/follows.component.ts
+++ b/client/src/app/+admin/follows/follows.component.ts
@@ -1,14 +1,14 @@
1import { AfterViewInit, Component, OnInit, ViewChild } from '@angular/core' 1import { AfterViewInit, Component, OnInit, ViewChild } from '@angular/core'
2import { NavigationEnd, Router } from '@angular/router' 2import { NavigationEnd, Router } from '@angular/router'
3import { TabsetComponent } from 'ngx-bootstrap/tabs'
4import { I18n } from '@ngx-translate/i18n-polyfill' 3import { I18n } from '@ngx-translate/i18n-polyfill'
4import { NgbTabset } from '@ng-bootstrap/ng-bootstrap'
5 5
6@Component({ 6@Component({
7 templateUrl: './follows.component.html', 7 templateUrl: './follows.component.html',
8 styleUrls: [ './follows.component.scss' ] 8 styleUrls: [ './follows.component.scss' ]
9}) 9})
10export class FollowsComponent implements OnInit, AfterViewInit { 10export class FollowsComponent implements OnInit, AfterViewInit {
11 @ViewChild('followsMenuTabs') followsMenuTabs: TabsetComponent 11 @ViewChild('followsMenuTabs') followsMenuTabs: NgbTabset
12 12
13 links: { path: string, title: string }[] = [] 13 links: { path: string, title: string }[] = []
14 14
@@ -53,8 +53,8 @@ export class FollowsComponent implements OnInit, AfterViewInit {
53 for (let i = 0; i < this.links.length; i++) { 53 for (let i = 0; i < this.links.length; i++) {
54 const path = this.links[i].path 54 const path = this.links[i].path
55 55
56 if (url.endsWith(path) === true && this.followsMenuTabs.tabs[i]) { 56 if (url.endsWith(path) === true) {
57 this.followsMenuTabs.tabs[i].active = true 57 this.followsMenuTabs.select(path)
58 return 58 return
59 } 59 }
60 } 60 }