hobo 0.8.3 → 0.8.4
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES.txt +330 -0
- data/Manifest +12 -4
- data/Rakefile +4 -6
- data/dryml_generators/rapid/cards.dryml.erb +5 -1
- data/dryml_generators/rapid/forms.dryml.erb +8 -10
- data/dryml_generators/rapid/pages.dryml.erb +65 -36
- data/hobo.gemspec +28 -15
- data/lib/active_record/association_collection.rb +3 -22
- data/lib/hobo.rb +25 -258
- data/lib/hobo/accessible_associations.rb +131 -0
- data/lib/hobo/authentication_support.rb +15 -9
- data/lib/hobo/composite_model.rb +1 -1
- data/lib/hobo/controller.rb +7 -8
- data/lib/hobo/dryml.rb +9 -10
- data/lib/hobo/dryml/dryml_builder.rb +7 -1
- data/lib/hobo/dryml/dryml_doc.rb +161 -0
- data/lib/hobo/dryml/dryml_generator.rb +18 -9
- data/lib/hobo/dryml/part_context.rb +76 -42
- data/lib/hobo/dryml/tag_parameters.rb +1 -0
- data/lib/hobo/dryml/taglib.rb +2 -1
- data/lib/hobo/dryml/template.rb +39 -29
- data/lib/hobo/dryml/template_environment.rb +79 -37
- data/lib/hobo/dryml/template_handler.rb +66 -21
- data/lib/hobo/guest.rb +2 -10
- data/lib/hobo/hobo_helper.rb +125 -53
- data/lib/hobo/include_in_save.rb +0 -1
- data/lib/hobo/lifecycles.rb +54 -24
- data/lib/hobo/lifecycles/actions.rb +95 -31
- data/lib/hobo/lifecycles/creator.rb +18 -23
- data/lib/hobo/lifecycles/lifecycle.rb +86 -62
- data/lib/hobo/lifecycles/state.rb +1 -2
- data/lib/hobo/lifecycles/transition.rb +22 -28
- data/lib/hobo/model.rb +64 -176
- data/lib/hobo/model_controller.rb +67 -54
- data/lib/hobo/model_router.rb +5 -2
- data/lib/hobo/permissions.rb +397 -0
- data/lib/hobo/permissions/associations.rb +167 -0
- data/lib/hobo/scopes.rb +15 -38
- data/lib/hobo/scopes/association_proxy_extensions.rb +15 -5
- data/lib/hobo/scopes/automatic_scopes.rb +43 -18
- data/lib/hobo/scopes/named_scope_extensions.rb +2 -2
- data/lib/hobo/user.rb +10 -4
- data/lib/hobo/user_controller.rb +6 -5
- data/lib/hobo/view_hints.rb +58 -0
- data/rails_generators/hobo/hobo_generator.rb +7 -3
- data/rails_generators/hobo/templates/guest.rb +1 -13
- data/rails_generators/hobo_front_controller/hobo_front_controller_generator.rb +1 -1
- data/rails_generators/hobo_model/hobo_model_generator.rb +4 -2
- data/rails_generators/hobo_model/templates/hints.rb +4 -0
- data/rails_generators/hobo_model/templates/model.rb +8 -8
- data/rails_generators/hobo_model_controller/hobo_model_controller_generator.rb +10 -0
- data/rails_generators/hobo_model_controller/templates/controller.rb +1 -1
- data/rails_generators/hobo_rapid/templates/hobo-rapid.js +91 -56
- data/rails_generators/hobo_rapid/templates/lowpro.js +15 -15
- data/rails_generators/hobo_rapid/templates/reset.css +36 -3
- data/rails_generators/hobo_rapid/templates/themes/clean/public/stylesheets/clean.css +13 -17
- data/rails_generators/hobo_user_controller/templates/controller.rb +1 -1
- data/rails_generators/hobo_user_model/templates/model.rb +18 -16
- data/taglibs/core.dryml +60 -18
- data/taglibs/rapid.dryml +8 -401
- data/taglibs/rapid_core.dryml +586 -0
- data/taglibs/rapid_document_tags.dryml +28 -10
- data/taglibs/rapid_editing.dryml +92 -55
- data/taglibs/rapid_forms.dryml +406 -87
- data/taglibs/rapid_generics.dryml +1 -1
- data/taglibs/rapid_navigation.dryml +2 -1
- data/taglibs/rapid_pages.dryml +7 -16
- data/taglibs/rapid_plus.dryml +39 -14
- data/taglibs/rapid_support.dryml +1 -1
- data/taglibs/rapid_user_pages.dryml +14 -4
- data/tasks/{generate_tag_reference.rb → generate_tag_reference.rake} +49 -18
- data/tasks/hobo_tasks.rake +16 -0
- data/test/permissions/models/models.rb +134 -0
- data/test/permissions/models/schema.rb +55 -0
- data/test/permissions/models/test.sqlite3 +0 -0
- data/test/permissions/test_permissions.rb +436 -0
- metadata +27 -14
- data/lib/hobo/mass_assignment.rb +0 -64
- data/rails_generators/hobo/templates/patch_routing.rb +0 -30
- data/uninstall.rb +0 -1
@@ -1,14 +1,22 @@
|
|
1
|
-
|
1
|
+
<!-- Extra tags for semantic markup -->
|
2
2
|
|
3
|
-
|
3
|
+
<!-- Used as a semantic wrapper around a group of sections and asides. CSS layouts can be provided based on this structure.
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
<def tag="item-value"><td merge-attrs><do param="default"/></td></def>
|
5
|
+
### Usage
|
8
6
|
|
7
|
+
<section-group>
|
8
|
+
<section>My First Section</section>
|
9
|
+
<section>My Second Section</section>
|
10
|
+
<aside>My Aside</aside>
|
11
|
+
</section-group>
|
12
|
+
-->
|
9
13
|
<def tag="section-group"><div class="section-group"><div class="section-group-inner" merge-attrs param="default"></div></div></def>
|
10
14
|
|
11
|
-
<!--
|
15
|
+
<!-- A proposed HTML 5 tag for representing a generic document or application section. Slightly more semantic than `<div>` for indicating document structure. For the time being, `<section>` is output as `<div class="section">`. In Hobo, `<section>` also has one other important behaviour which is different to using `<div>` directly, when the content of the section is empty, the wrapper tag will disappear. e.g.
|
16
|
+
|
17
|
+
<section>My Section</section> -> <div class="section">My Section</div>
|
18
|
+
<section><% # empty %></section> -> (nothing is generated)
|
19
|
+
-->
|
12
20
|
<def tag="section" attrs="empty, with-flash-messages">
|
13
21
|
<set body="¶meters.default" flash="&with_flash_messages && !scope.flash_rendered"/>
|
14
22
|
<div class="section #{'with-flash' if flash}" merge-attrs if="&!body.blank? || empty">
|
@@ -17,22 +25,32 @@
|
|
17
25
|
</div>
|
18
26
|
</def>
|
19
27
|
|
28
|
+
<!-- A proposed HTML 5 semantic tag. Outputs `<div class="aside">` and works in the same way as `<section>` with empty content. -->
|
20
29
|
<def tag="aside" attrs="empty">
|
21
30
|
<set body="¶meters.default"/>
|
22
31
|
<div class="aside" merge-attrs if="&!body.blank? || empty"><%= body %></div>
|
23
32
|
</def>
|
24
33
|
|
34
|
+
<!-- A proposed HTML 5 semantic tag. Outputs `<div class="header">` and works in the same way as `<section>` with empty content. -->
|
25
35
|
<def tag="header" attrs="empty">
|
26
36
|
<set body="¶meters.default"/>
|
27
37
|
<div class="header" merge-attrs if="&!body.blank? || empty"><%= body %></div>
|
28
38
|
</def>
|
29
39
|
|
40
|
+
<!-- A proposed HTML 5 semantic tag. Outputs `<div class="footer">` and works in the same way as `<section>` with empty content. -->
|
30
41
|
<def tag="footer" attrs="empty">
|
31
42
|
<set body="¶meters.default"/>
|
32
43
|
<div class="footer" merge-attrs if="&!body.blank? || empty"><%= body %></div>
|
33
44
|
</def>
|
34
45
|
|
35
|
-
<!--
|
36
|
-
<def tag="
|
37
|
-
|
38
|
-
|
46
|
+
<!-- nodoc - to be removed -->
|
47
|
+
<def tag="labelled-item-list"><table class="field-list" merge-attrs><do param="default"/></table></def>
|
48
|
+
|
49
|
+
<!-- nodoc - to be removed -->
|
50
|
+
<def tag="labelled-item"><tr merge-attrs><do param="default"/></tr></def>
|
51
|
+
|
52
|
+
<!-- nodoc - to be removed -->
|
53
|
+
<def tag="item-label"><th merge-attrs><do param="default"/></th></def>
|
54
|
+
|
55
|
+
<!-- nodoc - to be removed -->
|
56
|
+
<def tag="item-value"><td merge-attrs><do param="default"/></td></def>
|
data/taglibs/rapid_editing.dryml
CHANGED
@@ -1,3 +1,11 @@
|
|
1
|
+
<!-- Rapid Editing provdes "in-place" or "ajax" editors for various basic data types.
|
2
|
+
|
3
|
+
This area of Hobo has had less attention that the non-ajax forms of late, so it's lagging a little. There may be some rough edges. For example, the tags in this library do not (yet!) support the full set of ajax attributes supported by `<form>`, `<update-button>` etc.
|
4
|
+
|
5
|
+
-->
|
6
|
+
|
7
|
+
<!-- Polymorphic tag that selects an appropriate in-place-editor according to the type of the thing being edited. `<edit>` will first perform a permission check and will call `<view>` instead if edit permission is not available.
|
8
|
+
-->
|
1
9
|
<def tag="editor" ><%=
|
2
10
|
if !can_edit?
|
3
11
|
view(attributes)
|
@@ -18,57 +26,68 @@
|
|
18
26
|
%></def>
|
19
27
|
|
20
28
|
|
29
|
+
<!-- Not implemented - you just get links to the items in the collection -->
|
21
30
|
<def tag="has-many-editor">
|
22
31
|
<% #TODO: Implement %>
|
23
32
|
<a merge-attrs/>
|
24
33
|
</def>
|
25
34
|
|
26
|
-
|
35
|
+
<!-- Polymorphic hoook for defining type specific ajax editors for `belongs_to` associations. The default is `<select-one-editor>` -->
|
36
|
+
<def tag="belongs-to-editor" polymorphic><%= select_one_editor(attributes) %></def>
|
27
37
|
|
38
|
+
<!-- Provides a simple Scriptaculous in-place-editor that uses an `<input type='text'>` -->
|
28
39
|
<def tag="editor" for="string"><%= in_place_editor attributes %></def>
|
29
40
|
|
41
|
+
<!-- Provides a simple Scriptaculous in-place-editor that uses a `<textarea>` -->
|
30
42
|
<def tag="editor" for="text"><%= in_place_editor attributes %></def>
|
31
43
|
|
44
|
+
<!-- Provides a simple Scriptaculous in-place-editor that uses a `<textarea>`.
|
45
|
+
A JavaScript hook is available in order to replace the simple textarea with a rich-text editor.
|
46
|
+
For an example, see the [hoboyui](http://github.com/tablatom/hoboyui) plugin -->
|
32
47
|
<def tag="editor" for="html"><%= in_place_editor attributes %></def>
|
33
48
|
|
49
|
+
<!-- Provides a simple Scriptaculous in-place-editor that uses an `<input type='text'>` -->
|
34
50
|
<def tag="editor" for="datetime"><%= in_place_editor attributes %></def>
|
35
51
|
|
52
|
+
<!-- Provides a simple Scriptaculous in-place-editor that uses an `<input type='text'>` -->
|
36
53
|
<def tag="editor" for="date"><%= in_place_editor attributes %></def>
|
37
54
|
|
55
|
+
<!-- Provides a simple Scriptaculous in-place-editor that uses an `<input type='text'>` -->
|
38
56
|
<def tag="editor" for="integer"><%= in_place_editor attributes %></def>
|
39
57
|
|
58
|
+
<!-- Provides a simple Scriptaculous in-place-editor that uses an `<input type='text'>` -->
|
40
59
|
<def tag="editor" for="float"><%= in_place_editor attributes %></def>
|
41
60
|
|
61
|
+
<!-- Provides a simple Scriptaculous in-place-editor that uses an `<input type='text'>` -->
|
62
|
+
<def tag="editor" for="big_integer"><%= in_place_editor attributes %></def>
|
63
|
+
|
64
|
+
<!-- Raises an error - passwords cannot be edited in place -->
|
42
65
|
<def tag="editor" for="password"><% raise HoboError, "passwords cannot be edited in place" %></def>
|
43
66
|
|
67
|
+
<!-- calls `<boolean-checkbox-editor>` -->
|
44
68
|
<def tag="editor" for="boolean"><boolean-checkbox-editor merge-attrs/></def>
|
45
69
|
|
46
|
-
|
47
|
-
|
70
|
+
<!-- Provides an editor that uses a `<select>` menu. Uses the `<string-select-editor>` tag. -->
|
48
71
|
<def tag="editor" for="HoboFields::EnumString">
|
49
72
|
<string-select-editor values="&this_type.values" merge/>
|
50
73
|
</def>
|
51
74
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
<div id="#{id}-completions" class="completions-popup" style="display:none"></div>
|
69
|
-
</def>
|
70
|
-
|
71
|
-
|
75
|
+
<!-- Provides a `<select>` menu with an ajax callback to update a `belongs_to` relationship when changed.
|
76
|
+
By default the menu contains every record in the target model's table.
|
77
|
+
|
78
|
+
### Attributes
|
79
|
+
|
80
|
+
- include-none: Should the menu include a "none" option (true/false). Defaults: false, or true if the association is nil at render-time.
|
81
|
+
|
82
|
+
- blank-message: The text for the "none" option. Default: "(No Product)" (or whatever the model name is)
|
83
|
+
|
84
|
+
- sort: Sort the options (true/false)? Default: false
|
85
|
+
|
86
|
+
- update: one or more DOM ID's (comma separated string or an array) to be updated as part of the ajax call.
|
87
|
+
|
88
|
+
NOTE: yes that's *DOM ID's* not part-names. A common source of confusion because by default the part name and DOM ID are the same.
|
89
|
+
|
90
|
+
-->
|
72
91
|
<def tag="select-one-editor" attrs="include-none, blank-message, sort, update"><%
|
73
92
|
raise HoboError.new("Not allowed to edit") unless can_edit?
|
74
93
|
blank_message ||= "(No #{this_type.name.to_s.titleize})"
|
@@ -89,37 +108,23 @@
|
|
89
108
|
</select>
|
90
109
|
</def>
|
91
110
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
attributes.select_hash {|k,v| k.to_s.starts_with? "where_"})
|
110
|
-
%>
|
111
|
-
<form onsubmit="#{f}; $('#{id}').blur(); return false">
|
112
|
-
<input type="text" class="autocomplete-bhv autosubmit" id="#{id}" autocomplete-url="#{url}"
|
113
|
-
value="#{this && this.id_name}" merge-attrs />
|
114
|
-
<div id="#{id}-completions" class="completions-popup" style="display:none"></div>
|
115
|
-
</form>
|
116
|
-
</if-can-edit>
|
117
|
-
<else>
|
118
|
-
<object-link/>
|
119
|
-
</else>
|
120
|
-
</def>
|
121
|
-
|
122
|
-
|
111
|
+
<!-- Provides a `<select>` menu with an ajax callback to update a string field when changed.
|
112
|
+
|
113
|
+
### Attributes
|
114
|
+
|
115
|
+
- values: The values for the menu options. Required
|
116
|
+
|
117
|
+
- Labels: A hash that can be used to customise the labels for the menu.
|
118
|
+
Any value that does not have a corresponding key in this hash will have its label
|
119
|
+
generated by `value.titleize`
|
120
|
+
|
121
|
+
- titleize: Set to false to have the default labels be the same as the values. Default: true - the labels are generated by `value.titleize`
|
122
|
+
|
123
|
+
- update: one or more DOM ID's (comma separated string or an array) to be updated as part of the ajax call.
|
124
|
+
|
125
|
+
NOTE: yes that's *DOM ID's* not part-names. A common source of confusion because by default the part name and DOM ID are the same.
|
126
|
+
|
127
|
+
-->
|
123
128
|
<def tag="string-select-editor" attrs="update, values, labels, titleize"><%
|
124
129
|
raise HoboError.new("Not allowed to edit") unless can_edit?
|
125
130
|
|
@@ -140,7 +145,17 @@
|
|
140
145
|
</select>
|
141
146
|
</def>
|
142
147
|
|
148
|
+
<!-- A checkbox with an ajax callback to update a boolean field when clicked.
|
149
|
+
|
150
|
+
### Attributes
|
151
|
+
|
152
|
+
- update: one or more DOM ID's (comma separated string or an array) to be updated as part of the ajax call.
|
143
153
|
|
154
|
+
NOTE: yes that's *DOM ID's* not part-names. A common source of confusion because by default the part name and DOM ID are the same.
|
155
|
+
|
156
|
+
- message: A message to display in the ajax-progress spinner. Default: "Saving..."
|
157
|
+
|
158
|
+
-->
|
144
159
|
<def tag="boolean-checkbox-editor" attrs="update, message"><%
|
145
160
|
raise HoboError.new("Not allowed to edit") unless can_edit?
|
146
161
|
f = ajax_updater(object_url(this_parent, :method => :put),
|
@@ -157,6 +172,7 @@
|
|
157
172
|
</def>
|
158
173
|
|
159
174
|
|
175
|
+
<!-- nodoc. -->
|
160
176
|
<def tag="sti-type-editor" attrs="update">
|
161
177
|
<% base_class = this.class
|
162
178
|
base_class = base_class.superclass while base_class.superclass != ActiveRecord::Base
|
@@ -174,7 +190,26 @@
|
|
174
190
|
</select>
|
175
191
|
</def>
|
176
192
|
|
177
|
-
|
193
|
+
|
194
|
+
<!-- Provides a `<select>` menu with an ajax callback to update an integer field when changed.
|
195
|
+
|
196
|
+
### Attributes
|
197
|
+
|
198
|
+
- min: The minimum end of the range of numbers to include
|
199
|
+
|
200
|
+
- max: A male name, short for Maximilian
|
201
|
+
|
202
|
+
- options: An array of numbers to use if min..max is not enough for your needs.
|
203
|
+
|
204
|
+
- nil-option: Label to give if the current value is nil. Default: "Choose a value"
|
205
|
+
|
206
|
+
- message: A message to display in the ajax-progress spinner. Default: "Saving..."
|
207
|
+
|
208
|
+
- update: one or more DOM ID's (comma separated string or an array) to be updated as part of the ajax call.
|
209
|
+
|
210
|
+
NOTE: yes that's *DOM ID's* not part-names. A common source of confusion because by default the part name and DOM ID are the same.
|
211
|
+
|
212
|
+
-->
|
178
213
|
<def tag="integer-select-editor" attrs="options, min, max, update, nil-option, message">
|
179
214
|
<% options ||= (min.to_i..max.to_i).to_a %>
|
180
215
|
<select class="number-editor-bhv #{model_id_class} #{'update:' + comma_split(update).join(':') unless update.blank?}"
|
@@ -185,6 +220,7 @@
|
|
185
220
|
</def>
|
186
221
|
|
187
222
|
|
223
|
+
<!-- nodoc. -->
|
188
224
|
<def tag="has-many-checkbox-editor" attrs="model, update, message"><%=
|
189
225
|
raise HoboError.new("no update specified") unless update
|
190
226
|
|
@@ -216,6 +252,7 @@
|
|
216
252
|
"checkbox_input has_many_checkbox has_many_#{class_name}_checkbox")) if permission
|
217
253
|
%></def>
|
218
254
|
|
255
|
+
<!-- nodoc. -->
|
219
256
|
<def tag="has-many-checkbox-editors">
|
220
257
|
<table>
|
221
258
|
<tr:>
|
data/taglibs/rapid_forms.dryml
CHANGED
@@ -1,3 +1,47 @@
|
|
1
|
+
<!-- Rapid Forms provides various tags that make it quick and easy to produce working new or edit forms.
|
2
|
+
|
3
|
+
The main tags are:
|
4
|
+
|
5
|
+
- `<form>`, which acts like the dumb HTML tag if you provide the `action` attribute, and picks up various Rapid smarts
|
6
|
+
otherwise.
|
7
|
+
|
8
|
+
- `<input>`, which automatically choses an appropriate form control based on the type of the date.
|
9
|
+
|
10
|
+
### Ajax Attributes
|
11
|
+
|
12
|
+
Several of the tags in this taglib support the following set of ajax attributes:
|
13
|
+
|
14
|
+
- update: one or more DOM ID's (comma separated string or an array) to be updated as part of the ajax call. Default - no
|
15
|
+
update.
|
16
|
+
|
17
|
+
NOTE: yes that's *DOM ID's* not part-names. A common source of confusion because by default the part name and DOM ID are
|
18
|
+
the same.
|
19
|
+
|
20
|
+
- params: a hash of name/value pairs that will be converted to HTTP parameters in the ajax request
|
21
|
+
|
22
|
+
- confirm: a message to be displayed in a JavaScript confirm dialog. By default there is no confirm dialog
|
23
|
+
|
24
|
+
- message: a message to be displayed in the Ajax progress spinner. Default: "Saving..."
|
25
|
+
|
26
|
+
- spinner-next-to: DOM ID of an element to position the ajax progress spinner next to.
|
27
|
+
|
28
|
+
### Ajax Callbacks
|
29
|
+
|
30
|
+
The following attributes are also supported by all the ajax tags. Set them to fragments of javascript to have that script
|
31
|
+
executed at various points in the ajax request cycle:
|
32
|
+
|
33
|
+
- before: script to run befofre the request
|
34
|
+
|
35
|
+
- success: script to run on successful completion of the request
|
36
|
+
|
37
|
+
- failure: script to run on a request failure
|
38
|
+
|
39
|
+
- complete: script to run on completion, regardless of success or failure.
|
40
|
+
|
41
|
+
-->
|
42
|
+
|
43
|
+
|
44
|
+
<!-- nodoc. -->
|
1
45
|
<def tag="hidden-fields" attrs="fields, for-query-string, skip"><%=
|
2
46
|
pairs = if for_query_string
|
3
47
|
query_params.to_a
|
@@ -5,22 +49,75 @@
|
|
5
49
|
hiddens = case fields
|
6
50
|
when '*', nil
|
7
51
|
# TODO: Need a better (i.e. extensible) way to eleminate certain fields
|
8
|
-
this.class.column_names - ['type', 'created_at', 'updated_at']
|
52
|
+
this.class.column_names - ['type', 'created_at', 'updated_at']
|
9
53
|
else
|
10
54
|
comma_split(fields)
|
11
55
|
end
|
12
56
|
hiddens.map do |field|
|
13
57
|
val = this.send(field)
|
14
|
-
param_name = param_name_for(
|
58
|
+
param_name = param_name_for(form_field_path + [field])
|
15
59
|
[param_name, val] unless val.nil? ||
|
16
60
|
field.to_sym.in?(this.class.attr_protected) ||
|
17
61
|
(this.new_record? && val == this.class.column(field).default)
|
18
62
|
end.compact
|
19
63
|
end
|
64
|
+
skip = comma_split skip
|
65
|
+
pairs.reject! { |p| p.first.in?(skip) }
|
20
66
|
pairs.map { |n, v| hidden_field_tag(n, v.to_s) if v && n.not_in?(scope.form_field_names) }.compact.join("\n")
|
21
67
|
%></def>
|
22
68
|
|
23
69
|
|
70
|
+
<!--
|
71
|
+
`<form>` has been extended in Rapid to make it easier to construct and use forms with Hobo models. In addition to the base
|
72
|
+
`<form>` tag, a form with contents is generated for each Hobo model. These are found in
|
73
|
+
`app/views/taglibs/auto/rapid/forms.dryml`.
|
74
|
+
|
75
|
+
### Usage
|
76
|
+
|
77
|
+
`<form>` can be used as a regular HTML tag:
|
78
|
+
|
79
|
+
<form action="/blog_posts/1" method="POST">...</form>
|
80
|
+
|
81
|
+
If no `action` attribute is provided then the context is used to construct an appropriate action using restful routing:
|
82
|
+
|
83
|
+
* If the context is a new record then the form action will be a `POST` to the create action:
|
84
|
+
|
85
|
+
<form with="&BlogPost.new">...</form> -> <form action="/blog_posts" method="POST">...</form>
|
86
|
+
|
87
|
+
* If the context is a saved record then the form action will be a `PUT` to the update action. This is handled in a special
|
88
|
+
way by Rails due to current browsers not supporting `PUT`, the method is set to `POST` with a hidden input called `_method`
|
89
|
+
with a value of `PUT`. Hobo adds this automatically:
|
90
|
+
|
91
|
+
<% blog_post = BlogPost.find(1) %>
|
92
|
+
<form with="&blog_post">...</form> ->
|
93
|
+
<form action="/blog_posts/1" method="POST">
|
94
|
+
<input id="_method" type="hidden" value="PUT" name="_method"/>
|
95
|
+
...
|
96
|
+
</form>
|
97
|
+
|
98
|
+
AJAX based submission can be enabled by simply adding an `update` attribute. e.g.
|
99
|
+
|
100
|
+
<div part="comments"><collection:comments/></div>
|
101
|
+
<form with="&Comment.new" update="comments"/>
|
102
|
+
|
103
|
+
`<form>` support all of the standard ajax attributes.
|
104
|
+
|
105
|
+
### Additional Notes
|
106
|
+
|
107
|
+
- Hobo automatically inserts an `auth_token` hidden field if forgery protection is enabled
|
108
|
+
|
109
|
+
- Hobo inserts a `page_path` hidden field in create / update forms which it uses to re-render the correct page if a
|
110
|
+
validation error occurs.
|
111
|
+
|
112
|
+
- `<form>` supports all of the standrd ajax attributes - (see the main taglib docs for Rapid Forms)
|
113
|
+
|
114
|
+
### Attributes
|
115
|
+
|
116
|
+
- reset-form: Clear the form after submission (only makes sense for ajax forms)
|
117
|
+
|
118
|
+
- refocus-form: Refocus the first form-field after submission (only makes sense for ajax forms)
|
119
|
+
|
120
|
+
-->
|
24
121
|
<def tag="form" polymorphic attrs="update, hidden-fields, action, method, web-method, lifecycle, owner, multipart"><%=
|
25
122
|
ajax_attrs, html_attrs = attributes.partition_hash(Hobo::RapidHelper::AJAX_ATTRS)
|
26
123
|
html_attrs[:enctype] ||= "multipart/form-data" if multipart
|
@@ -45,7 +142,7 @@
|
|
45
142
|
end
|
46
143
|
|
47
144
|
if action.nil? && (html_attrs[:action].nil? ||
|
48
|
-
(lifecycle.nil? && new_record && !this.
|
145
|
+
(lifecycle.nil? && new_record && !this.creatable_by?(current_user)) ||
|
49
146
|
(lifecycle.nil? && !new_record && !can_edit?))
|
50
147
|
Hobo::Dryml.last_if = false
|
51
148
|
""
|
@@ -97,7 +194,7 @@
|
|
97
194
|
if web_method
|
98
195
|
add_classes!(html_attrs, "#{type_id.dasherize}-#{web_method}-form")
|
99
196
|
else
|
100
|
-
add_classes!(html_attrs, "#{'new
|
197
|
+
add_classes!(html_attrs, "#{'new ' if new_record}#{type_id.dasherize}")
|
101
198
|
end
|
102
199
|
end
|
103
200
|
|
@@ -106,7 +203,14 @@
|
|
106
203
|
end
|
107
204
|
%></def>
|
108
205
|
|
206
|
+
<!-- A shortcut for generating a submit button.
|
207
|
+
|
208
|
+
### Usage
|
109
209
|
|
210
|
+
<submit label="Go!"/> -> <input type="submit" value="Go!" class="button submit-button"/>
|
211
|
+
<submit image="/images/go.png"/> -> <input type="image" src="/images/go.png" class="button submit-button"/>
|
212
|
+
|
213
|
+
-->
|
110
214
|
<def tag="submit" attrs="label, image">
|
111
215
|
<input if="&image" type="image" src="&image" merge-attrs class="image-button submit-button"/>
|
112
216
|
<else>
|
@@ -114,82 +218,193 @@
|
|
114
218
|
</else>
|
115
219
|
</def>
|
116
220
|
|
221
|
+
<!--
|
222
|
+
Provides an editable control tailored to the type of the object in context. `<input>` tags should be used within a
|
223
|
+
`<form>`. `<input>` is a _polymorphic_ tag which means that there are a variety of definitions, each one written for a
|
224
|
+
particular type. For example there are inputs for `text`, `boolean`, `password`, `date`, `datetime`, `integer`,
|
225
|
+
`float`, `string` and more.
|
226
|
+
|
227
|
+
### Usage
|
228
|
+
|
229
|
+
The tag behaves as a regular HTML input if the type attribute is given:
|
230
|
+
|
231
|
+
<input type="text" name="my_input"/> -> Output is exactly as provided, untouched by Rapid
|
232
|
+
|
233
|
+
If no type attribute is given then the _context_ is used. For example if the context is a blog post:
|
234
|
+
|
235
|
+
<input:title/> ->
|
236
|
+
<input id="blog_post[name]" class="string blog-post-name" type="text" value="My Blog Post" name="blog_post[name]"/>
|
237
|
+
|
238
|
+
<input:created_at/> ->
|
239
|
+
<select id="blog_post_created_at_year" name="blog_post[created_at][year]">...</select>
|
240
|
+
<select id="blog_post_created_at_month" name="blog_post[created_at][month]">...</select>
|
241
|
+
<select id="blog_post_created_at_day" name="blog_post[created_at][day]">...</select>
|
117
242
|
|
118
|
-
<
|
243
|
+
<input:description/> ->
|
244
|
+
<textarea class="text blog-post-description" id="blog_post[description]" name="blog_post[description]">...</textarea>
|
245
|
+
|
246
|
+
If the context is a `belongs_to` association, the `<select-one>` tag is used.
|
247
|
+
|
248
|
+
If the context is a `has_many :through` association, the polymorphic `<collection-input>` tag is used.
|
249
|
+
|
250
|
+
### Attributes
|
251
|
+
|
252
|
+
- no-edit: control what happens if `can_edit?` is false. Can be one of:
|
253
|
+
|
254
|
+
- view: render the current value using the `<view>` tag
|
255
|
+
- disable: render the input as normal, but add HTML's `disabled` attribute
|
256
|
+
- skip: render nothing at all
|
257
|
+
- ignore: render the input normally. That is, don't even perform the edit check.
|
258
|
+
-->
|
259
|
+
<def tag="input" attrs="no-edit"><%=
|
119
260
|
if attributes[:type]
|
120
261
|
element :input, attributes, nil, true, true
|
121
|
-
elsif !(force || can_edit?)
|
122
|
-
view
|
123
262
|
else
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
263
|
+
no_edit ||= :view
|
264
|
+
no_edit = no_edit.to_sym
|
265
|
+
no_edit_permission = !can_edit? unless no_edit == :ignore
|
266
|
+
if no_edit_permission && no_edit == :view
|
267
|
+
view
|
268
|
+
elsif no_edit_permission && no_edit == :skip
|
269
|
+
""
|
270
|
+
else
|
271
|
+
attrs = add_classes(attributes, type_id.dasherize, type_and_field.dasherize)
|
272
|
+
attrs[:name] ||= param_name_for_this
|
273
|
+
attrs[:disabled] = true if no_edit_permission && no_edit == :disable
|
274
|
+
the_input = if (refl = this_field_reflection)
|
275
|
+
if refl.macro == :belongs_to
|
276
|
+
call_polymorphic_tag('input', attrs) or select_one(attrs)
|
277
|
+
elsif refl.macro == :has_many
|
278
|
+
if refl.options[:through]
|
279
|
+
collection_input(attrs)
|
280
|
+
else
|
281
|
+
raise NotImplementedError, "An input for has-many associations has not been implemented yet"
|
282
|
+
end
|
134
283
|
end
|
284
|
+
else
|
285
|
+
call_polymorphic_tag('input', attrs) or
|
286
|
+
(call_polymorphic_tag('input', HoboFields.to_class(this_type::COLUMN_TYPE), attrs) if defined?(this_type::COLUMN_TYPE)) or
|
287
|
+
raise HoboError, ("No input tag for #{this_field}:#{this_type} (this=#{this.inspect})")
|
135
288
|
end
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
if this_parent.errors[this_field]
|
142
|
-
"<span class='field-with-errors'>#{the_input}</span>"
|
143
|
-
else
|
144
|
-
the_input
|
289
|
+
if this_parent.errors[this_field]
|
290
|
+
"<span class='field-with-errors'>#{the_input}</span>"
|
291
|
+
else
|
292
|
+
the_input
|
293
|
+
end
|
145
294
|
end
|
146
295
|
end
|
147
296
|
%></def>
|
148
297
|
|
149
298
|
|
299
|
+
<!-- This tag is called by `<input>` when the context is a `has_many :through` collection. By default a `<select-many>`
|
300
|
+
is used, but this can be customised on a per-type basis. For example, say you would like the `<check-many>` tag used to
|
301
|
+
edit collections a `Category` model in your application:
|
302
|
+
|
303
|
+
<def tag="collection-input" for="Category"><check-many merge/></def>
|
304
|
+
-->
|
305
|
+
<def tag="collection-input" polymorphic></def>
|
306
|
+
|
307
|
+
<!-- The default `<collection-input>` - calls `<select-many>` -->
|
308
|
+
<def tag="collection-input" for="ActiveRecord::Base"><select-many merge/></def>
|
309
|
+
|
310
|
+
|
311
|
+
<!-- A `<textarea>` input -->
|
150
312
|
<def tag="input" for="text" attrs="name">
|
151
313
|
<%= text_area_tag(name, this, attributes) %>
|
152
314
|
</def>
|
153
315
|
|
316
|
+
<!-- A checkbox plus a hidden-field. The hidden field trick comes from Rails - it means that when the checkbox is not checked, the parameter name is still submitted, with a '0' value (the value is '1' when the checkbox is checked) -->
|
154
317
|
<def tag="input" for="boolean" attrs="name">
|
155
|
-
<%= check_box_tag(name, '1', this, attributes)
|
318
|
+
<%= check_box_tag(name, '1', this, attributes) %><%= hidden_field_tag(name, '0') unless attributes[:disabled] %>
|
156
319
|
</def>
|
157
320
|
|
321
|
+
<!-- A password input - `<input type='password'>` -->
|
158
322
|
<def tag="input" for="password" attrs="name">
|
159
323
|
<%= password_field_tag(name, this, attributes) %>
|
160
324
|
</def>
|
161
325
|
|
326
|
+
<!-- A date picker, using the `select_date` helper from Rails
|
327
|
+
|
328
|
+
### Attributes
|
329
|
+
|
330
|
+
- order: The order of the year, month and day menus. A comma separated string or an array. Default: "year, month, day"
|
331
|
+
|
332
|
+
Any other attributes are passed through to the `select_date` helper.
|
333
|
+
|
334
|
+
The menus default to the current date if the current value is nil.
|
335
|
+
|
336
|
+
-->
|
162
337
|
<def tag="input" for="date" attrs="order">
|
163
338
|
<% order = order.nil? ? [:year, :month, :day] : comma_split(order).*.to_sym -%>
|
164
339
|
<%= select_date(this || Time.now, attributes.merge(:prefix => param_name_for_this, :order => order)) %>
|
165
340
|
</def>
|
166
341
|
|
342
|
+
|
343
|
+
<!-- A date/time picker, using the `select_date` helper from Rails
|
344
|
+
|
345
|
+
### Attributes
|
346
|
+
|
347
|
+
- order: The order of the year, month and date menus. A comma separated string or an array. Default: "year, month,
|
348
|
+
day, hour, minute, second"
|
349
|
+
|
350
|
+
Any other attributes are passed through to the `select_date` helper.
|
351
|
+
|
352
|
+
The menus default to the current time if the current value is nil.
|
353
|
+
|
354
|
+
-->
|
167
355
|
<def tag="input" for="time" attrs="order">
|
168
356
|
<% order = order.nil? ? [:year, :month, :day, :hour, :minute, :second] : comma_split(order).*.to_sym -%>
|
169
357
|
<%= select_date(this || Time.now, attributes.merge(:prefix => param_name_for_this, :order => order)) %>
|
170
358
|
</def>
|
171
359
|
|
360
|
+
|
361
|
+
<!-- A date/time picker, using the `select_datetime` helper from Rails
|
362
|
+
|
363
|
+
### Attributes
|
364
|
+
|
365
|
+
- order: The order of the year, month and date menus. A comma separated string or an array. Default: "year, month,
|
366
|
+
day, hour, minute, second"
|
367
|
+
|
368
|
+
Any other attributes are passed through to the `select_datetime` helper.
|
369
|
+
|
370
|
+
The menus default to the current time if the current value is nil.
|
371
|
+
|
372
|
+
-->
|
172
373
|
<def tag="input" for="datetime" attrs="order">
|
173
374
|
<% order = order.nil? ? [:year, :month, :day, :hour, :minute, :second] : comma_split(order).*.to_sym -%>
|
174
375
|
<%= select_datetime(this || Time.now, attributes.merge(:prefix => param_name_for_this, :order => order)) %>
|
175
376
|
</def>
|
176
377
|
|
378
|
+
<!-- An `<input type='text'>` input. -->
|
177
379
|
<def tag="input" for="integer" attrs="name">
|
178
380
|
<%= text_field_tag(name, this, attributes) %>
|
179
381
|
</def>
|
180
382
|
|
383
|
+
<!-- An `<input type='text'>` input. -->
|
181
384
|
<def tag="input" for="float" attrs="name">
|
182
385
|
<%= text_field_tag(name, this, attributes) %>
|
183
386
|
</def>
|
184
387
|
|
388
|
+
<!-- An `<input type='text'>` input. -->
|
185
389
|
<def tag="input" for="string" attrs="name">
|
186
390
|
<%= text_field_tag(name, this, attributes) %>
|
187
391
|
</def>
|
188
392
|
|
393
|
+
<!-- An `<input type='text'>` input. -->
|
189
394
|
<def tag="input" for="big_integer" attrs="name">
|
190
395
|
<%= text_field_tag(name, this, attributes) %>
|
191
396
|
</def>
|
192
397
|
|
398
|
+
<!-- A `<select>` menu containing the values of an 'enum string'.
|
399
|
+
|
400
|
+
### Attributes
|
401
|
+
|
402
|
+
- labels: A hash that gives custom labels for the values of the enum.
|
403
|
+
Any values that do not have corresponding keys in this hash will get `value.titleize` as the label.
|
404
|
+
|
405
|
+
- titleize: Set to false to have the value itself (rather than `value.titleize`) be the default label. Default: true
|
406
|
+
|
407
|
+
-->
|
193
408
|
<def tag="input" for="HoboFields::EnumString" attrs="labels, titleize"><%
|
194
409
|
labels ||= {}
|
195
410
|
titleize = true if titleize.nil?
|
@@ -201,12 +416,29 @@
|
|
201
416
|
</def>
|
202
417
|
|
203
418
|
|
204
|
-
<!--
|
419
|
+
<!-- Provides either an ajax or non-ajax button to invoke a "remote method" or "web method" declared in the controller.
|
420
|
+
Web Methods provide support for the RPC model of client-server interaction, in contrast to the REST model. The
|
421
|
+
preference in Rails is to use REST as much as possible, but we are pragmatists, and sometimes you just to need a remote
|
422
|
+
procedure call.
|
423
|
+
|
424
|
+
The URL that the call is POSTed to is the `object_url` of `this`, plus the method name
|
425
|
+
|
426
|
+
`<remote-method-button>` supports all of the standard ajax attributes (see the main taglib documention for Rapid
|
427
|
+
Forms). If any ajax attributes are given, the button becomes an ajax button, if not,
|
205
428
|
|
429
|
+
### Attributes
|
430
|
+
|
431
|
+
- method: the name of the web-method to call
|
432
|
+
|
433
|
+
- label: the label on the button
|
434
|
+
|
435
|
+
-->
|
206
436
|
<def tag="remote-method-button" attrs="method, update, label, confirm"><%=
|
207
437
|
ajax_attributes, html_attributes = attributes.partition_hash(Hobo::RapidHelper::AJAX_ATTRS)
|
208
438
|
|
209
|
-
url = object_url(this, method, :method => :post)
|
439
|
+
url = object_url(this, method.to_s.gsub('-', '_'), :method => :post)
|
440
|
+
raise ArgumentError, "no such web method '#{method}' on #{this.typed_id}" unless url
|
441
|
+
|
210
442
|
add_classes!(html_attributes, "button remote-method-button #{method}-button")
|
211
443
|
label ||= method.titleize
|
212
444
|
if update || !ajax_attributes.empty?
|
@@ -218,8 +450,26 @@
|
|
218
450
|
button_to(label, url, html_attributes.merge(:confirm => confirm))
|
219
451
|
end
|
220
452
|
%></def>
|
453
|
+
|
454
|
+
|
455
|
+
<!-- Provides an ajax button to send a RESTful update or "PUT" to the server. i.e. to udate one or more fields of a
|
456
|
+
record.
|
221
457
|
|
458
|
+
Note that unlike simliar tags, `<update-button>` does not support both ajax and non-ajax modes at this time. It only
|
459
|
+
does ajax.
|
222
460
|
|
461
|
+
`<update-button>` supports all of the standard ajax attributes (see the main taglib documention for Rapid Forms).
|
462
|
+
|
463
|
+
### Attributes
|
464
|
+
|
465
|
+
- label: The label on the button.
|
466
|
+
|
467
|
+
- fields: A hash with new field values pairs to update the resource with. The items in the hash will be converted to
|
468
|
+
HTTP parameters.
|
469
|
+
|
470
|
+
- params: Another hash with additional HTTP parameters to include in the ajax request
|
471
|
+
|
472
|
+
-->
|
223
473
|
<def tag="update-button" attrs="label, update, fields, params"><%=
|
224
474
|
raise HoboError.new("no update specified") unless update
|
225
475
|
|
@@ -233,8 +483,39 @@
|
|
233
483
|
</def>
|
234
484
|
|
235
485
|
|
486
|
+
<!-- Provides either an ajax or non-ajax delete button to send a RESTful "DELETE". The context should be a record for
|
487
|
+
which you to want provide a delete button.
|
488
|
+
|
489
|
+
The Rapid Library has a convention of marking (in the output HTML, using a special CSS class) elements as "object
|
490
|
+
elements", with the class and ID of the ActiveRecord object that they represent. `<delete-button>` assumes it is placed
|
491
|
+
inside such an element, and will automatically find the right element to remove (fade out) from the DOM. The
|
492
|
+
`<collection>` tag adds this metadata (CSS class) automatically, so `<delete-button>` works well when used inside a
|
493
|
+
`<collection>`. This is a Clever Trick which needs to be revisted and perhaps simplified.
|
494
|
+
|
495
|
+
If used within a `<collection>`, `<delete-button>` also knows how to add an "empty message" such as "no comments to
|
496
|
+
display" when you delete the last item. Clever Tricks abound.
|
497
|
+
|
498
|
+
Current limitation: There is no support for the ajax callbacks at this time.
|
499
|
+
|
500
|
+
All the standard ajax attributes *except the callbacks* are supported (see the main taglib documention for Rapid Forms).
|
501
|
+
|
502
|
+
|
503
|
+
|
504
|
+
### Attributes
|
505
|
+
|
506
|
+
- label: The label for the button. Default: "Remove"
|
507
|
+
|
508
|
+
- in-place: delete in place (ajax)? Default: true, or false if the record to be deleted is the same as the top level
|
509
|
+
context of the page
|
510
|
+
|
511
|
+
- image: URL of an image for the button. Changes the rendered tag from `<input type='button'>` to `<input type='image'
|
512
|
+
src='...'>`
|
513
|
+
|
514
|
+
- fade: Perform the fade effect (true/false)? Default: true
|
515
|
+
|
516
|
+
-->
|
236
517
|
<def tag="delete-button" attrs="label, update, in-place, image, confirm, fade, subsite"><%=
|
237
|
-
in_place = false if in_place.nil? && this == @this &&
|
518
|
+
in_place = false if in_place.nil? && this == @this && request.method == :get
|
238
519
|
url = object_url(this, :method => :delete, :subsite => subsite)
|
239
520
|
if (Hobo::Dryml.last_if = url && can_delete?)
|
240
521
|
attributes = attributes.merge(if image
|
@@ -266,6 +547,15 @@
|
|
266
547
|
%></def>
|
267
548
|
|
268
549
|
|
550
|
+
<!-- Provides either an ajax or non-ajax create button that will send a RESTful "POST" to the server to create a new resource.
|
551
|
+
|
552
|
+
All of the standard ajax attributes are supported (see the main taglib documention for Rapid Forms).
|
553
|
+
|
554
|
+
### Attributes
|
555
|
+
|
556
|
+
- model: The class to instantiate, pass either the class name or the class object.
|
557
|
+
|
558
|
+
-->
|
269
559
|
<def tag="create-button" attrs="model, update, label, fields, message"><%=
|
270
560
|
raise HoboError.new("no update specified") unless update
|
271
561
|
|
@@ -293,19 +583,20 @@
|
|
293
583
|
|
294
584
|
|
295
585
|
<def tag="select-one" attrs="include-none, blank-message, options, sort"><%
|
296
|
-
raise HoboError.new("Not allowed to edit")
|
297
|
-
|
586
|
+
raise HoboError.new("Not allowed to edit #{this_field}") if !attributes[:disabled] && !can_edit?
|
587
|
+
|
298
588
|
blank_message ||= "(No #{this_type.name.to_s.titleize})"
|
299
|
-
|
300
|
-
options ||=
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
589
|
+
|
590
|
+
options ||= begin
|
591
|
+
conditions = ActiveRecord::Associations::BelongsToAssociation.new(this_parent, this_field_reflection).conditions
|
592
|
+
this_field_reflection.klass.all(:conditions => conditions).select {|x| can_view?(x)}
|
593
|
+
end
|
594
|
+
|
595
|
+
select_options = options.map { |x| [x.to_s, x.id ] }
|
305
596
|
select_options = select_options.sort if sort
|
306
597
|
select_options.insert(0, [blank_message, ""]) if include_none || (this.nil? && include_none != false)
|
307
598
|
attributes = add_classes(attributes, "input", "belongs_to", type_and_field)
|
308
|
-
|
599
|
+
-%>
|
309
600
|
<select name="#{param_name_for_this(true)}" merge-attrs="&attributes.except :name">
|
310
601
|
<%= options_for_select(select_options, this ? this.id : "") %>
|
311
602
|
</select>
|
@@ -315,9 +606,9 @@
|
|
315
606
|
<def tag="name-one" attrs="complete-target, completer"><%
|
316
607
|
complete_target ||= this_field_reflection.klass
|
317
608
|
completer ||= (complete_target.is_a?(Class) ? complete_target : complete_target.class).name_attribute
|
318
|
-
|
609
|
+
-%>
|
319
610
|
<input type="text" name="#{param_name_for_this}"
|
320
|
-
class="autocompleter #{type_and_field}
|
611
|
+
class="autocompleter #{type_and_field.dasherize} #{css_data :complete_on, typed_id(complete_target), completer}"
|
321
612
|
value="&name :no_wrapper => true, :if_present => true"
|
322
613
|
merge-attrs/>
|
323
614
|
<div class="completions-popup" style="display:none"></div>
|
@@ -325,7 +616,7 @@
|
|
325
616
|
|
326
617
|
|
327
618
|
<def tag="sti-type-input">
|
328
|
-
<select name="#{param_name_for(
|
619
|
+
<select name="#{param_name_for(form_field_path + ['type'])}">
|
329
620
|
<%= options_for_select(this.class.send(:subclasses).map{|x| [x.name.titleize, x.name]}, this.class.name) %>
|
330
621
|
</select>
|
331
622
|
</def>
|
@@ -340,24 +631,25 @@
|
|
340
631
|
<section class="error-messages" merge-attrs if="&this.errors.length > 0">
|
341
632
|
<h2 param="heading">To proceed please correct the following:</h2>
|
342
633
|
<ul param>
|
343
|
-
<% this.errors.each do |attr, message|; next if message == "..."
|
634
|
+
<% this.errors.each do |attr, message|; next if message == "..." -%>
|
344
635
|
<li param><%= attr.titleize %> <%= message %></li>
|
345
|
-
<% end
|
636
|
+
<% end -%>
|
346
637
|
</ul>
|
347
638
|
</section>
|
348
639
|
</def>
|
349
640
|
|
350
641
|
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
642
|
+
<!--
|
643
|
+
An input for `has_many :through` associations that lets the user chose the items from a `<select>` menu.
|
644
|
+
|
645
|
+
To use this tag, the model of the items the user is chosing *must* have unique names, and the
|
646
|
+
-->
|
647
|
+
<def tag="select-many" attrs="options, targets, remove-label, prompt, disabled"><%
|
648
|
+
prompt ||= "Add #{this_field.titleize.singularize}"
|
649
|
+
options ||= this_field_reflection.klass.all(:conditions =>this.conditions).select {|x| can_view?(x)}
|
358
650
|
|
359
651
|
values = this
|
360
|
-
|
652
|
+
-%>
|
361
653
|
<div class="input select-many" merge-attrs>
|
362
654
|
<div style="display:none" class="item-proto">
|
363
655
|
<div class="item" param="proto-item">
|
@@ -368,26 +660,52 @@
|
|
368
660
|
</div>
|
369
661
|
<div class="items">
|
370
662
|
<set param-name="¶m_name_for_this"/>
|
371
|
-
<repeat>
|
372
|
-
<
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
</
|
663
|
+
<div class="item" param="item" repeat>
|
664
|
+
<span><%= h this.to_s %></span>
|
665
|
+
<input type="hidden" name="#{param_name}[]" value="@#{h this.id}" disabled="&disabled"
|
666
|
+
param="hidden"/>
|
667
|
+
<input type="button" class="remove-item" value="#{remove_label || 'Remove'}" disabled="&disabled"
|
668
|
+
param="remove-button"/>
|
669
|
+
</div>
|
378
670
|
</div>
|
379
|
-
<select>
|
671
|
+
<select merge-attrs="&{:disabled => disabled}">
|
380
672
|
<option value=""><prompt/></option>
|
381
|
-
<option repeat="&options.sort_by {|x|
|
382
|
-
merge-attrs="&{:disabled => 'true'} if this.in?(values)"
|
673
|
+
<option repeat="&options.sort_by {|x| x.to_s.downcase}" value="@#{this.id}"
|
674
|
+
merge-attrs="&{:disabled => 'true'} if this.in?(values)"><%= h this.to_s %></option>
|
383
675
|
</select>
|
384
676
|
</div>
|
385
677
|
</def>
|
386
678
|
|
679
|
+
<!--
|
680
|
+
Used inside a form to specify where to redirect after successful submission. This works by inserting a hidden field called `after_submit` which is used by Hobo if present to perform a redirect after the form submission.
|
387
681
|
|
388
|
-
|
389
|
-
|
390
|
-
|
682
|
+
### Usage
|
683
|
+
|
684
|
+
Use the `stay-here` attribute to remain on the current page:
|
685
|
+
|
686
|
+
<form>
|
687
|
+
<after-submit stay-here/>
|
688
|
+
...
|
689
|
+
</form>
|
690
|
+
|
691
|
+
Use the `go-back` option to return to the previous page:
|
692
|
+
|
693
|
+
<form>
|
694
|
+
<after-submit go-back/>
|
695
|
+
...
|
696
|
+
</form>
|
697
|
+
|
698
|
+
Use the `uri` option to specify a redirect location:
|
699
|
+
|
700
|
+
<form>
|
701
|
+
<after-submit uri="/admin"/>
|
702
|
+
...
|
703
|
+
</form>
|
704
|
+
-->
|
705
|
+
<def tag="after-submit" attrs="uri, stay-here, go-back"><%
|
706
|
+
uri = "stay-here" if stay_here
|
707
|
+
uri = session[:previous_uri] if go_back
|
708
|
+
-%>
|
391
709
|
<input type="hidden" value="¶ms[:after_submit] || uri" name="after_submit" if="&uri"/>
|
392
710
|
</def>
|
393
711
|
|
@@ -400,6 +718,17 @@
|
|
400
718
|
</def>
|
401
719
|
|
402
720
|
|
721
|
+
<def tag="check-many">
|
722
|
+
<% collection = this; param_name = param_name_for_this; options ||= this.member_class.find(:all) -%>
|
723
|
+
<ul class="check-many">
|
724
|
+
<li repeat="&options">
|
725
|
+
<input type="checkbox" name="#{param_name}[]" value="&h this.to_s" checked="&this.in?(collection)"/>
|
726
|
+
<name/>
|
727
|
+
</li>
|
728
|
+
</ul>
|
729
|
+
</def>
|
730
|
+
|
731
|
+
|
403
732
|
<def tag="hidden-id-field">
|
404
733
|
<if:id><input type="hidden" name="#{param_name_for_this}" value="#{this}" /></if>
|
405
734
|
</def>
|
@@ -407,22 +736,22 @@
|
|
407
736
|
|
408
737
|
<def tag="input-many">
|
409
738
|
<set empty="&this.empty?"/>
|
410
|
-
<ul class="input-many #{this_field.dasherize}
|
739
|
+
<ul class="input-many #{this_field.dasherize} #{css_data :input_many_prefix, param_name_for_this}">
|
411
740
|
<li repeat class="#{'record-with-errors' unless this.errors.empty?}">
|
412
741
|
<error-messages without-heading class="sub-record"/>
|
413
742
|
<hidden-id-field/>
|
414
743
|
<div class="input-many-item" param="default"/>
|
415
744
|
<div class="buttons">
|
416
|
-
<button class="remove-item" unless="&this_parent.length == 1">-</button>
|
417
|
-
<button class="add-item" if="&last_item?">+</button>
|
745
|
+
<button class="remove-item" unless="&this_parent.length == 1" merge-attrs="disabled">-</button>
|
746
|
+
<button class="add-item" if="&last_item?" merge-attrs="disabled">+</button>
|
418
747
|
</div>
|
419
748
|
</li>
|
420
749
|
<li if="&empty">
|
421
|
-
<fake-field-context fake-field="0" context="&this.
|
750
|
+
<fake-field-context fake-field="0" context="&this.new">
|
422
751
|
<div class="input-many-item" param="default"/>
|
423
752
|
</fake-field-context>
|
424
753
|
<div class="buttons">
|
425
|
-
<button class="add-item">+</button>
|
754
|
+
<button class="add-item" merge-attrs="disabled">+</button>
|
426
755
|
</div>
|
427
756
|
</li>
|
428
757
|
</ul>
|
@@ -430,7 +759,7 @@
|
|
430
759
|
|
431
760
|
|
432
761
|
<def tag="input-all">
|
433
|
-
<% association_fkey = this_field_reflection.primary_key_name
|
762
|
+
<% association_fkey = this_field_reflection.primary_key_name -%>
|
434
763
|
<ul class="input-all #{this_field.dasherize}">
|
435
764
|
<li repeat class="#{'record-with-errors' unless this.errors.empty?}">
|
436
765
|
<set-scoped form-field-names="&[]">
|
@@ -443,19 +772,9 @@
|
|
443
772
|
</def>
|
444
773
|
|
445
774
|
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
775
|
+
<def tag="or-cancel">
|
776
|
+
<if test="&linkable?">or <a>Cancel</a></if>
|
777
|
+
<else>
|
778
|
+
<if test="&linkable?(this.class)">or <a to="&this.class">Cancel</a></if>
|
779
|
+
</else>
|
780
|
+
</def>
|