]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/commitdiff
fix missing title attribute on <iframe> tag suggested for embedding (#3901)
authorThavarasa Prasanth <45243326+pthavarasa@users.noreply.github.com>
Wed, 31 Mar 2021 06:32:05 +0000 (08:32 +0200)
committerGitHub <noreply@github.com>
Wed, 31 Mar 2021 06:32:05 +0000 (08:32 +0200)
* title attribute is missing on <iframe> tag suggested for embedding #3861

* fix #3901

* fix: escapeHTML #3901

* fix: playlist title instead of video title #3901

* fix #3901

* assign title directly #3901

13 files changed:
client/src/app/+admin/moderation/video-block-list/video-block-list.component.ts
client/src/app/+videos/+video-watch/video-watch.component.ts
client/src/app/shared/shared-abuse-list/abuse-list-table.component.ts
client/src/app/shared/shared-moderation/report-modals/video-report.component.ts
client/src/app/shared/shared-share-modal/video-share.component.ts
client/src/assets/player/peertube-player-manager.ts
client/src/assets/player/utils.ts
client/src/standalone/videos/embed.ts
server/controllers/services.ts
server/helpers/core-utils.ts
server/lib/client-html.ts
server/tests/api/server/services.ts
shared/core-utils/renderer/html.ts

index 82c371f4df142812981b48c1a4da1ec464a07682..d6aca10e7dfb8f3ee686e4558c537dfdb21d7b61 100644 (file)
@@ -164,7 +164,8 @@ export class VideoBlockListComponent extends RestTable implements OnInit, AfterV
         baseUrl: `${environment.originServerUrl}/videos/embed/${entry.video.uuid}`,
         title: false,
         warningTitle: false
-      })
+      }),
+      entry.video.name
     )
   }
 
index 7a98cab3b09f5f1b03e17355b6b702df7c365f94..ce115dfab99f4dbf72e800eb4ecfeffc3b523548 100644 (file)
@@ -815,6 +815,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
           ? this.videoService.getVideoViewUrl(video.uuid)
           : null,
         embedUrl: video.embedUrl,
+        embedTitle: video.name,
 
         isLive: video.isLive,
 
index e34836a189c1b76de07695fe5a9ddfc1fd87cd2a..eeb9f128b623e744a1212cba87900f4e0116994f 100644 (file)
@@ -117,7 +117,8 @@ export class AbuseListTableComponent extends RestTable implements OnInit, AfterV
         warningTitle: false,
         startTime: abuse.video.startAt,
         stopTime: abuse.video.endAt
-      })
+      }),
+      abuse.video.name
     )
   }
 
index 5b06c0bc7e07c8893af3633c126349a48f463d7b..4ca6f52ad499887872b7beb34f7dd98484580ba6 100644 (file)
@@ -61,7 +61,8 @@ export class VideoReportComponent extends FormReactive implements OnInit {
           baseUrl: this.video.embedUrl,
           title: false,
           warningTitle: false
-        })
+        }),
+        this.video.name
       )
     )
   }
