hobo 1.3.3 → 1.4.0.pre2
Sign up to get free protection for your applications and to get access to all the features.
- data/{CHANGES.txt → CHANGES-1.3.txt} +0 -0
- data/CHANGES-1.4.txt +678 -0
- data/Rakefile +13 -3
- data/TODO-1.4.txt +69 -0
- data/VERSION +1 -1
- data/app/helpers/hobo_debug_helper.rb +16 -0
- data/app/helpers/hobo_deprecated_helper.rb +45 -0
- data/app/helpers/hobo_helper_base.rb +8 -0
- data/app/helpers/hobo_permissions_helper.rb +136 -0
- data/app/helpers/hobo_route_helper.rb +196 -0
- data/{lib/hobo/helper/translations.rb → app/helpers/hobo_translations_helper.rb} +4 -7
- data/{lib/hobo/helper/translations/normalizer.rb → app/helpers/hobo_translations_normalizer_helper.rb} +3 -10
- data/app/helpers/hobo_type_helper.rb +24 -0
- data/app/helpers/hobo_view_hint_helper.rb +13 -0
- data/hobo.gemspec +3 -3
- data/lib/generators/hobo/admin_subsite/admin_subsite_generator.rb +0 -9
- data/lib/generators/hobo/admin_subsite/templates/application.dryml +2 -0
- data/lib/generators/hobo/admin_subsite/templates/gitkeep +0 -0
- data/lib/generators/hobo/admin_subsite/templates/site.css.erb +9 -0
- data/lib/generators/hobo/admin_subsite/templates/site.js.erb +10 -0
- data/lib/generators/hobo/assets/assets_generator.rb +16 -2
- data/lib/generators/hobo/assets/templates/application.css +9 -0
- data/lib/generators/hobo/assets/templates/application.dryml.erb +0 -5
- data/lib/generators/hobo/assets/templates/application.js +11 -0
- data/lib/generators/hobo/assets/templates/front.css +10 -0
- data/lib/generators/hobo/assets/templates/front.js +11 -0
- data/lib/generators/hobo/assets/templates/front_site.dryml.erb +6 -0
- data/lib/generators/hobo/assets/templates/gitkeep +0 -0
- data/lib/generators/hobo/dev_tweaks/dev_tweaks_generator.rb +31 -0
- data/lib/generators/hobo/i18n/templates/hobo.en.yml +1 -1
- data/lib/generators/hobo/install_plugin/USAGE +3 -0
- data/lib/generators/hobo/install_plugin/install_plugin_generator.rb +36 -0
- data/lib/generators/hobo/plugin.rb +112 -0
- data/lib/generators/hobo/setup_wizard/setup_wizard_generator.rb +31 -14
- data/lib/generators/hobo/subsite.rb +16 -2
- data/lib/generators/hobo/subsite/templates/gitkeep +0 -0
- data/lib/generators/hobo/subsite/templates/site.css.erb +9 -0
- data/lib/generators/hobo/subsite/templates/site.js.erb +10 -0
- data/lib/generators/hobo/subsite_taglib/templates/taglib.dryml.erb +0 -17
- data/lib/generators/hobo/test_framework/test_framework_generator.rb +1 -1
- data/lib/hobo.rb +3 -2
- data/lib/hobo/controller.rb +43 -24
- data/lib/hobo/controller/model.rb +63 -42
- data/lib/hobo/controller/user_base.rb +1 -3
- data/lib/hobo/engine.rb +1 -1
- data/lib/hobo/extensions/active_record/associations/association.rb +36 -0
- data/lib/hobo/extensions/active_record/associations/collection.rb +10 -19
- data/lib/hobo/extensions/active_record/associations/proxy.rb +3 -15
- data/lib/hobo/extensions/active_record/associations/scope.rb +2 -2
- data/lib/hobo/extensions/active_record/permissions.rb +32 -38
- data/lib/hobo/extensions/active_record/relation_with_origin.rb +5 -5
- data/lib/hobo/model.rb +12 -7
- data/lib/hobo/model/accessible_associations.rb +8 -15
- data/lib/hobo/model/lifecycles/creator.rb +1 -1
- data/lib/hobo/model/lifecycles/transition.rb +1 -1
- data/lib/hobo/model/permissions.rb +4 -4
- data/lib/hobo/model/scopes.rb +4 -17
- data/lib/hobo/model/scopes/automatic_scopes.rb +5 -13
- data/lib/hobo/rapid/helper.rb +1 -161
- data/lib/hobo/rapid/taglibs/rapid.dryml +3 -17
- data/test/doctest/hobo/hobo_helper.rdoctest +8 -44
- data/{doctests → test/doctest}/hobo/lifecycles.rdoctest +0 -0
- data/{doctests → test/doctest}/hobo/model.rdoctest +2 -4
- data/{doctests → test/doctest}/hobo/multi_model_forms.rdoctest +3 -24
- data/{doctests → test/doctest}/hobo/scopes.rdoctest +3 -53
- data/test/doctest/prepare_testapp.rb +11 -0
- data/test/irt/generators/admin_subsite.irt +1 -19
- data/test/irt/generators/assets.irt +4 -9
- data/test/irt/generators/controller.irt +0 -3
- data/test/irt/generators/front_controller.irt +0 -5
- data/test/irt/generators/{helper.rb → irt_helper.rb} +2 -2
- data/test/irt/generators/model.irt +1 -12
- data/test/irt/generators/partials/_account_user_model_tests.rb +0 -8
- data/test/irt/generators/partials/_accounts_users_controller_tests.rb +0 -2
- data/test/irt/generators/partials/_default_user_model_tests.rb +0 -8
- data/test/irt/generators/partials/_default_users_controller_tests.rb +0 -2
- data/test/irt/generators/partials/_house_controller_tests.rb +0 -2
- data/test/irt/generators/partials/_house_model_tests.rb +1 -9
- data/test/irt/generators/partials/_subsite_taglib_admin.rb +5 -2
- data/test/irt/generators/partials/_subsite_taglib_admin_invite_only.rb +1 -1
- data/test/irt/generators/partials/_subsite_taglib_noopt.rb +2 -2
- data/test/irt/generators/partials/_subsite_taglib_variables.rb +0 -15
- data/test/irt/generators/partials/_user_mailer_tests.rb +1 -3
- data/test/irt/generators/resource.irt +0 -3
- data/test/irt/generators/subsite.irt +6 -22
- data/test/irt/generators/subsite_taglib.irt +0 -18
- data/test/irt/generators/test_framework.irt +2 -5
- data/test/irt/generators/user_controller.irt +0 -3
- data/test/irt/generators/user_mailer.irt +0 -3
- data/test/irt/generators/user_model.irt +0 -3
- data/test/irt/generators/user_resource.irt +0 -3
- data/test/irt/readme.txt +6 -3
- metadata +116 -159
- data/app/controllers/dev_controller.rb +0 -25
- data/app/views/dev/summary.dryml +0 -102
- data/doctests/prepare_testapp.rb +0 -8
- data/lib/generators/hobo/admin_subsite/templates/admin.css +0 -20
- data/lib/generators/hobo/rapid/USAGE +0 -3
- data/lib/generators/hobo/rapid/rapid_generator.rb +0 -24
- data/lib/generators/hobo/rapid/templates/IE7.js +0 -2
- data/lib/generators/hobo/rapid/templates/blank.gif +0 -0
- data/lib/generators/hobo/rapid/templates/hobo-rapid.css +0 -94
- data/lib/generators/hobo/rapid/templates/hobo-rapid.js +0 -1015
- data/lib/generators/hobo/rapid/templates/ie7-recalc.js +0 -166
- data/lib/generators/hobo/rapid/templates/lowpro.js +0 -339
- data/lib/generators/hobo/rapid/templates/reset.css +0 -95
- data/lib/generators/hobo/rapid/templates/themes/clean/public/images/101-3B5F87-ACD3E6.png +0 -0
- data/lib/generators/hobo/rapid/templates/themes/clean/public/images/30-3E547A-242E42.png +0 -0
- data/lib/generators/hobo/rapid/templates/themes/clean/public/images/30-DBE1E5-FCFEF5.png +0 -0
- data/lib/generators/hobo/rapid/templates/themes/clean/public/images/300-ACD3E6-fff.png +0 -0
- data/lib/generators/hobo/rapid/templates/themes/clean/public/images/50-ACD3E6-fff.png +0 -0
- data/lib/generators/hobo/rapid/templates/themes/clean/public/images/fieldbg.gif +0 -0
- data/lib/generators/hobo/rapid/templates/themes/clean/public/images/pencil.png +0 -0
- data/lib/generators/hobo/rapid/templates/themes/clean/public/images/small_close.png +0 -0
- data/lib/generators/hobo/rapid/templates/themes/clean/public/images/spinner.gif +0 -0
- data/lib/generators/hobo/rapid/templates/themes/clean/public/stylesheets/clean.css +0 -327
- data/lib/generators/hobo/rapid/templates/themes/clean/public/stylesheets/rapid-ui.css +0 -102
- data/lib/generators/hobo/rapid/templates/themes/clean/views/clean.dryml +0 -10
- data/lib/hobo/helper.rb +0 -460
- data/lib/hobo/rapid/taglibs/rapid_core.dryml +0 -808
- data/lib/hobo/rapid/taglibs/rapid_document_tags.dryml +0 -56
- data/lib/hobo/rapid/taglibs/rapid_editing.dryml +0 -287
- data/lib/hobo/rapid/taglibs/rapid_forms.dryml +0 -1156
- data/lib/hobo/rapid/taglibs/rapid_generics.dryml +0 -48
- data/lib/hobo/rapid/taglibs/rapid_i18n.dryml +0 -173
- data/lib/hobo/rapid/taglibs/rapid_lifecycles.dryml +0 -96
- data/lib/hobo/rapid/taglibs/rapid_navigation.dryml +0 -108
- data/lib/hobo/rapid/taglibs/rapid_pages.dryml +0 -259
- data/lib/hobo/rapid/taglibs/rapid_plus.dryml +0 -247
- data/lib/hobo/rapid/taglibs/rapid_summary.dryml +0 -283
- data/lib/hobo/rapid/taglibs/rapid_support.dryml +0 -102
- data/lib/hobo/rapid/taglibs/rapid_user_pages.dryml +0 -182
- data/test/irt/generators/rapid.irt +0 -29
@@ -1,56 +0,0 @@
|
|
1
|
-
<!-- Extra tags for semantic markup -->
|
2
|
-
|
3
|
-
<!-- Used as a semantic wrapper around a group of sections and asides. CSS layouts can be provided based on this structure.
|
4
|
-
|
5
|
-
### Usage
|
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
|
-
-->
|
13
|
-
<def tag="section-group"><div class="section-group"><div class="section-group-inner" merge-attrs param="default"></div></div></def>
|
14
|
-
|
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
|
-
-->
|
20
|
-
<def tag="section" attrs="empty, with-flash-messages">
|
21
|
-
<set body="¶meters.default" flash="&with_flash_messages && !scope.flash_rendered"/>
|
22
|
-
<div class="section #{'with-flash' if flash}" merge-attrs if="&!body.blank? || empty || flash">
|
23
|
-
<flash-messages if="&flash"/>
|
24
|
-
<%= body %>
|
25
|
-
</div>
|
26
|
-
</def>
|
27
|
-
|
28
|
-
<!-- A proposed HTML 5 semantic tag. Outputs `<div class="aside">` and works in the same way as `<section>` with empty content. -->
|
29
|
-
<def tag="aside" attrs="empty">
|
30
|
-
<set body="¶meters.default"/>
|
31
|
-
<div class="aside" merge-attrs if="&!body.blank? || empty"><%= body %></div>
|
32
|
-
</def>
|
33
|
-
|
34
|
-
<!-- A proposed HTML 5 semantic tag. Outputs `<div class="header">` and works in the same way as `<section>` with empty content. -->
|
35
|
-
<def tag="header" attrs="empty">
|
36
|
-
<set body="¶meters.default"/>
|
37
|
-
<div class="header" merge-attrs if="&!body.blank? || empty"><%= body %></div>
|
38
|
-
</def>
|
39
|
-
|
40
|
-
<!-- A proposed HTML 5 semantic tag. Outputs `<div class="footer">` and works in the same way as `<section>` with empty content. -->
|
41
|
-
<def tag="footer" attrs="empty">
|
42
|
-
<set body="¶meters.default"/>
|
43
|
-
<div class="footer" merge-attrs if="&!body.blank? || empty"><%= body %></div>
|
44
|
-
</def>
|
45
|
-
|
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>
|
@@ -1,287 +0,0 @@
|
|
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
|
-
-->
|
9
|
-
<def tag="editor" ><%=
|
10
|
-
if !can_edit?
|
11
|
-
view(attributes)
|
12
|
-
else
|
13
|
-
attrs = add_classes(attributes, type_id, type_and_field, "editor")
|
14
|
-
if (refl = this_field_reflection)
|
15
|
-
if refl.macro == :belongs_to
|
16
|
-
belongs_to_editor(attrs)
|
17
|
-
else
|
18
|
-
has_many_editor(attrs)
|
19
|
-
end
|
20
|
-
else
|
21
|
-
call_polymorphic_tag("editor", attrs) or
|
22
|
-
raise Hobo::Error.new("<editor> not implemented for #{this.class.name}\##{this_field} " +
|
23
|
-
"(#{this.inspect}:#{this_type})")
|
24
|
-
end
|
25
|
-
end
|
26
|
-
%></def>
|
27
|
-
|
28
|
-
|
29
|
-
<!-- Not implemented - you just get links to the items in the collection -->
|
30
|
-
<def tag="has-many-editor">
|
31
|
-
<% #TODO: Implement
|
32
|
-
%>
|
33
|
-
<a merge-attrs/>
|
34
|
-
</def>
|
35
|
-
|
36
|
-
<!-- Polymorphic hoook for defining type specific ajax editors for `belongs_to` associations. The default is `<select-one-editor>` -->
|
37
|
-
<def tag="belongs-to-editor" polymorphic><%= select_one_editor(attributes) %></def>
|
38
|
-
|
39
|
-
<!-- Provides a simple Scriptaculous in-place-editor that uses an `<input type='text'>` -->
|
40
|
-
<def tag="editor" for="string"><%= in_place_editor(attributes, this) %></def>
|
41
|
-
|
42
|
-
<!-- Provides a simple Scriptaculous in-place-editor that uses a `<textarea>` -->
|
43
|
-
<def tag="editor" for="text"><%= in_place_editor(attributes, this) %></def>
|
44
|
-
|
45
|
-
<!-- Provides a simple Scriptaculous in-place-editor that uses a `<textarea>`.
|
46
|
-
A JavaScript hook is available in order to replace the simple textarea with a rich-text editor.
|
47
|
-
For an example, see the [hoboyui](http://github.com/tablatom/hoboyui) plugin
|
48
|
-
|
49
|
-
This tag does not sanitize HTML; this is provided by HtmlString before saving to the database.
|
50
|
-
-->
|
51
|
-
<def tag="editor" for="html"><%= in_place_editor(attributes, this) %></def>
|
52
|
-
|
53
|
-
<!-- Provides a simple Scriptaculous in-place-editor that uses an `<input type='text'>` -->
|
54
|
-
<def tag="editor" for="datetime"><%= in_place_editor(attributes, this) %></def>
|
55
|
-
|
56
|
-
<!-- Provides a simple Scriptaculous in-place-editor that uses an `<input type='text'>` -->
|
57
|
-
<def tag="editor" for="date"><%= in_place_editor(attributes, this) %></def>
|
58
|
-
|
59
|
-
<!-- Provides a simple Scriptaculous in-place-editor that uses an `<input type='integer'>` -->
|
60
|
-
<def tag="editor" for="integer"><%= in_place_editor(attributes, this) %></def>
|
61
|
-
|
62
|
-
<!-- Provides a simple Scriptaculous in-place-editor that uses an `<input type='BigDecimal'>` -->
|
63
|
-
<def tag="editor" for="BigDecimal"><%= in_place_editor(attributes, this) %></def>
|
64
|
-
|
65
|
-
<!-- Provides a simple Scriptaculous in-place-editor that uses an `<input type='text'>` -->
|
66
|
-
<def tag="editor" for="float"><%= in_place_editor(attributes, this) %></def>
|
67
|
-
|
68
|
-
<!-- Raises an error - passwords cannot be edited in place -->
|
69
|
-
<def tag="editor" for="password"><% raise Hobo::Error, "passwords cannot be edited in place" %></def>
|
70
|
-
|
71
|
-
<!-- calls `<boolean-checkbox-editor>` -->
|
72
|
-
<def tag="editor" for="boolean"><boolean-checkbox-editor merge-attrs/></def>
|
73
|
-
|
74
|
-
<!-- Provides an editor that uses a `<select>` menu. Uses the `<string-select-editor>` tag. -->
|
75
|
-
<def tag="editor" for="HoboFields::Types::EnumString">
|
76
|
-
<string-select-editor values="&this_type.values" merge/>
|
77
|
-
</def>
|
78
|
-
|
79
|
-
<!-- Provides a `<select>` menu with an ajax callback to update a `belongs_to` relationship when changed.
|
80
|
-
By default the menu contains every record in the target model's table.
|
81
|
-
|
82
|
-
### Attributes
|
83
|
-
|
84
|
-
- `include-none` - whether to include a 'none' option (i.e. set the foreign key to null). If this value is not supplied, the default is "true" if the current value is nil; otherwise the default is "false". One implication of this is that the default may change when the form is re-rendered due to a validation failure. Setting this value explicitly is recommended.
|
85
|
-
- `blank-message` - the message for the 'none' option. Defaults to "(No `<model-name>`)", e.g. "(No Product)"
|
86
|
-
- `options` - an array of records to include in the menu. Defaults to the all the records in the target table that match any `:conditions` declared on the `belongs_to` (subject to `limit`)
|
87
|
-
- `sort` - whether to sort the array of options. Defaults to no sorting.
|
88
|
-
- `limit` - if `options` is not specified, this limits the number of records. Default: 100
|
89
|
-
- `text_method` - The method to call on each record to get the text for the option. Multiple methods are supported ie "institution.name"
|
90
|
-
- `update` - one or more DOM ID's (comma separated string or an array) to be updated as part of the ajax call.
|
91
|
-
|
92
|
-
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.
|
93
|
-
|
94
|
-
-->
|
95
|
-
<def tag="select-one-editor" attrs="include-none, blank-message, options, sort, limit, text-method, update"><%
|
96
|
-
raise Hobo::Error.new("Not allowed to edit") unless can_edit?
|
97
|
-
blank_message ||= ht("#{this_type.name.to_s.underscore}.select_one_editor.blank_message", :count => 0, :default => "(No #{this_type.name.to_s.titleize})")
|
98
|
-
limit ||= 100
|
99
|
-
|
100
|
-
if options.blank? || !options.first.respond_to?(:first)
|
101
|
-
options ||= begin
|
102
|
-
conditions = ActiveRecord::Associations::BelongsToAssociation.new(this_parent, this_field_reflection).send(:conditions)
|
103
|
-
order = this_field_reflection.klass.default_order
|
104
|
-
this_field_reflection.klass.all(:conditions => conditions, :limit => limit, :order => order).select {|x| can_view?(x)}
|
105
|
-
end
|
106
|
-
|
107
|
-
id_method = this_field_reflection.options[:primary_key] || this_field_reflection.klass.primary_key
|
108
|
-
if text_method.nil?
|
109
|
-
select_options = options.map { |x| [name(:with => x, :no_wrapper => true), x.send(id_method)] }
|
110
|
-
else
|
111
|
-
select_options = options.map do |x|
|
112
|
-
[ text_method.split(".").inject(x) { |v, method| v.send(method) },
|
113
|
-
x.send(id_method) ]
|
114
|
-
end
|
115
|
-
end
|
116
|
-
else
|
117
|
-
# handle the old style, where options could be passed an options_for_select style array
|
118
|
-
select_options = options
|
119
|
-
end
|
120
|
-
select_options = select_options.sort if sort
|
121
|
-
select_options.insert(0, [blank_message, ""]) if include_none || (this.nil? && include_none != false)
|
122
|
-
|
123
|
-
f = ajax_updater(object_url(this_parent, :method => :put),
|
124
|
-
update,
|
125
|
-
:method => "put",
|
126
|
-
:params => { this_parent.class.name.underscore => {
|
127
|
-
this_field_reflection.primary_key_name => Hobo.raw_js('this.value')
|
128
|
-
} })
|
129
|
-
%>
|
130
|
-
<select onchange="#{f}" merge-attrs>
|
131
|
-
<%= options_for_select(select_options, this ? this.send(id_method) : "") %>
|
132
|
-
</select>
|
133
|
-
</def>
|
134
|
-
|
135
|
-
<!-- Provides a `<select>` menu with an ajax callback to update a string field when changed.
|
136
|
-
|
137
|
-
### Attributes
|
138
|
-
|
139
|
-
- values: The values for the menu options. Required
|
140
|
-
|
141
|
-
- Labels: A hash that can be used to customise the labels for the menu.
|
142
|
-
Any value that does not have a corresponding key in this hash will have its label
|
143
|
-
generated by `value.titleize`
|
144
|
-
|
145
|
-
- titleize: Set to false to have the default labels be the same as the values. Default: true - the labels are generated by `value.titleize`
|
146
|
-
|
147
|
-
- update: one or more DOM ID's (comma separated string or an array) to be updated as part of the ajax call.
|
148
|
-
|
149
|
-
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.
|
150
|
-
|
151
|
-
-->
|
152
|
-
<def tag="string-select-editor" attrs="update, values, labels, titleize"><%
|
153
|
-
raise Hobo::Error.new("Not allowed to edit") unless can_edit?
|
154
|
-
|
155
|
-
values = comma_split(values)
|
156
|
-
labels ||= {}
|
157
|
-
titleize = true if titleize.nil?
|
158
|
-
options = values.map {|v| [labels.fetch(v.to_sym, titleize ? v.titleize : v), v] }
|
159
|
-
|
160
|
-
f = ajax_updater(object_url(this_parent, :method => :put),
|
161
|
-
update,
|
162
|
-
:method => "put",
|
163
|
-
:params => { this_parent.class.name.underscore => {
|
164
|
-
this_field => Hobo.raw_js('this.value')
|
165
|
-
} })
|
166
|
-
%>
|
167
|
-
<select onchange="#{f}" merge-attrs>
|
168
|
-
<%= options_for_select(options, this) %>
|
169
|
-
</select>
|
170
|
-
</def>
|
171
|
-
|
172
|
-
<!-- A checkbox with an ajax callback to update a boolean field when clicked.
|
173
|
-
|
174
|
-
### Attributes
|
175
|
-
|
176
|
-
- update: one or more DOM ID's (comma separated string or an array) to be updated as part of the ajax call.
|
177
|
-
|
178
|
-
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.
|
179
|
-
|
180
|
-
- message: A message to display in the ajax-progress spinner. Default: "Saving..."
|
181
|
-
|
182
|
-
-->
|
183
|
-
<def tag="boolean-checkbox-editor" attrs="update, message"><%
|
184
|
-
raise Hobo::Error.new("Not allowed to edit") unless can_edit?
|
185
|
-
f = ajax_updater(object_url(this_parent, :method => :put),
|
186
|
-
update,
|
187
|
-
:method => "put",
|
188
|
-
:message => message,
|
189
|
-
:spinner_next_to => Hobo.raw_js("this"),
|
190
|
-
:params => { this_parent.class.name.underscore => {
|
191
|
-
this_field => Hobo.raw_js('this.checked')
|
192
|
-
} })
|
193
|
-
%>
|
194
|
-
<input type="checkbox" value="1" onclick="#{f}"
|
195
|
-
merge-attrs="& this ? attributes.merge(:checked => 'checked') : attributes" />
|
196
|
-
</def>
|
197
|
-
|
198
|
-
|
199
|
-
<!-- nodoc. -->
|
200
|
-
<def tag="sti-type-editor" attrs="update">
|
201
|
-
<% base_class = this.class
|
202
|
-
base_class = base_class.superclass while base_class.superclass != ActiveRecord::Base
|
203
|
-
f = ajax_updater("#{base_url}/#{controller_for base_class}/#{this.id}",
|
204
|
-
update,
|
205
|
-
:method => "put",
|
206
|
-
:params => { base_class.name.underscore => {
|
207
|
-
"type" => Hobo.raw_js('this.value')
|
208
|
-
} })
|
209
|
-
%>
|
210
|
-
|
211
|
-
<select onchange="#{f}">
|
212
|
-
<%= parameters.default || options_for_select(base_class.send(:descendants).map{|x| [x.name.titleize, x.name]},
|
213
|
-
this.class.name) %>
|
214
|
-
</select>
|
215
|
-
</def>
|
216
|
-
|
217
|
-
|
218
|
-
<!-- Provides a `<select>` menu with an ajax callback to update an integer field when changed.
|
219
|
-
|
220
|
-
### Attributes
|
221
|
-
|
222
|
-
- min: The minimum end of the range of numbers to include
|
223
|
-
|
224
|
-
- max: A male name, short for Maximilian
|
225
|
-
|
226
|
-
- options: An array of numbers to use if min..max is not enough for your needs.
|
227
|
-
|
228
|
-
- nil-option: Label to give if the current value is nil. Default: "Choose a value"
|
229
|
-
|
230
|
-
- message: A message to display in the ajax-progress spinner. Default: "Saving..."
|
231
|
-
|
232
|
-
- update: one or more DOM ID's (comma separated string or an array) to be updated as part of the ajax call.
|
233
|
-
|
234
|
-
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.
|
235
|
-
|
236
|
-
-->
|
237
|
-
<def tag="integer-select-editor" attrs="options, min, max, update, nil-option, message">
|
238
|
-
<% options ||= (min.to_i..max.to_i).to_a %>
|
239
|
-
<select class="integer editor #{update_elements_class(update)} #{model_id_class(this_parent, this_field)}"
|
240
|
-
merge-attrs="&message ? attributes.merge(:hobo_message => message) : attributes">
|
241
|
-
<if test="&this.nil?"><option value=""><%= nil_option || "Choose a value" %></option></if>
|
242
|
-
<%= options_for_select(options.*.to_s, this.to_s) %>
|
243
|
-
</select>
|
244
|
-
</def>
|
245
|
-
|
246
|
-
|
247
|
-
<!-- nodoc. -->
|
248
|
-
<def tag="has-many-checkbox-editor" attrs="model, update, message"><%=
|
249
|
-
raise Hobo::Error.new("no update specified") unless update
|
250
|
-
|
251
|
-
fields = attributes.delete_if{|k,v|!k.ends_with? "_id"}
|
252
|
-
conditions = fields.map{|k,v|"#{k}=#{v}"}.join " AND "
|
253
|
-
|
254
|
-
klass = model.is_a?(String) ? model.constantize : model
|
255
|
-
obj = klass.where(conditions).first
|
256
|
-
|
257
|
-
checkbox_attrs = {:type =>'checkbox'}
|
258
|
-
|
259
|
-
if obj == nil
|
260
|
-
new = klass.new(fields)
|
261
|
-
permission = if can_create?(new)
|
262
|
-
class_name = new.class.name.underscore
|
263
|
-
ajax_options = { :message => message }
|
264
|
-
ajax_options[:params] = { class_name => fields } unless fields.empty?
|
265
|
-
checkbox_attrs[:onclick] = ajax_updater(object_url(new.class, :method => :post), update, ajax_options)
|
266
|
-
end
|
267
|
-
else
|
268
|
-
permission = if can_delete?(obj)
|
269
|
-
checkbox_attrs[:checked] = 'checked'
|
270
|
-
message ||= "Unsetting #{obj.class.name.titleize}"
|
271
|
-
class_name = obj.class.name.underscore
|
272
|
-
checkbox_attrs[:onclick] = ajax_updater(object_url(obj, :method => :delete), update, {:message => message, :method => 'delete'})
|
273
|
-
end
|
274
|
-
end
|
275
|
-
element(:input, add_classes(attributes.merge(checkbox_attrs),
|
276
|
-
"checkbox_input has_many_checkbox has_many_#{class_name}_checkbox")) if permission
|
277
|
-
%></def>
|
278
|
-
|
279
|
-
<!-- nodoc. -->
|
280
|
-
<def tag="has-many-checkbox-editors">
|
281
|
-
<table>
|
282
|
-
<tr:>
|
283
|
-
<td><has-many-checkbox-editor param="editor" merge-attrs/></td>
|
284
|
-
<td><name param/></td>
|
285
|
-
</tr>
|
286
|
-
</table>
|
287
|
-
</def>
|
@@ -1,1156 +0,0 @@
|
|
1
|
-
<!-- Rapid Forms provides various tags that make it quick and easy to produce working new or edit forms.
|
2
|
-
|
3
|
-
### Overview
|
4
|
-
|
5
|
-
The main tags are:
|
6
|
-
|
7
|
-
- `<form>`, which acts like the dumb HTML tag if you provide the `action` attribute, and picks up various Rapid smarts
|
8
|
-
otherwise.
|
9
|
-
|
10
|
-
- `<input>`, which automatically choses an appropriate form control based on the type of the date.
|
11
|
-
|
12
|
-
### Ajax Attributes
|
13
|
-
|
14
|
-
Several of the tags in this taglib support the following set of ajax attributes:
|
15
|
-
|
16
|
-
- update: one or more DOM ID's (comma separated string or an array) to be updated as part of the ajax call. Default - no
|
17
|
-
update.
|
18
|
-
|
19
|
-
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
|
20
|
-
the same.
|
21
|
-
|
22
|
-
- params: a hash of name/value pairs that will be converted to HTTP parameters in the ajax request
|
23
|
-
|
24
|
-
- confirm: a message to be displayed in a JavaScript confirm dialog. By default there is no confirm dialog
|
25
|
-
|
26
|
-
- message: a message to be displayed in the Ajax progress spinner. Default: "Saving..."
|
27
|
-
|
28
|
-
- spinner-next-to: DOM ID of an element to position the ajax progress spinner next to.
|
29
|
-
|
30
|
-
### Ajax Callbacks
|
31
|
-
|
32
|
-
The following attributes are also supported by all the ajax tags. Set them to fragments of javascript to have that script
|
33
|
-
executed at various points in the ajax request cycle:
|
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. -->
|
45
|
-
<def tag="hidden-fields" attrs="fields, for-query-string, skip"><%=
|
46
|
-
pairs = if for_query_string
|
47
|
-
query_params.to_a
|
48
|
-
else
|
49
|
-
hiddens = case fields
|
50
|
-
when '*', nil
|
51
|
-
# TODO: Need a better (i.e. extensible) way to eleminate certain fields
|
52
|
-
this.class.column_names - ['type', 'created_at', 'updated_at']
|
53
|
-
else
|
54
|
-
comma_split(fields)
|
55
|
-
end
|
56
|
-
hiddens.map do |field|
|
57
|
-
val = this.send(field)
|
58
|
-
param_name = param_name_for(form_field_path + [field])
|
59
|
-
[param_name, val] unless val.nil? ||
|
60
|
-
field.to_sym.in?(this.class.attr_protected) ||
|
61
|
-
(this.new_record? && val == this.class.column(field).default)
|
62
|
-
end.compact
|
63
|
-
end
|
64
|
-
skip = comma_split skip
|
65
|
-
pairs.reject! { |p| p.first.in?(skip) }
|
66
|
-
pairs.map { |n, v| hidden_field_tag(n, v.to_s) if v && n.not_in?(scope.form_field_names) }.compact.safe_join("\n".html_safe)
|
67
|
-
%></def>
|
68
|
-
|
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
|
-
### Additional Notes
|
104
|
-
|
105
|
-
- Hobo automatically inserts an `auth_token` hidden field if forgery protection is enabled
|
106
|
-
|
107
|
-
- Hobo inserts a `page_path` hidden field in create / update forms which it uses to re-render the correct page if a
|
108
|
-
validation error occurs.
|
109
|
-
|
110
|
-
- `<form>` supports all of the standrd ajax attributes - (see the main taglib docs for Rapid Forms)
|
111
|
-
|
112
|
-
- `<form>` resets `last_if` if it does not have permission to display the form. The `<else>` clause may be used to display alternate content. For example:
|
113
|
-
|
114
|
-
<form>...</form>
|
115
|
-
<else>You do not have permission to edit this form</else>
|
116
|
-
|
117
|
-
or on a standard generated page using a default form:
|
118
|
-
|
119
|
-
<some-page>
|
120
|
-
<after-form:>
|
121
|
-
<else>You do not have permission to edit this form</else>
|
122
|
-
</after-form:>
|
123
|
-
</some-page>
|
124
|
-
|
125
|
-
### Attributes
|
126
|
-
|
127
|
-
- [all AJAX attributes](/api_taglibs/rapid_forms)
|
128
|
-
|
129
|
-
- action: the controller action. Default is create or update as appropriate
|
130
|
-
|
131
|
-
- method: PUT or POST
|
132
|
-
|
133
|
-
- web-method
|
134
|
-
|
135
|
-
- lifecycle
|
136
|
-
|
137
|
-
- owner
|
138
|
-
|
139
|
-
- multipart: if set, the encoding is set to multipart/form-data. The default is x-www-form-urlencoded
|
140
|
-
|
141
|
-
- reset-form: Clear the form after submission (only makes sense for ajax forms)
|
142
|
-
|
143
|
-
- refocus-form: Refocus the first form-field after submission (only makes sense for ajax forms)
|
144
|
-
|
145
|
-
### Parameters
|
146
|
-
|
147
|
-
The standard form tag does not have any parameters, nor does it have any default content. However, Hobo does autogenerate polymorphic form tags for each of your models into `app/views/taglibs/auto/rapid/forms.dryml`. These forms have the following parameters:
|
148
|
-
|
149
|
-
- error-messages
|
150
|
-
|
151
|
-
- field-list
|
152
|
-
|
153
|
-
- actions
|
154
|
-
|
155
|
-
- submit
|
156
|
-
|
157
|
-
- cancel
|
158
|
-
|
159
|
-
-->
|
160
|
-
<def tag="form" polymorphic attrs="update, hidden-fields, action, method, web-method, lifecycle, owner, multipart"><%=
|
161
|
-
ajax_attrs, html_attrs = attributes.partition_hash(Hobo::Rapid::Helper::AJAX_ATTRS)
|
162
|
-
html_attrs[:enctype] ||= "multipart/form-data" if multipart
|
163
|
-
|
164
|
-
new_record = this.try.new_record?
|
165
|
-
|
166
|
-
method = if method.nil?
|
167
|
-
(action || web_method || new_record) ? "post" : "put"
|
168
|
-
else
|
169
|
-
method.downcase
|
170
|
-
end
|
171
|
-
|
172
|
-
html_attrs[:action] = action || begin
|
173
|
-
target = if owner
|
174
|
-
collection_name = this.class.reverse_reflection(owner).name
|
175
|
-
this.send(owner).send(collection_name)
|
176
|
-
else
|
177
|
-
this
|
178
|
-
end
|
179
|
-
action = web_method || lifecycle
|
180
|
-
object_url(target, action, :method => method)
|
181
|
-
end
|
182
|
-
|
183
|
-
if action.nil? && (html_attrs[:action].nil? ||
|
184
|
-
(lifecycle.nil? && new_record && !this.creatable_by?(current_user)) ||
|
185
|
-
(lifecycle.nil? && !new_record && !can_edit?))
|
186
|
-
Dryml.last_if = false
|
187
|
-
""
|
188
|
-
else
|
189
|
-
if method == "put"
|
190
|
-
# browsers don't support put -- use post and add the Rails _method hack
|
191
|
-
http_method_hidden = hidden_field_tag("_method", "PUT")
|
192
|
-
html_attrs[:method] = "post"
|
193
|
-
else
|
194
|
-
http_method_hidden = ""
|
195
|
-
html_attrs[:method] = method
|
196
|
-
end
|
197
|
-
|
198
|
-
if update || !ajax_attrs.empty?
|
199
|
-
# add an onsubmit to convert to an ajax form if `update` is given
|
200
|
-
function = ajax_updater(:post_form, update, ajax_attrs)
|
201
|
-
html_attrs[:onsubmit] = [html_attrs[:onsubmit], "#{function}; return false;"].compact.safe_join("; ".html_safe)
|
202
|
-
end
|
203
|
-
|
204
|
-
hiddens = ""
|
205
|
-
body = with_form_context do
|
206
|
-
# It is important to evaluate parameters.default first, in order to populate scope.form_field_names
|
207
|
-
b = parameters.default
|
208
|
-
hiddens = self.hidden_fields(:fields => hidden_fields) if new_record
|
209
|
-
b
|
210
|
-
end
|
211
|
-
|
212
|
-
auth_token = if method.nil? || method == 'get' || !protect_against_forgery?
|
213
|
-
''
|
214
|
-
else
|
215
|
-
element(:input, {:type => "hidden",
|
216
|
-
:name => request_forgery_protection_token.to_s,
|
217
|
-
:value => form_authenticity_token}, nil, true, true)
|
218
|
-
end
|
219
|
-
|
220
|
-
unless method == "get"
|
221
|
-
page_path = if (request.post? || request.put?) && params[:page_path]
|
222
|
-
params[:page_path]
|
223
|
-
else
|
224
|
-
request.fullpath
|
225
|
-
end
|
226
|
-
page_path_hidden = hidden_field_tag("page_path", page_path)
|
227
|
-
end
|
228
|
-
|
229
|
-
hiddens_div = element(:div, {:class => "hidden-fields"}, [http_method_hidden, page_path_hidden, auth_token, hiddens].safe_join)
|
230
|
-
|
231
|
-
body = [hiddens_div, body].safe_join
|
232
|
-
|
233
|
-
if action.nil? # don't add automatic css classes if the action was specified
|
234
|
-
if web_method
|
235
|
-
add_classes!(html_attrs, "#{type_id.dasherize}-#{web_method}-form")
|
236
|
-
else
|
237
|
-
add_classes!(html_attrs, "#{'new ' if new_record}#{type_id.dasherize}")
|
238
|
-
end
|
239
|
-
end
|
240
|
-
|
241
|
-
Dryml.last_if = true
|
242
|
-
element("form", html_attrs, body)
|
243
|
-
end
|
244
|
-
%></def>
|
245
|
-
|
246
|
-
<!-- A shortcut for generating a submit button.
|
247
|
-
|
248
|
-
### Usage
|
249
|
-
|
250
|
-
<submit label="Go!"/> -> <input type="submit" value="Go!" class="button submit-button"/>
|
251
|
-
<submit image="/images/go.png"/> -> <input type="image" src="/images/go.png" class="button submit-button"/>
|
252
|
-
|
253
|
-
-->
|
254
|
-
<def tag="submit" attrs="label, image">
|
255
|
-
<input if="&image" type="image" src="&image" merge-attrs class="image-button submit-button"/>
|
256
|
-
<else>
|
257
|
-
<input type="submit" value="#{label}" merge-attrs class="button submit-button"/>
|
258
|
-
</else>
|
259
|
-
</def>
|
260
|
-
|
261
|
-
<!--
|
262
|
-
Provides an editable control tailored to the type of the object in context. `<input>` tags should be used within a
|
263
|
-
`<form>`. `<input>` is a _polymorphic_ tag which means that there are a variety of definitions, each one written for a
|
264
|
-
particular type. For example there are inputs for `text`, `boolean`, `password`, `date`, `datetime`, `integer`,
|
265
|
-
`float`, `string` and more.
|
266
|
-
|
267
|
-
### Usage
|
268
|
-
|
269
|
-
The tag behaves as a regular HTML input if the type attribute is given:
|
270
|
-
|
271
|
-
<input type="text" name="my_input"/> -> Output is exactly as provided, untouched by Rapid
|
272
|
-
|
273
|
-
If no type attribute is given then the _context_ is used. For example if the context is a blog post:
|
274
|
-
|
275
|
-
<input:title/> ->
|
276
|
-
<input id="blog_post[name]" class="string blog-post-name" type="text" value="My Blog Post" name="blog_post[name]"/>
|
277
|
-
|
278
|
-
<input:created_at/> ->
|
279
|
-
<select id="blog_post_created_at_year" name="blog_post[created_at][year]">...</select>
|
280
|
-
<select id="blog_post_created_at_month" name="blog_post[created_at][month]">...</select>
|
281
|
-
<select id="blog_post_created_at_day" name="blog_post[created_at][day]">...</select>
|
282
|
-
|
283
|
-
<input:description/> ->
|
284
|
-
<textarea class="text blog-post-description" id="blog_post[description]" name="blog_post[description]">...</textarea>
|
285
|
-
|
286
|
-
If the context is a `belongs_to` association, the `<select-one>` tag is used.
|
287
|
-
|
288
|
-
If the context is a `has_many :through` association, the polymorphic `<collection-input>` tag is used.
|
289
|
-
|
290
|
-
### Attributes
|
291
|
-
|
292
|
-
- no-edit: control what happens if `can_edit?` is false. Can be one of:
|
293
|
-
|
294
|
-
- view: render the current value using the `<view>` tag
|
295
|
-
- disable: render the input as normal, but add HTML's `disabled` attribute
|
296
|
-
- skip: render nothing at all
|
297
|
-
- ignore: render the input normally. That is, don't even perform the edit check.
|
298
|
-
-->
|
299
|
-
<def tag="input" attrs="no-edit"><%=
|
300
|
-
if attributes[:type]
|
301
|
-
element :input, attributes, nil, true, true
|
302
|
-
else
|
303
|
-
no_edit ||= :view
|
304
|
-
no_edit = no_edit.to_sym
|
305
|
-
no_edit_permission = !can_edit? unless no_edit == :ignore
|
306
|
-
if no_edit_permission && no_edit == :view
|
307
|
-
view
|
308
|
-
elsif no_edit_permission && no_edit == :skip
|
309
|
-
""
|
310
|
-
else
|
311
|
-
attrs = add_classes(attributes, type_id.dasherize, type_and_field.dasherize)
|
312
|
-
attrs[:name] ||= param_name_for_this
|
313
|
-
attrs[:disabled] = true if no_edit_permission && no_edit == :disable
|
314
|
-
the_input = if (refl = this_field_reflection)
|
315
|
-
if refl.macro == :belongs_to
|
316
|
-
call_polymorphic_tag('input', attrs) or select_one(attrs)
|
317
|
-
elsif refl.macro == :has_many
|
318
|
-
if refl.options[:through]
|
319
|
-
collection_input(attrs)
|
320
|
-
else
|
321
|
-
input_many(attrs)
|
322
|
-
end
|
323
|
-
end
|
324
|
-
else
|
325
|
-
call_polymorphic_tag('input', attrs) or
|
326
|
-
(call_polymorphic_tag('input', HoboFields.to_class(this_type::COLUMN_TYPE), attrs) if defined?(this_type::COLUMN_TYPE)) or
|
327
|
-
raise Hobo::Error, ("No input tag for #{this_field}:#{this_type} (this=#{this.inspect})")
|
328
|
-
end
|
329
|
-
unless this_parent.errors[this_field].empty?
|
330
|
-
"<span class='field-with-errors'>#{the_input}</span>".html_safe
|
331
|
-
else
|
332
|
-
the_input
|
333
|
-
end
|
334
|
-
end
|
335
|
-
end
|
336
|
-
%></def>
|
337
|
-
|
338
|
-
|
339
|
-
<!-- This tag is called by `<input>` when the context is a `has_many :through` collection. By default a `<select-many>`
|
340
|
-
is used, but this can be customised on a per-type basis. For example, say you would like the `<check-many>` tag used to
|
341
|
-
edit collections a `Category` model in your application:
|
342
|
-
|
343
|
-
<def tag="collection-input" for="Category"><check-many merge/></def>
|
344
|
-
-->
|
345
|
-
<def tag="collection-input" polymorphic></def>
|
346
|
-
|
347
|
-
<!-- The default `<collection-input>` - calls `<select-many>` -->
|
348
|
-
<def tag="collection-input" for="ActiveRecord::Base"><select-many merge/></def>
|
349
|
-
|
350
|
-
|
351
|
-
<!-- A `<textarea>` input -->
|
352
|
-
<def tag="input" for="text" attrs="name">
|
353
|
-
<%= text_area_tag(name, this, attributes) %>
|
354
|
-
</def>
|
355
|
-
|
356
|
-
<!-- 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) -->
|
357
|
-
<def tag="input" for="boolean" attrs="name">
|
358
|
-
<%= unless attributes[:disabled]
|
359
|
-
cb_tag = check_box_tag(name, '1', this, attributes)
|
360
|
-
cb_hidden_tag = hidden_field_tag(name, '0', :id => nil)
|
361
|
-
cb_hidden_tag + cb_tag
|
362
|
-
end %>
|
363
|
-
</def>
|
364
|
-
|
365
|
-
<!-- A password input - `<input type='password'>` -->
|
366
|
-
<def tag="input" for="password" attrs="name">
|
367
|
-
<%= password_field_tag(name, this, attributes) %>
|
368
|
-
</def>
|
369
|
-
|
370
|
-
<!-- A date picker, using the `select_date` helper from Rails
|
371
|
-
|
372
|
-
### Attributes
|
373
|
-
|
374
|
-
- all the options of select_date and date_select are passed to the select_date Rails
|
375
|
-
helper
|
376
|
-
|
377
|
-
All the other attributes are passed to the `select_date` helper as the html-options hash.
|
378
|
-
|
379
|
-
The menus default to the current date if the current value is nil.
|
380
|
-
|
381
|
-
Examples:
|
382
|
-
|
383
|
-
- override the input for date tag in a form
|
384
|
-
|
385
|
-
<my-special-date-view:>
|
386
|
-
<input start-year="&1940" order="day,month,year" />
|
387
|
-
</my-special-date-view:>
|
388
|
-
|
389
|
-
- override the tag application-wide
|
390
|
-
|
391
|
-
<extend tag='input' for='date'>
|
392
|
-
<old-input merge order="day,month,year" start-year="&1940" />
|
393
|
-
</extend>
|
394
|
-
|
395
|
-
-->
|
396
|
-
<def tag="input" for="date" attrs="use-month-numbers, use-short-month, add-month-numbers, use-month-names, date-separator, start-year, end-year, discard-day, discard-month, discard-year, order, include-blank, default, disabled, prompt, prefix">
|
397
|
-
<% order = order.nil? ? [:year, :month, :day] : comma_split(order).*.to_sym -%>
|
398
|
-
<%= select_date(this || current_time,
|
399
|
-
(all_attributes - attributes.keys).reverse_merge(:prefix => param_name_for_this).merge(:order => order),
|
400
|
-
attributes - [:name]) %>
|
401
|
-
</def>
|
402
|
-
|
403
|
-
|
404
|
-
<!-- A date/time picker, using the `select_time` helper from Rails
|
405
|
-
|
406
|
-
### Attributes
|
407
|
-
|
408
|
-
- include-seconds, time-separator, prompt and prefix are passed to the select_time helper as options
|
409
|
-
|
410
|
-
All the other attributes are passed to the `select_time` helper as the html-options hash.
|
411
|
-
|
412
|
-
The menus default to the current time if the current value is nil.
|
413
|
-
|
414
|
-
-->
|
415
|
-
<def tag="input" for="time" attrs="include-seconds, time-separator, prompt, prefix">
|
416
|
-
<%= select_time( this || current_time,
|
417
|
-
(all_attributes - attributes.keys).reverse_merge(:prefix => param_name_for_this),
|
418
|
-
attributes - [:name] ) %>
|
419
|
-
</def>
|
420
|
-
|
421
|
-
|
422
|
-
<!-- A date/time picker, using the `select_datetime` helper from Rails
|
423
|
-
|
424
|
-
### Attributes
|
425
|
-
|
426
|
-
- order: The order of the year, month and date menus. A comma separated string or an array. Default: "year, month,
|
427
|
-
day, hour, minute"
|
428
|
-
- date-separator, discard-type, prompt and prefix are passed to the select_date helper as options
|
429
|
-
|
430
|
-
All the other attributes are passed to the `select_date` helper as the html-options hash.
|
431
|
-
|
432
|
-
The menus default to the current time if the current value is nil.
|
433
|
-
|
434
|
-
-->
|
435
|
-
<def tag="input" for="datetime" attrs="order, date-separator, discard-type, prompt, prefix">
|
436
|
-
<% if ! order.nil?
|
437
|
-
order = comma_split(order).*.to_sym
|
438
|
-
attributes.merge!(:order => order)
|
439
|
-
end -%>
|
440
|
-
<%= select_datetime(this || current_time,
|
441
|
-
(all_attributes - attributes.keys).reverse_merge(:prefix => param_name_for_this).merge(:order => order),
|
442
|
-
attributes - [:name] ) %>
|
443
|
-
</def>
|
444
|
-
|
445
|
-
<!-- An `<input type='text'>` input. -->
|
446
|
-
<def tag="input" for="integer" attrs="name">
|
447
|
-
<%= text_field_tag(name, this, attributes) %>
|
448
|
-
</def>
|
449
|
-
|
450
|
-
<!-- An `<input type='text'>` input. -->
|
451
|
-
<def tag="input" for="BigDecimal" attrs="name">
|
452
|
-
<%= text_field_tag(name, this, attributes) %>
|
453
|
-
</def>
|
454
|
-
|
455
|
-
<!-- An `<input type='text'>` input. -->
|
456
|
-
<def tag="input" for="float" attrs="name">
|
457
|
-
<%= text_field_tag(name, this, attributes) %>
|
458
|
-
</def>
|
459
|
-
|
460
|
-
<!-- An `<input type='text'>` input. -->
|
461
|
-
<def tag="input" for="string" attrs="name">
|
462
|
-
<%= text_field_tag(name, this, attributes) %>
|
463
|
-
</def>
|
464
|
-
|
465
|
-
<!-- A `<select>` menu containing the values of an 'enum string'.
|
466
|
-
|
467
|
-
### Attributes
|
468
|
-
|
469
|
-
- `labels` - A hash that gives custom labels for the values of the enum.
|
470
|
-
Any values that do not have corresponding keys in this hash will get `value.titleize` as the label.
|
471
|
-
- `titleize` - Set to false to have the value itself (rather than `value.titleize`) be the default label. Default: true
|
472
|
-
- `first-option` - a string to be used for an extra option in the first position. E.g. "Please choose..."
|
473
|
-
- `first-value` - the value to be used with the `first-option`. Typically not used, meaning the option has a blank value.
|
474
|
-
|
475
|
-
-->
|
476
|
-
<def tag="input" for="HoboFields::Types::EnumString" attrs="labels, titleize, first-option, first-value"><%
|
477
|
-
labels ||= {}
|
478
|
-
labels = HashWithIndifferentAccess.new(labels)
|
479
|
-
titleize = true if titleize.nil?
|
480
|
-
options = this_type.values.map {|v| [labels.fetch(v, titleize ? this_type.translated_values[v].titleize : this_type.translated_values[v]), v] }
|
481
|
-
%>
|
482
|
-
<select name="#{param_name_for_this}" merge-attrs>
|
483
|
-
<option value="#{first_value}" unless="&first_option.nil?"><first-option/></option>
|
484
|
-
<%= options_for_select(options, this) %>
|
485
|
-
</select>
|
486
|
-
</def>
|
487
|
-
|
488
|
-
|
489
|
-
<!-- Provides either an ajax or non-ajax button to invoke a "remote method" or "web method" declared in the controller.
|
490
|
-
Web Methods provide support for the RPC model of client-server interaction, in contrast to the REST model. The
|
491
|
-
preference in Rails is to use REST as much as possible, but we are pragmatists, and sometimes you just to need a remote
|
492
|
-
procedure call.
|
493
|
-
|
494
|
-
The URL that the call is POSTed to is the `object_url` of `this`, plus the method name
|
495
|
-
|
496
|
-
`<remote-method-button>` supports all of the standard ajax attributes (see the main taglib documention for Rapid
|
497
|
-
Forms). If any ajax attributes are given, the button becomes an ajax button, if not, Rails' `button_to` is used, which behaves similarly to a standard link.
|
498
|
-
|
499
|
-
### Attributes
|
500
|
-
|
501
|
-
- method: the name of the web-method to call
|
502
|
-
|
503
|
-
- label: the label on the button
|
504
|
-
|
505
|
-
-->
|
506
|
-
<def tag="remote-method-button" attrs="method, update, label, confirm, url"><%=
|
507
|
-
ajax_attributes, html_attributes = attributes.partition_hash(Hobo::Rapid::Helper::AJAX_ATTRS)
|
508
|
-
|
509
|
-
url ||= object_url(this, method.to_s.gsub('-', '_'), :method => :post)
|
510
|
-
raise ArgumentError, "no such web method '#{method}' on #{this.typed_id}" unless url
|
511
|
-
|
512
|
-
add_classes!(html_attributes, "button remote-method-button #{method}-button")
|
513
|
-
label ||= method.titleize
|
514
|
-
if update || !ajax_attributes.empty?
|
515
|
-
ajax_attributes[:message] ||= label
|
516
|
-
func = ajax_updater(url, update, ajax_attributes.merge(:confirm => confirm))
|
517
|
-
html_attributes.update(:onclick => "var e = this; " + func, :type =>'button', :value => label)
|
518
|
-
element(:input, html_attributes, nil, true, true)
|
519
|
-
else
|
520
|
-
button_to(label, url, html_attributes.merge(:confirm => confirm))
|
521
|
-
end
|
522
|
-
%></def>
|
523
|
-
|
524
|
-
|
525
|
-
<!-- Provides an ajax button to send a RESTful update or "PUT" to the server. i.e. to udate one or more fields of a
|
526
|
-
record.
|
527
|
-
|
528
|
-
Note that unlike simliar tags, `<update-button>` does not support both ajax and non-ajax modes at this time. It only
|
529
|
-
does ajax.
|
530
|
-
|
531
|
-
`<update-button>` supports all of the standard ajax attributes (see the main taglib documention for Rapid Forms).
|
532
|
-
|
533
|
-
### Attributes
|
534
|
-
|
535
|
-
- label: The label on the button.
|
536
|
-
|
537
|
-
- fields: A hash with new field values pairs to update the resource with. The items in the hash will be converted to
|
538
|
-
HTTP parameters.
|
539
|
-
|
540
|
-
- params: Another hash with additional HTTP parameters to include in the ajax request
|
541
|
-
|
542
|
-
-->
|
543
|
-
<def tag="update-button" attrs="label, update, fields, params"><%=
|
544
|
-
raise Hobo::Error.new("no update specified") unless update
|
545
|
-
|
546
|
-
ajax_attributes, html_attributes = attributes.partition_hash(Hobo::Rapid::Helper::AJAX_ATTRS)
|
547
|
-
params = (params || {}).merge(this.class.name.underscore => fields)
|
548
|
-
ajax_attributes.reverse_merge!(:message => label, :params => params, :method => :put)
|
549
|
-
func = ajax_updater(object_url(this), update, ajax_attributes)
|
550
|
-
html_attributes.reverse_merge!(:type =>'button', :onclick => func, :value => label)
|
551
|
-
|
552
|
-
element :input, add_classes(html_attributes, "button update-button update-#{this.class.name.underscore}-button"), nil, true, true %>
|
553
|
-
</def>
|
554
|
-
|
555
|
-
|
556
|
-
<!-- Provides either an ajax or non-ajax delete button to send a RESTful "DELETE". The context should be a record for
|
557
|
-
which you to want provide a delete button.
|
558
|
-
|
559
|
-
The Rapid Library has a convention of marking (in the output HTML, using a special CSS class) elements as "object
|
560
|
-
elements", with the class and ID of the ActiveRecord object that they represent. `<delete-button>` assumes it is placed
|
561
|
-
inside such an element, and will automatically find the right element to remove (fade out) from the DOM. The
|
562
|
-
`<collection>` tag adds this metadata (CSS class) automatically, so `<delete-button>` works well when used inside a
|
563
|
-
`<collection>`. This is a Clever Trick which needs to be revisted and perhaps simplified.
|
564
|
-
|
565
|
-
If used within a `<collection>`, `<delete-button>` also knows how to add an "empty message" such as "no comments to
|
566
|
-
display" when you delete the last item. Clever Tricks abound.
|
567
|
-
|
568
|
-
Current limitation: There is no support for the ajax callbacks at this time.
|
569
|
-
|
570
|
-
All the standard ajax attributes *except the callbacks* are supported (see the main taglib documention for Rapid Forms).
|
571
|
-
|
572
|
-
|
573
|
-
|
574
|
-
### Attributes
|
575
|
-
|
576
|
-
- label: The label for the button. Default: "Remove"
|
577
|
-
|
578
|
-
- in-place: delete in place (ajax)? Default: true, or false if the record to be deleted is the same as the top level
|
579
|
-
context of the page
|
580
|
-
|
581
|
-
- image: URL of an image for the button. Changes the rendered tag from `<input type='button'>` to `<input type='image'
|
582
|
-
src='...'>`
|
583
|
-
|
584
|
-
- fade: Perform the fade effect (true/false)? Default: true
|
585
|
-
|
586
|
-
-->
|
587
|
-
<def tag="delete-button" attrs="label, update, in-place, image, confirm, fade, subsite"><%=
|
588
|
-
in_place = false if in_place.nil? && this == @this && request.method.downcase == "get"
|
589
|
-
url = object_url(this, :method => :delete, :subsite => subsite)
|
590
|
-
if (Dryml.last_if = url && can_delete?)
|
591
|
-
attributes = attributes.merge(if image
|
592
|
-
{ :type => "image", :src => "#{base_url}/images/#{image}" }
|
593
|
-
else
|
594
|
-
{ :type => "button" }
|
595
|
-
end)
|
596
|
-
label ||= t("hobo.actions.remove", :default=>"Remove")
|
597
|
-
confirm = t("hobo.messages.confirm", :default=>"Are you sure?") if confirm.nil?
|
598
|
-
|
599
|
-
add_classes!(attributes,
|
600
|
-
image ? "image-button" : "button",
|
601
|
-
"delete-button delete-#{this.class.name.underscore.dasherize}-button")
|
602
|
-
if url
|
603
|
-
if in_place == false
|
604
|
-
attributes[:confirm] = confirm if confirm
|
605
|
-
attributes[:method] = :delete
|
606
|
-
button_to(label, url, attributes)
|
607
|
-
else
|
608
|
-
fade = true if fade.nil?
|
609
|
-
attributes[:value] = label
|
610
|
-
attributes[:onclick] = "Hobo.removeButton(this, '#{url}', #{js_updates(update)}, {fade:#{fade}, confirm: '#{confirm}'})"
|
611
|
-
element(:input, attributes, nil, true, true)
|
612
|
-
end
|
613
|
-
end
|
614
|
-
else
|
615
|
-
""
|
616
|
-
end
|
617
|
-
%></def>
|
618
|
-
|
619
|
-
|
620
|
-
<!-- Provides an ajax create button that will send a RESTful "POST" to the server to create a new resource.
|
621
|
-
|
622
|
-
All of the standard ajax attributes are supported (see the main taglib documention for Rapid Forms).
|
623
|
-
|
624
|
-
### Attributes
|
625
|
-
|
626
|
-
- model: The class to instantiate, pass either the class name or the class object.
|
627
|
-
|
628
|
-
-->
|
629
|
-
<def tag="create-button" attrs="model, update, label, fields, message"><%=
|
630
|
-
raise Hobo::Error.new("no update specified") unless update
|
631
|
-
|
632
|
-
fields ||= {}
|
633
|
-
class_or_assoc = if model
|
634
|
-
model.is_a?(String) ? model.constantize : model
|
635
|
-
elsif Hobo.simple_has_many_association?(this)
|
636
|
-
id_method = this_field_reflection.options[:primary_key] || this_field_reflection.klass.primary_key
|
637
|
-
fields[this_field_reflection.primary_key_name] = this.proxy_owner.send(id_method)
|
638
|
-
this
|
639
|
-
else
|
640
|
-
raise Hobo::Error.new("invalid context for <create-button>")
|
641
|
-
end
|
642
|
-
new = class_or_assoc.new(fields)
|
643
|
-
new.set_creator(current_user)
|
644
|
-
if can_create?(new)
|
645
|
-
label ||= ht("#{new.class.to_s.underscore}.actions.new", :default=>"New #{new.class.model_name.human}")
|
646
|
-
class_name = new.class.name.underscore
|
647
|
-
params ||= params || {}
|
648
|
-
params = params.merge(class_name => fields) unless fields.blank?
|
649
|
-
|
650
|
-
ajax_attributes, html_attributes = attributes.partition_hash(Hobo::Rapid::Helper::AJAX_ATTRS)
|
651
|
-
|
652
|
-
ajax_attributes.reverse_merge!(:message => message, :params => params)
|
653
|
-
func = ajax_updater(object_url(new.class, :method => :post), update, ajax_attributes)
|
654
|
-
html_attributes.reverse_merge!(:type =>'button', :onclick => func, :value => label)
|
655
|
-
|
656
|
-
element :input, add_classes(html_attributes,
|
657
|
-
"button create-button create-#{class_name}-button"), nil, true, true
|
658
|
-
end
|
659
|
-
%></def>
|
660
|
-
|
661
|
-
|
662
|
-
<!-- A `<select>` menu from which the user can choose the target record for a `belongs_to` association.
|
663
|
-
|
664
|
-
This is the default input that Rapid uses for `belongs_to` associations. The menu is constructed using the `to_s` representation of the records.
|
665
|
-
|
666
|
-
### Attributes
|
667
|
-
|
668
|
-
- `include-none` - whether to include a 'none' option (i.e. set the foreign key to null). If this value is not supplied, the default is "true" if the current value is nil; otherwise the default is "false". One implication of this is that the default may change when the form is re-rendered due to a validation failure. Setting this value explicitly is recommended.
|
669
|
-
- `blank-message` - the message for the 'none' option. Defaults to "(No `<model-name>`)", e.g. "(No Product)"
|
670
|
-
- `options` - an array of records to include in the menu. Defaults to the all the records in the target table that match any `:conditions` declared on the `belongs_to` (subject to `limit`)
|
671
|
-
- `sort` - whether to sort the array of options. Defaults to no sorting.
|
672
|
-
- `limit` - if `options` is not specified, this limits the number of records. Default: 100
|
673
|
-
- `text_method` - The method to call on each record to get the text for the option. Multiple methods are supported ie "institution.name"
|
674
|
-
|
675
|
-
### See Also
|
676
|
-
|
677
|
-
For situations where there are too many target records to practically include in a menu, `<name-one>` provides an autocompleter which would be more suitable.
|
678
|
-
|
679
|
-
-->
|
680
|
-
<def tag="select-one" attrs="include-none, blank-message, options, sort, limit, text-method"><%
|
681
|
-
raise Hobo::PermissionDeniedError.new("Not allowed to edit #{this_field}") if !attributes[:disabled] && !can_edit?
|
682
|
-
blank_message ||= ht("#{this_type.name.underscore}.messages.none", :default=>"No #{this_type.model_name.human} available.")
|
683
|
-
limit ||= 100
|
684
|
-
|
685
|
-
options ||= begin
|
686
|
-
conditions = ActiveRecord::Associations::BelongsToAssociation.new(this_parent, this_field_reflection).send(:conditions)
|
687
|
-
order = this_field_reflection.klass.default_order
|
688
|
-
this_field_reflection.klass.all(:conditions => conditions, :limit => limit, :order => order).select {|x| can_view?(x)}
|
689
|
-
end
|
690
|
-
|
691
|
-
id_method = this_field_reflection.options[:primary_key] || this_field_reflection.klass.primary_key
|
692
|
-
if text_method.nil?
|
693
|
-
select_options = options.map { |x| [name(:with => x, :no_wrapper => true), x.send(id_method)] }
|
694
|
-
else
|
695
|
-
select_options = options.map do |x|
|
696
|
-
[ text_method.split(".").inject(x) { |v, method| v.send(method) },
|
697
|
-
x.send(id_method) ]
|
698
|
-
end
|
699
|
-
end
|
700
|
-
select_options = select_options.sort if sort
|
701
|
-
select_options.insert(0, [blank_message, ""]) if include_none || (this.nil? && include_none != false)
|
702
|
-
attributes = add_classes(attributes, "input", "belongs_to", type_and_field)
|
703
|
-
-%>
|
704
|
-
<select name="#{param_name_for_this(true)}" merge-attrs="&attributes.except :name">
|
705
|
-
<%= options_for_select(select_options, this ? this.send(id_method) : "") %>
|
706
|
-
</select>
|
707
|
-
</def>
|
708
|
-
|
709
|
-
|
710
|
-
<!-- An `<input type="text">` with auto-completion. Allows the user to chose the target of a `belongs_to` association by name.
|
711
|
-
|
712
|
-
This tag relies on an autocompleter being defined in a controller. A simple example:
|
713
|
-
|
714
|
-
<form with="&ProjectMembership.new">
|
715
|
-
<name-one:user>
|
716
|
-
</form>
|
717
|
-
|
718
|
-
class ProjectMembership < ActiveRecord::Base
|
719
|
-
hobo_model
|
720
|
-
belongs_to :user
|
721
|
-
end
|
722
|
-
|
723
|
-
class User < ActiveRecord::Base
|
724
|
-
hobo_user_model
|
725
|
-
has_many :project_memberships, :accessible => true, :dependent => :destroy
|
726
|
-
end
|
727
|
-
|
728
|
-
class UsersController < ApplicationController
|
729
|
-
autocomplete
|
730
|
-
end
|
731
|
-
|
732
|
-
The route used by the autocompleter looks something like `/users/complete_name`. The first part of this route is specified by the `complete-target` attribute, and the second part is specified by the `completer` attribute.
|
733
|
-
|
734
|
-
`complete-target` specifies the controller for the route. It can be specified by either supplying a model class or a model. If a model is supplied, the id of the model is passed as a parameter to the controller. (`?id=7`, for example) The default for this attribute is the class of the context. In other words, the class that contains the `has_many / has_one`, not the class with the `belongs_to`.
|
735
|
-
|
736
|
-
`completer` specifies the action for the route. `name-one` prepends `complete_` to the value given here. This should be exactly the same as the first parameter to `autocomplete` in your controller. As an example: `autocomplete :email_address` would correspond to `completer="email_address"`. The default for this attribute is the name field for the model being searched, which is usually `name`, but not always.
|
737
|
-
|
738
|
-
The query string is passed to the controller in the `query` parameter. (`?query=hello` for example).
|
739
|
-
|
740
|
-
For more information on how to customize the controller, see the [controller manual](http://cookbook.hobocentral.net/manual/controllers#autocompleters)
|
741
|
-
|
742
|
-
Here's a more complex example. This used to be a part of [agility](http://cookbook.hobocentral.net/tutorials/agility) until it was simplified.
|
743
|
-
|
744
|
-
class ProjectsController < ApplicationController
|
745
|
-
autocomplete :new_member_name do
|
746
|
-
project = find_instance
|
747
|
-
hobo_completions :name, User.without_project(project).is_not(project.owner)
|
748
|
-
end
|
749
|
-
end
|
750
|
-
|
751
|
-
Note that this was added to the projects controller, rather than the users controller as in the first example. You can read this as: create an auto-complete action called `new_member_name` that finds users that are not already members of the project, and not the owner of the project, and completes the :name field.
|
752
|
-
|
753
|
-
<name-one:user complete-target="&@project" completer="new_member_name"/>
|
754
|
-
|
755
|
-
We're using an object as the complete-target rather than a class. This allows the `find_instance` in our controller action to function.
|
756
|
-
|
757
|
-
There's another example of `<name-one>` use in the [recipes](http://cookbook.hobocentral.net/recipes/36-using-a-name-one-one-a).
|
758
|
-
|
759
|
-
### Attributes:
|
760
|
-
|
761
|
-
- `complete-target`, `completer`: see above
|
762
|
-
- `min-chars`: The minimum number of characters that must be entered in the input field before an Ajax request is made.
|
763
|
-
- `nil-value`: If there is no current value, this text will appear greyed out inside the control, and will disappear on focus.
|
764
|
-
|
765
|
-
### Note:
|
766
|
-
|
767
|
-
If you wish to set `min-chars` to 0, you will require this [patch to controls.js](http://github.com/bryanlarsen/scriptaculous/commit/3915b7b). 'controls.js' was added to your project via the rails generator, not via Hobo.
|
768
|
-
|
769
|
-
-->
|
770
|
-
<def tag="name-one" attrs="complete-target, completer, min-chars, nil-value"><%
|
771
|
-
complete_target ||= this_field_reflection.klass
|
772
|
-
completer ||= (complete_target.is_a?(Class) ? complete_target : complete_target.class).name_attribute
|
773
|
-
min_chars ||= 1
|
774
|
-
value = name(:no_wrapper => true, :if_present => true)
|
775
|
-
-%>
|
776
|
-
<wrap tag="span" class="field-with-errors" when="&!this_parent.nil? && !this_parent.errors[this_field].empty?">
|
777
|
-
<input type="text" name="#{param_name_for_this}"
|
778
|
-
class="autocompleter #{type_and_field._?.dasherize} #{css_data :complete_on, typed_id(complete_target), completer} #{css_data :min_chars, min_chars} #{'nil-value' if value==''}"
|
779
|
-
value="#{value=='' ? nil_value : value}"
|
780
|
-
merge-attrs/>
|
781
|
-
<div class="completions-popup" style="display:none"></div>
|
782
|
-
</wrap>
|
783
|
-
</def>
|
784
|
-
|
785
|
-
|
786
|
-
<!-- nodoc. -->
|
787
|
-
<def tag="sti-type-input">
|
788
|
-
<select name="#{param_name_for(form_field_path + ['type'])}">
|
789
|
-
<%= options_for_select(this.class.send(:descendants).map{|x| [x.name.titleize, x.name]}, this.class.name) %>
|
790
|
-
</select>
|
791
|
-
</def>
|
792
|
-
|
793
|
-
|
794
|
-
<!-- A `<select>` menu input. This tag differes from `<select-menu>` only in that it adds the correct `name` attribute for the current field, and `selected` default to `this`.
|
795
|
-
|
796
|
-
### Attributes
|
797
|
-
|
798
|
-
- `options` - an array of options suitable to be passed to the Rails `options_for_select` helper.
|
799
|
-
- `selected` - the value (from the `options` array) that should be initially selected. Defaults to `this`
|
800
|
-
- `first-option` - a string to be used for an extra option in the first position. E.g. "Please choose..."
|
801
|
-
- `first-value` - the value to be used with the `first-option`. Typically not used, meaning the option has a blank value.
|
802
|
-
|
803
|
-
-->
|
804
|
-
<def tag="select-input">
|
805
|
-
<select-menu name="#{param_name_for_this}" selected="&this" merge/>
|
806
|
-
</def>
|
807
|
-
|
808
|
-
|
809
|
-
<!-- Renders a localized list of error messages following a form submission. Expects the errors to be in `this.errors`. Renders nothing if there are no errors.
|
810
|
-
-->
|
811
|
-
<def tag="error-messages">
|
812
|
-
<section class="error-messages" merge-attrs if="&this.errors.size > 0">
|
813
|
-
<h2 param="heading">
|
814
|
-
<t key="activerecord.errors.template.header" model="&this.class.model_name.human" count="&this.errors.size">
|
815
|
-
<%= model = this.class.model_name.human
|
816
|
-
count = this.errors.size
|
817
|
-
count==1 ? "1 error prohibited this #{model} from being saved" :
|
818
|
-
"#{count} errors prohibited this #{model} from being saved"
|
819
|
-
%>
|
820
|
-
</t>
|
821
|
-
</h2>
|
822
|
-
<t key="activerecord.errors.template.body">There were problems with the following fields:</t>
|
823
|
-
<ul param>
|
824
|
-
<% this.errors.to_a.each do |message| -%>
|
825
|
-
<li param><%= message %></li>
|
826
|
-
<% end -%>
|
827
|
-
</ul>
|
828
|
-
</section>
|
829
|
-
</def>
|
830
|
-
|
831
|
-
|
832
|
-
<!--
|
833
|
-
An input for `has_many :through` associations that lets the user chose the items from a `<select>` menu.
|
834
|
-
|
835
|
-
To use this tag, the model of the items the user is chosing *must* have unique names, and the
|
836
|
-
-->
|
837
|
-
<def tag="select-many" attrs="options, targets, remove-label, prompt, disabled, name"><%
|
838
|
-
prompt ||= ht("#{this_field_reflection.klass.to_s.underscore}.form.select_many.prompt", :default=>"Add #{this_field.titleize.singularize}")
|
839
|
-
options ||= this_field_reflection.klass.all(:conditions =>this.send(:conditions)).select {|x| can_view?(x)}
|
840
|
-
name ||= param_name_for_this
|
841
|
-
|
842
|
-
values = this
|
843
|
-
remove_label ||= t("hobo.actions.remove", :default=>'Remove')
|
844
|
-
-%>
|
845
|
-
<div class="input select-many" merge-attrs>
|
846
|
-
<div style="display:none" class="item-proto">
|
847
|
-
<div class="item" param="proto-item">
|
848
|
-
<span></span>
|
849
|
-
<input type="hidden" name="#{name}[]" param="proto-hidden"/>
|
850
|
-
<input type="button" class="remove-item" value="#{remove_label}" param="proto-remove-button"/>
|
851
|
-
</div>
|
852
|
-
</div>
|
853
|
-
<div class="items">
|
854
|
-
<div class="item" param="item" repeat>
|
855
|
-
<span><%= h this.to_s %></span>
|
856
|
-
<input type="hidden" name="#{name}[]" value="@#{h this.id}" disabled="&disabled"
|
857
|
-
param="hidden"/>
|
858
|
-
<input type="button" class="remove-item" value="#{remove_label}" disabled="&disabled"
|
859
|
-
param="remove-button"/>
|
860
|
-
</div>
|
861
|
-
</div>
|
862
|
-
<select merge-attrs="&{:disabled => disabled}">
|
863
|
-
<option value=""><prompt/></option>
|
864
|
-
<repeat with="&options">
|
865
|
-
<if test="&this.in?(values)">
|
866
|
-
<optgroup class="disabled-option" label="#{h this.to_s}" alt="@#{this.id}"> </optgroup>
|
867
|
-
</if>
|
868
|
-
<else>
|
869
|
-
<option value="@#{this.id}"><%= h this.to_s %></option>
|
870
|
-
</else>
|
871
|
-
</repeat>
|
872
|
-
</select>
|
873
|
-
</div>
|
874
|
-
</def>
|
875
|
-
|
876
|
-
|
877
|
-
<!--
|
878
|
-
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.
|
879
|
-
|
880
|
-
### Usage
|
881
|
-
|
882
|
-
Use the `stay-here` attribute to remain on the current page:
|
883
|
-
|
884
|
-
<form>
|
885
|
-
<after-submit stay-here/>
|
886
|
-
...
|
887
|
-
</form>
|
888
|
-
|
889
|
-
Use the `go-back` option to return to the page in `session[:previous_uri]`:
|
890
|
-
|
891
|
-
<form>
|
892
|
-
<after-submit go-back/>
|
893
|
-
...
|
894
|
-
</form>
|
895
|
-
|
896
|
-
Use the `uri` option to specify a redirect location:
|
897
|
-
|
898
|
-
<form>
|
899
|
-
<after-submit uri="/admin"/>
|
900
|
-
...
|
901
|
-
</form>
|
902
|
-
-->
|
903
|
-
<def tag="after-submit" attrs="uri, stay-here, go-back"><%
|
904
|
-
uri = "stay-here" if stay_here
|
905
|
-
uri = session[:previous_uri] if go_back
|
906
|
-
-%>
|
907
|
-
<input type="hidden" value="¶ms[:after_submit] || uri" name="after_submit" if="&uri"/>
|
908
|
-
</def>
|
909
|
-
|
910
|
-
|
911
|
-
<!-- A simple wrapper around the `<select>` tag and `options_for_select` helper
|
912
|
-
|
913
|
-
### Attributes
|
914
|
-
|
915
|
-
- `options` - an array of options suitable to be passed to the Rails `options_for_select` helper.
|
916
|
-
- `selected` - the value (from the `options` array) that should be initially selected. Defaults to `this`
|
917
|
-
- `first-option` - a string to be used for an extra option in the first position. E.g. "Please choose..."
|
918
|
-
- `first-value` - the value to be used with the `first-option`. Typically not used, meaning the option has a blank value.
|
919
|
-
- `key` - the key used to lookup in the locale file or 'default' by default. If you pass it hobo will lookup in the namespace "tags.select_menu.#{key}" in order to find `options`, `first_option` and `first_value`. The passed attributes are used as a default in case the lookups fail. (see the documentation of filter-menu tag for a similar example).
|
920
|
-
-->
|
921
|
-
<def tag="select-menu" attrs="options, selected, first-option, first-value, key">
|
922
|
-
<% key ||= 'default'
|
923
|
-
%w[options first_option first_value].each do |a|
|
924
|
-
str = t("tags.select_menu.#{key}.#{a}", :default=>'')
|
925
|
-
eval "#{a} = str unless str.blank?"
|
926
|
-
end
|
927
|
-
-%>
|
928
|
-
<select merge-attrs param="default">
|
929
|
-
<% selected=this if selected.nil? %>
|
930
|
-
<option value="#{first_value}" unless="&first_option.nil?"><first-option/></option>
|
931
|
-
<do param="options"><%= options_for_select(options, selected) %></do>
|
932
|
-
</select>
|
933
|
-
</def>
|
934
|
-
|
935
|
-
|
936
|
-
<!-- Renders a `<ul>` list of checkboxes, one for each of the potential targt in a `has_many` association. The user can check the items they wish to have associated. A typical use might be selecting categories for a blog post.
|
937
|
-
|
938
|
-
### Attributes
|
939
|
-
|
940
|
-
- `options` - an array of models that may be added to the collection
|
941
|
-
- `disabled` - if true, sets the disabled flag on all check boxes.
|
942
|
-
|
943
|
-
-->
|
944
|
-
<def tag="check-many" attrs="options, disabled"><%
|
945
|
-
collection = this
|
946
|
-
param_name = param_name_for_this
|
947
|
-
options ||= begin
|
948
|
-
conditions = ActiveRecord::Associations::BelongsToAssociation.new(this_parent, this_field_reflection).send(:conditions)
|
949
|
-
this_field_reflection.klass.all(:conditions => conditions, :limit => 100).select {|x| can_view?(x)}
|
950
|
-
end
|
951
|
-
-%>
|
952
|
-
<ul class="check-many" param="default" merge-attrs>
|
953
|
-
<input type="hidden" name="#{param_name}[]" value=""/><% # ensure all items are removed when nothing checked
|
954
|
-
%>
|
955
|
-
<li repeat="&options" param>
|
956
|
-
<input id="#{dom_id(this, :check_many)}" type="checkbox" name="#{param_name}[]" value="@#{this.id}" checked="&this.in?(collection)" disabled="&disabled"/>
|
957
|
-
<label for="#{dom_id(this, :check_many)}"><name param/></label>
|
958
|
-
</li>
|
959
|
-
</ul>
|
960
|
-
</def>
|
961
|
-
|
962
|
-
|
963
|
-
<!-- Renders an `<input type='hidden'>` for the `id` field of the current context -->
|
964
|
-
<def tag="hidden-id-field">
|
965
|
-
<if:id><input type="hidden" name="#{param_name_for_this}" value="#{this}" /></if>
|
966
|
-
</def>
|
967
|
-
|
968
|
-
|
969
|
-
<!-- Creates a sub-section of the form which the user can repeat using (+) and (-) buttons, in order to allow an entire `has_many` collection to be created/edited in a single form.
|
970
|
-
|
971
|
-
This tag is very different from tags like `<select-many>` and `<check-many>` in that:
|
972
|
-
|
973
|
-
- Those tags are used to *choose existing records* to include in the association, while `<input-many>` is used to actually create or edit the records in the association.
|
974
|
-
|
975
|
-
### Example
|
976
|
-
|
977
|
-
Say you are creating a new `Category` in your online shop, and you want to create some initial products *in the same form*, you can add the following to your form:
|
978
|
-
|
979
|
-
<input-many:products><field-list fields="name, price"/></input-many>
|
980
|
-
|
981
|
-
The body of the tag will be repeated for each of the current records in the collection, or will just appear once (with blank fields) if the colleciton is empty.
|
982
|
-
|
983
|
-
### Attributes
|
984
|
-
|
985
|
-
- fields: If you do not specify any content for the input-many, a `<field-list>` is rendered. This attribute is passed through to the `<field-list>`
|
986
|
-
|
987
|
-
- skip: Passed through to the `<field-list>`. If not specified, it defaults to the parent association.
|
988
|
-
|
989
|
-
### Example
|
990
|
-
|
991
|
-
Say you are creating a new `Category` in your online shop, and you want to create some initial products *in the same form*, you can add the following to your form:
|
992
|
-
|
993
|
-
<hjq-input-many:products fields="name, price" />
|
994
|
-
|
995
|
-
You'll often want to provide the `item` parameter:
|
996
|
-
|
997
|
-
<hjq-input-many:products><item:><field-list fields="name, price" /></item:></hjq-input-many>
|
998
|
-
|
999
|
-
A fully worked up example of nested hjq-input-many's may be found in [agility/jquery-test](http://github.com/tablatom/agility/blob/jquery-test/app/views/projects/nested_has_many_test.dryml)
|
1000
|
-
|
1001
|
-
### Attributes
|
1002
|
-
|
1003
|
-
- `minimum`: the minimum number of items in the collection. Currently only '0' and '1' are supported values. The default is '0'.
|
1004
|
-
|
1005
|
-
- `fields`, `skip`: passed down to the `field-list` tag in the default `item`.
|
1006
|
-
|
1007
|
-
- `template`: the default values for new items. Normally this functionality is better provided by Model.new, but it's here if you need it.
|
1008
|
-
|
1009
|
-
### Events
|
1010
|
-
|
1011
|
-
- `rapid:add`: fired after the element is inserted. `memo.element`
|
1012
|
-
is set to the new element inserted.
|
1013
|
-
|
1014
|
-
- `rapid:remove`: fired before the element is
|
1015
|
-
inserted. `memo.element` is set to the element to be deleted. The
|
1016
|
-
removal may be cancelled by stopping the event.
|
1017
|
-
|
1018
|
-
- `rapid:change`: fired after an element has been removed or
|
1019
|
-
inserted. `memo.element` set as above.
|
1020
|
-
|
1021
|
-
Example javascript:
|
1022
|
-
|
1023
|
-
var last_added;
|
1024
|
-
var last_removed;
|
1025
|
-
Event.addBehavior({
|
1026
|
-
'.stories:rapid:add' : function(ev) {
|
1027
|
-
last_added = ev.memo.element;
|
1028
|
-
},
|
1029
|
-
'.stories:rapid:remove' : function(ev) {
|
1030
|
-
if(!confirm("really?")) ev.stop();
|
1031
|
-
}
|
1032
|
-
});
|
1033
|
-
|
1034
|
-
Note: if your javascript does not work, please ensure that you have
|
1035
|
-
the Hobo version of lowpro.js.
|
1036
|
-
|
1037
|
-
-->
|
1038
|
-
<def tag="input-many" attrs="minimum, fields, skip, more-skip, template" polymorphic >
|
1039
|
-
<%
|
1040
|
-
# helper function to create id's on buttons to facilitate testing
|
1041
|
-
def underize(s)
|
1042
|
-
s.gsub(/\[/,"_").gsub(/\]/,"")
|
1043
|
-
end
|
1044
|
-
%>
|
1045
|
-
<set empty="&this.empty?"/>
|
1046
|
-
<% template ||= this.try.new_candidate || this.member_class.new %>
|
1047
|
-
<% minimum ||= 0 ; minimum = minimum.to_i %>
|
1048
|
-
<% skip ||= this.proxy_reflection.klass.reflect_on_all_associations.detect {|p| p.primary_key_name==this.proxy_reflection.primary_key_name}.try.name.to_s if this.respond_to? :proxy_reflection %>
|
1049
|
-
<% skip += ",#{more_skip}" if more_skip -%>
|
1050
|
-
<ul class="input-many #{this_field.dasherize} #{css_data :input_many_prefix, param_name_for_this} #{css_data(:minimum, minimum)}" merge-attrs>
|
1051
|
-
<fake-field-context fake-field="-1" context="&template">
|
1052
|
-
<li class="input-many-li input-many-template" id="#{underize param_name_for_this}">
|
1053
|
-
<div class="input-many-item" param="default">
|
1054
|
-
<field-list param merge-attrs="fields" skip="&skip" />
|
1055
|
-
</div>
|
1056
|
-
<div class="buttons">
|
1057
|
-
<button param="remove-item" id="#{underize param_name_for_this}_remove">-</button>
|
1058
|
-
<button param="add-item" id="#{underize param_name_for_this}_add">+</button>
|
1059
|
-
</div>
|
1060
|
-
</li>
|
1061
|
-
</fake-field-context>
|
1062
|
-
<li class="input-many-li empty #{'hidden' unless this.empty? and minimum==0}" id="#{underize param_name_for_this}_-1_empty">
|
1063
|
-
<!-- HACK way to signal an empty collection to the controller -->
|
1064
|
-
<input type="hidden" class="empty-input" id="#{underize param_name_for_this}" name="#{param_name_for_this}" value="" disabled="&(!this.empty? || minimum>0)" />
|
1065
|
-
<fake-field-context fake-field="-1" context="&template">
|
1066
|
-
<div param="empty-message">
|
1067
|
-
<ht key="#{this.class.to_s.underscore}.collection.empty_message">
|
1068
|
-
No <%= this.class.name.titleize.downcase.pluralize %>.
|
1069
|
-
</ht>
|
1070
|
-
</div>
|
1071
|
-
<div class="buttons">
|
1072
|
-
<button param="remove-item" class="hidden" id="#{underize param_name_for_this}_remove">-</button>
|
1073
|
-
<button param="add-item" id="#{underize param_name_for_this}_add">+</button>
|
1074
|
-
</div>
|
1075
|
-
</fake-field-context>
|
1076
|
-
</li>
|
1077
|
-
<fake-field-context fake-field="0" context="&template">
|
1078
|
-
<li class="input-many-li" if="&(this_parent.empty? && minimum>0)" id="#{underize param_name_for_this}">
|
1079
|
-
<div class="input-many-item" param="default">
|
1080
|
-
<field-list param merge-attrs="fields" skip="&skip" />
|
1081
|
-
</div>
|
1082
|
-
<div class="buttons">
|
1083
|
-
<button param="remove-item" class="hidden" id="#{underize param_name_for_this}_remove">-</button>
|
1084
|
-
<button param="add-item" id="#{underize param_name_for_this}_add">+</button>
|
1085
|
-
</div>
|
1086
|
-
</li>
|
1087
|
-
</fake-field-context>
|
1088
|
-
<li repeat class="input-many-li #{'record-with-errors' unless this.errors.empty?}" id="#{underize param_name_for_this}">
|
1089
|
-
<error-messages without-heading class="sub-record"/>
|
1090
|
-
<hidden-id-field/>
|
1091
|
-
<div class="input-many-item" param="default">
|
1092
|
-
<field-list param merge-attrs="fields" skip="&skip" />
|
1093
|
-
</div>
|
1094
|
-
<div class="buttons">
|
1095
|
-
<button param="remove-item" class="#{'hidden' if this_parent.length<=minimum}" id="#{underize param_name_for_this}_remove">-</button>
|
1096
|
-
<button param="add-item" class="#{'hidden' if not last_item?}" id="#{underize param_name_for_this}_add">+</button>
|
1097
|
-
</div>
|
1098
|
-
</li>
|
1099
|
-
</ul>
|
1100
|
-
</def>
|
1101
|
-
|
1102
|
-
<!-- Renders a sub-section of a form with fields for every record in a `has_many` association. This is similar to `<input-many>` except there is no ability to add and remove items (i.e. no (+) and (-) buttons).
|
1103
|
-
-->
|
1104
|
-
<def tag="input-all">
|
1105
|
-
<% association_fkey = this_field_reflection.primary_key_name -%>
|
1106
|
-
<ul class="input-all #{this_field.dasherize}">
|
1107
|
-
<li repeat class="#{'record-with-errors' unless this.errors.empty?}">
|
1108
|
-
<set-scoped form-field-names="&[]">
|
1109
|
-
<hidden-id-field/>
|
1110
|
-
<do param="default"/>
|
1111
|
-
<hidden-fields skip="&association_fkey"/>
|
1112
|
-
</set-scoped>
|
1113
|
-
</li>
|
1114
|
-
</ul>
|
1115
|
-
</def>
|
1116
|
-
|
1117
|
-
<!-- An enhanced version of [`<input-many>`](/api_tag_defs/input-many) that supports drag-and-drop re-ordering.
|
1118
|
-
|
1119
|
-
Each item in the collection has a `<div class="ordering-handle" param="handle">` added, which can be used to drag the item up and down.
|
1120
|
-
|
1121
|
-
If the items in the collection contain an [`acts_as_list`](http://ar.rubyonrails.org/classes/ActiveRecord/Acts/List/ClassMethods.html) declaration, it is updated.
|
1122
|
-
|
1123
|
-
The specified sort order may be maintained even without `acts_as_list`. The items will be passed to the controller in the correct order, so the order may be persisted there.
|
1124
|
-
|
1125
|
-
### Attributes
|
1126
|
-
|
1127
|
-
- `id`: Due to a limitation in script.aculo.us, an id is required. If you do not supply one, one will be generated.
|
1128
|
-
- `position-column`: The position column may be specified via `acts_as_list`, via a `position_column` method on your model or via this attribute.
|
1129
|
-
- others: all other attributes are passed through to `<input-many>`
|
1130
|
-
|
1131
|
-
-->
|
1132
|
-
|
1133
|
-
<def tag="sortable-input-many" attrs="id, position-column, template">
|
1134
|
-
<% this_id = this_parent.id || rand(100000) -%>
|
1135
|
-
<% id ||= "sortable-input-many-#{this_parent.class.name.underscore.dasherize}-#{this_id}-#{this_field_reflection.name}" -%>
|
1136
|
-
<% template ||= this.try.new_candidate || this.member_class.new %>
|
1137
|
-
<% position_column ||= template.try.position_column -%>
|
1138
|
-
<input-many merge id="&id" class="sortable-input-many" template="&template" more-skip="&position_column">
|
1139
|
-
<default: replace>
|
1140
|
-
<div class="ordering-handle" param="handle" if="&can_edit?">↑<br/>↓</div>
|
1141
|
-
<if test="&position_column">
|
1142
|
-
<input class="sortable-position" type="hidden" value="&this.send(position_column)" name="#{param_name_for_this}[#{position_column}]" />
|
1143
|
-
</if>
|
1144
|
-
<default restore/>
|
1145
|
-
</default:>
|
1146
|
-
</input-many>
|
1147
|
-
</def>
|
1148
|
-
|
1149
|
-
<!-- Renders the common "or (Cancel)" for a form. Attributes are merged into the link (`<a>Cancel</a>`), making it easy to customise the destination of the cancel link. By default it will link to `this` or `this.class`.
|
1150
|
-
-->
|
1151
|
-
<def tag="or-cancel">
|
1152
|
-
<if test="&linkable?"><t key="hobo.support.or">or</t> <a merge-attrs><t key="hobo.actions.cancel">Cancel</t></a></if>
|
1153
|
-
<else>
|
1154
|
-
<if test="&linkable?(this.class)"><t key="hobo.support.or">or</t> <a to="&this.class" merge-attrs><t key="hobo.actions.cancel">Cancel</t></a></if>
|
1155
|
-
</else>
|
1156
|
-
</def>
|