Skip to content

Commit

Permalink
Add masto verification link support
Browse files Browse the repository at this point in the history
  • Loading branch information
Chocobozzz committed Dec 19, 2024
1 parent 9f33ae7 commit f368c6d
Show file tree
Hide file tree
Showing 19 changed files with 163 additions and 63 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ <h2 i18n>INSTANCE</h2>
<div class="form-group">
<label i18n for="instanceDescription">Description</label>
<div class="label-small-info">
<my-custom-markup-help></my-custom-markup-help>
<my-custom-markup-help supportRelMe="true"></my-custom-markup-help>
</div>

<my-markdown-textarea
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,14 @@

<div class="form-group">
<label i18n for="description">Description</label>
<textarea
id="description" formControlName="description" class="form-control"
[ngClass]="{ 'input-error': formErrors['description'] }"
></textarea>

<my-help helpType="markdownText" supportRelMe="true"></my-help>

<my-markdown-textarea
inputId="description" formControlName="description" class="form-control"
markdownType="enhanced" [formError]="formErrors['description']" withEmoji="true" withHtml="true"
></my-markdown-textarea>

<div *ngIf="formErrors.description" class="form-error" role="alert">
{{ formErrors.description }}
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,16 @@ import { Notifier, User, UserService } from '@app/core'
import { USER_DESCRIPTION_VALIDATOR, USER_DISPLAY_NAME_REQUIRED_VALIDATOR } from '@app/shared/form-validators/user-validators'
import { FormReactive } from '@app/shared/shared-forms/form-reactive'
import { FormReactiveService } from '@app/shared/shared-forms/form-reactive.service'
import { MarkdownTextareaComponent } from '@app/shared/shared-forms/markdown-textarea.component'
import { HelpComponent } from '@app/shared/shared-main/buttons/help.component'
import { AlertComponent } from '@app/shared/shared-main/common/alert.component'

