aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--server/lib/client-html.ts28
-rw-r--r--server/tests/fixtures/peertube-plugin-test/main.js15
-rw-r--r--server/tests/plugins/filter-hooks.ts21
-rw-r--r--shared/models/plugins/server/server-hook.model.ts2
4 files changed, 55 insertions, 11 deletions
diff --git a/server/lib/client-html.ts b/server/lib/client-html.ts
index 4fd9a4263..81b3fff02 100644
--- a/server/lib/client-html.ts
+++ b/server/lib/client-html.ts
@@ -27,9 +27,10 @@ import { AccountModel } from '../models/account/account'
27import { VideoModel } from '../models/video/video' 27import { VideoModel } from '../models/video/video'
28import { VideoChannelModel } from '../models/video/video-channel' 28import { VideoChannelModel } from '../models/video/video-channel'
29import { VideoPlaylistModel } from '../models/video/video-playlist' 29import { VideoPlaylistModel } from '../models/video/video-playlist'
30import { MAccountActor, MChannelActor } from '../types/models' 30import { MAccountActor, MChannelActor, MVideo, MVideoPlaylist } from '../types/models'
31import { getActivityStreamDuration } from './activitypub/activity' 31import { getActivityStreamDuration } from './activitypub/activity'
32import { getBiggestActorImage } from './actor-image' 32import { getBiggestActorImage } from './actor-image'
33import { Hooks } from './plugins/hooks'
33import { ServerConfigManager } from './server-config-manager' 34import { ServerConfigManager } from './server-config-manager'
34 35
35type Tags = { 36type Tags = {
@@ -64,6 +65,11 @@ type Tags = {
64 } 65 }
65} 66}
66 67
68type HookContext = {
69 video?: MVideo
70 playlist?: MVideoPlaylist
71}
72
67class ClientHtml { 73class ClientHtml {
68 74
69 private static htmlCache: { [path: string]: string } = {} 75 private static htmlCache: { [path: string]: string } = {}
@@ -129,7 +135,7 @@ class ClientHtml {
129 const twitterCard = CONFIG.SERVICES.TWITTER.WHITELISTED ? 'player' : 'summary_large_image' 135 const twitterCard = CONFIG.SERVICES.TWITTER.WHITELISTED ? 'player' : 'summary_large_image'
130 const schemaType = 'VideoObject' 136 const schemaType = 'VideoObject'
131 137
132 customHtml = ClientHtml.addTags(customHtml, { 138 customHtml = await ClientHtml.addTags(customHtml, {
133 url, 139 url,
134 originUrl, 140 originUrl,
135 escapedSiteName: escapeHTML(siteName), 141 escapedSiteName: escapeHTML(siteName),
@@ -141,7 +147,7 @@ class ClientHtml {
141 ogType, 147 ogType,
142 twitterCard, 148 twitterCard,
143 schemaType 149 schemaType
144 }) 150 }, { video })
145 151
146 return customHtml 152 return customHtml
147 } 153 }
@@ -193,7 +199,7 @@ class ClientHtml {
193 const twitterCard = CONFIG.SERVICES.TWITTER.WHITELISTED ? 'player' : 'summary' 199 const twitterCard = CONFIG.SERVICES.TWITTER.WHITELISTED ? 'player' : 'summary'
194 const schemaType = 'ItemList' 200 const schemaType = 'ItemList'
195 201
196 customHtml = ClientHtml.addTags(customHtml, { 202 customHtml = await ClientHtml.addTags(customHtml, {
197 url, 203 url,
198 originUrl, 204 originUrl,
199 escapedSiteName: escapeHTML(siteName), 205 escapedSiteName: escapeHTML(siteName),
@@ -206,7 +212,7 @@ class ClientHtml {
206 ogType, 212 ogType,
207 twitterCard, 213 twitterCard,
208 schemaType 214 schemaType
209 }) 215 }, { playlist: videoPlaylist })
210 216
211 return customHtml 217 return customHtml
212 } 218 }
@@ -290,7 +296,7 @@ class ClientHtml {
290 const twitterCard = 'summary' 296 const twitterCard = 'summary'
291 const schemaType = 'ProfilePage' 297 const schemaType = 'ProfilePage'
292 298
293 customHtml = ClientHtml.addTags(customHtml, { 299 customHtml = await ClientHtml.addTags(customHtml, {
294 url, 300 url,
295 originUrl, 301 originUrl,
296 escapedTitle: escapeHTML(title), 302 escapedTitle: escapeHTML(title),
@@ -301,7 +307,7 @@ class ClientHtml {
301 twitterCard, 307 twitterCard,
302 schemaType, 308 schemaType,
303 disallowIndexation: !entity.Actor.isOwned() 309 disallowIndexation: !entity.Actor.isOwned()
304 }) 310 }, {})
305 311
306 return customHtml 312 return customHtml
307 } 313 }
@@ -469,7 +475,7 @@ class ClientHtml {
469 return metaTags 475 return metaTags
470 } 476 }
471 477
472 private static generateSchemaTags (tags: Tags) { 478 private static async generateSchemaTags (tags: Tags, context: HookContext) {
473 const schema = { 479 const schema = {
474 '@context': 'http://schema.org', 480 '@context': 'http://schema.org',
475 '@type': tags.schemaType, 481 '@type': tags.schemaType,
@@ -495,14 +501,14 @@ class ClientHtml {
495 schema['contentUrl'] = tags.url 501 schema['contentUrl'] = tags.url
496 } 502 }
497 503
498 return schema 504 return Hooks.wrapObject(schema, 'filter:html.client.json-ld.result', context)
499 } 505 }
500 506
501 private static addTags (htmlStringPage: string, tagsValues: Tags) { 507 private static async addTags (htmlStringPage: string, tagsValues: Tags, context: HookContext) {
502 const openGraphMetaTags = this.generateOpenGraphMetaTags(tagsValues) 508 const openGraphMetaTags = this.generateOpenGraphMetaTags(tagsValues)
503 const standardMetaTags = this.generateStandardMetaTags(tagsValues) 509 const standardMetaTags = this.generateStandardMetaTags(tagsValues)
504 const twitterCardMetaTags = this.generateTwitterCardMetaTags(tagsValues) 510 const twitterCardMetaTags = this.generateTwitterCardMetaTags(tagsValues)
505 const schemaTags = this.generateSchemaTags(tagsValues) 511 const schemaTags = await this.generateSchemaTags(tagsValues, context)
506 512
507 const { url, escapedTitle, embed, originUrl, disallowIndexation } = tagsValues 513 const { url, escapedTitle, embed, originUrl, disallowIndexation } = tagsValues
508 514
diff --git a/server/tests/fixtures/peertube-plugin-test/main.js b/server/tests/fixtures/peertube-plugin-test/main.js
index 84b479548..36dd08d27 100644
--- a/server/tests/fixtures/peertube-plugin-test/main.js
+++ b/server/tests/fixtures/peertube-plugin-test/main.js
@@ -312,6 +312,8 @@ async function register ({ registerHook, registerSetting, settingsManager, stora
312 } 312 }
313 }) 313 })
314 314
315 // ---------------------------------------------------------------------------
316
315 registerHook({ 317 registerHook({
316 target: 'filter:html.embed.video.allowed.result', 318 target: 'filter:html.embed.video.allowed.result',
317 handler: (result, params) => { 319 handler: (result, params) => {
@@ -332,6 +334,19 @@ async function register ({ registerHook, registerSetting, settingsManager, stora
332 } 334 }
333 }) 335 })
334 336
337 // ---------------------------------------------------------------------------
338
339 registerHook({
340 target: 'filter:html.client.json-ld.result',
341 handler: (jsonld, context) => {
342 if (!context || !context.video) return jsonld
343
344 return Object.assign(jsonld, { recordedAt: 'http://example.com/recordedAt' })
345 }
346 })
347
348 // ---------------------------------------------------------------------------
349
335 registerHook({ 350 registerHook({
336 target: 'filter:api.server.stats.get.result', 351 target: 'filter:api.server.stats.get.result',
337 handler: (result) => { 352 handler: (result) => {
diff --git a/server/tests/plugins/filter-hooks.ts b/server/tests/plugins/filter-hooks.ts
index 4d26ff8b7..43749b0b5 100644
--- a/server/tests/plugins/filter-hooks.ts
+++ b/server/tests/plugins/filter-hooks.ts
@@ -605,6 +605,27 @@ describe('Test plugin filter hooks', function () {
605 }) 605 })
606 }) 606 })
607 607
608 describe('Client HTML filters', function () {
609 let videoUUID: string
610
611 before(async function () {
612 this.timeout(60000)
613
614 const { uuid } = await servers[0].videos.quickUpload({ name: 'html video' })
615 videoUUID = uuid
616 })
617
618 it('Should run filter:html.client.json-ld.result', async function () {
619 const res = await makeGetRequest({ url: servers[0].url, path: '/w/' + videoUUID, expectedStatus: HttpStatusCode.OK_200 })
620 expect(res.text).to.contain('"recordedAt":"http://example.com/recordedAt"')
621 })
622
623 it('Should not run filter:html.client.json-ld.result with an account', async function () {
624 const res = await makeGetRequest({ url: servers[0].url, path: '/a/root', expectedStatus: HttpStatusCode.OK_200 })
625 expect(res.text).not.to.contain('"recordedAt":"http://example.com/recordedAt"')
626 })
627 })
628
608 describe('Search filters', function () { 629 describe('Search filters', function () {
609 630
610 before(async function () { 631 before(async function () {
diff --git a/shared/models/plugins/server/server-hook.model.ts b/shared/models/plugins/server/server-hook.model.ts
index d2ebe936e..91509f2b9 100644
--- a/shared/models/plugins/server/server-hook.model.ts
+++ b/shared/models/plugins/server/server-hook.model.ts
@@ -107,6 +107,8 @@ export const serverFilterHookObject = {
107 'filter:html.embed.video.allowed.result': true, 107 'filter:html.embed.video.allowed.result': true,
108 'filter:html.embed.video-playlist.allowed.result': true, 108 'filter:html.embed.video-playlist.allowed.result': true,
109 109
110 'filter:html.client.json-ld.result': true,
111
110 'filter:job-queue.process.params': true, 112 'filter:job-queue.process.params': true,
111 'filter:job-queue.process.result': true, 113 'filter:job-queue.process.result': true,
112 114