hobo 0.8.3 → 0.8.4
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.
- 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>
|