@Component({
selector: 'my-account-profile',
templateUrl: './my-account-profile.component.html',
styleUrls: [ './my-account-profile.component.scss' ],
standalone: true,
imports: [ NgIf, FormsModule, ReactiveFormsModule, NgClass, AlertComponent ]
imports: [ NgIf, FormsModule, ReactiveFormsModule, NgClass, AlertComponent, HelpComponent, MarkdownTextareaComponent ]
})
export class MyAccountProfileComponent extends FormReactive implements OnInit {
@Input() user: User = null
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
<ng-container i18n>
<a class="text-decoration-underline" href="https://en.wikipedia.org/wiki/Markdown#Example" target="_blank" rel="noreferrer noopener">Markdown compatible</a> that also supports <a class="text-decoration-underline" href="https://docs.joinpeertube.org/api/custom-client-markup" target="_blank" rel="noreferrer noopener">custom PeerTube HTML tags</a>
<a class="text-decoration-underline" href="https://en.wikipedia.org/wiki/Markdown#Example" target="_blank" rel="noreferrer noopener">Markdown compatible</a> that also supports <a class="text-decoration-underline" href="https://docs.joinpeertube.org/api/custom-client-markup" target="_blank" rel="noreferrer noopener">custom PeerTube HTML tags</a>.
</ng-container>

@if (supportRelMe) {
<br />

<ng-container i18n>
<a href="https://docs.joinmastodon.org/user/profile/#verification" target="_blank" rel="noopener noreferrer">Mastodon verification link</a> is also supported.
</ng-container>
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { Component } from '@angular/core'
import { booleanAttribute, Component, Input } from '@angular/core'

@Component({
selector: 'my-custom-markup-help',
templateUrl: './custom-markup-help.component.html',
standalone: true
})
export class CustomMarkupHelpComponent {
@Input({ transform: booleanAttribute }) supportRelMe = false
}
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ export class MarkdownTextareaComponent implements ControlValueAccessor, OnInit {
@Input({ required: true }) inputId: string

@Input() dir: string

@Input({ transform: booleanAttribute }) withHtml = false
@Input({ transform: booleanAttribute }) withEmoji = false

@ViewChild('textarea') textareaElement: ElementRef
Expand Down Expand Up @@ -163,9 +165,9 @@ export class MarkdownTextareaComponent implements ControlValueAccessor, OnInit {

html = result
} else if (this.markdownType === 'text') {
html = await this.markdownService.textMarkdownToHTML({ markdown: text, withEmoji: this.withEmoji })
html = await this.markdownService.textMarkdownToHTML({ markdown: text, withEmoji: this.withEmoji, withHtml: this.withHtml })
} else if (this.markdownType === 'enhanced') {
html = await this.markdownService.enhancedMarkdownToHTML({ markdown: text, withEmoji: this.withEmoji })
html = await this.markdownService.enhancedMarkdownToHTML({ markdown: text, withEmoji: this.withEmoji, withHtml: this.withHtml })
} else if (this.markdownType === 'to-unsafe-html') {
html = await this.markdownService.markdownToUnsafeHTML({ markdown: text })
}
Expand Down
29 changes: 24 additions & 5 deletions client/src/app/shared/shared-main/buttons/help.component.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,19 @@
import { AfterContentInit, Component, ContentChildren, Input, OnChanges, OnInit, QueryList, TemplateRef } from '@angular/core'
import { NgIf, NgTemplateOutlet } from '@angular/common'
import {
AfterContentInit,
booleanAttribute,
Component,
ContentChildren,
Input,
OnChanges,
OnInit,
QueryList,
TemplateRef
} from '@angular/core'
import { GlobalIconName } from '@app/shared/shared-icons/global-icon.component'
import { NgbPopover } from '@ng-bootstrap/ng-bootstrap'
import { ENHANCED_RULES, TEXT_RULES } from '@peertube/peertube-core-utils'
import { GlobalIconComponent } from '../../shared-icons/global-icon.component'
import { NgbPopover } from '@ng-bootstrap/ng-bootstrap'
import { NgIf, NgTemplateOutlet } from '@angular/common'
import { PeerTubeTemplateDirective } from '../common/peertube-template.directive'

@Component({
Expand All @@ -20,6 +30,7 @@ export class HelpComponent implements OnInit, OnChanges, AfterContentInit {
@Input() iconName: GlobalIconName = 'help'
@Input() title = $localize`Get help`
@Input() autoClose = 'outside'
@Input({ transform: booleanAttribute }) supportRelMe = false

@ContentChildren(PeerTubeTemplateDirective) templates: QueryList<PeerTubeTemplateDirective<'preHtml' | 'customHtml' | 'postHtml'>>

Expand Down Expand Up @@ -76,9 +87,17 @@ export class HelpComponent implements OnInit, OnChanges, AfterContentInit {
}

private formatMarkdownSupport (rules: string[]) {
/* eslint-disable max-len */
return $localize`<a href="https://en.wikipedia.org/wiki/Markdown#Example" target="_blank" rel="noopener noreferrer">Markdown</a> compatible that supports:` +
let str =
// eslint-disable-next-line max-len
$localize`<a href="https://en.wikipedia.org/wiki/Markdown#Example" target="_blank" rel="noopener noreferrer">Markdown</a> compatible that supports:` +
this.createMarkdownList(rules)

if (this.supportRelMe) {
// eslint-disable-next-line max-len
str += $localize`<a href="https://docs.joinmastodon.org/user/profile/#verification" target="_blank" rel="noopener noreferrer">Mastodon verification link</a> is also supported.`
}

return str
}

private createMarkdownList (rules: string[]) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,8 @@ export class SupportModalComponent {

const support = this.video?.support || this.videoChannel.support

this.markdownService.enhancedMarkdownToHTML({ markdown: support })
.then(r => {
this.htmlSupport = r
})
this.markdownService.enhancedMarkdownToHTML({ markdown: support, withEmoji: true, withHtml: true })
.then(r => this.htmlSupport = r)

this.displayName = this.video
? this.video.channel.displayName
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ <h2 i18n>UPDATE CHANNEL</h2>
<div class="form-group">
<label i18n for="display-name">Display name</label>
<input
type="text" id="display-name" class="form-control d-block"
type="text" id="display-name" class="form-control"
formControlName="display-name" [ngClass]="{ 'input-error': formErrors['display-name'] }"
>
<div *ngIf="formErrors['display-name']" class="form-error" role="alert">
Expand All @@ -53,10 +53,15 @@ <h2 i18n>UPDATE CHANNEL</h2>

<div class="form-group">
<label i18n for="description">Description</label>
<textarea
id="description" formControlName="description" class="form-control d-block"
[ngClass]="{ 'input-error': formErrors['description'] }"
></textarea>


<my-help helpType="markdownText" supportRelMe="true"></my-help>

<my-markdown-textarea
inputId="description" formControlName="description" class="form-control"
markdownType="enhanced" [formError]="formErrors['description']" withEmoji="true" withHtml="true"
></my-markdown-textarea>

<div *ngIf="formErrors.description" class="form-error" role="alert">
{{ formErrors.description }}
</div>
Expand All @@ -70,8 +75,8 @@ <h2 i18n>UPDATE CHANNEL</h2>
></my-help>

<my-markdown-textarea
inputId="support" formControlName="support" class="d-block"
markdownType="enhanced" [formError]="formErrors['support']"
inputId="support" formControlName="support" class="form-control"
markdownType="enhanced" [formError]="formErrors['support']" withEmoji="true" withHtml="true"
></my-markdown-textarea>
</div>

Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@
"memoizee": "^0.4.14",
"morgan": "^1.5.3",
"multer": "^1.4.5-lts.1",
"node-html-parser": "^6.1.13",
"node-media-server": "^2.1.4",
"nodemailer": "^6.0.0",
"opentelemetry-instrumentation-sequelize": "^0.41.0",
Expand Down
49 changes: 48 additions & 1 deletion packages/tests/src/client/og-twitter-tags.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */

import { expect } from 'chai'
import { config, expect } from 'chai'
import { Account, HttpStatusCode, VideoPlaylistCreateResult } from '@peertube/peertube-models'
import { cleanupTests, makeGetRequest, PeerTubeServer } from '@peertube/peertube-server-commands'
import { getWatchPlaylistBasePaths, getWatchVideoBasePaths, prepareClientTests } from '@tests/shared/client.js'

config.truncateThreshold = 0

describe('Test Open Graph and Twitter cards HTML tags', function () {
let servers: PeerTubeServer[]
let account: Account
Expand Down Expand Up @@ -239,6 +241,51 @@ describe('Test Open Graph and Twitter cards HTML tags', function () {
})
})

describe('Mastodon link', function () {

async function check (path: string, mastoLink: string, exist = true) {
const res = await makeGetRequest({ url: servers[0].url, path, accept: 'text/html', expectedStatus: HttpStatusCode.OK_200 })
const text = res.text

const expected = `<link href="${mastoLink}" rel="me">`

if (exist)expect(text).to.contain(expected)
else expect(text).to.not.contain(expected)
}

it('Should correctly include Mastodon link in account', async function () {
await servers[0].users.updateMe({
description: 'hi, please <a href="https://social.example.com/@username" rel="me">Follow me on Mastodon!</a>'
})

await check('/a/root', 'https://social.example.com/@username')
})

it('Should correctly include Mastodon link in channel', async function () {
await servers[0].channels.update({
channelName: 'root_channel',
attributes: {
description: '<a rel="me" href="https://social.example.com/@username2">Follow me on Mastodon!</a>'
}
})

await check('/c/root_channel', 'https://social.example.com/@username2')
})

it('Should correctly include Mastodon link on homepage', async function () {
await servers[0].config.updateExistingConfig({
newConfig: {
instance: {
description: '<a>toto</a>coucou<a rel="me" href="https://social.example.com/@username3">Follow me on Mastodon!</a>'
}
}
})

await check('/', 'https://social.example.com/@username3')
await check('/about', 'https://social.example.com/@username3', false)
})
})

after(async function () {
await cleanupTests(servers)
})
Expand Down
4 changes: 2 additions & 2 deletions packages/tests/src/shared/client.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { omit } from '@peertube/peertube-core-utils'
import { omit, pick } from '@peertube/peertube-core-utils'
import {
VideoPrivacy,
VideoPlaylistPrivacy,
Expand Down Expand Up @@ -55,7 +55,7 @@ export async function prepareClientTests () {

await servers[0].config.updateExistingConfig({
newConfig: {
instance: { name: instanceConfig.name, shortDescription: instanceConfig.shortDescription }
instance: { ...pick(instanceConfig, [ 'name', 'shortDescription' ]) }
}
})
await servers[0].config.updateInstanceImage({ type: ActorImageType.AVATAR, fixture: instanceConfig.avatar })
Expand Down
4 changes: 2 additions & 2 deletions server/core/controllers/feeds/shared/common-feed-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Feed } from '@peertube/feed'
import { CustomTag, CustomXMLNS, Person } from '@peertube/feed/lib/typings/index.js'
import { maxBy, pick } from '@peertube/peertube-core-utils'
import { ActorImageType } from '@peertube/peertube-models'
import { mdToOneLinePlainText } from '@server/helpers/markdown.js'
import { mdToPlainText } from '@server/helpers/markdown.js'
import { CONFIG } from '@server/initializers/config.js'
import { WEBSERVER } from '@server/initializers/constants.js'
import { UserModel } from '@server/models/user/user.js'
Expand Down Expand Up @@ -35,7 +35,7 @@ export function initFeed (parameters: {

return new Feed({
title: name,
description: mdToOneLinePlainText(description),
description: mdToPlainText(description),
// updated: TODO: somehowGetLatestUpdate, // optional, default = today
id: link || webserverUrl,
link: link || webserverUrl,
Expand Down
4 changes: 2 additions & 2 deletions server/core/controllers/feeds/shared/video-feed-utils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { VideoIncludeType } from '@peertube/peertube-models'
import { mdToOneLinePlainText, toSafeHtml } from '@server/helpers/markdown.js'
import { mdToPlainText, toSafeHtml } from '@server/helpers/markdown.js'
import { CONFIG } from '@server/initializers/config.js'
import { WEBSERVER } from '@server/initializers/constants.js'
import { getServerActor } from '@server/models/application/application.js'
Expand Down Expand Up @@ -47,7 +47,7 @@ export function getCommonVideoFeedAttributes (video: VideoModel) {
return {
title: video.name,
link: localLink,
description: mdToOneLinePlainText(video.getTruncatedDescription()),
description: mdToPlainText(video.getTruncatedDescription()),
content: toSafeHtml(video.description),

date: video.publishedAt,
Expand Down
4 changes: 2 additions & 2 deletions server/core/helpers/markdown.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ const toSafeHtml = (text: string) => {
return sanitizeHtml(html, defaultSanitizeOptions)
}

const mdToOneLinePlainText = (text: string) => {
const mdToPlainText = (text: string) => {
if (!text) return ''

markdownItForPlainText.render(text)
Expand All @@ -42,7 +42,7 @@ const mdToOneLinePlainText = (text: string) => {

export {
toSafeHtml,
mdToOneLinePlainText
mdToPlainText
}

// ---------------------------------------------------------------------------
Expand Down
1 change: 1 addition & 0 deletions server/core/lib/html/shared/actor-html.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ export class ActorHtml {
escapedTitle: escapeHTML(title),
escapedSiteName: escapeHTML(siteName),
escapedTruncatedDescription,
relMe: TagsHtml.findRelMe(entity.description),
image,
ogType,
twitterCard,
Expand Down
4 changes: 4 additions & 0 deletions server/core/lib/html/shared/page-html.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ export class PageHtml {
escapedTitle: escapeHTML(CONFIG.INSTANCE.NAME),
escapedTruncatedDescription: escapeHTML(CONFIG.INSTANCE.SHORT_DESCRIPTION),

relMe: url === WEBSERVER.URL
? TagsHtml.findRelMe(CONFIG.INSTANCE.DESCRIPTION)
: undefined,

image: avatar
? { url: ActorImageModel.getImageUrl(avatar), width: avatar.width, height: avatar.height }
: undefined,
Expand Down
Loading

0 comments on commit f368c6d

Please sign in to comment.