aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--client/src/app/app.component.html4
-rw-r--r--client/src/app/app.component.ts71
-rw-r--r--client/src/app/app.module.ts8
-rw-r--r--client/src/app/core/users/user.model.ts2
-rw-r--r--client/src/app/modal/account-setup-modal.component.ts73
-rw-r--r--client/src/app/modal/account-setup-warning-modal.component.html (renamed from client/src/app/modal/account-setup-modal.component.html)10
-rw-r--r--client/src/app/modal/account-setup-warning-modal.component.scss (renamed from client/src/app/modal/account-setup-modal.component.scss)0
-rw-r--r--client/src/app/modal/account-setup-warning-modal.component.ts79
-rw-r--r--client/src/app/modal/admin-welcome-modal.component.html (renamed from client/src/app/modal/welcome-modal.component.html)0
-rw-r--r--client/src/app/modal/admin-welcome-modal.component.scss (renamed from client/src/app/modal/welcome-modal.component.scss)0
-rw-r--r--client/src/app/modal/admin-welcome-modal.component.ts (renamed from client/src/app/modal/welcome-modal.component.ts)20
-rw-r--r--client/src/app/modal/instance-config-warning-modal.component.ts23
-rw-r--r--server/controllers/api/users/me.ts1
-rw-r--r--server/helpers/custom-validators/users.ts9
-rw-r--r--server/initializers/constants.ts2
-rw-r--r--server/initializers/migrations/0665-no-account-warning-modal.ts27
-rw-r--r--server/middlewares/validators/users.ts12
-rw-r--r--server/models/user/user.ts19
-rw-r--r--server/tests/api/check-params/users.ts27
-rw-r--r--server/tests/api/users/users.ts5
-rw-r--r--shared/models/users/user-update-me.model.ts1
-rw-r--r--shared/models/users/user.model.ts1
-rw-r--r--support/doc/api/openapi.yaml4
23 files changed, 242 insertions, 156 deletions
diff --git a/client/src/app/app.component.html b/client/src/app/app.component.html
index da66e4ef0..6969329e8 100644
--- a/client/src/app/app.component.html
+++ b/client/src/app/app.component.html
@@ -61,8 +61,8 @@
61</p-toast> 61</p-toast>
62 62
63<ng-container *ngIf="isUserLoggedIn()"> 63<ng-container *ngIf="isUserLoggedIn()">
64 <my-account-setup-modal #accountSetupModal></my-account-setup-modal> 64 <my-account-setup-warning-modal #accountSetupWarningModal></my-account-setup-warning-modal>
65 <my-welcome-modal #welcomeModal></my-welcome-modal> 65 <my-admin-welcome-modal #adminWelcomeModal></my-admin-welcome-modal>
66 <my-instance-config-warning-modal #instanceConfigWarningModal></my-instance-config-warning-modal> 66 <my-instance-config-warning-modal #instanceConfigWarningModal></my-instance-config-warning-modal>
67</ng-container> 67</ng-container>
68 68
diff --git a/client/src/app/app.component.ts b/client/src/app/app.component.ts
index ae6046cc1..86b687173 100644
--- a/client/src/app/app.component.ts
+++ b/client/src/app/app.component.ts
@@ -1,5 +1,6 @@
1import { Hotkey, HotkeysService } from 'angular2-hotkeys' 1import { Hotkey, HotkeysService } from 'angular2-hotkeys'
2import { filter, map, switchMap } from 'rxjs/operators' 2import { forkJoin } from 'rxjs'
3import { filter, first, map } from 'rxjs/operators'
3import { DOCUMENT, getLocaleDirection, PlatformLocation } from '@angular/common' 4import { DOCUMENT, getLocaleDirection, PlatformLocation } from '@angular/common'
4import { AfterViewInit, Component, Inject, LOCALE_ID, OnInit, ViewChild } from '@angular/core' 5import { AfterViewInit, Component, Inject, LOCALE_ID, OnInit, ViewChild } from '@angular/core'
5import { DomSanitizer, SafeHtml } from '@angular/platform-browser' 6import { DomSanitizer, SafeHtml } from '@angular/platform-browser'
@@ -17,15 +18,15 @@ import {
17} from '@app/core' 18} from '@app/core'
18import { HooksService } from '@app/core/plugins/hooks.service' 19import { HooksService } from '@app/core/plugins/hooks.service'
19import { PluginService } from '@app/core/plugins/plugin.service' 20import { PluginService } from '@app/core/plugins/plugin.service'
21import { AccountSetupWarningModalComponent } from '@app/modal/account-setup-warning-modal.component'
20import { CustomModalComponent } from '@app/modal/custom-modal.component' 22import { CustomModalComponent } from '@app/modal/custom-modal.component'
21import { InstanceConfigWarningModalComponent } from '@app/modal/instance-config-warning-modal.component' 23import { InstanceConfigWarningModalComponent } from '@app/modal/instance-config-warning-modal.component'
22import { WelcomeModalComponent } from '@app/modal/welcome-modal.component' 24import { AdminWelcomeModalComponent } from '@app/modal/admin-welcome-modal.component'
23import { AccountSetupModalComponent } from '@app/modal/account-setup-modal.component'
24import { NgbConfig, NgbModal } from '@ng-bootstrap/ng-bootstrap' 25import { NgbConfig, NgbModal } from '@ng-bootstrap/ng-bootstrap'
25import { LoadingBarService } from '@ngx-loading-bar/core' 26import { LoadingBarService } from '@ngx-loading-bar/core'
26import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage' 27import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage'
27import { getShortLocale } from '@shared/core-utils/i18n' 28import { getShortLocale } from '@shared/core-utils/i18n'
28import { BroadcastMessageLevel, HTMLServerConfig, ServerConfig, UserRole } from '@shared/models' 29import { BroadcastMessageLevel, HTMLServerConfig, UserRole } from '@shared/models'
29import { MenuService } from './core/menu/menu.service' 30import { MenuService } from './core/menu/menu.service'
30import { POP_STATE_MODAL_DISMISS } from './helpers' 31import { POP_STATE_MODAL_DISMISS } from './helpers'
31import { InstanceService } from './shared/shared-instance' 32import { InstanceService } from './shared/shared-instance'
@@ -38,8 +39,8 @@ import { InstanceService } from './shared/shared-instance'
38export class AppComponent implements OnInit, AfterViewInit { 39export class AppComponent implements OnInit, AfterViewInit {
39 private static BROADCAST_MESSAGE_KEY = 'app-broadcast-message-dismissed' 40 private static BROADCAST_MESSAGE_KEY = 'app-broadcast-message-dismissed'
40 41
41 @ViewChild('accountSetupModal') accountSetupModal: AccountSetupModalComponent 42 @ViewChild('accountSetupWarningModal') accountSetupWarningModal: AccountSetupWarningModalComponent
42 @ViewChild('welcomeModal') welcomeModal: WelcomeModalComponent 43 @ViewChild('adminWelcomeModal') adminWelcomeModal: AdminWelcomeModalComponent
43 @ViewChild('instanceConfigWarningModal') instanceConfigWarningModal: InstanceConfigWarningModalComponent 44 @ViewChild('instanceConfigWarningModal') instanceConfigWarningModal: InstanceConfigWarningModalComponent
44 @ViewChild('customModal') customModal: CustomModalComponent 45 @ViewChild('customModal') customModal: CustomModalComponent
45 46
@@ -221,33 +222,41 @@ export class AppComponent implements OnInit, AfterViewInit {
221 } 222 }
222 223
223 private openModalsIfNeeded () { 224 private openModalsIfNeeded () {
224 this.authService.userInformationLoaded 225 const userSub = this.authService.userInformationLoaded
225 .pipe( 226 .pipe(map(() => this.authService.getUser()))
226 map(() => this.authService.getUser()), 227
227 filter(user => user.role === UserRole.ADMINISTRATOR), 228 // Admin modal
228 switchMap(user => { 229 userSub.pipe(
229 return this.serverService.getConfig() 230 filter(user => user.role === UserRole.ADMINISTRATOR)
230 .pipe(map(serverConfig => ({ serverConfig, user }))) 231 ).subscribe(user => this.openAdminModalsIfNeeded(user))
231 }) 232
232 ).subscribe(({ serverConfig, user }) => this._openAdminModalsIfNeeded(serverConfig, user)) 233 // Account modal
234 userSub.pipe(
235 filter(user => user.role !== UserRole.ADMINISTRATOR)
236 ).subscribe(user => this.openAccountModalsIfNeeded(user))
233 } 237 }
234 238
235 private _openAdminModalsIfNeeded (serverConfig: ServerConfig, user: User) { 239 private openAdminModalsIfNeeded (user: User) {
236 if (user.noWelcomeModal !== true) return this.welcomeModal.show() 240 if (this.adminWelcomeModal.shouldOpen(user)) {
237 241 return this.adminWelcomeModal.show()
238 if (user.noInstanceConfigWarningModal === true || !serverConfig.signup.allowed) return 242 }
239 243
240 this.instanceService.getAbout() 244 if (!this.instanceConfigWarningModal.shouldOpenByUser(user)) return
241 .subscribe(about => { 245
242 if ( 246 forkJoin([
243 this.serverConfig.instance.name.toLowerCase() === 'peertube' || 247 this.serverService.getConfig().pipe(first()),
244 !about.instance.terms || 248 this.instanceService.getAbout().pipe(first())
245 !about.instance.administrator || 249 ]).subscribe(([ config, about ]) => {
246 !about.instance.maintenanceLifetime 250 if (this.instanceConfigWarningModal.shouldOpen(config, about)) {
247 ) { 251 this.instanceConfigWarningModal.show(about)
248 this.instanceConfigWarningModal.show(about) 252 }
249 } 253 })
250 }) 254 }
255
256 private openAccountModalsIfNeeded (user: User) {
257 if (this.accountSetupWarningModal.shouldOpen(user)) {
258 this.accountSetupWarningModal.show(user)
259 }
251 } 260 }
252 261
253 private initHotkeys () { 262 private initHotkeys () {
diff --git a/client/src/app/app.module.ts b/client/src/app/app.module.ts
index 8d9c39ad8..bb20c2d83 100644
--- a/client/src/app/app.module.ts
+++ b/client/src/app/app.module.ts
@@ -17,8 +17,8 @@ import { ConfirmComponent } from './modal/confirm.component'
17import { CustomModalComponent } from './modal/custom-modal.component' 17import { CustomModalComponent } from './modal/custom-modal.component'
18import { InstanceConfigWarningModalComponent } from './modal/instance-config-warning-modal.component' 18import { InstanceConfigWarningModalComponent } from './modal/instance-config-warning-modal.component'
19import { QuickSettingsModalComponent } from './modal/quick-settings-modal.component' 19import { QuickSettingsModalComponent } from './modal/quick-settings-modal.component'
20import { WelcomeModalComponent } from './modal/welcome-modal.component' 20import { AdminWelcomeModalComponent } from './modal/admin-welcome-modal.component'
21import { AccountSetupModalComponent } from './modal/account-setup-modal.component' 21import { AccountSetupWarningModalComponent } from './modal/account-setup-warning-modal.component'
22import { SharedActorImageModule } from './shared/shared-actor-image/shared-actor-image.module' 22import { SharedActorImageModule } from './shared/shared-actor-image/shared-actor-image.module'
23import { SharedFormModule } from './shared/shared-forms' 23import { SharedFormModule } from './shared/shared-forms'
24import { SharedGlobalIconModule } from './shared/shared-icons' 24import { SharedGlobalIconModule } from './shared/shared-icons'
@@ -54,9 +54,9 @@ export function loadConfigFactory (server: ServerService, pluginService: PluginS
54 SuggestionComponent, 54 SuggestionComponent,
55 HighlightPipe, 55 HighlightPipe,
56 56
57 AccountSetupModalComponent, 57 AccountSetupWarningModalComponent,
58 CustomModalComponent, 58 CustomModalComponent,
59 WelcomeModalComponent, 59 AdminWelcomeModalComponent,
60 InstanceConfigWarningModalComponent, 60 InstanceConfigWarningModalComponent,
61 ConfirmComponent 61 ConfirmComponent
62 ], 62 ],
diff --git a/client/src/app/core/users/user.model.ts b/client/src/app/core/users/user.model.ts
index 7467519c4..00078af5d 100644
--- a/client/src/app/core/users/user.model.ts
+++ b/client/src/app/core/users/user.model.ts
@@ -55,6 +55,7 @@ export class User implements UserServerModel {
55 55
56 noInstanceConfigWarningModal: boolean 56 noInstanceConfigWarningModal: boolean
57 noWelcomeModal: boolean 57 noWelcomeModal: boolean
58 noAccountSetupWarningModal: boolean
58 59
59 pluginAuth: string | null 60 pluginAuth: string | null
60 61
@@ -98,6 +99,7 @@ export class User implements UserServerModel {
98 99
99 this.noInstanceConfigWarningModal = hash.noInstanceConfigWarningModal 100 this.noInstanceConfigWarningModal = hash.noInstanceConfigWarningModal
100 this.noWelcomeModal = hash.noWelcomeModal 101 this.noWelcomeModal = hash.noWelcomeModal
102 this.noAccountSetupWarningModal = hash.noAccountSetupWarningModal
101 103
102 this.notificationSettings = hash.notificationSettings 104 this.notificationSettings = hash.notificationSettings
103 105
diff --git a/client/src/app/modal/account-setup-modal.component.ts b/client/src/app/modal/account-setup-modal.component.ts
deleted file mode 100644
index 4cb8de2c7..000000000
--- a/client/src/app/modal/account-setup-modal.component.ts
+++ /dev/null
@@ -1,73 +0,0 @@
1import { Component, ElementRef, OnInit, ViewChild } from '@angular/core'
2import { AuthService, ServerService, User, UserService } from '@app/core'
3import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'
4import { HTMLServerConfig } from '@shared/models'
5
6@Component({
7 selector: 'my-account-setup-modal',
8 templateUrl: './account-setup-modal.component.html',
9 styleUrls: [ './account-setup-modal.component.scss' ]
10})
11export class AccountSetupModalComponent implements OnInit {
12 @ViewChild('modal', { static: true }) modal: ElementRef
13
14 user: User = null
15 ref: NgbModalRef = null
16
17 private serverConfig: HTMLServerConfig
18
19 constructor (
20 private userService: UserService,
21 private authService: AuthService,
22 private modalService: NgbModal,
23 private serverService: ServerService
24 ) { }
25
26 get userInformationLoaded () {
27 return this.authService.userInformationLoaded
28 }
29
30 get instanceName () {
31 return this.serverConfig.instance.name
32 }
33
34 get isUserRoot () {
35 return this.user.username === 'root'
36 }
37
38 get hasAccountAvatar () {
39 return !!this.user.account.avatar
40 }
41
42 get hasAccountDescription () {
43 return !!this.user.account.description
44 }
45
46 ngOnInit () {
47 this.serverConfig = this.serverService.getHTMLConfig()
48
49 this.authService.userInformationLoaded
50 .subscribe(
51 () => {
52 this.user = this.authService.getUser()
53
54 if (this.isUserRoot) return
55 if (this.hasAccountAvatar && this.hasAccountDescription) return
56 if (this.userService.hasSignupInThisSession()) return
57
58 this.show()
59 }
60 )
61 }
62
63 show () {
64 if (this.ref) return
65
66 this.ref = this.modalService.open(this.modal, {
67 centered: true,
68 backdrop: 'static',
69 keyboard: false,
70 size: 'md'
71 })
72 }
73}
diff --git a/client/src/app/modal/account-setup-modal.component.html b/client/src/app/modal/account-setup-warning-modal.component.html
index d1f153835..614c0693d 100644
--- a/client/src/app/modal/account-setup-modal.component.html
+++ b/client/src/app/modal/account-setup-warning-modal.component.html
@@ -12,12 +12,18 @@
12 <p i18n>Help moderators and other users to know <strong>who you are</strong> by:</p> 12 <p i18n>Help moderators and other users to know <strong>who you are</strong> by:</p>
13 13
14 <ul> 14 <ul>
15 <li *ngIf="!hasAccountAvatar" i18n>Uploading an <strong>avatar</strong></li> 15 <li *ngIf="!hasAccountAvatar(user)" i18n>Uploading an <strong>avatar</strong></li>
16 <li *ngIf="!hasAccountDescription" i18n>Writing a <strong>description</strong></li> 16 <li *ngIf="!hasAccountDescription(user)" i18n>Writing a <strong>description</strong></li>
17 </ul> 17 </ul>
18 </div> 18 </div>
19 19
20 <div class="modal-footer inputs"> 20 <div class="modal-footer inputs">
21 <my-peertube-checkbox
22 inputName="stopDisplayModal" [(ngModel)]="stopDisplayModal"
23 i18n-labelText labelText="Don't show me this anymore"
24 >
25 </my-peertube-checkbox>
26
21 <input 27 <input
22 type="button" role="button" i18n-value value="Remind me later" class="peertube-button grey-button" 28 type="button" role="button" i18n-value value="Remind me later" class="peertube-button grey-button"
23 (click)="hide()" (key.enter)="hide()" 29 (click)="hide()" (key.enter)="hide()"
diff --git a/client/src/app/modal/account-setup-modal.component.scss b/client/src/app/modal/account-setup-warning-modal.component.scss
index d99edaf7a..d99edaf7a 100644
--- a/client/src/app/modal/account-setup-modal.component.scss
+++ b/client/src/app/modal/account-setup-warning-modal.component.scss
diff --git a/client/src/app/modal/account-setup-warning-modal.component.ts b/client/src/app/modal/account-setup-warning-modal.component.ts
new file mode 100644
index 000000000..c78de447d
--- /dev/null
+++ b/client/src/app/modal/account-setup-warning-modal.component.ts
@@ -0,0 +1,79 @@
1import { Component, ElementRef, ViewChild } from '@angular/core'
2import { Notifier, ServerService, User, UserService } from '@app/core'
3import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'
4import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage'
5
6@Component({
7 selector: 'my-account-setup-warning-modal',
8 templateUrl: './account-setup-warning-modal.component.html',
9 styleUrls: [ './account-setup-warning-modal.component.scss' ]
10})
11export class AccountSetupWarningModalComponent {
12 @ViewChild('modal', { static: true }) modal: ElementRef
13
14 stopDisplayModal = false
15 ref: NgbModalRef
16
17 user: User
18
19 private LOCAL_STORAGE_KEYS = {
20 NO_ACCOUNT_SETUP_WARNING_MODAL: 'no_account_setup_warning_modal'
21 }
22
23 constructor (
24 private userService: UserService,
25 private modalService: NgbModal,
26 private notifier: Notifier,
27 private serverService: ServerService
28 ) { }
29
30 get instanceName () {
31 return this.serverService.getHTMLConfig().instance.name
32 }
33
34 hasAccountAvatar (user: User) {
35 return !!user.account.avatar
36 }
37
38 hasAccountDescription (user: User) {
39 return !!user.account.description
40 }
41
42 shouldOpen (user: User) {
43 if (user.noAccountSetupWarningModal === true) return false
44 if (peertubeLocalStorage.getItem(this.LOCAL_STORAGE_KEYS.NO_ACCOUNT_SETUP_WARNING_MODAL) === 'true') return false
45
46 if (this.hasAccountAvatar(user) && this.hasAccountDescription(user)) return false
47 if (this.userService.hasSignupInThisSession()) return false
48
49 return true
50 }
51
52 show (user: User) {
53 this.user = user
54
55 if (this.ref) return
56
57 this.ref = this.modalService.open(this.modal, {
58 centered: true,
59 backdrop: 'static',
60 keyboard: false,
61 size: 'md'
62 })
63
64 this.ref.result.finally(() => {
65 if (this.stopDisplayModal === true) this.doNotOpenAgain()
66 })
67 }
68
69 private doNotOpenAgain () {
70 peertubeLocalStorage.setItem(this.LOCAL_STORAGE_KEYS.NO_ACCOUNT_SETUP_WARNING_MODAL, 'true')
71
72 this.userService.updateMyProfile({ noAccountSetupWarningModal: true })
73 .subscribe({
74 next: () => console.log('We will not open the account setup modal again.'),
75
76 error: err => this.notifier.error(err.message)
77 })
78 }
79}
diff --git a/client/src/app/modal/welcome-modal.component.html b/client/src/app/modal/admin-welcome-modal.component.html
index f5d2b8799..f5d2b8799 100644
--- a/client/src/app/modal/welcome-modal.component.html
+++ b/client/src/app/modal/admin-welcome-modal.component.html
diff --git a/client/src/app/modal/welcome-modal.component.scss b/client/src/app/modal/admin-welcome-modal.component.scss
index 242a498d0..242a498d0 100644
--- a/client/src/app/modal/welcome-modal.component.scss
+++ b/client/src/app/modal/admin-welcome-modal.component.scss
diff --git a/client/src/app/modal/welcome-modal.component.ts b/client/src/app/modal/admin-welcome-modal.component.ts
index 06fe4cba5..3679f0847 100644
--- a/client/src/app/modal/welcome-modal.component.ts
+++ b/client/src/app/modal/admin-welcome-modal.component.ts
@@ -1,14 +1,14 @@
1import { Component, ElementRef, ViewChild } from '@angular/core' 1import { Component, ElementRef, ViewChild } from '@angular/core'
2import { Notifier, UserService } from '@app/core' 2import { Notifier, User, UserService } from '@app/core'
3import { NgbModal } from '@ng-bootstrap/ng-bootstrap' 3import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
4import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage' 4import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage'
5 5
6@Component({ 6@Component({
7 selector: 'my-welcome-modal', 7 selector: 'my-admin-welcome-modal',
8 templateUrl: './welcome-modal.component.html', 8 templateUrl: './admin-welcome-modal.component.html',
9 styleUrls: [ './welcome-modal.component.scss' ] 9 styleUrls: [ './admin-welcome-modal.component.scss' ]
10}) 10})
11export class WelcomeModalComponent { 11export class AdminWelcomeModalComponent {
12 @ViewChild('modal', { static: true }) modal: ElementRef 12 @ViewChild('modal', { static: true }) modal: ElementRef
13 13
14 private LOCAL_STORAGE_KEYS = { 14 private LOCAL_STORAGE_KEYS = {
@@ -21,10 +21,14 @@ export class WelcomeModalComponent {
21 private notifier: Notifier 21 private notifier: Notifier
22 ) { } 22 ) { }
23 23
24 show () { 24 shouldOpen (user: User) {
25 const result = peertubeLocalStorage.getItem(this.LOCAL_STORAGE_KEYS.NO_WELCOME_MODAL) 25 if (user.noWelcomeModal === true) return false
26 if (result === 'true') return 26 if (peertubeLocalStorage.getItem(this.LOCAL_STORAGE_KEYS.NO_WELCOME_MODAL) === 'true') return false
27
28 return true
29 }
27 30
31 show () {
28 this.modalService.open(this.modal, { 32 this.modalService.open(this.modal, {
29 centered: true, 33 centered: true,
30 backdrop: 'static', 34 backdrop: 'static',
diff --git a/client/src/app/modal/instance-config-warning-modal.component.ts b/client/src/app/modal/instance-config-warning-modal.component.ts
index e40958976..300738a41 100644
--- a/client/src/app/modal/instance-config-warning-modal.component.ts
+++ b/client/src/app/modal/instance-config-warning-modal.component.ts
@@ -1,9 +1,9 @@
1import { Location } from '@angular/common' 1import { Location } from '@angular/common'
2import { Component, ElementRef, ViewChild } from '@angular/core' 2import { Component, ElementRef, ViewChild } from '@angular/core'
3import { Notifier, UserService } from '@app/core' 3import { Notifier, User, UserService } from '@app/core'
4import { NgbModal } from '@ng-bootstrap/ng-bootstrap' 4import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
5import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage' 5import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage'
6import { About } from '@shared/models/server' 6import { About, ServerConfig } from '@shared/models/server'
7 7
8@Component({ 8@Component({
9 selector: 'my-instance-config-warning-modal', 9 selector: 'my-instance-config-warning-modal',
@@ -27,10 +27,23 @@ export class InstanceConfigWarningModalComponent {
27 private notifier: Notifier 27 private notifier: Notifier
28 ) { } 28 ) { }
29 29
30 show (about: About) { 30 shouldOpenByUser (user: User) {
31 const result = peertubeLocalStorage.getItem(this.LOCAL_STORAGE_KEYS.NO_INSTANCE_CONFIG_WARNING_MODAL) 31 if (user.noInstanceConfigWarningModal === true) return false
32 if (result === 'true') return 32 if (peertubeLocalStorage.getItem(this.LOCAL_STORAGE_KEYS.NO_INSTANCE_CONFIG_WARNING_MODAL) === 'true') return false
33
34 return true
35 }
36
37 shouldOpen (serverConfig: ServerConfig, about: About) {
38 if (!serverConfig.signup.allowed) return false
33 39
40 return serverConfig.instance.name.toLowerCase() === 'peertube' ||
41 !about.instance.terms ||
42 !about.instance.administrator ||
43 !about.instance.maintenanceLifetime
44 }
45
46 show (about: About) {
34 if (this.location.path().startsWith('/admin/config/edit-custom')) return 47 if (this.location.path().startsWith('/admin/config/edit-custom')) return
35 48
36 this.about = about 49 this.about = about
diff --git a/server/controllers/api/users/me.ts b/server/controllers/api/users/me.ts
index ac6faca9c..a98d79218 100644
--- a/server/controllers/api/users/me.ts
+++ b/server/controllers/api/users/me.ts
@@ -203,6 +203,7 @@ async function updateMe (req: express.Request, res: express.Response) {
203 'videoLanguages', 203 'videoLanguages',
204 'theme', 204 'theme',
205 'noInstanceConfigWarningModal', 205 'noInstanceConfigWarningModal',
206 'noAccountSetupWarningModal',
206 'noWelcomeModal' 207 'noWelcomeModal'
207 ] 208 ]
208 209
diff --git a/server/helpers/custom-validators/users.ts b/server/helpers/custom-validators/users.ts
index 5b21c3529..f52c60b60 100644
--- a/server/helpers/custom-validators/users.ts
+++ b/server/helpers/custom-validators/users.ts
@@ -81,11 +81,7 @@ function isUserAutoPlayNextVideoPlaylistValid (value: any) {
81 return isBooleanValid(value) 81 return isBooleanValid(value)
82} 82}
83 83
84function isNoInstanceConfigWarningModal (value: any) { 84function isUserNoModal (value: any) {
85 return isBooleanValid(value)
86}
87
88function isNoWelcomeModal (value: any) {
89 return isBooleanValid(value) 85 return isBooleanValid(value)
90} 86}
91 87
@@ -119,6 +115,5 @@ export {
119 isUserAutoPlayNextVideoPlaylistValid, 115 isUserAutoPlayNextVideoPlaylistValid,
120 isUserDisplayNameValid, 116 isUserDisplayNameValid,
121 isUserDescriptionValid, 117 isUserDescriptionValid,
122 isNoInstanceConfigWarningModal, 118 isUserNoModal
123 isNoWelcomeModal
124} 119}
diff --git a/server/initializers/constants.ts b/server/initializers/constants.ts
index 8a1526ae8..2d8087f46 100644
--- a/server/initializers/constants.ts
+++ b/server/initializers/constants.ts
@@ -24,7 +24,7 @@ import { CONFIG, registerConfigChangedHandler } from './config'
24 24
25// --------------------------------------------------------------------------- 25// ---------------------------------------------------------------------------
26 26
27const LAST_MIGRATION_VERSION = 660 27const LAST_MIGRATION_VERSION = 665
28 28
29// --------------------------------------------------------------------------- 29// ---------------------------------------------------------------------------
30 30
diff --git a/server/initializers/migrations/0665-no-account-warning-modal.ts b/server/initializers/migrations/0665-no-account-warning-modal.ts
new file mode 100644
index 000000000..6718aed85
--- /dev/null
+++ b/server/initializers/migrations/0665-no-account-warning-modal.ts
@@ -0,0 +1,27 @@
1import * as Sequelize from 'sequelize'
2
3async function up (utils: {
4 transaction: Sequelize.Transaction
5 queryInterface: Sequelize.QueryInterface
6 sequelize: Sequelize.Sequelize
7 db: any
8}): Promise<void> {
9 {
10 const data = {
11 type: Sequelize.BOOLEAN,
12 allowNull: false,
13 defaultValue: false
14 }
15
16 await utils.queryInterface.addColumn('user', 'noAccountSetupWarningModal', data)
17 }
18}
19
20function down (options) {
21 throw new Error('Not implemented.')
22}
23
24export {
25 up,
26 down
27}
diff --git a/server/middlewares/validators/users.ts b/server/middlewares/validators/users.ts
index bc8607523..c6977fcd9 100644
--- a/server/middlewares/validators/users.ts
+++ b/server/middlewares/validators/users.ts
@@ -9,14 +9,13 @@ import { UserRegister } from '../../../shared/models/users/user-register.model'
9import { toBooleanOrNull, toIntOrNull } from '../../helpers/custom-validators/misc' 9import { toBooleanOrNull, toIntOrNull } from '../../helpers/custom-validators/misc'
10import { isThemeNameValid } from '../../helpers/custom-validators/plugins' 10import { isThemeNameValid } from '../../helpers/custom-validators/plugins'
11import { 11import {
12 isNoInstanceConfigWarningModal,
13 isNoWelcomeModal,
14 isUserAdminFlagsValid, 12 isUserAdminFlagsValid,
15 isUserAutoPlayNextVideoValid, 13 isUserAutoPlayNextVideoValid,
16 isUserAutoPlayVideoValid, 14 isUserAutoPlayVideoValid,
17 isUserBlockedReasonValid, 15 isUserBlockedReasonValid,
18 isUserDescriptionValid, 16 isUserDescriptionValid,
19 isUserDisplayNameValid, 17 isUserDisplayNameValid,
18 isUserNoModal,
20 isUserNSFWPolicyValid, 19 isUserNSFWPolicyValid,
21 isUserPasswordValid, 20 isUserPasswordValid,
22 isUserPasswordValidOrEmpty, 21 isUserPasswordValidOrEmpty,
@@ -251,12 +250,17 @@ const usersUpdateMeValidator = [
251 body('theme') 250 body('theme')
252 .optional() 251 .optional()
253 .custom(v => isThemeNameValid(v) && isThemeRegistered(v)).withMessage('Should have a valid theme'), 252 .custom(v => isThemeNameValid(v) && isThemeRegistered(v)).withMessage('Should have a valid theme'),
253
254 body('noInstanceConfigWarningModal') 254 body('noInstanceConfigWarningModal')
255 .optional() 255 .optional()
256 .custom(v => isNoInstanceConfigWarningModal(v)).withMessage('Should have a valid noInstanceConfigWarningModal boolean'), 256 .custom(v => isUserNoModal(v)).withMessage('Should have a valid noInstanceConfigWarningModal boolean'),
257 body('noWelcomeModal') 257 body('noWelcomeModal')
258 .optional() 258 .optional()
259 .custom(v => isNoWelcomeModal(v)).withMessage('Should have a valid noWelcomeModal boolean'), 259 .custom(v => isUserNoModal(v)).withMessage('Should have a valid noWelcomeModal boolean'),
260 body('noAccountSetupWarningModal')
261 .optional()
262 .custom(v => isUserNoModal(v)).withMessage('Should have a valid noAccountSetupWarningModal boolean'),
263
260 body('autoPlayNextVideo') 264 body('autoPlayNextVideo')
261 .optional() 265 .optional()
262 .custom(v => isUserAutoPlayNextVideoValid(v)).withMessage('Should have a valid autoPlayNextVideo boolean'), 266 .custom(v => isUserAutoPlayNextVideoValid(v)).withMessage('Should have a valid autoPlayNextVideo boolean'),
diff --git a/server/models/user/user.ts b/server/models/user/user.ts
index 069d7266e..ddce455a1 100644
--- a/server/models/user/user.ts
+++ b/server/models/user/user.ts
@@ -39,8 +39,6 @@ import { UserAdminFlag } from '../../../shared/models/users/user-flag.model'
39import { NSFWPolicyType } from '../../../shared/models/videos/nsfw-policy.type' 39import { NSFWPolicyType } from '../../../shared/models/videos/nsfw-policy.type'
40import { isThemeNameValid } from '../../helpers/custom-validators/plugins' 40import { isThemeNameValid } from '../../helpers/custom-validators/plugins'
41import { 41import {
42 isNoInstanceConfigWarningModal,
43 isNoWelcomeModal,
44 isUserAdminFlagsValid, 42 isUserAdminFlagsValid,
45 isUserAutoPlayNextVideoPlaylistValid, 43 isUserAutoPlayNextVideoPlaylistValid,
46 isUserAutoPlayNextVideoValid, 44 isUserAutoPlayNextVideoValid,
@@ -48,6 +46,7 @@ import {
48 isUserBlockedReasonValid, 46 isUserBlockedReasonValid,
49 isUserBlockedValid, 47 isUserBlockedValid,
50 isUserEmailVerifiedValid, 48 isUserEmailVerifiedValid,
49 isUserNoModal,
51 isUserNSFWPolicyValid, 50 isUserNSFWPolicyValid,
52 isUserPasswordValid, 51 isUserPasswordValid,
53 isUserRoleValid, 52 isUserRoleValid,
@@ -349,7 +348,7 @@ export class UserModel extends Model<Partial<AttributesOnly<UserModel>>> {
349 @Default(false) 348 @Default(false)
350 @Is( 349 @Is(
351 'UserNoInstanceConfigWarningModal', 350 'UserNoInstanceConfigWarningModal',
352 value => throwIfNotValid(value, isNoInstanceConfigWarningModal, 'no instance config warning modal') 351 value => throwIfNotValid(value, isUserNoModal, 'no instance config warning modal')
353 ) 352 )
354 @Column 353 @Column
355 noInstanceConfigWarningModal: boolean 354 noInstanceConfigWarningModal: boolean
@@ -357,12 +356,21 @@ export class UserModel extends Model<Partial<AttributesOnly<UserModel>>> {
357 @AllowNull(false) 356 @AllowNull(false)
358 @Default(false) 357 @Default(false)
359 @Is( 358 @Is(
360 'UserNoInstanceConfigWarningModal', 359 'UserNoWelcomeModal',
361 value => throwIfNotValid(value, isNoWelcomeModal, 'no welcome modal') 360 value => throwIfNotValid(value, isUserNoModal, 'no welcome modal')
362 ) 361 )
363 @Column 362 @Column
364 noWelcomeModal: boolean 363 noWelcomeModal: boolean
365 364
365 @AllowNull(false)
366 @Default(false)
367 @Is(
368 'UserNoAccountSetupWarningModal',
369 value => throwIfNotValid(value, isUserNoModal, 'no account setup warning modal')
370 )
371 @Column
372 noAccountSetupWarningModal: boolean
373
366 @AllowNull(true) 374 @AllowNull(true)
367 @Default(null) 375 @Default(null)
368 @Column 376 @Column
@@ -920,6 +928,7 @@ export class UserModel extends Model<Partial<AttributesOnly<UserModel>>> {
920 928
921 noInstanceConfigWarningModal: this.noInstanceConfigWarningModal, 929 noInstanceConfigWarningModal: this.noInstanceConfigWarningModal,
922 noWelcomeModal: this.noWelcomeModal, 930 noWelcomeModal: this.noWelcomeModal,
931 noAccountSetupWarningModal: this.noAccountSetupWarningModal,
923 932
924 blocked: this.blocked, 933 blocked: this.blocked,
925 blockedReason: this.blockedReason, 934 blockedReason: this.blockedReason,
diff --git a/server/tests/api/check-params/users.ts b/server/tests/api/check-params/users.ts
index 9d8f933db..58b360f92 100644
--- a/server/tests/api/check-params/users.ts
+++ b/server/tests/api/check-params/users.ts
@@ -491,20 +491,20 @@ describe('Test users API validators', function () {
491 await makePutBodyRequest({ url: server.url, path: path + 'me', token: userToken, fields }) 491 await makePutBodyRequest({ url: server.url, path: path + 'me', token: userToken, fields })
492 }) 492 })
493 493
494 it('Should fail with an invalid noInstanceConfigWarningModal attribute', async function () { 494 it('Should fail with invalid no modal attributes', async function () {
495 const fields = { 495 const keys = [
496 noInstanceConfigWarningModal: -1 496 'noInstanceConfigWarningModal',
497 } 497 'noAccountSetupWarningModal',
498 498 'noWelcomeModal'
499 await makePutBodyRequest({ url: server.url, path: path + 'me', token: userToken, fields }) 499 ]
500 }) 500
501 for (const key of keys) {
502 const fields = {
503 [key]: -1
504 }
501 505
502 it('Should fail with an invalid noWelcomeModal attribute', async function () { 506 await makePutBodyRequest({ url: server.url, path: path + 'me', token: userToken, fields })
503 const fields = {
504 noWelcomeModal: -1
505 } 507 }
506
507 await makePutBodyRequest({ url: server.url, path: path + 'me', token: userToken, fields })
508 }) 508 })
509 509
510 it('Should succeed to change password with the correct params', async function () { 510 it('Should succeed to change password with the correct params', async function () {
@@ -516,7 +516,8 @@ describe('Test users API validators', function () {
516 email: 'super_email@example.com', 516 email: 'super_email@example.com',
517 theme: 'default', 517 theme: 'default',
518 noInstanceConfigWarningModal: true, 518 noInstanceConfigWarningModal: true,
519 noWelcomeModal: true 519 noWelcomeModal: true,
520 noAccountSetupWarningModal: true
520 } 521 }
521 522
522 await makePutBodyRequest({ 523 await makePutBodyRequest({
diff --git a/server/tests/api/users/users.ts b/server/tests/api/users/users.ts
index 318ff832a..085d9d870 100644
--- a/server/tests/api/users/users.ts
+++ b/server/tests/api/users/users.ts
@@ -597,6 +597,7 @@ describe('Test users', function () {
597 expect(user.account.description).to.equal('my super description updated') 597 expect(user.account.description).to.equal('my super description updated')
598 expect(user.noWelcomeModal).to.be.false 598 expect(user.noWelcomeModal).to.be.false
599 expect(user.noInstanceConfigWarningModal).to.be.false 599 expect(user.noInstanceConfigWarningModal).to.be.false
600 expect(user.noAccountSetupWarningModal).to.be.false
600 }) 601 })
601 602
602 it('Should be able to update my theme', async function () { 603 it('Should be able to update my theme', async function () {
@@ -612,12 +613,14 @@ describe('Test users', function () {
612 await server.users.updateMe({ 613 await server.users.updateMe({
613 token: userToken, 614 token: userToken,
614 noInstanceConfigWarningModal: true, 615 noInstanceConfigWarningModal: true,
615 noWelcomeModal: true 616 noWelcomeModal: true,
617 noAccountSetupWarningModal: true
616 }) 618 })
617 619
618 const user = await server.users.getMyInfo({ token: userToken }) 620 const user = await server.users.getMyInfo({ token: userToken })
619 expect(user.noWelcomeModal).to.be.true 621 expect(user.noWelcomeModal).to.be.true
620 expect(user.noInstanceConfigWarningModal).to.be.true 622 expect(user.noInstanceConfigWarningModal).to.be.true
623 expect(user.noAccountSetupWarningModal).to.be.true
621 }) 624 })
622 }) 625 })
623 626
diff --git a/shared/models/users/user-update-me.model.ts b/shared/models/users/user-update-me.model.ts
index db6539cd2..6d7df38fb 100644
--- a/shared/models/users/user-update-me.model.ts
+++ b/shared/models/users/user-update-me.model.ts
@@ -20,4 +20,5 @@ export interface UserUpdateMe {
20 20
21 noInstanceConfigWarningModal?: boolean 21 noInstanceConfigWarningModal?: boolean
22 noWelcomeModal?: boolean 22 noWelcomeModal?: boolean
23 noAccountSetupWarningModal?: boolean
23} 24}
diff --git a/shared/models/users/user.model.ts b/shared/models/users/user.model.ts
index 7a982c2e5..78870c556 100644
--- a/shared/models/users/user.model.ts
+++ b/shared/models/users/user.model.ts
@@ -51,6 +51,7 @@ export interface User {
51 51
52 noInstanceConfigWarningModal: boolean 52 noInstanceConfigWarningModal: boolean
53 noWelcomeModal: boolean 53 noWelcomeModal: boolean
54 noAccountSetupWarningModal: boolean
54 55
55 createdAt: Date 56 createdAt: Date
56 57
diff --git a/support/doc/api/openapi.yaml b/support/doc/api/openapi.yaml
index 76e78fe53..65a2ac897 100644
--- a/support/doc/api/openapi.yaml
+++ b/support/doc/api/openapi.yaml
@@ -6376,6 +6376,8 @@ components:
6376 format: date-time 6376 format: date-time
6377 noInstanceConfigWarningModal: 6377 noInstanceConfigWarningModal:
6378 type: boolean 6378 type: boolean
6379 noAccountSetupWarningModal:
6380 type: boolean
6379 noWelcomeModal: 6381 noWelcomeModal:
6380 type: boolean 6382 type: boolean
6381 nsfwPolicy: 6383 nsfwPolicy:
@@ -6530,6 +6532,8 @@ components:
6530 type: string 6532 type: string
6531 noInstanceConfigWarningModal: 6533 noInstanceConfigWarningModal:
6532 type: boolean 6534 type: boolean
6535 noAccountSetupWarningModal:
6536 type: boolean
6533 noWelcomeModal: 6537 noWelcomeModal:
6534 type: boolean 6538 type: boolean
6535 GetMeVideoRating: 6539 GetMeVideoRating: