FEATURE: pasting a link into the title of the composer can automatically onebox it and update the title
This commit is contained in:
parent
a9acced4ca
commit
fbd8e6ed4a
|
@ -1,8 +1,11 @@
|
||||||
import computed from 'ember-addons/ember-computed-decorators';
|
import { default as computed, observes } from 'ember-addons/ember-computed-decorators';
|
||||||
import InputValidation from 'discourse/models/input-validation';
|
import InputValidation from 'discourse/models/input-validation';
|
||||||
|
import { load, lookupCache } from 'pretty-text/oneboxer';
|
||||||
|
import { ajax } from 'discourse/lib/ajax';
|
||||||
|
|
||||||
export default Ember.Component.extend({
|
export default Ember.Component.extend({
|
||||||
classNames: ['title-input'],
|
classNames: ['title-input'],
|
||||||
|
watchForLink: Ember.computed.alias('composer.canEditTopicFeaturedLink'),
|
||||||
|
|
||||||
didInsertElement() {
|
didInsertElement() {
|
||||||
this._super();
|
this._super();
|
||||||
|
@ -26,5 +29,74 @@ export default Ember.Component.extend({
|
||||||
if (reason) {
|
if (reason) {
|
||||||
return InputValidation.create({ failed: true, reason, lastShownAt: lastValidatedAt });
|
return InputValidation.create({ failed: true, reason, lastShownAt: lastValidatedAt });
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
@observes('composer.titleLength')
|
||||||
|
_titleChanged() {
|
||||||
|
if (this.get('composer.titleLength') === 0) { this.set('autoPosted', false); }
|
||||||
|
if (this.get('autoPosted') || !this.get('watchForLink')) { return; }
|
||||||
|
|
||||||
|
if (Ember.testing) {
|
||||||
|
this._checkForUrl();
|
||||||
|
} else {
|
||||||
|
Ember.run.debounce(this, this._checkForUrl, 500);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
@observes('composer.replyLength')
|
||||||
|
_clearFeaturedLink() {
|
||||||
|
if (this.get('watchForLink') && this.get('composer.replyLength') === 0) {
|
||||||
|
this.set('composer.featuredLink', null);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_checkForUrl() {
|
||||||
|
if (this.get('isAbsoluteUrl') && (this.get('composer.reply')||"").length === 0) {
|
||||||
|
// Try to onebox. If success, update post body and title.
|
||||||
|
|
||||||
|
this.set('composer.loading', true);
|
||||||
|
|
||||||
|
const link = document.createElement('a');
|
||||||
|
link.href = this.get('composer.title');
|
||||||
|
|
||||||
|
let loadOnebox = load(link, false, ajax);
|
||||||
|
|
||||||
|
if (loadOnebox && loadOnebox.then) {
|
||||||
|
loadOnebox.then( () => {
|
||||||
|
this._updatePost(lookupCache(this.get('composer.title')));
|
||||||
|
}).finally(() => {
|
||||||
|
this.set('composer.loading', false);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this._updatePost(loadOnebox);
|
||||||
|
this.set('composer.loading', false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_updatePost(html) {
|
||||||
|
if (html) {
|
||||||
|
this.set('autoPosted', true);
|
||||||
|
this.set('composer.featuredLink', this.get('composer.title'));
|
||||||
|
|
||||||
|
const $h = $(html),
|
||||||
|
header = $h.find('h4').length > 0 ? $h.find('h4') : $h.find('h3');
|
||||||
|
|
||||||
|
this.set('composer.reply', this.get('composer.title'));
|
||||||
|
|
||||||
|
if (header.length > 0 && header.text().length > 0) {
|
||||||
|
this.set('composer.title', header.text().trim());
|
||||||
|
} else {
|
||||||
|
const filename = (this.get('composer.featuredLink')||"").split("/").pop();
|
||||||
|
if (filename && filename.length > 0) {
|
||||||
|
this.set('composer.title', filename.trim());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
@computed('composer.title')
|
||||||
|
isAbsoluteUrl() {
|
||||||
|
return this.get('composer.titleLength') > 0 && /^(https?:)?\/\/[\w\.\-]+/i.test(this.get('composer.title'));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -146,6 +146,11 @@ const Composer = RestModel.extend({
|
||||||
return categoryIds === undefined || !categoryIds.length || categoryIds.indexOf(categoryId) !== -1;
|
return categoryIds === undefined || !categoryIds.length || categoryIds.indexOf(categoryId) !== -1;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@computed('canEditTopicFeaturedLink')
|
||||||
|
titlePlaceholder() {
|
||||||
|
return this.get('canEditTopicFeaturedLink') ? 'composer.title_or_link_placeholder' : 'composer.title_placeholder';
|
||||||
|
},
|
||||||
|
|
||||||
// Determine the appropriate title for this action
|
// Determine the appropriate title for this action
|
||||||
actionTitle: function() {
|
actionTitle: function() {
|
||||||
const topic = this.get('topic');
|
const topic = this.get('topic');
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
tabindex="2"
|
tabindex="2"
|
||||||
id="reply-title"
|
id="reply-title"
|
||||||
maxLength=siteSettings.max_topic_title_length
|
maxLength=siteSettings.max_topic_title_length
|
||||||
placeholderKey="composer.title_placeholder"
|
placeholderKey=composer.titlePlaceholder
|
||||||
disabled=composer.loading}}
|
disabled=composer.loading}}
|
||||||
|
|
||||||
{{popup-input-tip validation=validation}}
|
{{popup-input-tip validation=validation}}
|
||||||
|
|
|
@ -80,11 +80,6 @@
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{render "additional-composer-buttons" model}}
|
{{render "additional-composer-buttons" model}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{#if model.canEditTopicFeaturedLink}}
|
|
||||||
<div class="topic-featured-link-input">
|
|
||||||
{{text-field tabindex="4" type="url" value=model.featuredLink id='topic-featured-link' placeholderKey="composer.topic_featured_link_placeholder"}}
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{plugin-outlet "composer-fields"}}
|
{{plugin-outlet "composer-fields"}}
|
||||||
|
|
|
@ -25,9 +25,6 @@
|
||||||
{{category-chooser valueAttribute="id" value=buffered.category_id}}
|
{{category-chooser valueAttribute="id" value=buffered.category_id}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{#if canEditTopicFeaturedLink}}
|
|
||||||
{{text-field type="url" value=buffered.featured_link id='topic-featured-link' placeholderKey="composer.topic_featured_link_placeholder"}}
|
|
||||||
{{/if}}
|
|
||||||
{{#if canEditTags}}
|
{{#if canEditTags}}
|
||||||
<br>
|
<br>
|
||||||
{{tag-chooser tags=buffered.tags categoryId=buffered.category_id}}
|
{{tag-chooser tags=buffered.tags categoryId=buffered.category_id}}
|
||||||
|
|
|
@ -1072,6 +1072,7 @@ en:
|
||||||
|
|
||||||
users_placeholder: "Add a user"
|
users_placeholder: "Add a user"
|
||||||
title_placeholder: "What is this discussion about in one brief sentence?"
|
title_placeholder: "What is this discussion about in one brief sentence?"
|
||||||
|
title_or_link_placeholder: "Type title, or paste a link here"
|
||||||
edit_reason_placeholder: "why are you editing?"
|
edit_reason_placeholder: "why are you editing?"
|
||||||
show_edit_reason: "(add edit reason)"
|
show_edit_reason: "(add edit reason)"
|
||||||
topic_featured_link_placeholder: "Enter link shown with title."
|
topic_featured_link_placeholder: "Enter link shown with title."
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
import { acceptance } from "helpers/qunit-helpers";
|
||||||
|
|
||||||
|
acceptance("Composer topic featured links", {
|
||||||
|
loggedIn: true,
|
||||||
|
settings: {
|
||||||
|
topic_featured_link_enabled: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
test("onebox with title", () => {
|
||||||
|
visit("/");
|
||||||
|
click('#create-topic');
|
||||||
|
fillIn('#reply-title', "http://www.example.com/has-title.html");
|
||||||
|
andThen(() => {
|
||||||
|
ok(find('.d-editor-preview').html().trim().indexOf('onebox') > 0, "it pastes the link into the body and previews it");
|
||||||
|
ok(exists('.d-editor-textarea-wrapper .popup-tip.good'), 'the body is now good');
|
||||||
|
equal(find('.title-input input').val(), "An interesting article", "title is from the oneboxed article");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("onebox result doesn't include a title", () => {
|
||||||
|
visit("/");
|
||||||
|
click('#create-topic');
|
||||||
|
fillIn('#reply-title', 'http://www.example.com/no-title.html');
|
||||||
|
andThen(() => {
|
||||||
|
ok(find('.d-editor-preview').html().trim().indexOf('onebox') > 0, "it pastes the link into the body and previews it");
|
||||||
|
ok(exists('.d-editor-textarea-wrapper .popup-tip.good'), 'the body is now good');
|
||||||
|
equal(find('.title-input input').val(), "no-title.html", "title is from the end of the url");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("no onebox result", () => {
|
||||||
|
visit("/");
|
||||||
|
click('#create-topic');
|
||||||
|
fillIn('#reply-title', "http://www.example.com/nope-onebox.html");
|
||||||
|
andThen(() => {
|
||||||
|
equal(find('.d-editor-preview').html().trim().indexOf('onebox'), -1, "link isn't put into the post");
|
||||||
|
equal(find('.d-editor-input').val().length, 0, "link isn't put into the post");
|
||||||
|
equal(find('.title-input input').val(), "http://www.example.com/nope-onebox.html", "title is unchanged");
|
||||||
|
});
|
||||||
|
});
|
|
@ -320,6 +320,26 @@ export default function() {
|
||||||
this.delete('/admin/users/:user_id/revoke_api_key', success);
|
this.delete('/admin/users/:user_id/revoke_api_key', success);
|
||||||
this.post('/admin/badges', success);
|
this.post('/admin/badges', success);
|
||||||
this.delete('/admin/badges/:id', success);
|
this.delete('/admin/badges/:id', success);
|
||||||
|
|
||||||
|
this.get('/onebox', request => {
|
||||||
|
if (request.queryParams.url === 'http://www.example.com/has-title.html') {
|
||||||
|
return [
|
||||||
|
200,
|
||||||
|
{"Content-Type": "application/html"},
|
||||||
|
'<aside class="onebox"><article class="onebox-body"><h3><a href="http://www.example.com/article.html">An interesting article</a></h3></article></aside>'
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (request.queryParams.url === 'http://www.example.com/no-title.html') {
|
||||||
|
return [
|
||||||
|
200,
|
||||||
|
{"Content-Type": "application/html"},
|
||||||
|
'<aside class="onebox"><article class="onebox-body"><p>No title</p></article></aside>'
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [404, {"Content-Type": "application/html"}, ''];;
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
server.prepareBody = function(body){
|
server.prepareBody = function(body){
|
||||||
|
|
|
@ -231,3 +231,19 @@ test("Title length for static page topics as admin", function() {
|
||||||
composer.set('title', '');
|
composer.set('title', '');
|
||||||
ok(!composer.get('titleLengthValid'), "admins must set title to at least 1 character");
|
ok(!composer.get('titleLengthValid'), "admins must set title to at least 1 character");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("title placeholder depends on what you're doing", function() {
|
||||||
|
let composer = createComposer({action: Composer.CREATE_TOPIC});
|
||||||
|
equal(composer.get('titlePlaceholder'), 'composer.title_placeholder', "placeholder for normal topic");
|
||||||
|
|
||||||
|
composer = createComposer({action: Composer.PRIVATE_MESSAGE});
|
||||||
|
equal(composer.get('titlePlaceholder'), 'composer.title_placeholder', "placeholder for private message");
|
||||||
|
|
||||||
|
Discourse.SiteSettings.topic_featured_link_enabled = true;
|
||||||
|
|
||||||
|
composer = createComposer({action: Composer.CREATE_TOPIC});
|
||||||
|
equal(composer.get('titlePlaceholder'), 'composer.title_or_link_placeholder', "placeholder invites you to paste a link");
|
||||||
|
|
||||||
|
composer = createComposer({action: Composer.PRIVATE_MESSAGE});
|
||||||
|
equal(composer.get('titlePlaceholder'), 'composer.title_placeholder', "placeholder for private message with topic links enabled");
|
||||||
|
});
|
||||||
|
|
Loading…
Reference in New Issue