Comment embeds: add UI for creating embed code

This commit is contained in:
David Wick
2015-01-13 17:23:37 -08:00
parent 6493c95ea2
commit e5d579e020
16 changed files with 285 additions and 44 deletions

View File

@@ -254,7 +254,7 @@ class FrontController(RedditController):
#check for 304
self.check_modified(article, 'comments')
embeds.setup_embed()
embeds.setup_embed(thing=comment)
# If there is a focal comment, communicate down to
# comment_skeleton.html who that will be. Also, skip

View File

@@ -1,20 +1,34 @@
from pylons import c, g, request
from pylons.controllers.util import abort
from r2.controllers.reddit_base import (
get_browser_langs,
UnloggedUser,
)
from r2.controllers.reddit_base import UnloggedUser
from r2.models.subreddit import Subreddit
def setup_embed():
DISALLOWED_SR_TYPES = {"private", "gold_restricted"}
def can_embed(thing):
try:
sr = Subreddit._byID(thing.sr_id) if thing.sr_id else None
except NotFound:
sr = None
return (sr != None and not sr.type in DISALLOWED_SR_TYPES)
def setup_embed(thing):
if request.GET.get("embed") == "true":
if request.host != g.media_domain:
# don't serve up untrusted content except on our
# specifically untrusted domain
abort(404)
if not can_embed(thing):
abort(404)
c.render_style = "iframe"
c.user = UnloggedUser(get_browser_langs())
c.user = UnloggedUser([c.lang])
c.user_is_loggedin = False
c.forced_loggedout = True
c.allow_framing = True

View File