index b06ff375114dad261cb34b68a7e91d00bac66d06..e8760bfcc2ab7d94b03a2eb1e0b1826cf8e87f57 100644 (file)
@@ -86,14 +86,14 @@ export class VideoShareComponent {
     const options = this.getVideoOptions(this.video.embedUrl)
 
     const embedUrl = buildVideoLink(options)
-    return buildVideoOrPlaylistEmbed(embedUrl)
+    return buildVideoOrPlaylistEmbed(embedUrl, this.video.name)
   }
 
   getPlaylistIframeCode () {
     const options = this.getPlaylistOptions(this.playlist.embedUrl)
 
     const embedUrl = buildPlaylistLink(options)
-    return buildVideoOrPlaylistEmbed(embedUrl)
+    return buildVideoOrPlaylistEmbed(embedUrl, this.playlist.displayName)
   }
 
   getVideoUrl () {
index e6d614c47011303e5d839562d9bdd7abbc2d981f..1d335805bf56de859c46c399acdb1cf72a5c8669 100644 (file)
@@ -98,6 +98,7 @@ export interface CommonOptions extends CustomizationOptions {
 
   videoViewUrl: string
   embedUrl: string
+  embedTitle: string
 
   isLive: boolean
 
@@ -165,7 +166,7 @@ export class PeertubePlayerManager {
           PeertubePlayerManager.alreadyPlayed = true
         })
 
-        self.addContextMenu(mode, player, options.common.embedUrl)
+        self.addContextMenu(mode, player, options.common.embedUrl, options.common.embedTitle)
 
         player.bezels()
 
@@ -203,7 +204,7 @@ export class PeertubePlayerManager {
     videojs(newVideoElement, videojsOptions, function (this: videojs.Player) {
       const player = this
 
-      self.addContextMenu(mode, player, options.common.embedUrl)
+      self.addContextMenu(mode, player, options.common.embedUrl, options.common.embedTitle)
 
       PeertubePlayerManager.onPlayerChange(player)
     })
@@ -492,7 +493,7 @@ export class PeertubePlayerManager {
     return children
   }
 
-  private static addContextMenu (mode: PlayerMode, player: videojs.Player, videoEmbedUrl: string) {
+  private static addContextMenu (mode: PlayerMode, player: videojs.Player, videoEmbedUrl: string, videoEmbedTitle: string) {
     const content = [
       {
         label: player.localize('Copy the video URL'),
@@ -509,7 +510,7 @@ export class PeertubePlayerManager {
       {
         label: player.localize('Copy embed code'),
         listener: () => {
-          copyToClipboard(buildVideoOrPlaylistEmbed(videoEmbedUrl))
+          copyToClipboard(buildVideoOrPlaylistEmbed(videoEmbedUrl, videoEmbedTitle))
         }
       }
     ]
index 6767459ce9fbc800788cb39bd96a2855b3f92d97..d7451fa1d1fcf3f3c1037f28cb5cf6fdd6140003 100644 (file)
@@ -1,4 +1,5 @@
 import { VideoFile } from '@shared/models'
+import { escapeHTML } from '@shared/core-utils/renderer'
 
 function toTitleCase (str: string) {
   return str.charAt(0).toUpperCase() + str.slice(1)
@@ -170,9 +171,11 @@ function secondsToTime (seconds: number, full = false, symbol?: string) {
   return time
 }
 
-function buildVideoOrPlaylistEmbed (embedUrl: string) {
+function buildVideoOrPlaylistEmbed (embedUrl: string, embedTitle: string) {
+  const title = escapeHTML(embedTitle)
   return '<iframe width="560" height="315" ' +
     'sandbox="allow-same-origin allow-scripts allow-popups" ' +
+    'title="' + title + '" ' +
     'src="' + embedUrl + '" ' +
     'frameborder="0" allowfullscreen>' +
     '</iframe>'
index c8727002731fbf0be6a1ebbafff9a5fc6aa5ae37..614a1cc0b2a4ba261a85781c320090e8a5e36c55 100644 (file)
@@ -545,7 +545,8 @@ export class PeerTubeEmbed {
 
         serverUrl: window.location.origin,
         language: navigator.language,
-        embedUrl: window.location.origin + videoInfo.embedPath
+        embedUrl: window.location.origin + videoInfo.embedPath,
+        embedTitle: videoInfo.name
       },
 
       webtorrent: {
index d0217c30ae9db1dc7ee882674f79fa04c00488a0..189e1651bae8d8e03d9851714afb5df7912fc8f6 100644 (file)
@@ -3,6 +3,7 @@ import { EMBED_SIZE, PREVIEWS_SIZE, WEBSERVER, THUMBNAILS_SIZE } from '../initia
 import { asyncMiddleware, oembedValidator } from '../middlewares'
 import { accountNameWithHostGetValidator } from '../middlewares/validators'
 import { MChannelSummary } from '@server/types/models'
+import { escapeHTML } from '@shared/core-utils/renderer'
 
 const servicesRouter = express.Router()
 
@@ -79,6 +80,7 @@ function buildOEmbed (options: {
   const embedUrl = webserverUrl + embedPath
   let embedWidth = EMBED_SIZE.width
   let embedHeight = EMBED_SIZE.height
+  const embedTitle = escapeHTML(title)
 
   let thumbnailUrl = previewPath
     ? webserverUrl + previewPath
@@ -96,7 +98,7 @@ function buildOEmbed (options: {
   }
 
   const html = `<iframe width="${embedWidth}" height="${embedHeight}" sandbox="allow-same-origin allow-scripts" ` +
-    `src="${embedUrl}" frameborder="0" allowfullscreen></iframe>`
+    `title="${embedTitle}" src="${embedUrl}" frameborder="0" allowfullscreen></iframe>`
 
   const json: any = {
     type: 'video',
index 0bd84ffaa88f2f6c59578210e7b654186673a673..b93868c1235913078bf2bd139153306e57bcebcd 100644 (file)
@@ -154,24 +154,6 @@ function root () {
   return rootPath
 }
 
-// Thanks: https://stackoverflow.com/a/12034334
-function escapeHTML (stringParam) {
-  if (!stringParam) return ''
-
-  const entityMap = {
-    '&': '&amp;',
-    '<': '&lt;',
-    '>': '&gt;',
-    '"': '&quot;',
-    '\'': '&#39;',
-    '/': '&#x2F;',
-    '`': '&#x60;',
-    '=': '&#x3D;'
-  }
-
-  return String(stringParam).replace(/[&<>"'`=/]/g, s => entityMap[s])
-}
-
 function pageToStartAndCount (page: number, itemsPerPage: number) {
   const start = (page - 1) * itemsPerPage
 
@@ -278,7 +260,6 @@ export {
 
   objectConverter,
   root,
-  escapeHTML,
   pageToStartAndCount,
   sanitizeUrl,
   sanitizeHost,
index f19ec7df02ebe6ca540c932b951c21a310b51e32..fcc11c7b2eeaa80a03e1ca66f1badddb2dd99b22 100644 (file)
@@ -5,7 +5,8 @@ import validator from 'validator'
 import { buildFileLocale, getDefaultLocale, is18nLocale, POSSIBLE_LOCALES } from '../../shared/core-utils/i18n/i18n'
 import { HttpStatusCode } from '../../shared/core-utils/miscs/http-error-codes'
 import { VideoPlaylistPrivacy, VideoPrivacy } from '../../shared/models/videos'
-import { escapeHTML, isTestInstance, sha256 } from '../helpers/core-utils'
+import { isTestInstance, sha256 } from '../helpers/core-utils'
+import { escapeHTML } from '@shared/core-utils/renderer'
 import { logger } from '../helpers/logger'
 import { CONFIG } from '../initializers/config'
 import {
index df910c11149317013247ab1d7e6274ced4035a7d..6202eb66cb4f4b2191f2ae615e2b6bd92853581b 100644 (file)
@@ -20,6 +20,7 @@ const expect = chai.expect
 describe('Test services', function () {
   let server: ServerInfo = null
   let playlistUUID: string
+  let playlistDisplayName: string
   let video: Video
 
   before(async function () {
@@ -52,6 +53,7 @@ describe('Test services', function () {
       })
 
       playlistUUID = res.body.videoPlaylist.uuid
+      playlistDisplayName = 'The Life and Times of Scrooge McDuck'
 
       await addVideoInPlaylist({
         url: server.url,
@@ -69,7 +71,7 @@ describe('Test services', function () {
 
     const res = await getOEmbed(server.url, oembedUrl)
     const expectedHtml = '<iframe width="560" height="315" sandbox="allow-same-origin allow-scripts" ' +
-      `src="http://localhost:${server.port}/videos/embed/${video.uuid}" ` +
+      `title="${video.name}" src="http://localhost:${server.port}/videos/embed/${video.uuid}" ` +
       'frameborder="0" allowfullscreen></iframe>'
     const expectedThumbnailUrl = 'http://localhost:' + server.port + video.previewPath
 
@@ -88,7 +90,7 @@ describe('Test services', function () {
 
     const res = await getOEmbed(server.url, oembedUrl)
     const expectedHtml = '<iframe width="560" height="315" sandbox="allow-same-origin allow-scripts" ' +
-      `src="http://localhost:${server.port}/video-playlists/embed/${playlistUUID}" ` +
+      `title="${playlistDisplayName}" src="http://localhost:${server.port}/video-playlists/embed/${playlistUUID}" ` +
       'frameborder="0" allowfullscreen></iframe>'
 
     expect(res.body.html).to.equal(expectedHtml)
@@ -109,7 +111,7 @@ describe('Test services', function () {
 
     const res = await getOEmbed(server.url, oembedUrl, format, maxHeight, maxWidth)
     const expectedHtml = '<iframe width="50" height="50" sandbox="allow-same-origin allow-scripts" ' +
-      `src="http://localhost:${server.port}/videos/embed/${video.uuid}" ` +
+      `title="${video.name}" src="http://localhost:${server.port}/videos/embed/${video.uuid}" ` +
       'frameborder="0" allowfullscreen></iframe>'
 
     expect(res.body.html).to.equal(expectedHtml)
index 1220848a0a2cc4fcbb45ebebd8d9fb37f25ab9fd..de4ad47ac8569c47d41f89f405fb2af34a273724 100644 (file)
@@ -19,3 +19,21 @@ export const SANITIZE_OPTIONS = {
     }
   }
 }
+
+// Thanks: https://stackoverflow.com/a/12034334
+export function escapeHTML (stringParam: string) {
+  if (!stringParam) return ''
+
+  const entityMap = {
+    '&': '&amp;',
+    '<': '&lt;',
+    '>': '&gt;',
+    '"': '&quot;',
+    '\'': '&#39;',
+    '/': '&#x2F;',
+    '`': '&#x60;',
+    '=': '&#x3D;'
+  }
+
+  return String(stringParam).replace(/[&<>"'`=/]/g, s => entityMap[s])
+}