Merge branch 'next-minor' into develop
This commit is contained in:
commit
0b921c6657
16 changed files with 240 additions and 20 deletions
|
|
@ -14,6 +14,7 @@
|
|||
## Bug fixes
|
||||
|
||||
## Features
|
||||
* Add basic html5 audio/video embedding support [#6418](https://github.com/diaspora/diaspora/pull/6418)
|
||||
|
||||
# 0.7.3.1
|
||||
|
||||
|
|
|
|||
6
Gemfile
6
Gemfile
|
|
@ -26,7 +26,7 @@ gem "json-schema", "2.8.0"
|
|||
|
||||
# Authentication
|
||||
|
||||
gem "devise", "4.3.0"
|
||||
gem "devise", "4.4.1"
|
||||
gem "devise_lastseenable", "0.0.6"
|
||||
|
||||
# Captcha
|
||||
|
|
@ -122,6 +122,8 @@ source "https://rails-assets.org" do
|
|||
gem "rails-assets-perfect-scrollbar", "0.6.16"
|
||||
end
|
||||
|
||||
gem "markdown-it-html5-embed", "1.0.0"
|
||||
|
||||
# Localization
|
||||
|
||||
gem "http_accept_language", "2.1.1"
|
||||
|
|
@ -186,7 +188,7 @@ gem "typhoeus", "1.3.0"
|
|||
# Views
|
||||
|
||||
gem "gon", "6.1.0"
|
||||
gem "hamlit", "2.8.4"
|
||||
gem "hamlit", "2.8.6"
|
||||
gem "mobile-fu", "1.4.0"
|
||||
gem "rails-timeago", "2.16.0"
|
||||
gem "will_paginate", "3.1.6"
|
||||
|
|
|
|||
31
Gemfile.lock
31
Gemfile.lock
|
|
@ -133,7 +133,7 @@ GEM
|
|||
tins (~> 1.6)
|
||||
crack (0.4.3)
|
||||
safe_yaml (~> 1.0.0)
|
||||
crass (1.0.2)
|
||||
crass (1.0.3)
|
||||
cucumber (2.4.0)
|
||||
builder (>= 2.1.2)
|
||||
cucumber-core (~> 1.5.0)
|
||||
|
|
@ -156,7 +156,7 @@ GEM
|
|||
railties (>= 4, < 5.2)
|
||||
cucumber-wire (0.0.1)
|
||||
database_cleaner (1.6.1)
|
||||
devise (4.3.0)
|
||||
devise (4.4.1)
|
||||
bcrypt (~> 3.0)
|
||||
orm_adapter (~> 0.1)
|
||||
railties (>= 4.1.0, < 5.2)
|
||||
|
|
@ -187,7 +187,7 @@ GEM
|
|||
entypo-rails (3.0.0)
|
||||
railties (>= 4.1, < 6)
|
||||
equalizer (0.0.11)
|
||||
erubi (1.6.1)
|
||||
erubi (1.7.0)
|
||||
eslintrb (2.1.0)
|
||||
execjs
|
||||
multi_json (>= 1.3)
|
||||
|
|
@ -273,7 +273,7 @@ GEM
|
|||
guard-rubocop (1.3.0)
|
||||
guard (~> 2.0)
|
||||
rubocop (~> 0.20)
|
||||
haml (5.0.3)
|
||||
haml (5.0.4)
|
||||
temple (>= 0.8.0)
|
||||
tilt
|
||||
haml_lint (0.26.0)
|
||||
|
|
@ -282,7 +282,7 @@ GEM
|
|||
rake (>= 10, < 13)
|
||||
rubocop (>= 0.49.0)
|
||||
sysexits (~> 1.1)
|
||||
hamlit (2.8.4)
|
||||
hamlit (2.8.6)
|
||||
temple (>= 0.8.0)
|
||||
thor
|
||||
tilt
|
||||
|
|
@ -306,7 +306,8 @@ GEM
|
|||
httparty (0.15.6)
|
||||
multi_xml (>= 0.5.2)
|
||||
httpclient (2.8.3)
|
||||
i18n (0.8.6)
|
||||
i18n (0.9.5)
|
||||
concurrent-ruby (~> 1.0)
|
||||
i18n-inflector (2.6.7)
|
||||
i18n (>= 0.4.1)
|
||||
i18n-inflector-rails (1.0.7)
|
||||
|
|
@ -359,7 +360,7 @@ GEM
|
|||
multi_json (~> 1.10)
|
||||
logging-rails (0.6.0)
|
||||
logging (>= 1.8)
|
||||
loofah (2.1.1)
|
||||
loofah (2.2.0)
|
||||
crass (~> 1.0.2)
|
||||
nokogiri (>= 1.5.9)
|
||||
lumberjack (1.0.12)
|
||||
|
|
@ -367,6 +368,7 @@ GEM
|
|||
systemu (~> 2.6.2)
|
||||
mail (2.6.6)
|
||||
mime-types (>= 1.16, < 4)
|
||||
markdown-it-html5-embed (1.0.0)
|
||||
markerb (1.1.0)
|
||||
memoizable (0.4.2)
|
||||
thread_safe (~> 0.3, >= 0.3.1)
|
||||
|
|
@ -377,7 +379,7 @@ GEM
|
|||
mini_magick (4.8.0)
|
||||
mini_mime (0.1.4)
|
||||
mini_portile2 (2.3.0)
|
||||
minitest (5.10.3)
|
||||
minitest (5.11.3)
|
||||
mobile-fu (1.4.0)
|
||||
rack-mobile-detect
|
||||
rails
|
||||
|
|
@ -473,7 +475,7 @@ GEM
|
|||
byebug (~> 9.1)
|
||||
pry (~> 0.10)
|
||||
public_suffix (3.0.0)
|
||||
rack (2.0.3)
|
||||
rack (2.0.4)
|
||||
rack-cors (1.0.1)
|
||||
rack-google-analytics (1.2.0)
|
||||
actionpack
|
||||
|
|
@ -492,7 +494,7 @@ GEM
|
|||
rack-rewrite (1.5.1)
|
||||
rack-ssl (1.4.1)
|
||||
rack
|
||||
rack-test (0.7.0)
|
||||
rack-test (0.8.2)
|
||||
rack (>= 1.0, < 3)
|
||||
rails (5.1.4)
|
||||
actioncable (= 5.1.4)
|
||||
|
|
@ -576,7 +578,7 @@ GEM
|
|||
rainbow (2.2.2)
|
||||
rake
|
||||
raindrops (0.19.0)
|
||||
rake (12.1.0)
|
||||
rake (12.3.0)
|
||||
rb-fsevent (0.10.2)
|
||||
rb-inotify (0.9.10)
|
||||
ffi (>= 0.5.0, < 2)
|
||||
|
|
@ -712,7 +714,7 @@ GEM
|
|||
unf (~> 0.1.0)
|
||||
typhoeus (1.3.0)
|
||||
ethon (>= 0.9.0)
|
||||
tzinfo (1.2.3)
|
||||
tzinfo (1.2.5)
|
||||
thread_safe (~> 0.1)
|
||||
uglifier (3.2.0)
|
||||
execjs (>= 0.3.0, < 3)
|
||||
|
|
@ -780,7 +782,7 @@ DEPENDENCIES
|
|||
cucumber-api-steps (= 0.13)
|
||||
cucumber-rails (= 1.5.0)
|
||||
database_cleaner (= 1.6.1)
|
||||
devise (= 4.3.0)
|
||||
devise (= 4.4.1)
|
||||
devise_lastseenable (= 0.0.6)
|
||||
diaspora-prosody-config (= 0.0.7)
|
||||
diaspora_federation-json_schema (= 0.2.3)
|
||||
|
|
@ -801,7 +803,7 @@ DEPENDENCIES
|
|||
guard-rspec (= 4.7.3)
|
||||
guard-rubocop (= 1.3.0)
|
||||
haml_lint (= 0.26.0)
|
||||
hamlit (= 2.8.4)
|
||||
hamlit (= 2.8.6)
|
||||
handlebars_assets (= 0.23.2)
|
||||
http_accept_language (= 2.1.1)
|
||||
i18n-inflector-rails (= 1.0.7)
|
||||
|
|
@ -815,6 +817,7 @@ DEPENDENCIES
|
|||
json-schema-rspec (= 0.0.4)
|
||||
leaflet-rails (= 1.2.0)
|
||||
logging-rails (= 0.6.0)
|
||||
markdown-it-html5-embed (= 1.0.0)
|
||||
markerb (= 1.1.0)
|
||||
mini_magick (= 4.8.0)
|
||||
minitest
|
||||
|
|
|
|||
|
|
@ -1,6 +1,11 @@
|
|||
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
|
||||
|
||||
(function(){
|
||||
app.helpers.allowedEmbedsMime = function(mimetype) {
|
||||
var v = document.createElement(mimetype[1]);
|
||||
return v.canPlayType && v.canPlayType(mimetype[0]) !== "";
|
||||
};
|
||||
|
||||
app.helpers.textFormatter = function(text, mentions) {
|
||||
mentions = mentions ? mentions : [];
|
||||
|
||||
|
|
@ -83,6 +88,30 @@
|
|||
|
||||
// Bootstrap table markup
|
||||
md.renderer.rules.table_open = function () { return "<table class=\"table table-striped\">\n"; };
|
||||
|
||||
var html5medialPlugin = window.markdownitHTML5Embed;
|
||||
md.use(html5medialPlugin, {html5embed: {
|
||||
inline: false,
|
||||
autoAppend: true,
|
||||
renderFn: function handleBarsRenderFn(parsed, mediaAttributes) {
|
||||
var attributes = mediaAttributes[parsed.mediaType];
|
||||
return HandlebarsTemplates["media-embed_tpl"]({
|
||||
mediaType: parsed.mediaType,
|
||||
attributes: attributes,
|
||||
mimetype: parsed.mimeType,
|
||||
sourceURL: parsed.url,
|
||||
title: parsed.title,
|
||||
fallback: parsed.fallback,
|
||||
needsCover: parsed.mediaType === "video"
|
||||
});
|
||||
},
|
||||
attributes: {
|
||||
"audio": "controls preload=none",
|
||||
"video": "preload=none"
|
||||
},
|
||||
isAllowedMimeType: app.helpers.allowedEmbedsMime
|
||||
}});
|
||||
|
||||
return md.render(text);
|
||||
};
|
||||
})();
|
||||
|
|
|
|||
|
|
@ -63,7 +63,29 @@ app.views.Content = app.views.Base.extend({
|
|||
}
|
||||
},
|
||||
|
||||
// This function is called when user clicks cover for HTML5 embedded video
|
||||
onVideoThumbClick: function(evt) {
|
||||
var clickedThumb;
|
||||
if ($(evt.target).hasClass("thumb")) {
|
||||
clickedThumb = $(evt.target);
|
||||
} else {
|
||||
clickedThumb = $(evt.target).parent(".thumb");
|
||||
}
|
||||
clickedThumb.find(".video-overlay").addClass("hidden");
|
||||
clickedThumb.parents(".collapsed").children(".expander").click();
|
||||
var video = clickedThumb.find("video");
|
||||
video.attr("controls", "");
|
||||
video.get(0).load();
|
||||
video.get(0).play();
|
||||
clickedThumb.unbind("click");
|
||||
},
|
||||
|
||||
bindMediaEmbedThumbClickEvent: function() {
|
||||
this.$(".media-embed .thumb").bind("click", this.onVideoThumbClick);
|
||||
},
|
||||
|
||||
postRenderTemplate : function(){
|
||||
this.bindMediaEmbedThumbClickEvent();
|
||||
_.defer(_.bind(this.collapseOversized, this));
|
||||
|
||||
// run collapseOversized again after all contained images are loaded
|
||||
|
|
@ -93,6 +115,8 @@ app.views.StatusMessage = app.views.Content.extend({
|
|||
|
||||
app.views.ExpandedStatusMessage = app.views.StatusMessage.extend({
|
||||
postRenderTemplate : function(){
|
||||
this.bindMediaEmbedThumbClickEvent();
|
||||
|
||||
var photoAttachments = this.$(".photo-attachments");
|
||||
if(photoAttachments.length > 0) {
|
||||
new app.views.Gallery({ el: photoAttachments });
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@
|
|||
//= require markdown-it-sanitizer
|
||||
//= require markdown-it-sub
|
||||
//= require markdown-it-sup
|
||||
//= require markdown-it-html5-embed
|
||||
//= require highlightjs
|
||||
//= require clear-form
|
||||
//= require corejs-typeahead
|
||||
|
|
|
|||
|
|
@ -88,6 +88,7 @@
|
|||
@import 'chat';
|
||||
@import 'markdown-content';
|
||||
@import 'oembed';
|
||||
@import 'media-embed';
|
||||
@import 'post-content';
|
||||
|
||||
// contacts
|
||||
|
|
|
|||
21
app/assets/stylesheets/media-embed.scss
Normal file
21
app/assets/stylesheets/media-embed.scss
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
$stub-bg-color: #ddd;
|
||||
|
||||
.media-embed {
|
||||
margin-top: 5px;
|
||||
|
||||
.thumb {
|
||||
@include video-overlay;
|
||||
|
||||
background-color: $stub-bg-color;
|
||||
|
||||
video {
|
||||
min-height: 60%;
|
||||
vertical-align: middle;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
audio {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
19
app/assets/templates/media-embed_tpl.jst.hbs
Normal file
19
app/assets/templates/media-embed_tpl.jst.hbs
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
<div class="media-embed">
|
||||
{{#if needsCover}}
|
||||
<div class="thumb">
|
||||
{{/if}}
|
||||
|
||||
<{{mediaType}} {{{attributes}}}>
|
||||
<source type="{{mimetype}}" src="{{sourceURL}}" />
|
||||
{{title}}
|
||||
</{{mediaType}}>
|
||||
|
||||
{{#if needsCover}}
|
||||
<div class="video-overlay">
|
||||
<div class="video-info">
|
||||
<div class="title">{{title}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
|
|
@ -9,7 +9,7 @@ screenshot_opts = "--require features --format pretty"
|
|||
%>
|
||||
|
||||
# 'normal' test runs
|
||||
default: <%= std_opts %> -r features
|
||||
default: <%= std_opts %> -r features --tags ~@nophantomjs
|
||||
wip: -r features --tags @wip:3 --wip features
|
||||
rerun: <%= rerun_opts %> --format rerun --out rerun.txt --strict --tags ~@wip --tags ~@screenshots
|
||||
|
||||
|
|
|
|||
|
|
@ -436,7 +436,7 @@ en:
|
|||
size_of_images_q: "Can I customize the size of images in posts or comments?"
|
||||
size_of_images_a: "No. Images are resized automatically to fit the stream or single-post view. Markdown does not have a code for specifying the size of an image."
|
||||
embed_multimedia_q: "How do I embed a video, audio, or other multimedia content into a post?"
|
||||
embed_multimedia_a: "You can usually just paste the URL (e.g. http://www.youtube.com/watch?v=nnnnnnnnnnn ) into your post and the video or audio will be embedded automatically. The sites supported include: YouTube, Vimeo, SoundCloud, Flickr and a few more. diaspora* uses oEmbed for this feature. We’re supporting more media sources all the time. Remember to always post simple, full links – no shortened links; no operators after the base URL – and give it a little time before you refresh the page after posting for seeing the preview."
|
||||
embed_multimedia_a: "You can usually just paste the URL (e.g. http://www.youtube.com/watch?v=nnnnnnnnnnn ) into your post and the video or audio will be embedded automatically. The sites supported include: YouTube, Vimeo, SoundCloud, Flickr and a few more. diaspora* uses oEmbed for this feature. If you post a direct link to an audio or video file, diaspora* will embed it using standard HTML5 player. We’re supporting more media sources all the time. Remember to always post simple, full links – no shortened links; no operators after the base URL – and give it a little time before you refresh the page after posting for seeing the preview."
|
||||
post_location_q: "How do I add my location to a post?"
|
||||
post_location_a: "Click the pin icon next to the camera in the publisher. This will insert your location from OpenStreetMap. You can edit your location – you might only want to include the city you’re in rather than the specific street address."
|
||||
post_poll_q: "How do I add a poll to my post?"
|
||||
|
|
|
|||
22
features/desktop/media-embed.feature
Normal file
22
features/desktop/media-embed.feature
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
# We can create a separate cucumber profile that will run these tests with Selenium
|
||||
@nophantomjs
|
||||
@javascript
|
||||
Feature: oembed
|
||||
In order to make videos easy accessible
|
||||
As a user
|
||||
I want the media links in my posts be replaced by an embedded player
|
||||
|
||||
Background:
|
||||
Given following user exists:
|
||||
| username | email |
|
||||
| Alice Smith | alice@alice.alice |
|
||||
And I sign in as "alice@alice.alice"
|
||||
|
||||
Scenario: Post a video link
|
||||
When I click the publisher and post "[title](http://example.com/file.ogv)"
|
||||
Then I should see a HTML5 video player
|
||||
|
||||
Scenario: Post an audio link
|
||||
When I click the publisher and post "[title](http://example.com/file.ogg)"
|
||||
Then I should see a HTML5 audio player
|
||||
|
||||
6
features/step_definitions/media_embed_steps.rb
Normal file
6
features/step_definitions/media_embed_steps.rb
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
Then /^I should see a HTML5 (video|audio) player$/ do |type|
|
||||
find(".post-content .media-embed")
|
||||
find(".stream-container").should have_css(".post-content .media-embed #{type}")
|
||||
end
|
||||
|
|
@ -26,13 +26,17 @@ module PublishingCukeHelpers
|
|||
submit_publisher
|
||||
end
|
||||
|
||||
def visible_text_from_markdown(text)
|
||||
CGI.unescapeHTML(ActionController::Base.helpers.strip_tags(Diaspora::MessageRenderer.new(text).markdownified.strip))
|
||||
end
|
||||
|
||||
def submit_publisher
|
||||
txt = find("#publisher #status_message_text").value
|
||||
find("#publisher .btn-primary").click
|
||||
# wait for the publisher to be closed
|
||||
expect(find("#publisher")["class"]).to include("closed")
|
||||
# wait for the content to appear
|
||||
expect(find("#main-stream")).to have_content(txt)
|
||||
expect(find("#main-stream")).to have_content(visible_text_from_markdown(txt))
|
||||
end
|
||||
|
||||
def click_and_post(text)
|
||||
|
|
|
|||
|
|
@ -347,6 +347,54 @@ describe("app.helpers.textFormatter", function(){
|
|||
}
|
||||
});
|
||||
});
|
||||
|
||||
context("media embed", function() {
|
||||
beforeEach(function() {
|
||||
spyOn(app.helpers, "allowedEmbedsMime").and.returnValue(true);
|
||||
});
|
||||
|
||||
it("embeds audio", function() {
|
||||
var html =
|
||||
'<p><a href="https://example.org/file.mp3" target="_blank" rel="noopener noreferrer">title</a></p>\n' +
|
||||
'<div class="media-embed">\n' +
|
||||
"\n" +
|
||||
" <audio controls preload=none>\n" +
|
||||
' <source type="audio/mpeg" src="https://example.org/file.mp3" />\n' +
|
||||
" title\n" +
|
||||
" </audio>\n" +
|
||||
"\n" +
|
||||
"</div>\n";
|
||||
var content = "[title](https://example.org/file.mp3)";
|
||||
var parsed = this.formatter(content);
|
||||
|
||||
expect(parsed).toContain(html);
|
||||
});
|
||||
|
||||
it("embeds video", function() {
|
||||
var html =
|
||||
'<p><a href="https://example.org/file.mp4" target="_blank" rel="noopener noreferrer">title</a></p>\n' +
|
||||
'<div class="media-embed">\n' +
|
||||
' <div class="thumb">\n' +
|
||||
"\n" +
|
||||
" <video preload=none>\n" +
|
||||
' <source type="video/mp4" src="https://example.org/file.mp4" />\n' +
|
||||
" title\n" +
|
||||
" </video>\n" +
|
||||
"\n" +
|
||||
' <div class="video-overlay">\n' +
|
||||
' <div class="video-info">\n' +
|
||||
' <div class="title">title</div>\n' +
|
||||
" </div>\n" +
|
||||
" </div>\n" +
|
||||
" </div>\n" +
|
||||
"</div>\n";
|
||||
|
||||
var content = "[title](https://example.org/file.mp4)";
|
||||
var parsed = this.formatter(content);
|
||||
|
||||
expect(parsed).toContain(html);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
context("real world examples", function(){
|
||||
|
|
|
|||
|
|
@ -36,4 +36,43 @@ describe("app.views.Content", function(){
|
|||
expect(this.view.presenter().location).toEqual(factory.location());
|
||||
});
|
||||
});
|
||||
|
||||
// These tests don't work in PhantomJS because it doesn't support HTML5 <video>.
|
||||
if (/PhantomJS/.exec(navigator.userAgent) === null) {
|
||||
describe("onVideoThumbClick", function() {
|
||||
beforeEach(function() {
|
||||
this.post = new app.models.StatusMessage({text: "[title](https://www.w3schools.com/html/mov_bbb.mp4)"});
|
||||
this.view = new app.views.StatusMessage({model: this.post});
|
||||
|
||||
this.view.render();
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
this.view.$("video").stop();
|
||||
});
|
||||
|
||||
it("hides video overlay", function() {
|
||||
expect(this.view.$(".video-overlay").length).toBe(1);
|
||||
this.view.$(".media-embed .thumb").click();
|
||||
expect(this.view.$(".video-overlay")).toHaveClass("hidden");
|
||||
});
|
||||
|
||||
it("expands posts on click", function() {
|
||||
this.view.$(".collapsible").height(500);
|
||||
this.view.collapseOversized();
|
||||
|
||||
expect(this.view.$(".collapsed").length).toBe(1);
|
||||
this.view.$(".media-embed .thumb").click();
|
||||
expect(this.view.$(".opened").length).toBe(1);
|
||||
});
|
||||
|
||||
it("plays video", function(done) {
|
||||
this.view.$("video").on("playing", function() {
|
||||
done();
|
||||
});
|
||||
|
||||
this.view.$(".media-embed .thumb").click();
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue