diff options
-rw-r--r-- | server/lib/client-html.ts | 28 | ||||
-rw-r--r-- | server/tests/fixtures/peertube-plugin-test/main.js | 15 | ||||
-rw-r--r-- | server/tests/plugins/filter-hooks.ts | 21 | ||||
-rw-r--r-- | shared/models/plugins/server/server-hook.model.ts | 2 |
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' | |||
27 | import { VideoModel } from '../models/video/video' | 27 | import { VideoModel } from '../models/video/video' |
28 | import { VideoChannelModel } from '../models/video/video-channel' | 28 | import { VideoChannelModel } from '../models/video/video-channel' |
29 | import { VideoPlaylistModel } from '../models/video/video-playlist' | 29 | import { VideoPlaylistModel } from '../models/video/video-playlist' |
30 | import { MAccountActor, MChannelActor } from '../types/models' | 30 | import { MAccountActor, MChannelActor, MVideo, MVideoPlaylist } from '../types/models' |
31 | import { getActivityStreamDuration } from './activitypub/activity' | 31 | import { getActivityStreamDuration } from './activitypub/activity' |
32 | import { getBiggestActorImage } from './actor-image' | 32 | import { getBiggestActorImage } from './actor-image' |
33 | import { Hooks } from './plugins/hooks' | ||
33 | import { ServerConfigManager } from './server-config-manager' | 34 | import { ServerConfigManager } from './server-config-manager' |
34 | 35 | ||
35 | type Tags = { | 36 | type Tags = { |
@@ -64,6 +65,11 @@ type Tags = { | |||
64 | } | 65 | } |
65 | } | 66 | } |
66 | 67 | ||
68 | type HookContext = { | ||
69 | video?: MVideo | ||
70 | playlist?: MVideoPlaylist | ||
71 | } | ||
72 | |||
67 | class ClientHtml { | 73 | class 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 | ||