diff --git a/app/assets/javascripts/discourse/components/composer-title.js.es6 b/app/assets/javascripts/discourse/components/composer-title.js.es6
index 3d806ca0f7d..ddc6a4c4053 100644
--- a/app/assets/javascripts/discourse/components/composer-title.js.es6
+++ b/app/assets/javascripts/discourse/components/composer-title.js.es6
@@ -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 { load, lookupCache } from 'pretty-text/oneboxer';
+import { ajax } from 'discourse/lib/ajax';
export default Ember.Component.extend({
classNames: ['title-input'],
+ watchForLink: Ember.computed.alias('composer.canEditTopicFeaturedLink'),
didInsertElement() {
this._super();
@@ -26,5 +29,74 @@ export default Ember.Component.extend({
if (reason) {
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'));
}
});
diff --git a/app/assets/javascripts/discourse/models/composer.js.es6 b/app/assets/javascripts/discourse/models/composer.js.es6
index 52d8cde6aef..eacdd96c78c 100644
--- a/app/assets/javascripts/discourse/models/composer.js.es6
+++ b/app/assets/javascripts/discourse/models/composer.js.es6
@@ -146,6 +146,11 @@ const Composer = RestModel.extend({
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
actionTitle: function() {
const topic = this.get('topic');
diff --git a/app/assets/javascripts/discourse/templates/components/composer-title.hbs b/app/assets/javascripts/discourse/templates/components/composer-title.hbs
index 1294aa29661..7da7bb6ec38 100644
--- a/app/assets/javascripts/discourse/templates/components/composer-title.hbs
+++ b/app/assets/javascripts/discourse/templates/components/composer-title.hbs
@@ -2,7 +2,7 @@
tabindex="2"
id="reply-title"
maxLength=siteSettings.max_topic_title_length
- placeholderKey="composer.title_placeholder"
+ placeholderKey=composer.titlePlaceholder
disabled=composer.loading}}
{{popup-input-tip validation=validation}}
diff --git a/app/assets/javascripts/discourse/templates/composer.hbs b/app/assets/javascripts/discourse/templates/composer.hbs
index c8712e360e1..1034e3a23fa 100644
--- a/app/assets/javascripts/discourse/templates/composer.hbs
+++ b/app/assets/javascripts/discourse/templates/composer.hbs
@@ -80,11 +80,6 @@
{{/if}}
{{render "additional-composer-buttons" model}}
{{/if}}
- {{#if model.canEditTopicFeaturedLink}}
-
- {{text-field tabindex="4" type="url" value=model.featuredLink id='topic-featured-link' placeholderKey="composer.topic_featured_link_placeholder"}}
-
- {{/if}}
{{/if}}
{{plugin-outlet "composer-fields"}}
diff --git a/app/assets/javascripts/discourse/templates/topic.hbs b/app/assets/javascripts/discourse/templates/topic.hbs
index aed888a39f8..65c0f3f6a81 100644
--- a/app/assets/javascripts/discourse/templates/topic.hbs
+++ b/app/assets/javascripts/discourse/templates/topic.hbs
@@ -25,9 +25,6 @@
{{category-chooser valueAttribute="id" value=buffered.category_id}}
{{/if}}
- {{#if canEditTopicFeaturedLink}}
- {{text-field type="url" value=buffered.featured_link id='topic-featured-link' placeholderKey="composer.topic_featured_link_placeholder"}}
- {{/if}}
{{#if canEditTags}}
{{tag-chooser tags=buffered.tags categoryId=buffered.category_id}}
diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml
index 4255aaa32a6..820734bf5c9 100644
--- a/config/locales/client.en.yml
+++ b/config/locales/client.en.yml
@@ -1072,6 +1072,7 @@ en:
users_placeholder: "Add a user"
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?"
show_edit_reason: "(add edit reason)"
topic_featured_link_placeholder: "Enter link shown with title."
diff --git a/test/javascripts/acceptance/composer-topic-links-test.js.es6 b/test/javascripts/acceptance/composer-topic-links-test.js.es6
new file mode 100644
index 00000000000..98dac6b8409
--- /dev/null
+++ b/test/javascripts/acceptance/composer-topic-links-test.js.es6
@@ -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");
+ });
+});
diff --git a/test/javascripts/helpers/create-pretender.js.es6 b/test/javascripts/helpers/create-pretender.js.es6
index bfb071f90c0..fe1b4d1a294 100644
--- a/test/javascripts/helpers/create-pretender.js.es6
+++ b/test/javascripts/helpers/create-pretender.js.es6
@@ -320,6 +320,26 @@ export default function() {
this.delete('/admin/users/:user_id/revoke_api_key', success);
this.post('/admin/badges', 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"},
+ ''
+ ];
+ }
+
+ if (request.queryParams.url === 'http://www.example.com/no-title.html') {
+ return [
+ 200,
+ {"Content-Type": "application/html"},
+ ''
+ ];
+ }
+
+ return [404, {"Content-Type": "application/html"}, ''];;
+ });
});
server.prepareBody = function(body){
diff --git a/test/javascripts/models/composer-test.js.es6 b/test/javascripts/models/composer-test.js.es6
index 1ed5a691c37..aa400397eba 100644
--- a/test/javascripts/models/composer-test.js.es6
+++ b/test/javascripts/models/composer-test.js.es6
@@ -231,3 +231,19 @@ test("Title length for static page topics as admin", function() {
composer.set('title', '');
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");
+});