safe_in_place_editing 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +318 -0
- data/app/assets/safe_in_place_editing/safe_in_place_editing.js +78 -0
- data/lib/safe_in_place_editing.rb +15 -0
- data/lib/safe_in_place_editing/controller_methods.rb +123 -0
- data/lib/safe_in_place_editing/helper_methods.rb +287 -0
- data/lib/safe_in_place_editing/version.rb +3 -0
- metadata +68 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA1:
|
|
3
|
+
metadata.gz: 384a023dadf89e878b381e57b2c352ecd7317916
|
|
4
|
+
data.tar.gz: 7ef531b3a8ee0bbeb7ea6c7ce983f0de1593cf5d
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 21f0dedc4d6d5643730e53bb73569e0657b8ec39e05c1d1a7b4e0d61cc17b2e50a9f39e97aba73d33b08b4e8b4ed49f726798dbdce7d12aa36571815caa17149
|
|
7
|
+
data.tar.gz: 620f9756d1595728dc2e9665e51e99788eaddb8021a615032c9681fab3139a45b2ece720e032e43d1d4f02b86059d960be7a3215e47bb4891e668126195ae7ea
|
data/MIT-LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
Copyright (c) 2013 Hipposoft
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
4
|
+
a copy of this software and associated documentation files (the
|
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
9
|
+
the following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be
|
|
12
|
+
included in all copies or substantial portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
= Safe In Place Editing v2.00 (2013-10-22)
|
|
2
|
+
|
|
3
|
+
Early versions of Ruby On Rails shipped with an in-place editing mechanism
|
|
4
|
+
which allowed simple edits to be performed on model attribute values without
|
|
5
|
+
having to render a full 'edit' view for that model.
|
|
6
|
+
|
|
7
|
+
Unfortunately this suffered a significant vulnerability which basically meant
|
|
8
|
+
that any in-place editor field was an open JavaScript console, much to the
|
|
9
|
+
presumed delight of malicious users. In-place editing features were later moved
|
|
10
|
+
into a plug-in and the cross-site scripting fault was fixed; this simple plugin
|
|
11
|
+
is available from GitHub:
|
|
12
|
+
|
|
13
|
+
* https://github.com/amerine/in_place_editing
|
|
14
|
+
|
|
15
|
+
The original version's vulnerability compelled me to write a replacement. Even
|
|
16
|
+
after the original was fixed, it still had some shortcomings and the Safe In
|
|
17
|
+
Place Editing code addresses those. The full rationale for its creation, along
|
|
18
|
+
with instructions on its use are given below.
|
|
19
|
+
|
|
20
|
+
Version 1.03 of this software is designed for Rails 3, which deprecates plug-in
|
|
21
|
+
code. Installation is therefore a bit more complex than running "script/plugin"
|
|
22
|
+
as under Rails 2. Such is progress, it seems...!
|
|
23
|
+
|
|
24
|
+
This plug-in would not exist without the original in-place editor code,
|
|
25
|
+
from which it borrows very heavily. My thanks to all involved.
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
== Upgrading
|
|
29
|
+
|
|
30
|
+
If you were using the plug-in version of Safe In Place Editing, you need to
|
|
31
|
+
delete the old Safe In Place Editing code from inside "vendor/plugins" in your
|
|
32
|
+
app. You will also need to modify the way you include the support JavaScript
|
|
33
|
+
code from this:
|
|
34
|
+
|
|
35
|
+
<%= javascript_include_tag( "safe_in_place_editing/safe_in_place_editing" ) %>
|
|
36
|
+
|
|
37
|
+
...to this:
|
|
38
|
+
|
|
39
|
+
<%= javascript_include_tag( "safe_in_place_editing" ) %>
|
|
40
|
+
|
|
41
|
+
A global search and replace of "safe_in_place_editing/safe_in_place_editing/"
|
|
42
|
+
to just "safe_in_place_editing" across your application files may suffice.
|
|
43
|
+
|
|
44
|
+
Now follow the steps given in "Installation" below, to install the new Gem
|
|
45
|
+
version of Safe In Place Editing.
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
== Installation
|
|
49
|
+
|
|
50
|
+
Add this line to your application's Gemfile:
|
|
51
|
+
|
|
52
|
+
gem 'safe_in_place_editing'
|
|
53
|
+
|
|
54
|
+
You will need to use the Prototype JavaScript library rather than jQuery (the
|
|
55
|
+
two may coexist peacefully, but I haven't tested it). To use that, add the
|
|
56
|
+
following to your Gemfile, if you don't already have it:
|
|
57
|
+
|
|
58
|
+
gem 'prototype-rails'
|
|
59
|
+
|
|
60
|
+
You can find out more about proper usage of the JavaScript files provided by
|
|
61
|
+
this Gem here:
|
|
62
|
+
|
|
63
|
+
* https://github.com/rails/prototype-rails
|
|
64
|
+
|
|
65
|
+
Finally, execute:
|
|
66
|
+
|
|
67
|
+
bundle
|
|
68
|
+
|
|
69
|
+
Please note that the Gem here has only been tested in Rails 3 applications
|
|
70
|
+
that use the Rails Asset Pipeline, so though it might work on (for example)
|
|
71
|
+
legacy upgraded Rails 2 applications that don't use the asset pipeline file
|
|
72
|
+
structure, I can't be sure. If you encounter problems, consider switching
|
|
73
|
+
over to the pipeline instead.
|
|
74
|
+
|
|
75
|
+
* http://guides.rubyonrails.org/asset_pipeline.html
|
|
76
|
+
|
|
77
|
+
See "Usage exmaples" later for information on how to use the Gem.
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
== InPlaceEditing problems and Safe In Place Editing solutions
|
|
81
|
+
=== 1: InPlaceEditing's XSS fixes prevent other features from working
|
|
82
|
+
|
|
83
|
+
At the time of writing the original in-place-editor plugin escapes values
|
|
84
|
+
written into the view coming from the database and values written into the
|
|
85
|
+
view after a user makes an edit, avoiding the vulnerability which it used
|
|
86
|
+
to introduce. This introduces some problems though:
|
|
87
|
+
|
|
88
|
+
1. It's no longer possible to deliberately insert HTML at all, as per older
|
|
89
|
+
documentation examples for Textile and Markaby-based in-place editors.
|
|
90
|
+
|
|
91
|
+
2. The in-place editor form, when shown, shows the _literal_ escaped text (so
|
|
92
|
+
you get double-escape problems, with things like "&" showing up instead
|
|
93
|
+
of "&") because it uses a "getText" call in JavaScript that reads
|
|
94
|
+
"innerHTML" of the <span> used to mark up the text to be used for
|
|
95
|
+
editing. There are two ways around this:
|
|
96
|
+
|
|
97
|
+
* Patch getText to a better implementation as described on the
|
|
98
|
+
Scriptaculous Wiki (why is this not in their core release?!)
|
|
99
|
+
|
|
100
|
+
https://madrobby.github.com/scriptaculous/ajax-inplaceeditor/
|
|
101
|
+
|
|
102
|
+
Object.extend(Ajax.InPlaceEditor.prototype, {
|
|
103
|
+
getText: function() {
|
|
104
|
+
return this.element.childNodes[0] ?
|
|
105
|
+
this.element.childNodes[0].nodeValue : '';
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
* Have a custom method to return the unescaped value of the field
|
|
110
|
+
sitting on the server side (again, as for the Textile and Markaby
|
|
111
|
+
examples) - but this means one more controller method and a server
|
|
112
|
+
round-trip each time an editor is shown.
|
|
113
|
+
|
|
114
|
+
Safe In Place Editing patches getText as described above, escapes values in
|
|
115
|
+
the initially created form and its auto-generated helpers, if used, escape
|
|
116
|
+
updated values when rendering them for the in-place view update. You can
|
|
117
|
+
defeat escaping in the safe_in_place_editor_field helper method with a
|
|
118
|
+
special override parameter but this is strongly discouraged.
|
|
119
|
+
|
|
120
|
+
You must include the JavaScript code for the getText patch and supporting
|
|
121
|
+
methods when using the plugin:
|
|
122
|
+
|
|
123
|
+
<%= javascript_include_tag( "safe_in_place_editing/safe_in_place_editing" ) %>
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
=== 2: InPlaceEditing bypasses optimistic locking
|
|
127
|
+
|
|
128
|
+
Optimistic locking is a safety feature in Rails which even the core
|
|
129
|
+
developers seem to forget about sometimes! It associates a version number
|
|
130
|
+
with models. If two users are viewing an 'edit' page for that model and one of
|
|
131
|
+
them submits their form first, then when the other user tries to submit their
|
|
132
|
+
own edits, Rails will detect that the associated version number on their form
|
|
133
|
+
is too old and raise a locking error. Without this, the second user would be
|
|
134
|
+
able to just overwrite the edits made by the first user without anybody
|
|
135
|
+
realising this error had happened.
|
|
136
|
+
|
|
137
|
+
Unfortunately the stock InPlaceEditing plugin is written in such a way that
|
|
138
|
+
locking is bypassed; an in-place editor always succeeds, no matter how out of
|
|
139
|
+
date the view in which the editor resides happens to be.
|
|
140
|
+
|
|
141
|
+
One might take the approach of the anti-forgery request mechanism patch needed
|
|
142
|
+
to get the in-place edit plug-in working with Rails 2, extending the ":with"
|
|
143
|
+
key's value in the options hash with a query string passing the lock_version
|
|
144
|
+
through:
|
|
145
|
+
|
|
146
|
+
if ( object.respond_to?( :lock_version ) )
|
|
147
|
+
in_place_editor_options[ :with ] ||= "Form.serialize(form)"
|
|
148
|
+
in_place_editor_options[ :with ] += " + '&lock_version=#{ object.lock_version }'"
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
The update code on the server side could manually check this against the
|
|
152
|
+
object it just found in the database. Unfortunately the client JavaScript
|
|
153
|
+
code is static, so after a first update the form itself is out of date,
|
|
154
|
+
passing an old lock version through and all subsequent update attempts
|
|
155
|
+
fail until the whole view is reloaded.
|
|
156
|
+
|
|
157
|
+
lock_version = nil
|
|
158
|
+
lock_version = @item.lock_version.to_s if ( @item.respond_to?( :lock_version ) )
|
|
159
|
+
|
|
160
|
+
if ( lock_version != params[ :lock_version ] )
|
|
161
|
+
# Somebody else already edited this item. Do "something"
|
|
162
|
+
# (see later).
|
|
163
|
+
else
|
|
164
|
+
@item.update_attribute( attribute, params[ :value ] )
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
We might attempt to write out JS which assigns global variables unique to
|
|
168
|
+
each form with the initial lock value. An on-complete JS handler could
|
|
169
|
+
then increment the lock version at the client side. This seems ridiculously
|
|
170
|
+
over complicated given the task at hand, requires one to override the
|
|
171
|
+
default on-complete handler, bypass or extend the Rails plug-in (since it
|
|
172
|
+
offers no interface to change the on-complete handler details) and the
|
|
173
|
+
client might still get out of sync with the server's lock versions. Since
|
|
174
|
+
there is little alternative, however, Safe In Place Editing takes this
|
|
175
|
+
heavyweight approach, using extra JS support methods to try and reduce the
|
|
176
|
+
inline code baggage.
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
=== 3: InPlaceEditing's error handling seems to be faulty
|
|
180
|
+
|
|
181
|
+
In theory, returning a 500 error should lead to the onFailure handler running
|
|
182
|
+
in the JS domain, but when used from Rails 2.0.2, just about all properties of
|
|
183
|
+
the 'transport' object used in the default handler function are undefined with
|
|
184
|
+
the standard InPlaceEditing plugin. As a result, no alert box can be shown to the user. The onComplete handler is *always* run, regardless of whether the
|
|
185
|
+
request returns a 2xx or other status code and this leads to numerous problems
|
|
186
|
+
when trying to elegantly handle errors.
|
|
187
|
+
|
|
188
|
+
The JavaScript assistance functions included with SafeInPlaceEditing take
|
|
189
|
+
care of error handling for you. If the on-failure code seems to be having
|
|
190
|
+
trouble then the on-complete code will take over. These functions are also
|
|
191
|
+
used to support optimistic locking as described above.
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
== Usage examples
|
|
195
|
+
|
|
196
|
+
Any view using Safe In Place Editing requires the Prototype JavaScript library
|
|
197
|
+
to be included. See https://github.com/rails/prototype-rails for details. Most
|
|
198
|
+
people just do this in the "app/assets/javascripts/application.js" file.
|
|
199
|
+
|
|
200
|
+
Either in your main application layout file, add:
|
|
201
|
+
|
|
202
|
+
<%= javascript_include_tag( "safe_in_place_editing" ) %>
|
|
203
|
+
|
|
204
|
+
...in the document <head> section, or include the script via the asset pipeline
|
|
205
|
+
by editing "app/assets/javascripts/application.js" and adding:
|
|
206
|
+
|
|
207
|
+
//= require safe_in_place_editing
|
|
208
|
+
|
|
209
|
+
Whether you include these resources globally or only for the views that require
|
|
210
|
+
them, don't forget to add both the Prototype JavaScript library files and the
|
|
211
|
+
Safe In Place Editing support code.
|
|
212
|
+
|
|
213
|
+
In your controller, declare the models and model attribute names which are
|
|
214
|
+
to be available for in-place editing. These declarations cause actions to be
|
|
215
|
+
defined in your controller on your behalf; the actions handle the XHR requests
|
|
216
|
+
from the client JavaScript code executing in web browsers when users alter
|
|
217
|
+
attributes of a model via an in-place editor control.
|
|
218
|
+
|
|
219
|
+
safe_in_place_edit_for( :customer, :title )
|
|
220
|
+
safe_in_place_edit_for( :customer, :code )
|
|
221
|
+
|
|
222
|
+
The above sets up a controller so it allows edits to a model called "Customer"
|
|
223
|
+
for attributes "title" and "code".
|
|
224
|
+
|
|
225
|
+
In the view, wherever you want an editor to be available, add a call to the
|
|
226
|
+
"safe_in_place_editor_field" method. For example, in the case of the controller
|
|
227
|
+
for the "Customer" model above, we might produce a 'show' view for a model
|
|
228
|
+
instance stored in "<tt>@customer</tt>" which includes:
|
|
229
|
+
|
|
230
|
+
<strong>Title:</strong>
|
|
231
|
+
<%= safe_in_place_editor_field( @customer, :title ) %>
|
|
232
|
+
<br />
|
|
233
|
+
|
|
234
|
+
<strong>Code:</strong>
|
|
235
|
+
<%= safe_in_place_editor_field( @customer, :code ) %>
|
|
236
|
+
<br />
|
|
237
|
+
|
|
238
|
+
If you're familiar with the API for the InPlaceEditing plugin, you may be
|
|
239
|
+
surprised at the use of "<tt>@customer</tt>" rather than ":customer" in the
|
|
240
|
+
call to "safe_in_place_editor_field". In fact, this call supports _either_
|
|
241
|
+
form - you can pass a symbol "<tt>:foo</tt>", in which case the plugin assumes
|
|
242
|
+
that an instance variable "<tt>@foo</tt>" is available - or you can just pass
|
|
243
|
+
in the variable value directly. Judging by Google searches this quirk of the
|
|
244
|
+
InPlaceEditing API seemed to trip up quite a few people and I saw no reason to
|
|
245
|
+
duplicate that quirk with Safe In Place Editing!
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
=== Boolean values
|
|
249
|
+
|
|
250
|
+
As an added bonus, Safe In Place Editing has special support for boolean values
|
|
251
|
+
in models. If you have a true/false field, an in-place editor will show a small
|
|
252
|
+
pop-up menu including "Yes" and "No" entries.
|
|
253
|
+
|
|
254
|
+
Suppose we have a Task model and the task can be marked as currently active, or
|
|
255
|
+
inactive. To this end it has an attribute "active" which is a boolean property.
|
|
256
|
+
We can create an in-place editor for this by first enabling editing in the
|
|
257
|
+
controller for Tasks:
|
|
258
|
+
|
|
259
|
+
safe_in_place_edit_for( :task, :active )
|
|
260
|
+
|
|
261
|
+
...then in the view, inserting an in-place editor where we might otherwise have
|
|
262
|
+
just shown the value of the 'active' attribute as a simple piece of text:
|
|
263
|
+
|
|
264
|
+
<strong>Active:</strong>
|
|
265
|
+
<%= safe_in_place_editor_field( @task, :active ) %>
|
|
266
|
+
|
|
267
|
+
It's that simple; the plugin code takes care of the rest. There are numerous
|
|
268
|
+
additional options which can be passed to the various calls; see the API
|
|
269
|
+
documentation for InPlaceEditing for the basics, then check the API
|
|
270
|
+
documentation here for any exceptions or additions.
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
=== Suggested CSS
|
|
274
|
+
|
|
275
|
+
You can style in-place editors however you like; the default Rails scaffold
|
|
276
|
+
styles may be sufficient. If using scaffolding, though, you might like to try
|
|
277
|
+
out the following additional styles as I think they give good results:
|
|
278
|
+
|
|
279
|
+
form.inplaceeditor-form {
|
|
280
|
+
position: absolute;
|
|
281
|
+
background: white;
|
|
282
|
+
border: 2px solid #888;
|
|
283
|
+
text-align: center;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
form.inplaceeditor-form input[ type="text" ] {
|
|
287
|
+
margin: 5px;
|
|
288
|
+
width: 90%;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
form.inplaceeditor-form input[ type="submit" ] {
|
|
292
|
+
margin: 5px;
|
|
293
|
+
float: left;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
form.inplaceeditor-form a {
|
|
297
|
+
margin: 5px;
|
|
298
|
+
float: right;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
form.inplaceeditor-form select {
|
|
302
|
+
margin: 5px;
|
|
303
|
+
float: left;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
== Contacts
|
|
308
|
+
|
|
309
|
+
Ideally please raise issues, suggestions, pull requests etc. via GitHub:
|
|
310
|
+
|
|
311
|
+
* https://github.com/pond/safe_in_place_editing
|
|
312
|
+
|
|
313
|
+
Alternatively free to contact me at "ahodgkin@rowing.org.uk".
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
= Copyright
|
|
317
|
+
|
|
318
|
+
Copyright (c) 2008-2013 Hipposoft, released under the MIT license.
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/************************************************************************\
|
|
2
|
+
* File: safe_in_place_editing.js *
|
|
3
|
+
* Hipposoft 2008 *
|
|
4
|
+
* *
|
|
5
|
+
* Purpose: Safe, lockable in-place editing - client-side code. *
|
|
6
|
+
* *
|
|
7
|
+
* History: 24-Jun-2008 (ADH): Created. *
|
|
8
|
+
\************************************************************************/
|
|
9
|
+
|
|
10
|
+
/* Stop "Jack & Jill", written for display purposes into an HTML page,
|
|
11
|
+
* from being edited as exactly that - "Jack & Jill" - if the in-place
|
|
12
|
+
* editor is activated due to Prototype's use of "innerHTML" in its internal
|
|
13
|
+
* "getText" function. See:
|
|
14
|
+
*
|
|
15
|
+
* http://github.com/madrobby/scriptaculous/wikis/ajax-inplaceeditor
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
Object.extend
|
|
19
|
+
(
|
|
20
|
+
Ajax.InPlaceEditor.prototype,
|
|
21
|
+
{
|
|
22
|
+
getText: function()
|
|
23
|
+
{
|
|
24
|
+
return this.element.childNodes[ 0 ] ? this.element.childNodes[ 0 ].nodeValue : '';
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
/* Support the "on success" and "on failure" functions */
|
|
30
|
+
|
|
31
|
+
var safeInPlaceEditorDoneFailureReport = false;
|
|
32
|
+
|
|
33
|
+
/* Custom in-place editor "on failure" function */
|
|
34
|
+
|
|
35
|
+
function safeInPlaceEditorOnFailure( transport )
|
|
36
|
+
{
|
|
37
|
+
if ( transport.responseText )
|
|
38
|
+
{
|
|
39
|
+
safeInPlaceEditorRaiseAlert( transport );
|
|
40
|
+
safeInPlaceEditorDoneFailureReport = true;
|
|
41
|
+
}
|
|
42
|
+
else
|
|
43
|
+
{
|
|
44
|
+
safeInPlaceEditorDoneFailureReport = false;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/* Custom in-place editor "on complete" function (call by proxy to set
|
|
49
|
+
* the value of 'lockVar' with the name of the lock variable, if any,
|
|
50
|
+
* held in global (i.e. 'window') context).
|
|
51
|
+
*/
|
|
52
|
+
|
|
53
|
+
function safeInPlaceEditorOnComplete( transport, element, lockVar )
|
|
54
|
+
{
|
|
55
|
+
if ( transport.status == 200 )
|
|
56
|
+
{
|
|
57
|
+
if ( lockVar ) window[ lockVar ] += 1;
|
|
58
|
+
}
|
|
59
|
+
else if ( ! safeInPlaceEditorDoneFailureReport )
|
|
60
|
+
{
|
|
61
|
+
safeInPlaceEditorRaiseAlert( transport );
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
safeInPlaceEditorDoneFailureReport = false;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/* Helper function - raise an alert describing the given transport object's
|
|
68
|
+
* responseText value.
|
|
69
|
+
*/
|
|
70
|
+
|
|
71
|
+
function safeInPlaceEditorRaiseAlert( transport )
|
|
72
|
+
{
|
|
73
|
+
alert
|
|
74
|
+
(
|
|
75
|
+
"Error communicating with the server: " +
|
|
76
|
+
transport.responseText.stripTags()
|
|
77
|
+
);
|
|
78
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
require 'safe_in_place_editing/version'
|
|
2
|
+
require 'safe_in_place_editing/controller_methods'
|
|
3
|
+
require 'safe_in_place_editing/helper_methods'
|
|
4
|
+
|
|
5
|
+
if defined? ActionController
|
|
6
|
+
ActionController::Base.send :include, SafeInPlaceEditing
|
|
7
|
+
ActionController::Base.helper SafeInPlaceEditingHelper
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
module SafeInPlaceEditing
|
|
11
|
+
module Rails
|
|
12
|
+
class Engine < ::Rails::Engine
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
########################################################################
|
|
2
|
+
# File:: safe_in_place_editing.rb
|
|
3
|
+
# (C):: Hipposoft 2008, 2009
|
|
4
|
+
#
|
|
5
|
+
# Purpose:: Safe, lockable in-place editing - controller support.
|
|
6
|
+
# ----------------------------------------------------------------------
|
|
7
|
+
# 24-Jun-2008 (ADH): Created.
|
|
8
|
+
# 22-Oct-2013 (ADH): Incorporated into 'gemified' sources.
|
|
9
|
+
########################################################################
|
|
10
|
+
|
|
11
|
+
module SafeInPlaceEditing
|
|
12
|
+
|
|
13
|
+
def self.included( base ) # :nodoc:
|
|
14
|
+
base.extend( ClassMethods )
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
module ClassMethods
|
|
18
|
+
|
|
19
|
+
# == Overview
|
|
20
|
+
#
|
|
21
|
+
# API equivalent of in_place_edit_for circa 2009, except:
|
|
22
|
+
#
|
|
23
|
+
# - Runs all user data through "ERB::Util::html_escape" when sending it to
|
|
24
|
+
# the view to avoid associated vulnerabilities with otherwise-unescaped
|
|
25
|
+
# user-supplied data; the current InPlaceEditing plugin does this too,
|
|
26
|
+
# albeit using "CGI::escapeHTML" for some reason.
|
|
27
|
+
#
|
|
28
|
+
# - Supports optimistic locking if a lock_version CGI parameter is
|
|
29
|
+
# supplied, by explicitly checking the version being updated.
|
|
30
|
+
#
|
|
31
|
+
# - Explicitly catches errors and returns them as 500 status codes
|
|
32
|
+
# with a plain text message regardless of Rails environment.
|
|
33
|
+
#
|
|
34
|
+
# - You must include some support JavaScript code and Prototype 1.7.x is
|
|
35
|
+
# assumed - Rails default tends to be 1.6.x - though it _should_ still
|
|
36
|
+
# work with older Prototype library versions. See the example below for
|
|
37
|
+
# details.
|
|
38
|
+
#
|
|
39
|
+
# See +safe_in_place_editor+ and +safe_in_place_editor_field+ for the
|
|
40
|
+
# counterpart helper functions.
|
|
41
|
+
#
|
|
42
|
+
#
|
|
43
|
+
# == Simple example
|
|
44
|
+
#
|
|
45
|
+
# This is adapted from the repository at:
|
|
46
|
+
#
|
|
47
|
+
# * https://github.com/amerine/in_place_editing
|
|
48
|
+
#
|
|
49
|
+
# ...many thanks to those involved.
|
|
50
|
+
#
|
|
51
|
+
# # Controller
|
|
52
|
+
# #
|
|
53
|
+
# class BlogController < ApplicationController
|
|
54
|
+
# safe_in_place_edit_for( :post, :title )
|
|
55
|
+
# end
|
|
56
|
+
#
|
|
57
|
+
# # View
|
|
58
|
+
# #
|
|
59
|
+
# <%= safe_in_place_editor_field( :post, 'title' ) %>
|
|
60
|
+
#
|
|
61
|
+
# # Application layout file, document <head> section
|
|
62
|
+
# #
|
|
63
|
+
# <%= javascript_include_tag( "safe_in_place_editing/safe_in_place_editing" ) %>
|
|
64
|
+
#
|
|
65
|
+
def safe_in_place_edit_for( object, attribute, options = {} )
|
|
66
|
+
define_method( "set_#{ object }_#{ attribute }" ) do
|
|
67
|
+
safe_in_place_edit_backend( object, attribute, options )
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
private
|
|
73
|
+
|
|
74
|
+
# Back-end for "safe_in_place_edit_for" - the actual invoked implementation
|
|
75
|
+
# of the dynamically created functions.
|
|
76
|
+
#
|
|
77
|
+
def safe_in_place_edit_backend( object, attribute, options )
|
|
78
|
+
@item = object.to_s.camelize.constantize.find( params[ :id ] )
|
|
79
|
+
|
|
80
|
+
lock_version = nil
|
|
81
|
+
lock_version = @item.lock_version.to_s if ( @item.respond_to?( :lock_version ) )
|
|
82
|
+
|
|
83
|
+
if ( params.include?( :lock_version ) and lock_version != params[ :lock_version ] )
|
|
84
|
+
render( { :status => 500, :text => "Somebody else already edited this #{ object.to_s.humanize.downcase }. Reload the page to obtain the updated version." } )
|
|
85
|
+
return
|
|
86
|
+
else
|
|
87
|
+
begin
|
|
88
|
+
|
|
89
|
+
# Call "touch" to make sure the item is modified even if the user has
|
|
90
|
+
# actually just submitted the form with an unchanged variable. This
|
|
91
|
+
# makes sure that Rails sees the object as 'dirty' and saves it. For
|
|
92
|
+
# objects with lock versions, that means the lock version always
|
|
93
|
+
# increments. The JavaScript code has to assume such an increment and
|
|
94
|
+
# has no clear way to know if it doesn't happen; we could dream up
|
|
95
|
+
# something complex but simpler just to ensure Rails is in step.
|
|
96
|
+
#
|
|
97
|
+
# In the worst possible case, JavaScript and Rails end up out of step
|
|
98
|
+
# with the lock version and the user gets told there's a mismatch. A
|
|
99
|
+
# page reload later and everything is sorted out.
|
|
100
|
+
|
|
101
|
+
success = @item.update_attribute( attribute, params[ :value ] )
|
|
102
|
+
success = @item.touch if ( success && ! lock_version.nil? && @item.lock_version.to_s == lock_version )
|
|
103
|
+
|
|
104
|
+
raise "Unable to save changes to database" unless ( success )
|
|
105
|
+
|
|
106
|
+
rescue => error
|
|
107
|
+
render( { :status => 500, :text => error.message } )
|
|
108
|
+
return
|
|
109
|
+
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
value = @item.send( attribute )
|
|
114
|
+
|
|
115
|
+
if ( ( value.is_a? TrueClass ) || ( value.is_a? FalseClass ) )
|
|
116
|
+
value = value ? 'Yes' : 'No'
|
|
117
|
+
else
|
|
118
|
+
value = ERB::Util::html_escape( value.to_s )
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
render( { :text => value } )
|
|
122
|
+
end
|
|
123
|
+
end
|
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
########################################################################
|
|
2
|
+
# File:: safe_in_place_editing_helper.rb
|
|
3
|
+
# (C):: Hipposoft 2008, 2009
|
|
4
|
+
#
|
|
5
|
+
# Purpose:: Safe, lockable in-place editing - helper methods.
|
|
6
|
+
# ----------------------------------------------------------------------
|
|
7
|
+
# 24-Jun-2008 (ADH): Created.
|
|
8
|
+
# 22-Oct-2013 (ADH): Incorporated into 'gemified' sources.
|
|
9
|
+
########################################################################
|
|
10
|
+
|
|
11
|
+
module SafeInPlaceEditingHelper
|
|
12
|
+
|
|
13
|
+
# == Overview
|
|
14
|
+
#
|
|
15
|
+
# API equivalent of in_place_editor circa 2009, except fixes various bugs
|
|
16
|
+
# (see README.rdoc for the rationale) and:
|
|
17
|
+
#
|
|
18
|
+
# * New option ":lock_var", which is the name of a global variable to be
|
|
19
|
+
# used in the JS domain to track the lock version at the client side. By
|
|
20
|
+
# default set to nil, meaning no optimistic locking support. Options
|
|
21
|
+
# value ":lock_version" MUST be set to the lock version of the object for
|
|
22
|
+
# which the in-place editor is being created in this case. The variable
|
|
23
|
+
# is incremented each time the object is successfully updated through the
|
|
24
|
+
# in-place editor, since the server will have incremented its lock version
|
|
25
|
+
# so the client must keep in step. If someone else edits the item, the
|
|
26
|
+
# client and server lock versions will not match and the update will fail,
|
|
27
|
+
# which is the desired result.
|
|
28
|
+
#
|
|
29
|
+
# * The ":save_text" option is set to "OK" by default, since I detest that
|
|
30
|
+
# nasty lower case "ok" button that's produced by the JS code otherwise.
|
|
31
|
+
#
|
|
32
|
+
# * The ":cancel_text" option is set to "Cancel" by default to match the
|
|
33
|
+
# above change.
|
|
34
|
+
#
|
|
35
|
+
# * New option ":is_boolean" indicating a true/false popup should be offered
|
|
36
|
+
# instead of a text field; if omitted, a text field is assumed. If present
|
|
37
|
+
# and 'true', additional optional value ":first_value" says whether or not
|
|
38
|
+
# the pop-up menu should start with True/Yes (if ":first_value"'s value is
|
|
39
|
+
# itself true), or False/No (if ":first_value"'s value is itself false, or
|
|
40
|
+
# if the option is omitted).
|
|
41
|
+
#
|
|
42
|
+
# Custom on-failure and on-complete functions are used. To try and reduce
|
|
43
|
+
# the code bulk for each instance of the editor, hard-coded JS function names
|
|
44
|
+
# are used with the support code placed in 'safe_in_place_editing.js'. See
|
|
45
|
+
# there for a reference implementation if intending to write customised
|
|
46
|
+
# equivalents. To override the default names of these functions for any
|
|
47
|
+
# reason, give the names as strings in options properties :on_complete and
|
|
48
|
+
# :on_failure, then make sure appropriate JS functions are actually defined.
|
|
49
|
+
#
|
|
50
|
+
#
|
|
51
|
+
# == Original In Place Editor documentation
|
|
52
|
+
#
|
|
53
|
+
# This is copied from the repository at:
|
|
54
|
+
#
|
|
55
|
+
# * https://github.com/amerine/in_place_editing
|
|
56
|
+
#
|
|
57
|
+
# ...many thanks to those involved.
|
|
58
|
+
#
|
|
59
|
+
# Makes an HTML element specified by the DOM ID +field_id+ become an in-place
|
|
60
|
+
# editor of a property.
|
|
61
|
+
#
|
|
62
|
+
# A form is automatically created and displayed when the user clicks the element,
|
|
63
|
+
# something like this:
|
|
64
|
+
#
|
|
65
|
+
# <form id="myElement-in-place-edit-form" target="specified url">
|
|
66
|
+
# <input name="value" text="The content of myElement"/>
|
|
67
|
+
# <input type="submit" value="ok"/>
|
|
68
|
+
# <a onclick="javascript to cancel the editing">cancel</a>
|
|
69
|
+
# </form>
|
|
70
|
+
#
|
|
71
|
+
# The form is serialized and sent to the server using an AJAX call, the action on
|
|
72
|
+
# the server should process the value and return the updated value in the body of
|
|
73
|
+
# the reponse. The element will automatically be updated with the changed value
|
|
74
|
+
# (as returned from the server).
|
|
75
|
+
#
|
|
76
|
+
# options with (Must be a function) means that you must pass a string like:
|
|
77
|
+
#
|
|
78
|
+
# function(transport, element) {what_to_do()}
|
|
79
|
+
#
|
|
80
|
+
# Required +options+ are:
|
|
81
|
+
# <tt>:url</tt>:: Specifies the url where the updated value should
|
|
82
|
+
# be sent after the user presses "ok".
|
|
83
|
+
#
|
|
84
|
+
# Addtional +options+ are:
|
|
85
|
+
# <tt>:rows</tt>:: Number of rows (more than 1 will use a TEXTAREA)
|
|
86
|
+
# <tt>:cols</tt>:: Number of characters the text input should span (works for both INPUT and TEXTAREA)
|
|
87
|
+
# <tt>:size</tt>:: Synonym for :cols when using a single line text input.
|
|
88
|
+
# <tt>:cancel_text</tt>:: The text on the cancel link. (default: "cancel")
|
|
89
|
+
# <tt>:save_text</tt>:: The text on the save link. (default: "ok")
|
|
90
|
+
# <tt>:loading_text</tt>:: The text to display while the data is being loaded from the server (default: "Loading...")
|
|
91
|
+
# <tt>:saving_text</tt>:: The text to display when submitting to the server (default: "Saving...")
|
|
92
|
+
# <tt>:external_control</tt>:: The id of an external control used to enter edit mode.
|
|
93
|
+
# <tt>:load_text_url</tt>:: URL where initial value of editor (content) is retrieved.
|
|
94
|
+
# <tt>:options</tt>:: Pass through options to the AJAX call (see prototype's Ajax.Updater)
|
|
95
|
+
# <tt>:with</tt>:: JavaScript snippet that should return what is to be sent
|
|
96
|
+
# in the AJAX call, +form+ is an implicit parameter
|
|
97
|
+
# <tt>:script</tt>:: Instructs the in-place editor to evaluate the remote JavaScript response (default: false)
|
|
98
|
+
# <tt>:click_to_edit_text</tt>:: The text shown during mouseover the editable text (default: "Click to edit")
|
|
99
|
+
# <tt>:on_complete</tt>:: (Must be a function)Code run if update successful with server. Also if user cancels the form (see https://prototype.lighthouseapp.com/projects/8887/tickets/243).
|
|
100
|
+
# <tt>:on_failure</tt>:: (Must be a function)Code run if update failed with server
|
|
101
|
+
#
|
|
102
|
+
def safe_in_place_editor( field_id, options = {} )
|
|
103
|
+
|
|
104
|
+
# Set up some default values
|
|
105
|
+
|
|
106
|
+
options[ :with ] ||= "Form.serialize(form).replace(/\\+/g,'%20')"
|
|
107
|
+
|
|
108
|
+
if protect_against_forgery?
|
|
109
|
+
options[ :with ] += " + '&authenticity_token=' + encodeURIComponent('#{ form_authenticity_token }')"
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
options[ :on_complete ] ||= 'safeInPlaceEditorOnComplete'
|
|
113
|
+
options[ :on_failure ] ||= 'safeInPlaceEditorOnFailure'
|
|
114
|
+
options[ :save_text ] ||= 'OK'
|
|
115
|
+
options[ :cancel_text ] ||= 'Cancel'
|
|
116
|
+
|
|
117
|
+
# Preliminary script data
|
|
118
|
+
|
|
119
|
+
if ( options.include?( :lock_var ) )
|
|
120
|
+
function = "window['#{ options[ :lock_var ] }']=#{ options[ :lock_version ] };"
|
|
121
|
+
else
|
|
122
|
+
function = ''
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
function_name = options[ :is_boolean ] ? 'InPlaceCollectionEditor' : 'InPlaceEditor'
|
|
126
|
+
|
|
127
|
+
function << "new Ajax.#{ function_name }("
|
|
128
|
+
function << "'#{ field_id }', "
|
|
129
|
+
function << "'#{ url_for( options[ :url ] ) }'"
|
|
130
|
+
|
|
131
|
+
# Map Rails in-place editor options to JS in-place editor options - see:
|
|
132
|
+
#
|
|
133
|
+
# http://github.com/madrobby/scriptaculous/wikis/ajax-inplaceeditor
|
|
134
|
+
|
|
135
|
+
js_options = {}
|
|
136
|
+
|
|
137
|
+
js_options[ 'rows' ] = options[ :rows ] if options[ :rows ]
|
|
138
|
+
js_options[ 'cols' ] = options[ :cols ] if options[ :cols ]
|
|
139
|
+
js_options[ 'size' ] = options[ :size ] if options[ :size ]
|
|
140
|
+
js_options[ 'ajaxOptions' ] = options[ :options ] if options[ :options ]
|
|
141
|
+
js_options[ 'htmlResponse' ] = ! options[ :script ] if options[ :script ]
|
|
142
|
+
|
|
143
|
+
js_options[ 'cancelText' ] = %('#{ options[ :cancel_text ] }') if options[ :cancel_text ]
|
|
144
|
+
js_options[ 'okText' ] = %('#{ options[ :save_text ] }') if options[ :save_text ]
|
|
145
|
+
js_options[ 'loadingText' ] = %('#{ options[ :loading_text ] }') if options[ :loading_text ]
|
|
146
|
+
js_options[ 'savingText' ] = %('#{ options[ :saving_text ] }') if options[ :saving_text ]
|
|
147
|
+
js_options[ 'clickToEditText' ] = %('#{ options[ :click_to_edit_text ] }') if options[ :click_to_edit_text ]
|
|
148
|
+
js_options[ 'textBetweenControls' ] = %('#{ options[ :text_between_controls ] }') if options[ :text_between_controls ]
|
|
149
|
+
|
|
150
|
+
js_options[ 'externalControl' ] = "'#{ options[ :external_control ] }'" if options[ :external_control ]
|
|
151
|
+
js_options[ 'loadTextURL' ] = "'#{ url_for( options[ :load_text_url ] ) }'" if options[ :load_text_url ]
|
|
152
|
+
|
|
153
|
+
js_options[ 'callback' ] = "function(form) { return #{ options[ :with ] }; }" if options[ :with ]
|
|
154
|
+
|
|
155
|
+
if options[ :is_boolean ]
|
|
156
|
+
if options[ :start_value ]
|
|
157
|
+
js_options[ 'collection' ] = "[['true','Yes'],['false','No']]"
|
|
158
|
+
else
|
|
159
|
+
js_options[ 'collection' ] = "[['false','No'],['true','Yes']]"
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
# Set up the custom on-failure and on-complete handlers
|
|
164
|
+
|
|
165
|
+
js_options[ 'onFailure' ] = "#{ options[ :on_failure ] }"
|
|
166
|
+
|
|
167
|
+
if ( options.include?( :lock_var ) )
|
|
168
|
+
js_options['onComplete'] = "function(transport, element) {#{ options[ :on_complete ] }(transport,element,'#{ options[ :lock_var ] }');}"
|
|
169
|
+
else
|
|
170
|
+
js_options['onComplete'] = "#{ options[ :on_complete ] }"
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
# Assemble the content
|
|
174
|
+
|
|
175
|
+
function << ( ', ' + options_for_javascript( js_options ) ) unless js_options.empty?
|
|
176
|
+
function << ')'
|
|
177
|
+
|
|
178
|
+
return javascript_tag( function )
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
# Close API equivalent of in_place_editor_field, except fixes various bugs
|
|
182
|
+
# (see the README for rationale). Allows either an object name in the first
|
|
183
|
+
# parameter (e.g. ":foo", in which case instance variable "@foo" must point
|
|
184
|
+
# to the object instance of interest) or an object instance (to save messing
|
|
185
|
+
# around with magic instance variables, but obtains the object name from
|
|
186
|
+
# "class.name.underscore", so may not be appropriate for unusual object
|
|
187
|
+
# classes). Anyway, clearer error reporting and the ability to pass in an
|
|
188
|
+
# object reference directly may help avoid a common error experienced with
|
|
189
|
+
# the InPlaceEditing plug-in code, as described here at the time of writing:
|
|
190
|
+
#
|
|
191
|
+
# http://oldwiki.rubyonrails.org/rails/pages/InPlaceEditing
|
|
192
|
+
#
|
|
193
|
+
# Includes the following options:
|
|
194
|
+
#
|
|
195
|
+
# * :lock_var is the name of the variable used for optimistic locking, by
|
|
196
|
+
# default set to a row-unique value. The assumption is that this same
|
|
197
|
+
# variable gets used throughout the row so that multiple edits on that
|
|
198
|
+
# row all cause the same variable to be incremented. If your back-end
|
|
199
|
+
# update function has side effects that might invalidate the value shown
|
|
200
|
+
# in another column on that row - which would be pretty strange! - you'd
|
|
201
|
+
# need to override the lock variable name with something that's unique to
|
|
202
|
+
# both the row and the column. When using a lock variable, additional
|
|
203
|
+
# option ":lock_version" is always set internally to the lock version of
|
|
204
|
+
# the object for which the field is being built and cannot be overridden.
|
|
205
|
+
#
|
|
206
|
+
# * :with is extended to include a "lock_version" parameter in the query
|
|
207
|
+
# string so that the client side's idea of the current object's lock
|
|
208
|
+
# version may be communicated to the server's attribute update action.
|
|
209
|
+
# This is done internally; there is no need to set the option yourself.
|
|
210
|
+
#
|
|
211
|
+
# The editor options also support "is_boolean", which overrides the default
|
|
212
|
+
# setting of whether or not the column value is considered to be a string
|
|
213
|
+
# or a boolean quantity. This is provided just-in-case, with no current
|
|
214
|
+
# known cases where the automatic detection isn't sufficient.
|
|
215
|
+
#
|
|
216
|
+
# The Prototype library getText function must be patched as described in
|
|
217
|
+
# the README rationale; "application.js" is a good place to do this.
|
|
218
|
+
#
|
|
219
|
+
# Note an optional fifth parameter which if 'true' will prevent HTML
|
|
220
|
+
# escaping of the value for values which are really meant to contain HTML
|
|
221
|
+
# code. Be very, very careful with this.
|
|
222
|
+
#
|
|
223
|
+
def safe_in_place_editor_field( object, method, tag_options = {}, editor_options = {}, no_escape = false )
|
|
224
|
+
|
|
225
|
+
# Allow a symbol or object instance to be passed. Since the symbol use
|
|
226
|
+
# case involves accessing a 'magic' related instance variable name and
|
|
227
|
+
# since there are lots of examples via Google of this confusing people,
|
|
228
|
+
# raise a helpful error message if the relevant variable is missing.
|
|
229
|
+
|
|
230
|
+
if ( object.instance_of?( Symbol ) )
|
|
231
|
+
object_name = object
|
|
232
|
+
var_name = "@#{ object_name }"
|
|
233
|
+
if ( instance_variable_defined?( var_name ) )
|
|
234
|
+
object = instance_variable_get( var_name )
|
|
235
|
+
else
|
|
236
|
+
raise( 'If passing \':foo\' to in_place_editor_field, \'@foo\' must refer to the object for which the field is being built' )
|
|
237
|
+
end
|
|
238
|
+
else
|
|
239
|
+
object_name = object.class.name.underscore
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
# Pass the lock version in for optimistic locking support, should the
|
|
243
|
+
# object support it. The update callback function must manually compare
|
|
244
|
+
# the params[ :lock_version ] value against the lock_version.to_s()
|
|
245
|
+
# value of the object that's being updated.
|
|
246
|
+
|
|
247
|
+
if ( object.respond_to?( :lock_version ) )
|
|
248
|
+
var = "#{ object_name }_#{ object.id }_safeInPlaceEditorLockVersion"
|
|
249
|
+
|
|
250
|
+
editor_options[ :lock_version ] = object.lock_version.to_s
|
|
251
|
+
editor_options[ :lock_var ] ||= var
|
|
252
|
+
editor_options[ :with ] ||= "Form.serialize(form).replace(/\\+/g,'%20')"
|
|
253
|
+
editor_options[ :with ] += " + '&lock_version=' + #{ var }"
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
# Escape the value unless told not to and construct the complete in-place
|
|
257
|
+
# editor assembly. Check for boolean values too, allowing caller-override.
|
|
258
|
+
|
|
259
|
+
column_value = object.send( method )
|
|
260
|
+
|
|
261
|
+
is_boolean = ( editor_options[ :is_boolean ] || ( column_value.is_a? TrueClass ) || ( column_value.is_a? FalseClass ) )
|
|
262
|
+
|
|
263
|
+
if ( is_boolean )
|
|
264
|
+
editor_options[ :start_value ] = !! column_value
|
|
265
|
+
column_value = column_value ? 'Yes' : 'No'
|
|
266
|
+
else
|
|
267
|
+
column_value = ERB::Util::html_escape( column_value ) unless ( no_escape )
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
tag_options = {
|
|
271
|
+
:id => "#{ object_name }_#{ method }_#{ object.id }_in_place_editor",
|
|
272
|
+
:class => "in_place_editor_field"
|
|
273
|
+
}.merge!( tag_options )
|
|
274
|
+
|
|
275
|
+
editor_options[ :url ] ||= url_for( {
|
|
276
|
+
:action => "set_#{ object_name }_#{ method }",
|
|
277
|
+
:id => object.id
|
|
278
|
+
} )
|
|
279
|
+
|
|
280
|
+
# Update the boolean value flag, unless the caller had already set one.
|
|
281
|
+
|
|
282
|
+
editor_options[ :is_boolean ] = is_boolean unless editor_options.has_key?( :is_boolean )
|
|
283
|
+
|
|
284
|
+
return content_tag( :span, column_value.html_safe, tag_options ) +
|
|
285
|
+
safe_in_place_editor( tag_options[ :id ], editor_options )
|
|
286
|
+
end
|
|
287
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: safe_in_place_editing
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 2.0.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Andrew Hodgkinson
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
|
|
12
|
+
date: 2013-11-29 00:00:00 Z
|
|
13
|
+
dependencies:
|
|
14
|
+
- !ruby/object:Gem::Dependency
|
|
15
|
+
name: railties
|
|
16
|
+
prerelease: false
|
|
17
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
|
18
|
+
requirements:
|
|
19
|
+
- - ~>
|
|
20
|
+
- !ruby/object:Gem::Version
|
|
21
|
+
version: "3.2"
|
|
22
|
+
type: :runtime
|
|
23
|
+
version_requirements: *id001
|
|
24
|
+
description: Safe In Place Editing Rails extension, providing flexible HTML safe in-place editing with string and boolean type support
|
|
25
|
+
email:
|
|
26
|
+
- ahodgkin@rowing.org.uk
|
|
27
|
+
executables: []
|
|
28
|
+
|
|
29
|
+
extensions: []
|
|
30
|
+
|
|
31
|
+
extra_rdoc_files: []
|
|
32
|
+
|
|
33
|
+
files:
|
|
34
|
+
- lib/safe_in_place_editing/controller_methods.rb
|
|
35
|
+
- lib/safe_in_place_editing/helper_methods.rb
|
|
36
|
+
- lib/safe_in_place_editing/version.rb
|
|
37
|
+
- lib/safe_in_place_editing.rb
|
|
38
|
+
- app/assets/safe_in_place_editing/safe_in_place_editing.js
|
|
39
|
+
- MIT-LICENSE
|
|
40
|
+
- README.rdoc
|
|
41
|
+
homepage: https://github.com/pond/safe_in_place_editing
|
|
42
|
+
licenses: []
|
|
43
|
+
|
|
44
|
+
metadata: {}
|
|
45
|
+
|
|
46
|
+
post_install_message:
|
|
47
|
+
rdoc_options: []
|
|
48
|
+
|
|
49
|
+
require_paths:
|
|
50
|
+
- lib
|
|
51
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
52
|
+
requirements:
|
|
53
|
+
- &id002
|
|
54
|
+
- ">="
|
|
55
|
+
- !ruby/object:Gem::Version
|
|
56
|
+
version: "0"
|
|
57
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- *id002
|
|
60
|
+
requirements: []
|
|
61
|
+
|
|
62
|
+
rubyforge_project:
|
|
63
|
+
rubygems_version: 2.1.11
|
|
64
|
+
signing_key:
|
|
65
|
+
specification_version: 4
|
|
66
|
+
summary: Safe In Place Editing Rails extension
|
|
67
|
+
test_files: []
|
|
68
|
+
|