Skip to content
This repository was archived by the owner on Jul 14, 2022. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 79 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@ A small demo app demonstrating a working setup for Rails 3.2 ([demo](http://murm
### Add the gem to your Gemfile

gem 'tinymce-rails-imageupload', '~> 4.0.0.beta'

# or use git

gem 'tinymce-rails-imageupload', github: 'PerfectlyNormal/tinymce-rails-imageupload'

### Set up TinyMCE as you would normally, but in the call to `.tinymce()`, add
Expand All @@ -41,6 +41,20 @@ A small demo app demonstrating a working setup for Rails 3.2 ([demo](http://murm

and the rest should happen automatically.

You can also globally have imageupload globally disabled but enabled on specific instances.

~~~yml
# tinymce.yml
toolbar: bold italic underline | uploadimage
plugins:
- uploadimage
uploadimage: false
~~~

~~~erb
<%= tinymce uploadimage: true %>
~~~

### Set up upload URL and handler

The plugin defaults to POSTing to `/tinymce_assets`. You may modify it by
Expand All @@ -51,10 +65,6 @@ Set it up using something similar in `routes.rb`:

post '/tinymce_assets' => 'tinymce_assets#create'

The plugin will relay option `uploadimage_hint` in the call to `.tinymce()`
to the POSTed URL as param `hint`. You may use this to relay any hints
you wish (for example, blog post ID #) to the controller.

This action gets called with a file parameter creatively called `file`,
and must respond with JSON, containing the URL to the image.

Expand All @@ -76,19 +86,81 @@ Example:
end
end


If the JSON response contains a `width` and/or `height` key,
those will be used in the inserted HTML (`<img src="..." width="..." height="...">`),
but if those are not present, the inserted HTML is just `<img src="...">`.

### Hint param

Per request `hint` data can be sent to the `create` action through the call to `.tinymce()` or `tinymce.yml`. You may use this to relay any hints you wish (for example, blog post ID #) to the controller.

- `uploadimage_hint_key` - override the hint key. Default is `hint`.
- `uploadimage_hint` - hint value.

Example:

~~~erb
<%= tinymce uploadimage_hint_key: 'post_id', uploadimage_hint: @post.id %>
~~~

Would result in a `params` object that looks like this:

~~~ruby
{
"post_id": 1,
"file": ...,
// ...
}
~~~

### Model attributes

Params can be sent in a more standard attributes format by setting `uploadimage_model`.

- `uploadimage_model` - nest attributes within model namespace.

Example:

~~~erb
<%= tinymce uploadimage_model: 'post' %>
~~~

Would result in a `params` object that looks like this:

~~~ruby
{
"post": {
"file": ...,
// ...
},
}
~~~

### Default class for img tag

By default the plugin doesn't assign any class to the img tag.
You can set the class(es) by supplying the `uploadimage_default_img_class`
option in the call to `.tinymce()`.

`class="..."` will only be added to the img tag if a default is specified.
`class="..."` will only be added to the img tag if a default or custom class is specified.
Otherwise the inserted HTML is just `<img src="...">`.

### Custom classes

You can set `image_class_list` to an array of `title`, `value` objects to provide uploaders a pre-defined list of CSS classes to apply.

~~~yml
# tinymce.yml
image_class_list:
- title: 'Center'
value: 'img-center'
- title: 'Left thumbnail'
value: 'img-left img-thumbnail'
- title: 'Right thumbnail'
value: 'img-right img-thumbnail'
~~~

## Asset Pipeline

Several people have had trouble with asset precompilation using the asset pipeline, both for the locales, and the plugin itself.
Expand Down
127 changes: 88 additions & 39 deletions app/assets/javascripts/tinymce/plugins/uploadimage/plugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,44 @@
iframe,
win,
throbber,
selected_class = '',
editor = ed;

function showDialog() {
var classList = getClassList();
var body = [
{type: 'iframe', url: 'javascript:void(0)'},
{type: 'textbox', name: 'file', label: ed.translate('Choose an image'), subtype: 'file'},
{type: 'textbox', name: 'alt', label: ed.translate('Image description')}
];

if (classList.length > 0) {
selected_class = classList[0].value;
body = body.concat([
{
type: 'listbox',
name: 'class',
label: ed.translate('Class'),
values: classList,
onSelect: function(e) {
selected_class = this.value();
}
}
]);
}

body = body.concat([
{type: 'container', classes: 'error', html: "<p style='color: #b94a48;'>&nbsp;</p>"},

// Trick TinyMCE to add a empty div that "preloads" the throbber image
{type: 'container', classes: 'throbber'}
]);

win = editor.windowManager.open({
title: ed.translate('Insert an image from your computer'),
width: 520 + parseInt(editor.getLang('uploadimage.delta_width', 0), 10),
height: 180 + parseInt(editor.getLang('uploadimage.delta_height', 0), 10),
body: [
{type: 'iframe', url: 'javascript:void(0)'},
{type: 'textbox', name: 'file', label: ed.translate('Choose an image'), subtype: 'file'},
{type: 'textbox', name: 'alt', label: ed.translate('Image description')},
{type: 'container', classes: 'error', html: "<p style='color: #b94a48;'>&nbsp;</p>"},

// Trick TinyMCE to add a empty div that "preloads" the throbber image
{type: 'container', classes: 'throbber'},
],
body: body,
buttons: [
{
text: ed.translate('Insert'),
Expand All @@ -38,10 +60,6 @@
plugin_url: url
});

// TinyMCE likes pointless submit handlers
win.off('submit');
win.on('submit', insertImage);

/* WHY DO YOU HATE <form>, TINYMCE!? */
iframe = win.find("iframe")[0];
form = createElement('form', {
Expand All @@ -59,7 +77,7 @@
// Create some needed hidden inputs
form.appendChild(createElement('input', {type: "hidden", name: "utf8", value: "✓"}));
form.appendChild(createElement('input', {type: 'hidden', name: 'authenticity_token', value: getMetaContents('csrf-token')}));
form.appendChild(createElement('input', {type: 'hidden', name: 'hint', value: ed.getParam("uploadimage_hint", "")}));
form.appendChild(createElement('input', {type: 'hidden', name: hintName(), value: hintValue()}));

var el = win.getEl();
var body = document.getElementById(el.id + "-body");
Expand All @@ -77,7 +95,7 @@

if(ctrl.tagName.toLowerCase() == 'input' && ctrl.type != "hidden") {
if(ctrl.type == "file") {
ctrl.name = "file";
ctrl.name = inputName('file');

// Hack styles
tinymce.DOM.setStyles(ctrl, {
Expand All @@ -86,16 +104,32 @@
'webkitBoxShadow': 'none',
});
} else {
ctrl.name = "alt";
ctrl.name = inputName('alt');
}
}
}

body.appendChild(form);
}

function hintName() {
return inputName(ed.getParam('uploadimage_hint_key', 'hint'));
}

function hintValue() {
return ed.getParam('uploadimage_hint', '');
}

function inputName(name) {
if (ed.getParam('uploadimage_model', false)) {
return ed.getParam('uploadimage_model') + '[' + name + ']';
} else {
return name;
}
}

function insertImage() {
if(getInputValue("file") == "") {
if(getInputValue(inputName('file')) == "") {
return handleError('You must choose a file');
}

Expand Down Expand Up @@ -128,7 +162,7 @@
var target = iframe.getEl();
if(target.document || target.contentDocument) {
var doc = target.contentDocument || target.contentWindow.document;
if(String(doc.contentType).includes("html")) {
if(String(doc.contentType).indexOf("html") > -1) {
handleResponse(doc.getElementsByTagName("body")[0].innerHTML);
} else {
handleResponse(doc.getElementsByTagName("pre")[0].innerHTML);
Expand Down Expand Up @@ -180,19 +214,20 @@
}

function buildHTML(json) {
var image = json[ed.getParam('uploadimage_model', 'image')];
var default_class = ed.getParam("uploadimage_default_img_class", "");
var figure = ed.getParam("uploadimage_figure", false);
var alt_text = getInputValue("alt");
var alt_text = getInputValue(inputName('alt'));

var imgstr = "<img src='" + json["image"]["url"] + "'";
var imgstr = "<img src='" + image["url"] + "'";

if(default_class != "")
imgstr += " class='" + default_class + "'";
imgstr += " class='" + default_class + ' ' + selected_class + "'";

if(json["image"]["height"])
imgstr += " height='" + json["image"]["height"] + "'";
if(json["image"]["width"])
imgstr += " width='" + json["image"]["width"] + "'";
if(image["height"])
imgstr += " height='" + image["height"] + "'";
if(image["width"])
imgstr += " width='" + image["width"] + "'";

imgstr += " alt='" + alt_text + "'/>";

Expand Down Expand Up @@ -237,20 +272,34 @@
return null;
}

// Add a button that opens a window
editor.addButton('uploadimage', {
tooltip: ed.translate('Insert an image from your computer'),
icon : 'image',
onclick: showDialog
});

// Adds a menu item to the tools menu
editor.addMenuItem('uploadimage', {
text: ed.translate('Insert an image from your computer'),
icon : 'image',
context: 'insert',
onclick: showDialog
});
function getClassList() {
var config = ed.getParam('image_class_list', []);
var values = [];
for (var i = 0; i < config.length; i++) {
values[i] = {
text: config[i]['title'],
value: config[i]['value']
};
}
return values;
};

if (editor.getParam('uploadimage', true)) {
// Add a button that opens a window
editor.addButton('uploadimage', {
tooltip: ed.translate('Insert an image from your computer'),
icon : 'image',
onclick: showDialog
});

// Adds a menu item to the tools menu
editor.addMenuItem('uploadimage', {
text: ed.translate('Insert an image from your computer'),
icon : 'image',
context: 'insert',
onclick: showDialog
});
}
}
});

Expand Down