diff --git a/app/assets/javascripts/discourse/components/d-editor.js.es6 b/app/assets/javascripts/discourse/components/d-editor.js.es6
index 3f07c7b2f40..78731bef8fe 100644
--- a/app/assets/javascripts/discourse/components/d-editor.js.es6
+++ b/app/assets/javascripts/discourse/components/d-editor.js.es6
@@ -9,6 +9,8 @@ import { emojiUrlFor } from 'discourse/lib/text';
import { getRegister } from 'discourse-common/lib/get-owner';
import { findRawTemplate } from 'discourse/lib/raw-templates';
import { determinePostReplaceSelection, clipboardData } from 'discourse/lib/utilities';
+import { ajax } from 'discourse/lib/ajax';
+import { popupAjaxError } from 'discourse/lib/ajax-error';
import deprecated from 'discourse-common/lib/deprecated';
// Our head can be a static string or a function that returns a string
@@ -616,7 +618,7 @@ export default Ember.Component.extend({
Ember.run.scheduleOnce("afterRender", () => $textarea.focus());
},
- _detectTable(text) {
+ _extractTable(text) {
if (text.endsWith("\n")) {
text = text.substring(0, text.length - 1);
}
@@ -639,21 +641,43 @@ export default Ember.Component.extend({
paste(e) {
const clipboard = clipboardData(e);
- const types = clipboard.types;
- let preventDefault = false;
+ const placeholder = `${ I18n.t('pasting') }`;
+ let plainText = clipboard.getData("text/plain");
+ const html = clipboard.getData("text/html");
+ let handled = false;
- if (types.some(t => t === "text/plain")) {
- const text = clipboard.getData("text/plain");
- const table = this._detectTable(text);
+ if (plainText) {
+ plainText = plainText.trim();
+ const table = this._extractTable(plainText);
if (table) {
this._addText(this._getSelected(), table);
- preventDefault = true;
+ handled = true;
}
- } else if (types.some(t => t === "Files")) {
- preventDefault = true;
}
- if (preventDefault) {
+ if (html && !handled) {
+ const self = this;
+
+ this.appEvents.trigger('composer:insert-text', placeholder);
+ handled = true;
+
+ ajax('/composer/parse_html', {
+ type: 'POST',
+ data: { html }
+ }).then(response => {
+ self.appEvents.trigger('composer:replace-text', placeholder, response.markdown);
+ }).catch(error => {
+ if (plainText) {
+ self.appEvents.trigger('composer:replace-text', placeholder, plainText);
+ } else {
+ popupAjaxError(error);
+ }
+ });
+ }
+
+ const uploadFiles = clipboard.types.includes("Files") && !plainText && !html;
+
+ if (handled || uploadFiles) {
e.preventDefault();
}
},
diff --git a/app/controllers/composer_controller.rb b/app/controllers/composer_controller.rb
new file mode 100644
index 00000000000..6eb78a5d061
--- /dev/null
+++ b/app/controllers/composer_controller.rb
@@ -0,0 +1,12 @@
+require_dependency 'html_to_markdown'
+
+class ComposerController < ApplicationController
+
+ before_action :ensure_logged_in
+
+ def parse_html
+ markdown_text = HtmlToMarkdown.new(params[:html]).to_markdown
+
+ render json: { markdown: markdown_text }
+ end
+end
diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml
index 67e35a9cb2b..4662366d966 100644
--- a/config/locales/client.en.yml
+++ b/config/locales/client.en.yml
@@ -309,6 +309,8 @@ en:
uploading_filename: "Uploading {{filename}}..."
uploaded: "Uploaded!"
+ pasting: "Pasting..."
+
enable: "Enable"
disable: "Disable"
undo: "Undo"
diff --git a/config/routes.rb b/config/routes.rb
index 394ad7d9bde..c66d91e15e4 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -301,6 +301,7 @@ Discourse::Application.routes.draw do
get "session/current" => "session#current"
get "session/csrf" => "session#csrf"
get "composer_messages" => "composer_messages#index"
+ post "composer/parse_html" => "composer#parse_html"
resources :static
post "login" => "static#enter", constraints: { format: /(json|html)/ }
diff --git a/spec/requests/composer_controller_spec.rb b/spec/requests/composer_controller_spec.rb
new file mode 100644
index 00000000000..2a8923c37b2
--- /dev/null
+++ b/spec/requests/composer_controller_spec.rb
@@ -0,0 +1,26 @@
+require 'rails_helper'
+
+RSpec.describe ComposerController do
+ let(:user) { Fabricate(:user) }
+
+ describe '#parse_html' do
+
+ it "should not be able access without sign in" do
+ expect {
+ post "/composer/parse_html.json", params: {
+ html: "hello"
+ }
+ }.to raise_error(Discourse::NotLoggedIn)
+ end
+
+ it "should convert html tags to markdown text" do
+ sign_in(user)
+
+ post "/composer/parse_html.json", params: {
+ html: "hello"
+ }
+
+ expect(response.body).to eq("{\"markdown\":\"**hello**\"}")
+ end
+ end
+end