Merge pull request #5548 from consuldemocracy/video_cookies
Do not use third-party cookies in embedded videos
This commit is contained in:
5
app/components/shared/embedded_video_component.html.erb
Normal file
5
app/components/shared/embedded_video_component.html.erb
Normal file
@@ -0,0 +1,5 @@
|
||||
<div class="small-12 medium-7 small-centered">
|
||||
<div class="flex-video">
|
||||
<div id="js-embedded-video" data-video-code="<%= embedded_video_code %>"></div>
|
||||
</div>
|
||||
</div>
|
||||
59
app/components/shared/embedded_video_component.rb
Normal file
59
app/components/shared/embedded_video_component.rb
Normal file
@@ -0,0 +1,59 @@
|
||||
class Shared::EmbeddedVideoComponent < ApplicationComponent
|
||||
attr_reader :record
|
||||
|
||||
def initialize(record)
|
||||
@record = record
|
||||
end
|
||||
|
||||
def render?
|
||||
record.video_url.present?
|
||||
end
|
||||
|
||||
def embedded_video_code
|
||||
if match && match[2]
|
||||
"<iframe #{iframe_attributes}></iframe>"
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def link
|
||||
record.video_url
|
||||
end
|
||||
|
||||
def title
|
||||
t("proposals.show.embed_video_title", proposal: record.title)
|
||||
end
|
||||
|
||||
def server
|
||||
if link =~ /vimeo.*/
|
||||
"Vimeo"
|
||||
elsif link =~ /youtu*.*/
|
||||
"YouTube"
|
||||
end
|
||||
end
|
||||
|
||||
def regex
|
||||
if server == "Vimeo"
|
||||
record.class::VIMEO_REGEX
|
||||
elsif server == "YouTube"
|
||||
record.class::YOUTUBE_REGEX
|
||||
end
|
||||
end
|
||||
|
||||
def src
|
||||
if server == "Vimeo"
|
||||
"https://player.vimeo.com/video/#{match[2]}?dnt=1"
|
||||
elsif server == "YouTube"
|
||||
"https://www.youtube-nocookie.com/embed/#{match[2]}"
|
||||
end
|
||||
end
|
||||
|
||||
def match
|
||||
@match ||= link.match(regex) if regex
|
||||
end
|
||||
|
||||
def iframe_attributes
|
||||
tag.attributes(src: src, style: "border:0;", allowfullscreen: true, title: title)
|
||||
end
|
||||
end
|
||||
@@ -1,40 +0,0 @@
|
||||
module EmbedVideosHelper
|
||||
VIMEO_REGEX = /vimeo.*(staffpicks\/|channels\/|videos\/|video\/|\/)([^#\&\?]*).*/
|
||||
YOUTUBE_REGEX = /youtu.*(be\/|v\/|u\/\w\/|embed\/|watch\?v=|\&v=)([^#\&\?]*).*/
|
||||
|
||||
def embedded_video_code(resource)
|
||||
link = resource.video_url
|
||||
title = t("proposals.show.embed_video_title", proposal: resource.title)
|
||||
if link =~ /vimeo.*/
|
||||
server = "Vimeo"
|
||||
elsif link =~ /youtu*.*/
|
||||
server = "YouTube"
|
||||
end
|
||||
|
||||
if server == "Vimeo"
|
||||
reg_exp = VIMEO_REGEX
|
||||
src = "https://player.vimeo.com/video/"
|
||||
elsif server == "YouTube"
|
||||
reg_exp = YOUTUBE_REGEX
|
||||
src = "https://www.youtube.com/embed/"
|
||||
end
|
||||
|
||||
if reg_exp
|
||||
match = link.match(reg_exp)
|
||||
end
|
||||
|
||||
if match && match[2]
|
||||
'<iframe src="' + src + match[2] + '" style="border:0;" allowfullscreen title="' + title + '"></iframe>'
|
||||
else
|
||||
""
|
||||
end
|
||||
end
|
||||
|
||||
def valid_video_url?
|
||||
return if video_url.blank?
|
||||
return if video_url.match(VIMEO_REGEX)
|
||||
return if video_url.match(YOUTUBE_REGEX)
|
||||
|
||||
errors.add(:video_url, :invalid)
|
||||
end
|
||||
end
|
||||
28
app/models/concerns/videoable.rb
Normal file
28
app/models/concerns/videoable.rb
Normal file
@@ -0,0 +1,28 @@
|
||||
module Videoable
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
validate :valid_video_url?
|
||||
end
|
||||
|
||||
VIMEO_REGEX = /vimeo.*(staffpicks\/|channels\/|videos\/|video\/|\/)([^#\&\?]*).*/
|
||||
YOUTUBE_REGEX = /youtu.*(be\/|v\/|u\/\w\/|embed\/|watch\?v=|\&v=)([^#\&\?]*).*/
|
||||
|
||||
def valid_video_url?
|
||||
url = send(video_url_field)
|
||||
|
||||
return if url.blank?
|
||||
return if url.match(VIMEO_REGEX)
|
||||
return if url.match(YOUTUBE_REGEX)
|
||||
|
||||
errors.add(video_url_field, :invalid)
|
||||
end
|
||||
|
||||
def video_url_field
|
||||
if has_attribute?(:video_url)
|
||||
:video_url
|
||||
else
|
||||
:url
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,17 +1,6 @@
|
||||
class Poll::Question::Answer::Video < ApplicationRecord
|
||||
belongs_to :answer, class_name: "Poll::Question::Answer"
|
||||
|
||||
VIMEO_REGEX = /vimeo.*(staffpicks\/|channels\/|videos\/|video\/|\/)([^#\&\?]*).*/
|
||||
YOUTUBE_REGEX = /youtu.*(be\/|v\/|u\/\w\/|embed\/|watch\?v=|\&v=)([^#\&\?]*).*/
|
||||
include Videoable
|
||||
|
||||
validates :title, presence: true
|
||||
validate :valid_url?
|
||||
|
||||
def valid_url?
|
||||
return if url.blank?
|
||||
return if url.match(VIMEO_REGEX)
|
||||
return if url.match(YOUTUBE_REGEX)
|
||||
|
||||
errors.add(:url, :invalid)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -14,7 +14,7 @@ class Proposal < ApplicationRecord
|
||||
include Mappable
|
||||
include Notifiable
|
||||
include Documentable
|
||||
include EmbedVideosHelper
|
||||
include Videoable
|
||||
include Relationable
|
||||
include Milestoneable
|
||||
include Randomizable
|
||||
@@ -59,8 +59,6 @@ class Proposal < ApplicationRecord
|
||||
|
||||
validates :terms_of_service, acceptance: { allow_nil: false }, on: :create
|
||||
|
||||
validate :valid_video_url?
|
||||
|
||||
before_validation :set_responsible_name
|
||||
|
||||
before_save :calculate_hot_score, :calculate_confidence_score
|
||||
|
||||
@@ -65,13 +65,7 @@
|
||||
|
||||
<blockquote><%= @proposal.summary %></blockquote>
|
||||
|
||||
<% if @proposal.video_url.present? %>
|
||||
<div class="small-12 medium-7 small-centered">
|
||||
<div class="flex-video">
|
||||
<div id="js-embedded-video" data-video-code="<%= embedded_video_code(@proposal) %>"></div>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
<%= render Shared::EmbeddedVideoComponent.new(@proposal) %>
|
||||
|
||||
<%= auto_link_already_sanitized_html wysiwyg(@proposal.description) %>
|
||||
|
||||
|
||||
@@ -30,13 +30,7 @@
|
||||
|
||||
<blockquote><%= @proposal.summary %></blockquote>
|
||||
|
||||
<% if @proposal.video_url.present? %>
|
||||
<div class="small-12 medium-7 small-centered">
|
||||
<div class="flex-video">
|
||||
<div id="js-embedded-video" data-video-code="<%= embedded_video_code(@proposal) %>"></div>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
<%= render Shared::EmbeddedVideoComponent.new(@proposal) %>
|
||||
|
||||
<%= auto_link_already_sanitized_html wysiwyg(@proposal.description) %>
|
||||
|
||||
|
||||
43
spec/components/shared/embedded_video_component_spec.rb
Normal file
43
spec/components/shared/embedded_video_component_spec.rb
Normal file
@@ -0,0 +1,43 @@
|
||||
require "rails_helper"
|
||||
|
||||
describe Shared::EmbeddedVideoComponent do
|
||||
describe "src attribute" do
|
||||
before do
|
||||
dummy_class = Class.new do
|
||||
include ActiveModel::Model
|
||||
attr_accessor :title, :video_url
|
||||
|
||||
include Videoable
|
||||
end
|
||||
|
||||
stub_const("DummyClass", dummy_class)
|
||||
end
|
||||
|
||||
let(:record) { DummyClass.new(title: "Dummy Video", video_url: "") }
|
||||
let(:component) { Shared::EmbeddedVideoComponent.new(record) }
|
||||
|
||||
it "does not render anything for empty URls" do
|
||||
render_inline component
|
||||
|
||||
expect(page).not_to be_rendered
|
||||
end
|
||||
|
||||
it "embeds a youtube video for youtube URLs" do
|
||||
allow(record).to receive(:video_url).and_return "http://www.youtube.com/watch?v=a7UFm6ErMPU"
|
||||
embed_url = "https://www.youtube-nocookie.com/embed/a7UFm6ErMPU"
|
||||
|
||||
render_inline component
|
||||
|
||||
expect(page).to have_css "[data-video-code*='src=\"#{embed_url}\"']"
|
||||
end
|
||||
|
||||
it "embeds a vimeo video for vimeo URLs" do
|
||||
allow(record).to receive(:video_url).and_return "https://vimeo.com/7232823"
|
||||
embed_url = "https://player.vimeo.com/video/7232823?dnt=1"
|
||||
|
||||
render_inline component
|
||||
|
||||
expect(page).to have_css "[data-video-code*='src=\"#{embed_url}\"']"
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -301,23 +301,30 @@ describe "Proposals" do
|
||||
context "Embedded video" do
|
||||
scenario "Show YouTube video" do
|
||||
proposal = create(:proposal, video_url: "http://www.youtube.com/watch?v=a7UFm6ErMPU")
|
||||
|
||||
visit proposal_path(proposal)
|
||||
expect(page).to have_css "div[id='js-embedded-video']"
|
||||
expect(page.html).to include "https://www.youtube.com/embed/a7UFm6ErMPU"
|
||||
|
||||
within "#js-embedded-video" do
|
||||
expect(page).to have_css "iframe[src='https://www.youtube-nocookie.com/embed/a7UFm6ErMPU']"
|
||||
end
|
||||
end
|
||||
|
||||
scenario "Show Vimeo video" do
|
||||
proposal = create(:proposal, video_url: "https://vimeo.com/7232823")
|
||||
|
||||
visit proposal_path(proposal)
|
||||
expect(page).to have_css "div[id='js-embedded-video']"
|
||||
expect(page.html).to include "https://player.vimeo.com/video/7232823"
|
||||
|
||||
within "#js-embedded-video" do
|
||||
expect(page).to have_css "iframe[src='https://player.vimeo.com/video/7232823?dnt=1']"
|
||||
end
|
||||
end
|
||||
|
||||
scenario "Dont show video" do
|
||||
proposal = create(:proposal, video_url: nil)
|
||||
|
||||
visit proposal_path(proposal)
|
||||
expect(page).not_to have_css "div[id='js-embedded-video']"
|
||||
|
||||
expect(page).not_to have_css "#js-embedded-video"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
Reference in New Issue
Block a user