@@ -493,8 +493,13 @@ module["reddit"] = LocalizedModule("reddit.js",
"filter.js",
"recommender.js",
"action-forms.js",
"embed.js",
"saved.js",
"messages.js",
"embed/custom-event.js",
"embed/utils.js",
"embed/post-message.js",
"embed/comment-embed.js",
PermissionsDataSource({
"moderator": ModeratorPermissionSet,
"moderator_invite": ModeratorPermissionSet,

View File

@@ -310,6 +310,8 @@ class Reddit(Templated):
if srbar and not c.cname and not is_api():
self.srtopbar = SubredditTopBar()
panes = [content]
if c.user_is_loggedin and not is_api() and not self.show_wiki_actions:
# insert some form templates for js to use
# TODO: move these to client side templates
@@ -337,16 +339,15 @@ class Reddit(Templated):
)
report_form = ReportForm()
panes = [ShareLink(), content, report_form]
panes.extend([ShareLink(), report_form])
if self.show_sidebar:
panes.extend([gold_comment, gold_link])
if c.user_is_sponsor:
panes.append(FraudForm())
self._content = PaneStack(panes)
else:
self._content = content
self._content = PaneStack(panes)
self.show_chooser = (
show_chooser and

View File

@@ -21,7 +21,12 @@
###############################################################################
from r2.lib.db.thing import NotFound
from r2.lib.menus import Styled
from r2.lib.menus import (
JsButton,
NavButton,
NavMenu,
Styled,
)
from r2.lib.wrapped import Wrapped
from r2.models import LinkListing, Link, PromotedLink, Report
from r2.models import make_wrapper, IDBuilder, Thing
@@ -166,6 +171,22 @@ class CommentButtons(PrintableButtons):
show_givegold = thing.can_gild
embed_button = False
from r2.lib import embeds
if embeds.can_embed(thing):
embed_button = JsButton("embed",
css_class="embed-comment",
data={
"media": g.media_domain or g.domain,
"comment": thing.permalink,
"link": thing.link.make_permalink(thing.subreddit),
"title": thing.link.title,
"root": ("true" if thing.parent_id is None else "false"),
})
embed_button.build()
PrintableButtons.__init__(self, "commentbuttons", thing,
can_save=thing.can_save,
is_author = is_author,
@@ -179,12 +200,13 @@ class CommentButtons(PrintableButtons):
parent_permalink = thing.parent_permalink,
can_reply = thing.can_reply,
suppress_reply_buttons = suppress_reply_buttons,
show_report = show_report,
show_report=show_report,
mod_reports=thing.mod_reports,
user_reports=thing.user_reports,
show_distinguish = show_distinguish,
show_delete = show_delete,
show_givegold=show_givegold,
embed_button=embed_button,
)
class MessageButtons(PrintableButtons):

View File

@@ -184,6 +184,7 @@ def js_config(extra_config=None):
"adtracker_url": g.adtracker_url,
"clicktracker_url": g.clicktracker_url,
"uitracker_url": g.uitracker_url,
"comment_embed_scripts": js.src("comment-embed", absolute=True),
"static_root": static(''),
"over_18": bool(c.over18),
"new_window": bool(c.user.pref_newwindow),

View File

@@ -18,6 +18,10 @@
}
.input-placeholder();
textarea& {
height: auto;
}
}
.c-form-group {
@@ -30,7 +34,6 @@
position: relative;
display: block;
font-size: 12px;
min-height: @input-height-base;
margin-top: 10px;
margin-bottom: 10px;
@@ -47,7 +50,8 @@
line-height: normal;
position: absolute;
margin-left: -21px;
margin-top: 4px \9;
margin-top: 0;
margin-top: 4px \9; // ie hack
&:focus {
outline: thin dotted;

View File

@@ -82,7 +82,7 @@ p {
list-style: none;
margin-top: 36px;
.reddit-embed-content > .reddit-embed-list > & {
.reddit-embed-content > .reddit-embed-list > &:first-child {
margin-top: 0;
}
}

View File

@@ -10551,3 +10551,24 @@ body.banned.deleted div#header {
#compose-message select {
font-size: 100%;
}
#embed-preview {
margin: -5px;
margin-bottom: 0;
overflow-y: hidden;
// re-add margin to non-iframed version
.reddit-embed {
margin: 5px;
}
}
.more-actions {
.title {
color: #888;
&:hover {
cursor: pointer;
}
}
}

View File

@@ -24,7 +24,11 @@ r.actionForm = {
e.preventDefault();
if ($thingForm.length > 0) {
$thingForm.toggle();
if ($el.parents('.drop-choices').length) {
$thingForm.show();
} else {
$thingForm.toggle();
}
} else {
var $form = $(formSelector);
var $clonedForm = $form.clone();

View File

@@ -0,0 +1,142 @@
;(function($, undefined) {
var COMMENT_EMBED_SCRIPTS = r.config.comment_embed_scripts.map(function (src) {
var attrs = r.config.comment_embed_scripts.length === 1 ? 'async' : '';
return '<script ' + attrs + ' src="' + src + '"></script>';
}).join('');
var commentModalTemplate = _.template(
'<div class="modal fade" tabindex="-1" role="dialog">' +
'<div class="modal-dialog modal-dialog-lg">' +
'<div class="modal-content">' +
'<div class="modal-header">' +
'<a href="javascript: void 0;" class="c-close c-hide-text" data-dismiss="modal">' +
_.escape(r._('close this window')) +
'</a>' +
'</div>' +
'<div class="modal-body">' +
'<h4 class="modal-title">' +
_.escape(r._('Embed preview:')) +
'</h4>' +
'<div id="embed-preview">' +
'<%= html %>' +
'</div>' +
'<% if (!root) { %>' +
'<div class="c-checkbox">' +
'<label class="remember">' +
'<input type="checkbox" name="parent" <% if (parent) { %> checked <% } %>>' +
_.escape(r._('Include parent comment.')) +
'</label>' +
'</div>' +
'<% } %>' +
'<div class="c-checkbox">' +
'<label>' +
'<input type="checkbox" name="live" <% if (!live) { %> checked <% } %> data-rerender="false">' +
_.escape(r._('Do not show comment if edited.')) +
'&nbsp;' +
'<a href="/help/embed#live-update">' +
_.escape(r._('Learn more')) +
'</a>' +
'</label>' +
'</div>' +
'</div>' +
'<div class="modal-footer">' +
'<div class="c-form-group">' +
'<label for="embed-code" class="modal-title">' +
_.escape(r._('Copy this code and paste it into your website:')) +
'</label>' +
'<textarea class="c-form-control" id="embed-code" rows="3" readonly>' +
'<%= html %>' +
'<%- scripts %>' +
'</textarea>' +
'</div>' +
'</div>' +
'</div>' +
'</div>' +
'</div>'
);
var embedCodeTemplate = _.template(
'<div class="reddit-embed" ' +
' data-embed-media="<%- media %>" ' +
'<% if (parent) { %> data-embed-parent="true" <% } %>' +
'<% if (live) { %> data-embed-live="true" <% } %>' +
' data-embed-created="<%- new Date().toISOString() %>">' +
'<a href="<%- comment %>">Comment</a> from discussion <a href="<%- link %>"><%- title %></a>.' +
'</div>'
);
function absolute(url) {
if (/^https?:\/\//.test(url)) {
return url;
}
return 'https://' + location.host + '/' + (url.replace(/^\//, ''));
}
function getEmbedOptions(data) {
var defaults = {
live: true,
parent: false,
media: location.host,
};
data = _.defaults({}, data, defaults);
data.comment = absolute(data.comment);
data.link = absolute(data.link);
return _.extend({
html: embedCodeTemplate(data),
scripts: COMMENT_EMBED_SCRIPTS,
}, data);
}
$('body').on('click', '.embed-comment', function(e) {
var $el = $(e.target);
var data = $el.data();
var $dialog = $(commentModalTemplate(getEmbedOptions(data)));
var $textarea = $dialog.find('textarea');
var $preview = $dialog.find('#embed-preview');
$dialog.on('change', '[type="checkbox"]', function(e) {
var option = e.target.name;
var $option = $(e.target);
var prev = $el.data(option);
if (prev === undefined) {
prev = embedOptions[option]
}
$el.data(e.target.name, !prev);
var data = $el.data();
var options = getEmbedOptions(data);
var html = options.html;
var height = $preview.height();
$textarea.val(html + options.scripts);
if ($option.data('rerender') !== false) {
$preview.height(height).html(html);
window.rembeddit.init(function () {
$preview.css({height: 'auto'});
});
}
});
$textarea.on('focus', function() {
$(this).select();
});
$dialog.on('hidden.bs.modal', function() {
$dialog.remove();
});
$dialog.on('shown.bs.modal', function() {
window.rembeddit.init();
});
$dialog.modal();
});
})(window.jQuery);

View File

@@ -1,10 +1,11 @@
;(function(App, window, undefined) {
var RE_COMMENT = /(?:https?\:)?(\/\/(?:www\.)?reddit\.(?:com|local)(?:\:\d+)?\/r\/[\w_]+\/comments\/(?:[\w_]+\/){2,}[\w_]+\/?)/i;
var RE_ABS = /^https?:\/\//i;
var RE_COMMENT = /\/?r\/[\w_]+\/comments\/(?:[\w_]+\/){2,}[\w_]+\/?/i;
var PROTOCOL = location.protocol === 'file:' ? 'https:' : '';
function isComment(url) {
return typeof url === 'string' && RE_COMMENT.test(url);
function isComment(anchor) {
return RE_ABS.test(anchor.href) && RE_COMMENT.test(anchor.pathname);
}
function getCommentPathname(anchor) {
@@ -30,13 +31,14 @@
context++;
}
var query = 'context=' + context +
var query = 'embed=true' +
'&context=' + context +
'&depth=' + (++context) +
'&showedits=' + data.embedLive +
'&created=' + data.embedCreated +
'&showmore=false';
return PROTOCOL + (commentUrl.replace(/\/$/,'')) + '.iframe?' + query;
return PROTOCOL + (commentUrl.replace(/\/$/,'')) + '?' + query;
}
App.init = function(callback) {
@@ -46,7 +48,6 @@
var iframe = document.createElement('iframe');
var anchors = embed.getElementsByTagName('a');
var commentUrl = getCommentUrl(anchors, embed.dataset.embedMedia);
var loaded = false;
if (!commentUrl) {
return;
@@ -59,18 +60,24 @@
iframe.style.display = 'none';
iframe.src = getEmbedUrl(commentUrl, embed.dataset);
App.receiveMessage('resize', function(e) {
iframe.height = (e.detail + 'px');
iframe.style.display = 'block';
if (!loaded) {
loaded = true;
callback && callback(e);
}
App.receiveMessageOnce('loaded', function() {
embed.parentNode.removeChild(embed);
callback && callback(e);
});
embed.parentNode.replaceChild(iframe, embed);
var resizer = App.receiveMessage('resize', function(e) {
if (!iframe.parentNode) {
resizer.off();
return;
}
iframe.height = (e.detail + 'px');
iframe.style.display = 'block';
});
embed.parentNode.insertBefore(iframe, embed);
});
};

View File

@@ -19,7 +19,7 @@
}
function compileOriginRegExp(origins) {
return new RegExp('http(s)?:\\/\\/' + origins.join('|'), 'i');
return new RegExp('^http(s)?:\\/\\/' + origins.join('|'), 'i');
}
function isWildcard(origin) {
@@ -53,6 +53,16 @@
};
},
receiveMessageOnce: function(type, callback, context) {
var listener = App.receiveMessage(type, function() {
callback && callback();
listener.off();
}, context);
return listener;
},
addPostMessageOrigin: function(origin) {
if (isWildcard(origin)) {
allowedOrigins = [ALLOW_WILDCARD];

View File

@@ -9,6 +9,11 @@ function open_menu(menu) {
.addClass("active inuse");
};
function close_menu(item) {
$(item).closest('.drop-choices')
.removeClass('active inuse');
}
function close_menus(event) {
$(".drop-choices.inuse").not(".active")
.removeClass("inuse");

View File

@@ -171,7 +171,7 @@ r.saved.SaveButton = {
r.saved.categories = new r.saved.SaveCategories()
r.saved.init = function() {
$('body').on('click', '.save-button a', function(e) {
$('body').on('click', '.save-button a, a.save-button', function(e) {
e.stopPropagation()
e.preventDefault()
r.saved.SaveButton.toggleSaved($(this))

View File

@@ -333,16 +333,21 @@
<li class="first">
${self.bylink_button(_("permalink"), thing.permalink)}
</li>
%if thing.embed_button:
<li>
${thing.embed_button.render()}
</li>
%endif
%if thing.can_save:
%if thing.saved:
<li class="comment-unsave-button save-button">
<a href="javascript:void(0)">${_("unsave")}</a>
</li>
%else:
<li class="comment-save-button save-button">
<a href="javascript:void(0)">${_("save")}</a>
</li>
%endif
%if thing.saved:
<li class="comment-unsave-button save-button">
<a href="javascript:void(0)">${_("unsave")}</a>
</li>
%else:
<li class="comment-save-button save-button">
<a href="javascript:void(0)">${_("save")}</a>
</li>
%endif
%endif
%if c.profilepage: