Add about information in registration page
authorChocobozzz <me@florianbigard.com>
Tue, 27 Aug 2019 15:09:43 +0000 (17:09 +0200)
committerChocobozzz <chocobozzz@cpy.re>
Thu, 5 Sep 2019 08:17:02 +0000 (10:17 +0200)
22 files changed:
client/src/app/+about/about-instance/about-instance.component.ts
client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.html
client/src/app/+my-account/my-account-settings/my-account-video-settings/my-account-video-settings.component.html
client/src/app/+signup/+register/register-step-user.component.html
client/src/app/+signup/+register/register-step-user.component.ts
client/src/app/+signup/+register/register.component.html
client/src/app/+signup/+register/register.component.scss
client/src/app/+signup/+register/register.component.ts
client/src/app/+signup/+register/register.module.ts
client/src/app/login/login.component.html
client/src/app/shared/angular/peertube-template.directive.ts
client/src/app/shared/forms/peertube-checkbox.component.html
client/src/app/shared/forms/peertube-checkbox.component.ts
client/src/app/shared/instance/instance-features-table.component.html
client/src/app/shared/instance/instance.service.ts
client/src/app/shared/misc/help.component.html
client/src/app/shared/misc/help.component.ts
client/src/app/shared/user-subscription/remote-subscribe.component.html
client/src/app/shared/video/videos-selection.component.ts
client/src/app/videos/+video-edit/shared/video-edit.component.html
client/src/app/videos/+video-edit/video-add-components/video-import-torrent.component.html
client/src/app/videos/+video-edit/video-add-components/video-import-url.component.html

index 0af1dca9cc9710675a9e124f0886acfe8b8e5174..e2c4485017d9471b8fd3284ac6c1c498c378de5a 100644 (file)
@@ -6,7 +6,6 @@ import { InstanceService } from '@app/shared/instance/instance.service'
 import { MarkdownService } from '@app/shared/renderer'
 import { forkJoin } from 'rxjs'
 import { first } from 'rxjs/operators'
-import { peertubeTranslate } from '@shared/models'
 
 @Component({
   selector: 'my-about-instance',
@@ -59,32 +58,16 @@ export class AboutInstanceComponent implements OnInit {
       this.serverService.videoLanguagesLoaded.pipe(first()),
       this.serverService.videoCategoriesLoaded.pipe(first())
     ]).subscribe(
-      async ([ res, translations ]) => {
-        this.shortDescription = res.instance.shortDescription
+      async ([ about, translations ]) => {
+        this.shortDescription = about.instance.shortDescription
 
-        this.maintenanceLifetime = res.instance.maintenanceLifetime
-        this.businessModel = res.instance.businessModel
+        this.maintenanceLifetime = about.instance.maintenanceLifetime
+        this.businessModel = about.instance.businessModel
 
-        for (const key of [ 'description', 'terms', 'codeOfConduct', 'moderationInformation', 'administrator' ]) {
-          this.html[ key ] = await this.markdownService.textMarkdownToHTML(res.instance[ key ])
-        }
+        this.html = await this.instanceService.buildHtml(about)
 
-        const languagesArray = this.serverService.getVideoLanguages()
-        const categoriesArray = this.serverService.getVideoCategories()
-
-        this.languages = res.instance.languages
-                            .map(l => {
-                              const languageObj = languagesArray.find(la => la.id === l)
-
-                              return peertubeTranslate(languageObj.label, translations)
-                            })
-
-        this.categories = res.instance.categories
-                             .map(c => {
-                               const categoryObj = categoriesArray.find(ca => ca.id === c)
-
-                               return peertubeTranslate(categoryObj.label, translations)
-                             })
+        this.languages = this.instanceService.buildTranslatedLanguages(about, translations)
+        this.categories = this.instanceService.buildTranslatedCategories(about, translations)
       },
 
       () => this.notifier.error(this.i18n('Cannot get about information from server'))
index 50df8a8ac4fd65c02ff04c797588f2c705932ac9..5aa6fda3cea39cbadc06065bb92215d6e0499263 100644 (file)
           <div i18n class="inner-form-title">Moderation & NSFW</div>
 
           <div class="form-group">
-            <my-peertube-checkbox
-              inputName="instanceIsNSFW" formControlName="isNSFW"
-              i18n-labelText labelText="This instance is dedicated to sensitive or NSFW content"
-              i18n-helpHtml helpHtml="Enabling it will allow other administrators to know that you are mainly federating sensitive content.<br /><br />
-              Moreover, the NSFW checkbox on video upload will be automatically checked by default."
-            ></my-peertube-checkbox>
+            <my-peertube-checkbox inputName="instanceIsNSFW" formControlName="isNSFW">
+              <ng-template ptTemplate="label">
+                <ng-container i18n>This instance is dedicated to sensitive or NSFW content</ng-container>
+              </ng-template>
+
+              <ng-template ptTemplate="help">
+                <ng-container i18n>
+                  Enabling it will allow other administrators to know that you are mainly federating sensitive content.<br /><br />
+                  Moreover, the NSFW checkbox on video upload will be automatically checked by default.
+                </ng-container>
+              </ng-template>
+            </my-peertube-checkbox>
           </div>
 
           <div class="form-group">
             <label i18n for="instanceDefaultNSFWPolicy">Policy on videos containing sensitive content</label>
-            <my-help
-              helpType="custom" i18n-customHtml
-              customHtml="With <strong>Do not list</strong> or <strong>Blur thumbnails</strong>, a confirmation will be requested to watch the video."
-            ></my-help>
+
+            <my-help>
+              <ng-template ptTemplate="customHtml">
+                <ng-container i18n>
+                  With <strong>Do not list</strong> or <strong>Blur thumbnails</strong>, a confirmation will be requested to watch the video.
+                </ng-container>
+              </ng-template>
+            </my-help>
 
             <div class="peertube-select-container">
               <select id="instanceDefaultNSFWPolicy" formControlName="defaultNSFWPolicy">
 
             <div class="form-group">
               <label i18n for="signupLimit">Your Twitter username</label>
-              <my-help
-                helpType="custom" i18n-customHtml
-                customHtml="Indicates the Twitter account for the website or platform on which the content was published."
-              ></my-help>
+
+              <my-help>
+                <ng-template ptTemplate="customHtml">
+                  <ng-container i18n>Indicates the Twitter account for the website or platform on which the content was published.</ng-container>
+                </ng-template>
+              </my-help>
+
               <input
                 type="text" id="servicesTwitterUsername"
                 formControlName="username" [ngClass]="{ 'input-error': formErrors['services.twitter.username'] }"
             </div>
 
             <div class="form-group">
-              <my-peertube-checkbox
-                inputName="servicesTwitterWhitelisted" formControlName="whitelisted"
-                i18n-labelText labelText="Instance whitelisted by Twitter"
-                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 />
-        If the instance is not whitelisted, we use an image link card that will redirect on your PeerTube instance.<br /><br />
-        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."
-              ></my-peertube-checkbox>
+              <my-peertube-checkbox inputName="servicesTwitterWhitelisted" formControlName="whitelisted">
+                <ng-template ptTemplate="label">
+                  <ng-container i18n>Instance whitelisted by Twitter</ng-container>
+                </ng-template>
+
+                <ng-template ptTemplate="help">
+                  <ng-container i18n>
+                    If your instance is whitelisted by Twitter, a video player will be embedded in the Twitter feed on PeerTube video share.<br />
+                    If the instance is not whitelisted, we use an image link card that will redirect on your PeerTube instance.<br /><br />
+                    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.
+                  </ng-container>
+                </ng-template>
+              </my-peertube-checkbox>
             </div>
 
           </ng-container>
 
         <ng-container formGroupName="transcoding">
           <div class="form-group">
-            <my-peertube-checkbox
-              inputName="transcodingEnabled" formControlName="enabled"
-              i18n-labelText labelText="Transcoding enabled"
-              i18n-helpHtml helpHtml="If you disable transcoding, many videos from your users will not work!"
-            ></my-peertube-checkbox>
+            <my-peertube-checkbox inputName="transcodingEnabled" formControlName="enabled">
+              <ng-template ptTemplate="label">
+                <ng-container i18n>Transcoding enabled</ng-container>
+              </ng-template>
+
+              <ng-template ptTemplate="help">
+                <ng-container i18n>If you disable transcoding, many videos from your users will not work!</ng-container>
+              </ng-template>
+            </my-peertube-checkbox>
           </div>
 
           <ng-container *ngIf="isTranscodingEnabled()">
               <my-peertube-checkbox
                 inputName="transcodingAllowAdditionalExtensions" formControlName="allowAdditionalExtensions"
                 i18n-labelText labelText="Allow additional extensions"
-                i18n-helpHtml helpHtml="Allow your users to upload .mkv, .mov, .avi, .flv videos"
-              ></my-peertube-checkbox>
+              >
+                <ng-template ptTemplate="help">
+                  <ng-container i18n>Allow your users to upload .mkv, .mov, .avi, .flv videos</ng-container>
+                </ng-template>
+              </my-peertube-checkbox>
             </div>
 
             <div class="form-group">
               <my-peertube-checkbox
                 inputName="transcodingAllowAudioFiles" formControlName="allowAudioFiles"
                 i18n-labelText labelText="Allow audio files upload"
-                i18n-helpHtml helpHtml="Allow your users to upload audio files that will be merged with the preview file on upload"
-              ></my-peertube-checkbox>
+              >
+                <ng-template ptTemplate="help">
+                  <ng-container i18n>Allow your users to upload audio files that will be merged with the preview file on upload</ng-container>
+                </ng-template>
+              </my-peertube-checkbox>
             </div>
 
             <div class="form-group">
         <div i18n class="inner-form-title">
           Cache
 
-          <my-help
-            helpType="custom" i18n-customHtml
-            customHtml="Some files are not federated (previews, captions). We fetch them directly from the origin instance and cache them."
-          ></my-help>
+          <my-help>
+            <ng-template ptTemplate="customHtml">
+              <ng-container i18n>Some files are not federated (previews, captions). We fetch them directly from the origin instance and cache them.</ng-container>
+            </ng-template>
+          </my-help>
         </div>
 
         <ng-container formGroupName="cache">
           <ng-container formGroupName="customizations">
             <div class="form-group">
               <label i18n for="customizationJavascript">JavaScript</label>
-              <my-help
-                helpType="custom" i18n-customHtml
-                customHtml="Write directly JavaScript code.<br />Example: <pre>console.log('my instance is amazing');</pre>"
-              ></my-help>
+              <my-help>
+                <ng-template ptTemplate="customHtml">
+                  <ng-container i18n>
+                    Write directly JavaScript code.<br />Example: <pre>console.log('my instance is amazing');</pre>
+                  </ng-container>
+                </ng-template>
+              </my-help>
+
               <textarea
                 id="customizationJavascript" formControlName="javascript"
                 [ngClass]="{ 'input-error': formErrors['instance.customizations.javascript'] }"
               ></textarea>
+
               <div *ngIf="formErrors.instance.customizations.javascript" class="form-error">{{ formErrors.instance.customizations.javascript }}</div>
             </div>
 
             <div class="form-group">
               <label for="customizationCSS">CSS</label>
-              <my-help
-                  helpType="custom"
-                  i18n-customHtml
-                  customHtml="
+
+              <my-help>
+                <ng-template ptTemplate="customHtml">
+                  <ng-container i18n>
                     Write directly CSS code. Example:<br /><br />
-                    <pre>
-  #custom-css {{ '{' }}
-    color: red;
-  {{ '}' }}
-                    </pre>
+<pre>
+#custom-css {{ '{' }}
+  color: red;
+{{ '}' }}
+</pre>
 
                     Prepend with <em>#custom-css</em> to override styles. Example:<br /><br />
-                    <pre>
-  #custom-css .logged-in-email {{ '{' }}
-    color: red;
-  {{ '}' }}
-                    </pre>
-                  "
-              ></my-help>
+<pre>
+#custom-css .logged-in-email {{ '{' }}
+  color: red;
+{{ '}' }}
+</pre>
+                  </ng-container>
+                </ng-template>
+              </my-help>
+
               <textarea
                 id="customizationCSS" formControlName="css"
                 [ngClass]="{ 'input-error': formErrors['instance.customizations.css'] }"
index caa032149c8d04ca6b6649ad4437bdbeaa69c8b5..a11238925da04f521331278a6d55cb29d2d407e2 100644 (file)
@@ -1,10 +1,13 @@
 <form role="form" (ngSubmit)="updateDetails()" [formGroup]="form">
   <div class="form-group">
     <label i18n for="nsfwPolicy">Default policy on videos containing sensitive content</label>
-    <my-help
-      helpType="custom" i18n-customHtml
-      customHtml="With <strong>Do not list</strong> or <strong>Blur thumbnails</strong>, a confirmation will be requested to watch the video."
-    ></my-help>
+    <my-help>
+      <ng-template ptTemplate="customHtml">
+        <ng-container i18n>
+          With <strong>Do not list</strong> or <strong>Blur thumbnails</strong>, a confirmation will be requested to watch the video.
+        </ng-container>
+      </ng-template>
+    </my-help>
 
     <div class="peertube-select-container">
       <select id="nsfwPolicy" formControlName="nsfwPolicy">
 
   <div class="form-group">
     <label i18n for="videoLanguages">Only display videos in the following languages</label>
-    <my-help i18n-customHtml
-             customHtml="In Recently added, Trending, Local and Search pages"
-    ></my-help>
+    <my-help>
+      <ng-template ptTemplate="customHtml">
+        <ng-container i18n>In Recently added, Trending, Local and Search pages</ng-container>
+      </ng-template>
+    </my-help>
 
     <div>
       <p-multiSelect
index 47b3be8cc5a19ffe4146b8b2fec698bd660c8e1e..4381702ae525be0255005b466a236f2f1a574d92 100644 (file)
   </div>
 
   <div class="form-group form-group-terms">
-    <my-peertube-checkbox
-      inputName="terms" formControlName="terms"
-      i18n-labelHtml
-      labelHtml="I am at least 16 years old and agree to the <a href='/about/instance#terms-section' target='_blank'rel='noopener noreferrer'>Terms</a> of this instance"
-    ></my-peertube-checkbox>
+    <my-peertube-checkbox inputName="terms" formControlName="terms">
+      <ng-template ptTemplate="label">
+        <ng-container i18n>
+          I am at least 16 years old and agree
+          to the <a (click)="onTermsClick($event)" href='#'>Terms</a>
+          <ng-container *ngIf="hasCodeOfConduct"> and to the <a (click)="onCodeOfConductClick($event)" href='#'>Code of Conduct</a></ng-container>
+          of this instance
+        </ng-container>
+      </ng-template>
+    </my-peertube-checkbox>
 
     <div *ngIf="formErrors.terms" class="form-error">
       {{ formErrors.terms }}
index 3b71fd3c4139028536dbeba4fc626c0ccb5e452d..6c96f20b448af548bd9112a2fb828c37e822bb0b 100644 (file)
@@ -1,4 +1,4 @@
-import { Component, EventEmitter, OnInit, Output } from '@angular/core'
+import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'
 import { AuthService } from '@app/core'
 import { FormReactive, UserService, UserValidatorsService } from '@app/shared'
 import { FormValidatorService } from '@app/shared/forms/form-validators/form-validator.service'
@@ -12,7 +12,11 @@ import { concat, of } from 'rxjs'
   styleUrls: [ './register.component.scss' ]
 })
 export class RegisterStepUserComponent extends FormReactive implements OnInit {
+  @Input() hasCodeOfConduct = false
+
   @Output() formBuilt = new EventEmitter<FormGroup>()
+  @Output() termsClick = new EventEmitter<void>()
+  @Output() codeOfConductClick = new EventEmitter<void>()
 
   constructor (
     protected formValidatorService: FormValidatorService,
@@ -45,6 +49,16 @@ export class RegisterStepUserComponent extends FormReactive implements OnInit {
      .subscribe(([ oldValue, newValue ]) => this.onDisplayNameChange(oldValue, newValue))
   }
 
+  onTermsClick (event: Event) {
+    event.preventDefault()
+    this.termsClick.emit()
+  }
+
+  onCodeOfConductClick (event: Event) {
+    event.preventDefault()
+    this.codeOfConductClick.emit()
+  }
+
   private onDisplayNameChange (oldDisplayName: string, newDisplayName: string) {
     const username = this.form.value['username'] || ''
 
index d7e47c1a80bf6beab46a6247b736194811e360d5..e7440fe1e02fc04c3898c8bfeb4b757a494f9687 100644 (file)
@@ -7,11 +7,15 @@
   <my-signup-success *ngIf="signupDone" [message]="success"></my-signup-success>
   <div *ngIf="info" class="alert alert-info">{{ info }}</div>
 
-  <div class="wrapper" *ngIf="!signupDone">
-    <div>
+  <div class="wrapper" [hidden]="signupDone">
+    <div class="register-form">
       <my-custom-stepper linear *ngIf="!signupDone">
         <cdk-step [stepControl]="formStepUser" i18n-label label="User information">
-          <my-register-step-user (formBuilt)="onUserFormBuilt($event)"></my-register-step-user>
+          <my-register-step-user
+            [hasCodeOfConduct]="!!aboutHtml.codeOfConduct"
+            (formBuilt)="onUserFormBuilt($event)" (termsClick)="onTermsClick()" (codeOfConductClick)="onCodeOfConductClick()"
+          >
+          </my-register-step-user>
 
           <button i18n cdkStepperNext [disabled]="!formStepUser || !formStepUser.valid">Next</button>
         </cdk-step>
       </my-custom-stepper>
     </div>
 
-    <div>
-      <label i18n>Features found on this instance</label>
-      <my-instance-features-table></my-instance-features-table>
+    <div class="instance-information">
+      <ngb-accordion [closeOthers]="true" #accordion="ngbAccordion">
+        <ngb-panel id="instance-features" i18n-title title="Features found on this instance">
+          <ng-template ngbPanelContent>
+            <my-instance-features-table></my-instance-features-table>
+          </ng-template>
+        </ngb-panel>
+
+        <ng-container *ngIf="about">
+          <ngb-panel
+            *ngIf="aboutHtml.administrator || about.instance.maintenanceLifetime || about.instance.businessModel"
+            id="admin-sustainability" i18n-title title="Administrators & Sustainability"
+          >
+            <ng-template ngbPanelContent>
+              <div class="block">
+                <strong i18n>Who are we?</strong>
+                <div [innerHTML]="aboutHtml.administrator"></div>
+              </div>
+
+              <div class="block">
+                <strong i18n>How long do we plan to maintain this instance?</strong>
+                <div [innerHTML]="about.instance.maintenanceLifetime"></div>
+              </div>
+
+              <div class="block">
+                <strong i18n>How will we pay this instance?</strong>
+                <div [innerHTML]="about.instance.businessModel"></div>
+              </div>
+            </ng-template>
+          </ngb-panel>
+
+          <ngb-panel *ngIf="aboutHtml.moderationInformation" id="moderation-information" i18n-title title="Moderation information">
+            <ng-template ngbPanelContent>
+              <div class="block" [innerHTML]="aboutHtml.moderationInformation"></div>
+            </ng-template>
+          </ngb-panel>
+
+          <ngb-panel *ngIf="aboutHtml.codeOfConduct" id="code-of-conduct" i18n-title title="Code of conduct">
+            <ng-template ngbPanelContent>
+              <div class="block" [innerHTML]="aboutHtml.codeOfConduct"></div>
+            </ng-template>
+          </ngb-panel>
+
+          <ngb-panel *ngIf="aboutHtml.terms" id="terms" i18n-title title="Terms">
+            <ng-template ngbPanelContent>
+              <div class="block" [innerHTML]="aboutHtml.terms"></div>
+            </ng-template>
+          </ngb-panel>
+        </ng-container>
+      </ngb-accordion>
     </div>
   </div>
 
index 9405b5293c5eee49d4171bf08c6b313f4b9f7428..2f62dd59db594a39eb87f44a0aba61ce44edbaf6 100644 (file)
@@ -1,5 +1,9 @@
 @import '_variables';
 @import '_mixins';
+@import "./_bootstrap-variables";
+
+@import '~bootstrap/scss/functions';
+@import '~bootstrap/scss/variables';
 
 .alert {
   font-size: 15px;
 
   & > div {
     margin-bottom: 40px;
-    width: 450px;
+
+    &.register-form {
+      width: 450px;
+    }
+
+    &.instance-information {
+      width: 600px;
+      margin-bottom: 40px;
+
+      .block {
+        font-size: 15px;
+        margin-bottom: 15px;
+        padding: 0 $btn-padding-x;
+      }
+
+      @media screen and (max-width: 1500px) {
+        width: 450px;
+      }
+
+      ngb-accordion ::ng-deep {
+        .btn {
+          font-weight: $font-semibold !important;
+          color: var(--mainForegroundColor) !important;
+        }
+      }
+    }
 
     @media screen and (max-width: 500px) {
       width: auto;
   }
 }
 
-my-instance-features-table {
-  display: block;
-
-  margin-bottom: 40px;
-}
-
 .form-group-terms {
   margin: 30px 0;
 }
index cd605972842e9f7c471c7ce8ad7e68d857a9b02b..d470ef4dcc1830f5f1e1f9ea4bc37f34ee5ffb9a 100644 (file)
@@ -1,21 +1,35 @@
-import { Component } from '@angular/core'
+import { Component, OnInit, ViewChild } from '@angular/core'
 import { AuthService, Notifier, RedirectService, ServerService } from '@app/core'
 import { UserService, UserValidatorsService } from '@app/shared'
 import { I18n } from '@ngx-translate/i18n-polyfill'
 import { UserRegister } from '@shared/models/users/user-register.model'
 import { FormGroup } from '@angular/forms'
+import { About } from '@shared/models/server'
+import { InstanceService } from '@app/shared/instance/instance.service'
+import { NgbAccordion } from '@ng-bootstrap/ng-bootstrap'
 
 @Component({
   selector: 'my-register',
   templateUrl: './register.component.html',
   styleUrls: [ './register.component.scss' ]
 })
-export class RegisterComponent {
+export class RegisterComponent implements OnInit {
+  @ViewChild('accordion', { static: true }) accordion: NgbAccordion
+
   info: string = null
   error: string = null
   success: string = null
   signupDone = false
 
+  about: About
+  aboutHtml = {
+    description: '',
+    terms: '',
+    codeOfConduct: '',
+    moderationInformation: '',
+    administrator: ''
+  }
+
   formStepUser: FormGroup
   formStepChannel: FormGroup
 
@@ -26,6 +40,7 @@ export class RegisterComponent {
     private userService: UserService,
     private serverService: ServerService,
     private redirectService: RedirectService,
+    private instanceService: InstanceService,
     private i18n: I18n
   ) {
   }
@@ -34,6 +49,19 @@ export class RegisterComponent {
     return this.serverService.getConfig().signup.requiresEmailVerification
   }
 
+  ngOnInit (): void {
+    this.instanceService.getAbout()
+      .subscribe(
+        async about => {
+          this.about = about
+
+          this.aboutHtml = await this.instanceService.buildHtml(about)
+        },
+
+        err => this.notifier.error(err.message)
+      )
+  }
+
   hasSameChannelAndAccountNames () {
     return this.getUsername() === this.getChannelName()
   }
@@ -58,6 +86,14 @@ export class RegisterComponent {
     this.formStepChannel = form
   }
 
+  onTermsClick () {
+    if (this.accordion) this.accordion.toggle('terms')
+  }
+
+  onCodeOfConductClick () {
+    if (this.accordion) this.accordion.toggle('code-of-conduct')
+  }
+
   signup () {
     this.error = null
 
index 46336cbd00e1c2a3fb3cdb861db8acd8aa6bc34a..e55f83990fcb41a90d23139656ba7c4f066b7295 100644 (file)
@@ -7,13 +7,15 @@ import { RegisterStepChannelComponent } from './register-step-channel.component'
 import { RegisterStepUserComponent } from './register-step-user.component'
 import { CustomStepperComponent } from './custom-stepper.component'
 import { SignupSharedModule } from '@app/+signup/shared/signup-shared.module'
+import { NgbAccordionModule } from '@ng-bootstrap/ng-bootstrap'
 
 @NgModule({
   imports: [
     RegisterRoutingModule,
     SharedModule,
     CdkStepperModule,
-    SignupSharedModule
+    SignupSharedModule,
+    NgbAccordionModule
   ],
 
   declarations: [
index 4efe3fb222600fa02ce820f7a0b39a0e81b916be..6833559600feefd0fe26165231136246a4d5a108 100644 (file)
           or create an account on another instance
         </a>
 
-        <my-help
-          *ngIf="signupAllowed === false" helpType="custom" i18n-customHtml
-          customHtml="User registration is not allowed on this instance, but you can register on many others!"
-        ></my-help>
+        <my-help *ngIf="signupAllowed === false">
+          <ng-template ptTemplate="customHtml">
+            <ng-container i18n>User registration is not allowed on this instance, but you can register on many others!</ng-container>
+          </ng-template>
+        </my-help>
       </div>
 
       <div *ngIf="formErrors.username" class="form-error">
index a514b6057c38d2d68ee3d0e016d2ca572ef1ac22..e04c25d9a20fbf0c1cb7ffbe9a65f5b95ddc3936 100644 (file)
@@ -3,8 +3,8 @@ import { Directive, Input, TemplateRef } from '@angular/core'
 @Directive({
   selector: '[ptTemplate]'
 })
-export class PeerTubeTemplateDirective {
-  @Input('ptTemplate') name: string
+export class PeerTubeTemplateDirective <T extends string> {
+  @Input('ptTemplate') name: T
 
   constructor (public template: TemplateRef<any>) {
     // empty
index 571a1a673d75484591c8880ebc116315130b59a0..f1e3bf0bf586ea4a9222fc8da0911b5d0794e848 100644 (file)
@@ -3,8 +3,15 @@
     <input type="checkbox" [(ngModel)]="checked" (ngModelChange)="onModelChange()" [id]="inputName" [disabled]="disabled" />
     <span role="checkbox" [attr.aria-checked]="checked"></span>
     <span *ngIf="labelText">{{ labelText }}</span>
-    <span *ngIf="labelHtml" [innerHTML]="labelHtml"></span>
+
+    <span *ngIf="labelTemplate">
+      <ng-container *ngTemplateOutlet="labelTemplate"></ng-container>
+    </span>
   </label>
 
-  <my-help *ngIf="helpHtml" [tooltipPlacement]="helpPlacement" helpType="custom" i18n-customHtml [customHtml]="helpHtml"></my-help>
+  <my-help *ngIf="helpTemplate" [tooltipPlacement]="helpPlacement" helpType="custom">
+    <ng-template ptTemplate="customHtml">
+      <ng-template *ngTemplateOutlet="helpTemplate"></ng-template>
+    </ng-template>
+  </my-help>
 </div>
index a4b72aa37ad0c3c50bb521db0e8076f49c5e7b8f..3b8f39ed080110a9dd0a8c458962f3942f620e39 100644 (file)
@@ -1,5 +1,6 @@
-import { ChangeDetectorRef, Component, forwardRef, Input, OnChanges, SimpleChanges } from '@angular/core'
+import { AfterContentInit, ChangeDetectorRef, Component, ContentChildren, forwardRef, Input, QueryList, TemplateRef } from '@angular/core'
 import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'
+import { PeerTubeTemplateDirective } from '@app/shared/angular/peertube-template.directive'
 
 @Component({
   selector: 'my-peertube-checkbox',
@@ -13,20 +14,35 @@ import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'
     }
   ]
 })
-export class PeertubeCheckboxComponent implements ControlValueAccessor {
+export class PeertubeCheckboxComponent implements ControlValueAccessor, AfterContentInit {
   @Input() checked = false
   @Input() inputName: string
   @Input() labelText: string
-  @Input() labelHtml: string
-  @Input() helpHtml: string
   @Input() helpPlacement = 'top'
   @Input() disabled = false
 
+  @ContentChildren(PeerTubeTemplateDirective) templates: QueryList<PeerTubeTemplateDirective<'label' | 'help'>>
+
   // FIXME: https://github.com/angular/angular/issues/10816#issuecomment-307567836
   @Input() onPushWorkaround = false
 
+  labelTemplate: TemplateRef<any>
+  helpTemplate: TemplateRef<any>
+
   constructor (private cdr: ChangeDetectorRef) { }
 
+  ngAfterContentInit () {
+    {
+      const t = this.templates.find(t => t.name === 'label')
+      if (t) this.labelTemplate = t.template
+    }
+
+    {
+      const t = this.templates.find(t => t.name === 'help')
+      if (t) this.helpTemplate = t.template
+    }
+  }
+
   propagateChange = (_: any) => { /* empty */ }
 
   writeValue (checked: boolean) {
index 845876f5564fd158f6aaa0f2fc9480471dffc220..d1cb8fcbe55a4ec30b39930231ff800234a79691 100644 (file)
         <ng-container *ngIf="initialUserVideoQuota !== -1">
           {{ initialUserVideoQuota | bytes: 0 }} <ng-container *ngIf="dailyUserVideoQuota !== -1">({{ dailyUserVideoQuota | bytes: 0 }} per day)</ng-container>
 
-          <my-help tooltipPlacement="auto" helpType="custom" [customHtml]="quotaHelpIndication"></my-help>
+          <my-help tooltipPlacement="auto" helpType="custom">
+            <ng-template ptTemplate="customHtml">
+              <div [innerHTML]="quotaHelpIndication"></div>
+            </ng-template>
+          </my-help>
         </ng-container>
 
         <ng-container i18n *ngIf="initialUserVideoQuota === -1">
index d0c96941d168a2dbf85cfb6759d708889fbf36a2..7c76bc98b5f0dd729e9c4528d71375bd71f62a73 100644 (file)
@@ -4,6 +4,9 @@ import { Injectable } from '@angular/core'
 import { environment } from '../../../environments/environment'
 import { RestExtractor, RestService } from '../rest'
 import { About } from '../../../../../shared/models/server'
+import { MarkdownService } from '@app/shared/renderer'
+import { peertubeTranslate } from '@shared/models'
+import { ServerService } from '@app/core'
 
 @Injectable()
 export class InstanceService {
@@ -13,7 +16,9 @@ export class InstanceService {
   constructor (
     private authHttp: HttpClient,
     private restService: RestService,
-    private restExtractor: RestExtractor
+    private restExtractor: RestExtractor,
+    private markdownService: MarkdownService,
+    private serverService: ServerService
   ) {
   }
 
@@ -34,4 +39,42 @@ export class InstanceService {
                .pipe(catchError(res => this.restExtractor.handleError(res)))
 
   }
+
+  async buildHtml (about: About) {
+    const html = {
+      description: '',
+      terms: '',
+      codeOfConduct: '',
+      moderationInformation: '',
+      administrator: ''
+    }
+
+    for (const key of [ 'description', 'terms', 'codeOfConduct', 'moderationInformation', 'administrator' ]) {
+      html[ key ] = await this.markdownService.textMarkdownToHTML(about.instance[ key ])
+    }
+
+    return html
+  }
+
+  buildTranslatedLanguages (about: About, translations: any) {
+    const languagesArray = this.serverService.getVideoLanguages()
+
+    return about.instance.languages
+                .map(l => {
+                  const languageObj = languagesArray.find(la => la.id === l)
+
+                  return peertubeTranslate(languageObj.label, translations)
+                })
+  }
+
+  buildTranslatedCategories (about: About, translations: any) {
+    const categoriesArray = this.serverService.getVideoCategories()
+
+    return about.instance.categories
+                .map(c => {
+                  const categoryObj = categoriesArray.find(ca => ca.id === c)
+
+                  return peertubeTranslate(categoryObj.label, translations)
+                })
+  }
 }
index e31eef06a69204975519ca539921466df47f42fa..9a6d3e48e74290c82babbdf76441d6c5d8ab3552 100644 (file)
@@ -1,15 +1,25 @@
 <ng-template #tooltipTemplate>
-  <ng-template [ngIf]="preHtml">
-    <p [innerHTML]="preHtml"></p>
-    <br />
-  </ng-template>
+  <p *ngIf="preHtmlTemplate">
+    <ng-template *ngTemplateOutlet="preHtmlTemplate"></ng-template>
+  </p>
 
-  <p [innerHTML]="mainHtml"></p>
+  <ng-container *ngIf="preHtmlTemplate && (customHtmlTemplate || mainHtml || postHtmlTemplate)">
+    <br /><br />
+  </ng-container>
 
-  <ng-template [ngIf]="postHtml">
-    <br />
-    <p [innerHTML]="postHtml"></p>
-  </ng-template>
+  <p *ngIf="customHtmlTemplate">
+    <ng-template *ngTemplateOutlet="customHtmlTemplate"></ng-template>
+  </p>
+
+  <p *ngIf="mainHtml" [innerHTML]="mainHtml"></p>
+
+  <ng-container *ngIf="(customHtmlTemplate || mainHtml) && postHtmlTemplate">
+    <br /><br />
+  </ng-container>
+
+  <p *ngIf="postHtmlTemplate">
+    <ng-template *ngTemplateOutlet="postHtmlTemplate"></ng-template>
+  </p>
 </ng-template>
 
 <span
index f3426f70ff4246a2e8aff0ac3ce1167f06a3c454..18ba8ad5e943cfbd2ef947a1940cc55c547c7ac6 100644 (file)
@@ -1,6 +1,7 @@
-import { Component, Input, OnChanges, OnInit } from '@angular/core'
+import { AfterContentInit, Component, ContentChildren, Input, OnChanges, OnInit, QueryList, TemplateRef } from '@angular/core'
 import { I18n } from '@ngx-translate/i18n-polyfill'
 import { MarkdownService } from '@app/shared/renderer'
+import { PeerTubeTemplateDirective } from '@app/shared/angular/peertube-template.directive'
 
 @Component({
   selector: 'my-help',
@@ -8,22 +9,42 @@ import { MarkdownService } from '@app/shared/renderer'
   templateUrl: './help.component.html'
 })
 
-export class HelpComponent implements OnInit, OnChanges {
-  @Input() preHtml = ''
-  @Input() postHtml = ''
-  @Input() customHtml = ''
+export class HelpComponent implements OnInit, OnChanges, AfterContentInit {
   @Input() helpType: 'custom' | 'markdownText' | 'markdownEnhanced' = 'custom'
   @Input() tooltipPlacement = 'right'
 
+  @ContentChildren(PeerTubeTemplateDirective) templates: QueryList<PeerTubeTemplateDirective<'preHtml' | 'customHtml' | 'postHtml'>>
+
   isPopoverOpened = false
   mainHtml = ''
 
+  preHtmlTemplate: TemplateRef<any>
+  customHtmlTemplate: TemplateRef<any>
+  postHtmlTemplate: TemplateRef<any>
+
   constructor (private i18n: I18n) { }
 
   ngOnInit () {
     this.init()
   }
 
+  ngAfterContentInit () {
+    {
+      const t = this.templates.find(t => t.name === 'preHtml')
+      if (t) this.preHtmlTemplate = t.template
+    }
+
+    {
+      const t = this.templates.find(t => t.name === 'customHtml')
+      if (t) this.customHtmlTemplate = t.template
+    }
+
+    {
+      const t = this.templates.find(t => t.name === 'postHtml')
+      if (t) this.postHtmlTemplate = t.template
+    }
+  }
+
   ngOnChanges () {
     this.init()
   }
@@ -37,11 +58,6 @@ export class HelpComponent implements OnInit, OnChanges {
   }
 
   private init () {
-    if (this.helpType === 'custom') {
-      this.mainHtml = this.customHtml
-      return
-    }
-
     if (this.helpType === 'markdownText') {
       this.mainHtml = this.formatMarkdownSupport(MarkdownService.TEXT_RULES)
       return
index ec3636b3e03bb80f55dacd9953e7ed8c9534a108..59ee1cb046319c583eb6fcef8a5d98db4ab84177 100644 (file)
     <span *ngIf="interact">Remote interact</span>
   </button>
 
-  <my-help *ngIf="!interact && showHelp"
-           helpType="custom"
-           i18n-customHtml customHtml="You can subscribe to the channel via any ActivityPub-capable fediverse instance. For instance with Mastodon or Pleroma you can type the channel URL in the search box and subscribe there.">
+  <my-help *ngIf="!interact && showHelp">
+    <ng-template ptTemplate="customHtml">
+      <ng-container i18n>
+        You can subscribe to the channel via any ActivityPub-capable fediverse instance.<br /><br />
+        For instance with Mastodon or Pleroma you can type the channel URL in the search box and subscribe there.
+      </ng-container>
+    </ng-template>
   </my-help>
 
-  <my-help *ngIf="showHelp && interact"
-           helpType="custom"
-           i18n-customHtml customHtml="You can interact with this via any ActivityPub-capable fediverse instance. For instance with Mastodon or Pleroma you can type the current URL in the search box and interact with it there.">
+  <my-help *ngIf="showHelp && interact">
+    <ng-template ptTemplate="customHtml">
+      <ng-container i18n>
+        You can interact with this via any ActivityPub-capable fediverse instance.<br /><br />
+        For instance with Mastodon or Pleroma you can type the current URL in the search box and interact with it there.
+      </ng-container>
+    </ng-template>
   </my-help>
-</form>
\ No newline at end of file
+</form>
index 994e0fa1ec0774ec1322386f3a0fc17c318a76d8..0644200568c195a4767fd7d1132dde462738aea1 100644 (file)
@@ -35,7 +35,7 @@ export class VideosSelectionComponent extends AbstractVideoList implements OnIni
   @Input() titlePage: string
   @Input() miniatureDisplayOptions: MiniatureDisplayOptions
   @Input() getVideosObservableFunction: (page: number, sort?: VideoSortField) => Observable<ResultList<Video>>
-  @ContentChildren(PeerTubeTemplateDirective) templates: QueryList<PeerTubeTemplateDirective>
+  @ContentChildren(PeerTubeTemplateDirective) templates: QueryList<PeerTubeTemplateDirective<'rowButtons' | 'globalButtons'>>
 
   @Output() selectionChange = new EventEmitter<SelectionType>()
   @Output() videosModelChange = new EventEmitter<Video[]>()
index 217cadc663461a065ac1c679d08786d6aa183e0a..245ae42b67486c820e67f85dd17522ac27b46526 100644 (file)
 
             <div class="form-group">
               <label i18n class="label-tags">Tags</label>
-              <my-help i18n-preHtml preHtml="Tags could be used to suggest relevant recommendations.</br>Press Enter to add a new tag."></my-help>
+
+              <my-help>
+                <ng-template ptTemplate="customHtml">
+                  <ng-container i18n>
+                    Tags could be used to suggest relevant recommendations. <br />
+                    Press Enter to add a new tag.
+                  </ng-container>
+                </ng-template>
+              </my-help>
+
               <tag-input
                 [validators]="tagValidators" [errorMessages]="tagValidatorsMessages"
                 i18n-placeholder placeholder="+ Tag" i18n-secondaryPlaceholder secondaryPlaceholder="Enter a new tag"
 
             <div class="form-group">
               <label i18n for="description">Description</label>
-              <my-help helpType="markdownText" i18n-preHtml preHtml="Video descriptions are truncated by default and require manual action to expand them."></my-help>
+
+              <my-help helpType="markdownText">
+                <ng-template ptTemplate="preHtml">
+                  <ng-container i18n>
+                    Video descriptions are truncated by default and require manual action to expand them.
+                  </ng-container>
+                </ng-template>
+              </my-help>
+
               <my-markdown-textarea truncate="250" formControlName="description"></my-markdown-textarea>
 
               <div *ngIf="formErrors.description" class="form-error">
               </div>
             </div>
 
-            <my-peertube-checkbox
-              inputName="nsfw" formControlName="nsfw"
-              i18n-labelText labelText="This video contains mature or explicit content"
-              i18n-helpHtml helpHtml="Some instances do not list videos containing mature or explicit content by default."
-              helpPlacement="bottom-right"
-            ></my-peertube-checkbox>
-
-            <my-peertube-checkbox
-              *ngIf="waitTranscodingEnabled"
-              inputName="waitTranscoding" formControlName="waitTranscoding"
-              i18n-labelText labelText="Wait transcoding before publishing the video"
-              i18n-helpHtml helpHtml="If you decide not to wait for transcoding before publishing the video, it could be unplayable until transcoding ends."
-              helpPlacement="bottom-right"
-            ></my-peertube-checkbox>
+            <my-peertube-checkbox inputName="nsfw" formControlName="nsfw" helpPlacement="bottom-right">
+              <ng-template ptTemplate="label">
+                <ng-container i18n>This video contains mature or explicit content</ng-container>
+              </ng-template>
+
+              <ng-template ptTemplate="help">
+                <ng-container i18n>Some instances do not list videos containing mature or explicit content by default.</ng-container>
+              </ng-template>
+            </my-peertube-checkbox>
+
+            <my-peertube-checkbox *ngIf="waitTranscodingEnabled" inputName="waitTranscoding" formControlName="waitTranscoding" helpPlacement="bottom-right">
+              <ng-template ptTemplate="label">
+                <ng-container i18n>Wait transcoding before publishing the video</ng-container>
+              </ng-template>
+
+              <ng-template ptTemplate="help">
+                <ng-container i18n>If you decide not to wait for transcoding before publishing the video, it could be unplayable until transcoding ends.</ng-container>
+              </ng-template>
+            </my-peertube-checkbox>
 
           </div>
         </div>
index 7a495fea5997d400ff8e7ec86738e6d68f44d26e..c290fd4b1bb7142aa043da16960a8ce16fca9e95 100644 (file)
 
     <div class="form-group form-group-magnet-uri">
       <label i18n for="magnetUri">Paste magnet URI</label>
-      <my-help
-        helpType="custom" i18n-customHtml
-        customHtml="You can import any torrent file that points to a mp4 file. You should make sure you have diffusion rights over the content it points to, otherwise it could cause legal trouble to yourself and your instance."
-      ></my-help>
+      <my-help>
+        <ng-template ptTemplate="customHtml">
+          <ng-container i18n>
+            You can import any torrent file that points to a mp4 file.
+            You should make sure you have diffusion rights over the content it points to, otherwise it could cause legal trouble to yourself and your instance.
+          </ng-container>
+        </ng-template>
+      </my-help>
 
       <input type="text" id="magnetUri" [(ngModel)]="magnetUri" />
     </div>
index e4f19faa866ca4a618ee6994d83f9bf55d26c557..09d0b8272ae689314448f84179b0824754c5c1e0 100644 (file)
@@ -4,10 +4,16 @@
 
     <div class="form-group">
       <label i18n for="targetUrl">URL</label>
-      <my-help
-        helpType="custom" i18n-customHtml
-        customHtml="You can import any URL <a href='https://rg3.github.io/youtube-dl/supportedsites.html' target='_blank' rel='noopener noreferrer'>supported by youtube-dl</a> or URL that points to a raw MP4 file. You should make sure you have diffusion rights over the content it points to, otherwise it could cause legal trouble to yourself and your instance."
-      ></my-help>
+
+      <my-help>
+        <ng-template ptTemplate="customHtml">
+          <ng-container i18n>
+            You can import any URL <a href='https://rg3.github.io/youtube-dl/supportedsites.html' target='_blank' rel='noopener noreferrer'>supported by youtube-dl</a>
+            or URL that points to a raw MP4 file.
+            You should make sure you have diffusion rights over the content it points to, otherwise it could cause legal trouble to yourself and your instance.
+          </ng-container>
+        </ng-template>
+      </my-help>
 
       <input type="text" id="targetUrl" [(ngModel)]="targetUrl" />
     </div>