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.
Files changed (133) hide show
  1. data/{CHANGES.txt → CHANGES-1.3.txt} +0 -0
  2. data/CHANGES-1.4.txt +678 -0
  3. data/Rakefile +13 -3
  4. data/TODO-1.4.txt +69 -0
  5. data/VERSION +1 -1
  6. data/app/helpers/hobo_debug_helper.rb +16 -0
  7. data/app/helpers/hobo_deprecated_helper.rb +45 -0
  8. data/app/helpers/hobo_helper_base.rb +8 -0
  9. data/app/helpers/hobo_permissions_helper.rb +136 -0
  10. data/app/helpers/hobo_route_helper.rb +196 -0
  11. data/{lib/hobo/helper/translations.rb → app/helpers/hobo_translations_helper.rb} +4 -7
  12. data/{lib/hobo/helper/translations/normalizer.rb → app/helpers/hobo_translations_normalizer_helper.rb} +3 -10
  13. data/app/helpers/hobo_type_helper.rb +24 -0
  14. data/app/helpers/hobo_view_hint_helper.rb +13 -0
  15. data/hobo.gemspec +3 -3
  16. data/lib/generators/hobo/admin_subsite/admin_subsite_generator.rb +0 -9
  17. data/lib/generators/hobo/admin_subsite/templates/application.dryml +2 -0
  18. data/lib/generators/hobo/admin_subsite/templates/gitkeep +0 -0
  19. data/lib/generators/hobo/admin_subsite/templates/site.css.erb +9 -0
  20. data/lib/generators/hobo/admin_subsite/templates/site.js.erb +10 -0
  21. data/lib/generators/hobo/assets/assets_generator.rb +16 -2
  22. data/lib/generators/hobo/assets/templates/application.css +9 -0
  23. data/lib/generators/hobo/assets/templates/application.dryml.erb +0 -5
  24. data/lib/generators/hobo/assets/templates/application.js +11 -0
  25. data/lib/generators/hobo/assets/templates/front.css +10 -0
  26. data/lib/generators/hobo/assets/templates/front.js +11 -0
  27. data/lib/generators/hobo/assets/templates/front_site.dryml.erb +6 -0
  28. data/lib/generators/hobo/assets/templates/gitkeep +0 -0
  29. data/lib/generators/hobo/dev_tweaks/dev_tweaks_generator.rb +31 -0
  30. data/lib/generators/hobo/i18n/templates/hobo.en.yml +1 -1
  31. data/lib/generators/hobo/install_plugin/USAGE +3 -0
  32. data/lib/generators/hobo/install_plugin/install_plugin_generator.rb +36 -0
  33. data/lib/generators/hobo/plugin.rb +112 -0
  34. data/lib/generators/hobo/setup_wizard/setup_wizard_generator.rb +31 -14
  35. data/lib/generators/hobo/subsite.rb +16 -2
  36. data/lib/generators/hobo/subsite/templates/gitkeep +0 -0
  37. data/lib/generators/hobo/subsite/templates/site.css.erb +9 -0
  38. data/lib/generators/hobo/subsite/templates/site.js.erb +10 -0
  39. data/lib/generators/hobo/subsite_taglib/templates/taglib.dryml.erb +0 -17
  40. data/lib/generators/hobo/test_framework/test_framework_generator.rb +1 -1
  41. data/lib/hobo.rb +3 -2
  42. data/lib/hobo/controller.rb +43 -24
  43. data/lib/hobo/controller/model.rb +63 -42
  44. data/lib/hobo/controller/user_base.rb +1 -3
  45. data/lib/hobo/engine.rb +1 -1
  46. data/lib/hobo/extensions/active_record/associations/association.rb +36 -0
  47. data/lib/hobo/extensions/active_record/associations/collection.rb +10 -19
  48. data/lib/hobo/extensions/active_record/associations/proxy.rb +3 -15
  49. data/lib/hobo/extensions/active_record/associations/scope.rb +2 -2
  50. data/lib/hobo/extensions/active_record/permissions.rb +32 -38
  51. data/lib/hobo/extensions/active_record/relation_with_origin.rb +5 -5
  52. data/lib/hobo/model.rb +12 -7
  53. data/lib/hobo/model/accessible_associations.rb +8 -15
  54. data/lib/hobo/model/lifecycles/creator.rb +1 -1
  55. data/lib/hobo/model/lifecycles/transition.rb +1 -1
  56. data/lib/hobo/model/permissions.rb +4 -4
  57. data/lib/hobo/model/scopes.rb +4 -17
  58. data/lib/hobo/model/scopes/automatic_scopes.rb +5 -13
  59. data/lib/hobo/rapid/helper.rb +1 -161
  60. data/lib/hobo/rapid/taglibs/rapid.dryml +3 -17
  61. data/test/doctest/hobo/hobo_helper.rdoctest +8 -44
  62. data/{doctests → test/doctest}/hobo/lifecycles.rdoctest +0 -0
  63. data/{doctests → test/doctest}/hobo/model.rdoctest +2 -4
  64. data/{doctests → test/doctest}/hobo/multi_model_forms.rdoctest +3 -24
  65. data/{doctests → test/doctest}/hobo/scopes.rdoctest +3 -53
  66. data/test/doctest/prepare_testapp.rb +11 -0
  67. data/test/irt/generators/admin_subsite.irt +1 -19
  68. data/test/irt/generators/assets.irt +4 -9
  69. data/test/irt/generators/controller.irt +0 -3
  70. data/test/irt/generators/front_controller.irt +0 -5
  71. data/test/irt/generators/{helper.rb → irt_helper.rb} +2 -2
  72. data/test/irt/generators/model.irt +1 -12
  73. data/test/irt/generators/partials/_account_user_model_tests.rb +0 -8
  74. data/test/irt/generators/partials/_accounts_users_controller_tests.rb +0 -2
  75. data/test/irt/generators/partials/_default_user_model_tests.rb +0 -8
  76. data/test/irt/generators/partials/_default_users_controller_tests.rb +0 -2
  77. data/test/irt/generators/partials/_house_controller_tests.rb +0 -2
  78. data/test/irt/generators/partials/_house_model_tests.rb +1 -9
  79. data/test/irt/generators/partials/_subsite_taglib_admin.rb +5 -2
  80. data/test/irt/generators/partials/_subsite_taglib_admin_invite_only.rb +1 -1
  81. data/test/irt/generators/partials/_subsite_taglib_noopt.rb +2 -2
  82. data/test/irt/generators/partials/_subsite_taglib_variables.rb +0 -15
  83. data/test/irt/generators/partials/_user_mailer_tests.rb +1 -3
  84. data/test/irt/generators/resource.irt +0 -3
  85. data/test/irt/generators/subsite.irt +6 -22
  86. data/test/irt/generators/subsite_taglib.irt +0 -18
  87. data/test/irt/generators/test_framework.irt +2 -5
  88. data/test/irt/generators/user_controller.irt +0 -3
  89. data/test/irt/generators/user_mailer.irt +0 -3
  90. data/test/irt/generators/user_model.irt +0 -3
  91. data/test/irt/generators/user_resource.irt +0 -3
  92. data/test/irt/readme.txt +6 -3
  93. metadata +116 -159
  94. data/app/controllers/dev_controller.rb +0 -25
  95. data/app/views/dev/summary.dryml +0 -102
  96. data/doctests/prepare_testapp.rb +0 -8
  97. data/lib/generators/hobo/admin_subsite/templates/admin.css +0 -20
  98. data/lib/generators/hobo/rapid/USAGE +0 -3
  99. data/lib/generators/hobo/rapid/rapid_generator.rb +0 -24
  100. data/lib/generators/hobo/rapid/templates/IE7.js +0 -2
  101. data/lib/generators/hobo/rapid/templates/blank.gif +0 -0
  102. data/lib/generators/hobo/rapid/templates/hobo-rapid.css +0 -94
  103. data/lib/generators/hobo/rapid/templates/hobo-rapid.js +0 -1015
  104. data/lib/generators/hobo/rapid/templates/ie7-recalc.js +0 -166
  105. data/lib/generators/hobo/rapid/templates/lowpro.js +0 -339
  106. data/lib/generators/hobo/rapid/templates/reset.css +0 -95
  107. data/lib/generators/hobo/rapid/templates/themes/clean/public/images/101-3B5F87-ACD3E6.png +0 -0
  108. data/lib/generators/hobo/rapid/templates/themes/clean/public/images/30-3E547A-242E42.png +0 -0
  109. data/lib/generators/hobo/rapid/templates/themes/clean/public/images/30-DBE1E5-FCFEF5.png +0 -0
  110. data/lib/generators/hobo/rapid/templates/themes/clean/public/images/300-ACD3E6-fff.png +0 -0
  111. data/lib/generators/hobo/rapid/templates/themes/clean/public/images/50-ACD3E6-fff.png +0 -0
  112. data/lib/generators/hobo/rapid/templates/themes/clean/public/images/fieldbg.gif +0 -0
  113. data/lib/generators/hobo/rapid/templates/themes/clean/public/images/pencil.png +0 -0
  114. data/lib/generators/hobo/rapid/templates/themes/clean/public/images/small_close.png +0 -0
  115. data/lib/generators/hobo/rapid/templates/themes/clean/public/images/spinner.gif +0 -0
  116. data/lib/generators/hobo/rapid/templates/themes/clean/public/stylesheets/clean.css +0 -327
  117. data/lib/generators/hobo/rapid/templates/themes/clean/public/stylesheets/rapid-ui.css +0 -102
  118. data/lib/generators/hobo/rapid/templates/themes/clean/views/clean.dryml +0 -10
  119. data/lib/hobo/helper.rb +0 -460
  120. data/lib/hobo/rapid/taglibs/rapid_core.dryml +0 -808
  121. data/lib/hobo/rapid/taglibs/rapid_document_tags.dryml +0 -56
  122. data/lib/hobo/rapid/taglibs/rapid_editing.dryml +0 -287
  123. data/lib/hobo/rapid/taglibs/rapid_forms.dryml +0 -1156
  124. data/lib/hobo/rapid/taglibs/rapid_generics.dryml +0 -48
  125. data/lib/hobo/rapid/taglibs/rapid_i18n.dryml +0 -173
  126. data/lib/hobo/rapid/taglibs/rapid_lifecycles.dryml +0 -96
  127. data/lib/hobo/rapid/taglibs/rapid_navigation.dryml +0 -108
  128. data/lib/hobo/rapid/taglibs/rapid_pages.dryml +0 -259
  129. data/lib/hobo/rapid/taglibs/rapid_plus.dryml +0 -247
  130. data/lib/hobo/rapid/taglibs/rapid_summary.dryml +0 -283
  131. data/lib/hobo/rapid/taglibs/rapid_support.dryml +0 -102
  132. data/lib/hobo/rapid/taglibs/rapid_user_pages.dryml +0 -182
  133. data/test/irt/generators/rapid.irt +0 -29
@@ -1,808 +0,0 @@
1
- <!-- Core Rapid tags and tags that don't belong anywhere else. -->
2
-
3
- <!-- Renders a table with one row per field, where each row contains a `<th>` with the field name, and a `<td>` with (by default)
4
- a `<view>` of the field.
5
-
6
- ### Attributes
7
-
8
- - fields: Comma separated list of field names to display. Defaults to the fields returned by the `standard_fields` helper. That is, all fields apart from IDs and timestamps.
9
-
10
- - force-all: All non-viewable fields will be skipped unless this attribute is given
11
-
12
- - skip: Comma separated list of fields to exclude
13
-
14
- - tag: The name of a tag to use inside the `<td>` to display the value. Defaults to `view`
15
-
16
- - no-edit: Controls the behavior of `<input>` tags when the user doesn't have edit permission for a field.
17
- - view: render the current value using the `<view>` tag
18
- - disable: render the input as normal, but add HTML's `disabled` attribute
19
- - skip: render nothing at all. This will omit the entire row (including the label)
20
- - ignore: render the input normally. That is, don't even perform the edit check.
21
-
22
- - no-blanks: (boolean) Controls the behavior of `<view>` tags. The entire row (including the label) will be omitted if `this` is blank. Default false.
23
-
24
- ### Example
25
-
26
- <field-list fields="first-name, last-name, city">
27
- <first-name-label:>Given Name</first-name-label:>
28
- <last-name-label:>Family Name</last-name-label:>
29
- <city-view:><name-one/></city-view:>
30
- </field-list>
31
-
32
- -->
33
- <def tag="field-list" attrs="tag, no-edit, no-blanks">
34
- <% tag ||= scope.in_form ? "input" : "view"; no_edit ||= "skip" %>
35
- <labelled-item-list merge-attrs="&attributes - attrs_for(:with_fields)">
36
- <with-fields merge-attrs="&attributes & attrs_for(:with_fields)">
37
- <% field_name = this_field_name
38
- input_attrs = {:no_edit => no_edit} if tag == "input"
39
- -%>
40
- <labelled-item unless="&(tag == 'input' && no_edit == 'skip' && !can_edit?) || (tag == 'view' && no_blanks && this.blank?)">
41
- <item-label param="#{scope.field_name.to_s.sub('?', '').gsub('.', '-')}-label" unless="&field_name.blank?">
42
- <do param="label"><%= field_name %></do>
43
- </item-label>
44
- <item-value param="#{scope.field_name.to_s.sub('?', '').gsub('.', '-')}-view" colspan="&2 if field_name.blank?">
45
- <do param="view"><call-tag tag="&tag" param="#{scope.field_name.to_s.sub('?', '').gsub('.', '-')}-tag" merge-attrs="&input_attrs"/></do>
46
- <div param="input-help" if="&tag.to_sym == :input && !this_field_help.blank?"><%= this_field_help %></div>
47
- </item-value>
48
- </labelled-item>
49
- </with-fields>
50
- </labelled-item-list>
51
- </def>
52
-
53
- <!-- Used to render nil values. By default renders "(Not Available)"
54
-
55
- ### Usage
56
-
57
- Redefine in your app to have nil values displayed differently, e.g.:
58
-
59
- <def tag="nil-view">-</def>
60
-
61
- -->
62
- <def tag="nil-view"><%= scope.nil_view || "(Not Available)" %></def>
63
-
64
- <!--
65
- `<table>` is extended in Rapid to provide a shorthand way to output a set of fields for a given collection. This is enabled using the `field` attribute (without the `field` attribute this is just the regular HTML `<table>` tag)
66
-
67
- ### Usage
68
-
69
- If the context is an array of blog posts...
70
-
71
- <table fields="name, created_at, description"/>
72
-
73
- This will output a header row containing "Name", "Created At" and "Description" followed by a row for each record in the collection. By default, the `<view/>` tag is called for each field in the row. This can be altered with the `field-tag` attribute, e.g.
74
-
75
- <table fields="name, created_at, description" field-tag="input"/>
76
-
77
- This will use `<input/>` as the tag in each table cell instead of `<view/>`
78
-
79
- ### Additional Notes
80
-
81
- * `<table>` provides parameters based on the names of the fields which can be used to further customise the output. For each field a heading parameter is provided, e.g. name-heading, created-at-heading, description-heading. These can be used to customise the headings:
82
-
83
- <table fields="name, created_at, description">
84
- <created-at-heading:>Creation Date</created-at-heading:>
85
- </table>
86
- * Similarly, "view" parameters are provided as an additional way to customise the table cells of the table body, e.g. `name-view`, `created-at-view`, `description-view`:
87
-
88
- <table fields="name, created_at, description">
89
- <created-at-view:><view format="%d %B %Y"/></created-at-view:>
90
- </table>
91
- * By adding an empty `control` parameter, the default control column is enable adding an edit link and delete button for each table row:
92
-
93
- <table fields="name, created_at, description">
94
- <controls:/>
95
- </table>
96
-
97
- The controls can be further customised using the "edit-link" and "delete-button" parameters:
98
-
99
- <table fields="name, created_at, description">
100
- <controls:/>
101
- <delete-button: label="Nuke Me"/>
102
- </table>
103
-
104
- or by providing completely new content for the control column:
105
-
106
- <table fields="name, created_at, description">
107
- <controls:>my controls!</controls:>
108
- </table>
109
- -->
110
- <def tag="table" attrs="fields, field-tag, empty">
111
- <if test="&!(fields || all_parameters.tr?)">
112
- <%= element("table", attributes, all_parameters.default) %>
113
- </if>
114
- <else>
115
- <% field_tag ||= "view" %>
116
- <unless test="&this.empty? && !empty">
117
- <% element "table", attributes - attrs_for(:with_fields) do %>
118
- <thead if="&all_parameters[:thead] || fields" param>
119
- <tr param="field-heading-row">
120
- <with-field-names merge-attrs="&all_attributes & attrs_for(:with_fields)">
121
- <th param="#{scope.field_name}-heading"><%= this.member_class.human_attribute_name(scope.field_name) if scope %></th>
122
- </with-field-names>
123
- <th if="&all_parameters[:controls]" class="controls" param="controls-heading"/>
124
- </tr>
125
- </thead>
126
- <tbody param>
127
- <repeat>
128
- <tr param if="&can_view?"
129
- class="#{scope.even_odd} #{this_type.name.underscore} #{model_id_class}">
130
- <if test="&fields">
131
- <with-fields merge-attrs="&all_attributes & attrs_for(:with_fields)" force-all>
132
- <td param="#{scope.field_name.to_s.sub('?', '').gsub('.', '-')}-view"><call-tag tag="&field_tag"/></td>
133
- </with-fields>
134
- <td class="controls" param="controls" if="&all_parameters[:controls]">
135
- <a param="edit-link" action="edit" if="&can_edit?">
136
- <t key="hobo.actions.edit_control">Edit</t>
137
- </a>
138
- <delete-button param/>
139
- </td>
140
- </if>
141
- </tr>
142
- </repeat>
143
- </tbody>
144
- <tfoot if="&all_parameters[:tfoot]" param/>
145
- <% end %>
146
- </unless>
147
- </else>
148
- </def>
149
-
150
- <!--
151
- Provides a short hand way of displaying images in public/images
152
-
153
- ### Usage
154
-
155
- <image src="hobo.png"/> -> <img src="/images/hobo.png"/>
156
- <image src="blog/funny.jpg" alt="Funny Scene"/> -> <img src="/images/blog/funny.jpg" alt="Funny Scene"/>
157
- -->
158
- <def tag="image" attrs="src">
159
- <img src="#{base_url}/images/#{src}" merge-attrs/>
160
- </def>
161
-
162
-
163
- <!-- Renders an ajax-progress 'spinner' using `spinner.gif` from the current theme, with a `class='hidden'` -->
164
- <def tag="spinner">
165
- <img src="#{base_url}/hobothemes/#{Hobo.current_theme}/images/spinner.gif" class="hidden" merge-attrs/>
166
- </def>
167
-
168
-
169
- <!-- Renders some standard JavaScript code that various features of the Rapid library rely on. This tag would typicallu be called from your `<page>` tag. The default Rapid pages include this already. -->
170
- <def tag="hobo-rapid-javascripts">
171
- <script type="text/javascript" param="default"><%=
172
- res = 'var hoboParts = {};'
173
- # FIXME: This should interrogate the model-router - not the models
174
- unless Hobo::Model.all_models.empty?
175
- # Tell JS code how to pluralize names, unless they follow the simple rule
176
- names = Hobo::Model.all_models.map do |m|
177
- m = m.name.underscore
178
- "#{m}: '#{m.pluralize}'" unless m.pluralize == m + 's'
179
- end.compact
180
- res << "var pluralisations = {#{names * ', '}}; "
181
- end
182
- base = [base_url, subsite].compact.join("/")
183
- res << "urlBase = '#{base}'; hoboPagePath = '#{CGI.escape(request.fullpath)}'"
184
- if protect_against_forgery?
185
- res << "; formAuthToken = { name: '#{request_forgery_protection_token}', value: '#{form_authenticity_token}' }"
186
- end
187
- res.html_safe
188
- %></script>
189
- </def>
190
-
191
-
192
- <def tag="part-contexts-javascripts">
193
- <% unless (storage = part_contexts_storage).blank? %>
194
- <script type="text/javascript"><%=
195
- storage
196
- %></script>
197
- <% end %>
198
- </def>
199
-
200
- <!-- Renders the name of the current context using a variety of methods.
201
-
202
- ### Details
203
-
204
- - Equivalent to `<nil-view>` if `this` is nil
205
- - Equivalent to `<count>` if `this` is an Array
206
- - Equivalent to `<type-name>` if `this` is a class
207
- - If the context has a `name_attribute` defined, equivalent to `<view:abc/>` (where `abc` is the name attribute)
208
- - Finally falls back to `this.to_s` (html escaped), but only if the user has view permission for `this`
209
-
210
- ### Attributes
211
-
212
- - if-present: if given, nothing at all will be rendered for nil values (as opposed to rendering `<nil-view>`)
213
-
214
- -->
215
- <def tag="name" attrs="if-present"><%=
216
- if this.nil?
217
- nil_view unless if_present
218
- else
219
- if this.is_a?(Array)
220
- count
221
- elsif this.is_a?(Class)
222
- type_name(attributes)
223
- elsif (name_attr = this.class.try.name_attribute) && can_view?(this, name_attr)
224
- view(merge_attrs(attributes, {:field => name_attr}))
225
- elsif can_view?(this)
226
- h this.to_s
227
- end
228
- end
229
- %></def>
230
-
231
-
232
- <!-- Renders a human readable version of the type of the context
233
-
234
- ### Details
235
-
236
- - If `this` is already a class, the name of that class is used
237
- - Otherwise, first `this.member_class` (for collections), then `this.class` are tried
238
- - By default the name is titleised and singular.
239
-
240
- ### Attributes
241
-
242
- - plural: pluralise the name
243
- - lowercase: render the name in all lower case
244
- - dasherize: render the name in lower case with dashes instead of spaces.
245
-
246
- -->
247
- <def tag="type-name" attrs="plural, lowercase, dasherize"><%=
248
- type ||= (this if this.is_a?(Class)) || this.try.member_class || this.class
249
-
250
- name = type.respond_to?(:model_name) ? type.model_name.human : type.name
251
- name = dasherize ? name.underscore.dasherize : name.titleize
252
- name = name.pluralize if plural
253
- name = name.downcase if lowercase
254
- name
255
- %></def>
256
-
257
-
258
- <!-- Renders a human readable name of a collection
259
-
260
- Use this tag for en locale only. Use human-collection-name for i18n.
261
-
262
- ### Details
263
-
264
- - Uses `this.origin_attribute` as the name.
265
- - Falls back to `<type-name>` otherwise.
266
- - By default the name is titleised and plural.
267
-
268
- ### Attributes
269
-
270
- - singular: singularise the name
271
- - lowercase: render the name in all lower case
272
- - dasherize: render the name in lower case with dashes instead of spaces.
273
-
274
- -->
275
- <def tag="collection-name" attrs="singular, lowercase, dasherize"><%=
276
- if (attr = this.try.origin_attribute)
277
- name = attr.to_s
278
- name = dasherize ? name.underscore.dasherize : name.titleize
279
- name = name.singularize if singular
280
- name = name.downcase if lowercase
281
- name
282
- else
283
- type_name(:plural => !singular, :lowercase => lowercase, :dasherize => dasherize)
284
- end
285
- %></def>
286
-
287
- <!--
288
- `<a>` is extended in Rapid to automatically provide URLs for Hobo model routes
289
-
290
- ### Usage
291
-
292
- The tag behaves as a regular HTML link or anchor if either the href or name attribute is given:
293
-
294
- <a href="/admin">Admin</a> -> Output is exactly as provided, untouched by Rapid
295
-
296
- If no href or name is given then the _context_ is used to determine the link URL.
297
- The helper method `object_url` is used to construct the URL using restful routing:
298
-
299
- If the context is a class then the link will be an index page:
300
-
301
- <a with="&BlogPost">My Blog</a> -> <a href="/blog_posts">My Blog</a>
302
-
303
- If the context is a hobo model instance then the link will be a show page:
304
-
305
- <% blog_post = BlogPost.find(1) %>
306
- <a with="&blog_post">My Blog Post</a> -> <a href="/blog_posts/1">My Blog Post</a>
307
-
308
- An action can be provided for an alternative show page:
309
-
310
- <a with="&blog_post" action="edit">Edit Post</a> -> <a href="/blog_posts/1/edit">Edit Post</a>
311
-
312
- Or a new page if the context is a class:
313
-
314
- <a with="&BlogPost" action="new">New Blog Post</a> -> <a href="/blog_posts/new">New Blog Post</a>
315
-
316
- ### Additional Features
317
-
318
- * If the constructed route does not exist then the link will not be created, but the content of the link will still be output. E.g. when `/blog_posts` does not exist (because the hobo model controller does not exist or the index action is disabled):
319
-
320
- <a with="&BlogPost">My Blog</a> -> My Blog
321
-
322
- when the show action `/blog_posts/:id` does not exist:
323
-
324
- <a with="&blog_post">My Blog Post</a> -> My Blog Post
325
- * If no content text is provided then `<a>` will use the name method on the context to provide the text. E.g.
326
-
327
- <a with="&blog_post"/> -> <a href="/blog_posts/1">My First Blog Post</a>`
328
- <a with="&BlogPost"/> -> <a href="/blog_posts">Blog Posts</a>`
329
- * If `action="new"` then `<a>` will check that the current user has permission to create the object
330
- * Several useful classes are added automatically to the output `<a>`.
331
-
332
- ### Attributes
333
-
334
- * action: If "new", triggers the special behaviour listed above. Otherwise, contains the action to be performed on the context. If neither `action` nor `method` are specified, the action will be "index" or "show", as appropriate.
335
-
336
- * to: Use this item as the target instead of the current context.
337
-
338
- * params, query-params: These are appended to the target as a query string after a "?". Params are passed as a ruby hash. Example: `params="&{id=>17, name=>'joe'}"`
339
-
340
- * href, name: If either of these attributes are present, the smart features of this tag are turned off.
341
-
342
- * format: this adds ".#{format}" to the end of the url
343
-
344
- * subsite: routes the URL using the subsite
345
-
346
- * force: overrides the permission check if `action` is "new"
347
-
348
- * method: "get", "put", "post" or "delete". "get" is the default
349
-
350
- -->
351
- <def tag="a" attrs="action, to, params, query-params, href, format, subsite, force"><%=
352
- content = parameters.default
353
-
354
- params = self.query_params.merge(params || HashWithIndifferentAccess.new) if query_params
355
-
356
- if href || attributes[:name]
357
- # Regular link
358
- href += "?" + params.map { |n, v| "#{n}=#{v}" }.join('&') if !params.blank?
359
- element(:a, attributes.update(:href => href), content)
360
- else
361
- target = to || this
362
-
363
- if target.nil?
364
- Dryml.last_if = false
365
- nil_view
366
- elsif action == "new"
367
- # Link to a new object form
368
- new_record = target.new
369
- new_record.set_creator(current_user)
370
- href = object_url(target, "new", params._?.merge(:subsite => subsite))
371
-
372
- if href && (force || can_create?(new_record))
373
- new_class_name = if target.respond_to?(:proxy_reflection)
374
- target.proxy_reflection.klass.name
375
- else
376
- target.name
377
- end
378
-
379
- add_classes!(attributes, "new-#{new_class_name.underscore}-link")
380
- content = "New #{new_class_name.titleize}" if content.blank?
381
- Dryml.last_if = true
382
- element(:a, attributes.update(:href => href), content)
383
- else
384
- Dryml.last_if = false
385
- ""
386
- end
387
- else
388
- # Link to an existing object
389
-
390
- content = name if content.blank?
391
-
392
- href = object_url(target, action, (params || {}).merge(:subsite => subsite)) unless (action.nil? && target.try.new_record?)
393
- if href.nil?
394
- # This target is registered with Hobo::Routes as not linkable
395
- content
396
- else
397
- css_class = target.try.origin_attribute || target.class.name.underscore.dasherize
398
- add_classes!(attributes, "#{css_class}-link")
399
-
400
- href.sub!(/\?|$/, ".#{format}\\0") unless format.blank?
401
-
402
- # Set default link text if none given
403
- element(:a, attributes.update(:href => href), content)
404
- end
405
- end
406
- end
407
- %></def>
408
-
409
- <!--
410
- Provides a read-only view tailored to the type of the object being viewed. `<view>` is a _polymorphic_ tag which means that there are a variety of definitions, each one written for a particular type. For example there are views for `Date`, `Time`, `Numeric`, `String` and `Boolean`. The type specific view is enclosed in a wrapper tag (typically a `<span>` or `<div>`) with some useful classes automatically added.
411
-
412
- ### Usage
413
-
414
- Assuming the context is a blog post...
415
-
416
- * Viewing a DateTime field:
417
-
418
- <view:created_at/> -> <span class="view blog-post-created-at">June 09, 2008 15:36</span>
419
- * Viewing a String field:
420
-
421
- <view:title/> -> <span class="view blog-post-title">My First Blog Post</span>
422
- * Viewing an Integer field:
423
-
424
- <view:comment_count/> -> <span class="view blog-post-comment-count">4</span>
425
- * Viewing the blog post itself results in a link to the blog post (using Rapid's `<a>` tag):
426
-
427
- <view/> -> <span class="view model:blog-post-1"><a href="/blog_posts/1">My First Blog Post</a></span>
428
-
429
- ### Additional Notes
430
-
431
- * The wrapper tag is `<span>` unless the field type is `Text` (different to `String`) where it is `<div>`. Use the `inline` or `block` attributes to force a `<span>` or a `<div>`, e.g.
432
-
433
- <view:body/> -> <div class="view blog-post-body">This is my blog post body</div>
434
-
435
- <view:body inline/> -> <span class="view blog-post-body">This is my blog post body</span>
436
-
437
- <view:created_at block/> -> <div class="view blog-post-created-at">June 09, 2008 15:36</div>
438
- * Use the `no-wrapper` attribute to remove the wrapper tag completely. e.g.
439
-
440
- <view:created_at no-wrapper/> -> June 09, 2008 15:36
441
- -->
442
- <def tag="view" attrs="inline, block, if-blank, no-wrapper, truncate"><%=
443
- raise Hobo::PermissionDeniedError, "view of non-viewable field '#{this_field}' of #{this_parent.typed_id rescue this_parent}" unless
444
- can_view?
445
-
446
- res = if this.nil? && if_blank.nil?
447
- this_type.is_a?(Class) && this_type <= String ? "" : nil_view
448
- elsif (refl = this_field_reflection) && refl.macro == :has_many
449
- collection_view(attributes)
450
- else
451
- view_tag = find_polymorphic_tag("view")
452
-
453
- if view_tag == "view" # i.e. it didn't find a type specific tag
454
- if this.respond_to?(:to_html)
455
- this.to_html(scope.xmldoctype)
456
- else
457
- this.to_s
458
- end
459
- else
460
- attrs = add_classes(attributes, "view", type_and_field._?.dasherize, model_id_class)
461
-
462
- view_attrs = attrs_for(view_tag)
463
- the_view = send(view_tag, attrs & view_attrs)
464
-
465
- the_view = if_blank if if_blank && the_view.blank?
466
-
467
- truncate = 30 if truncate == true
468
- the_view = self.truncate(the_view, :length => truncate.to_i) if truncate
469
- the_view = the_view.html_safe? ? the_view.strip.html_safe : the_view.strip
470
-
471
- if no_wrapper
472
- the_view
473
- else
474
- wrapper = if inline
475
- :span
476
- elsif block || this_type <= HoboFields::Types::Text
477
- :div
478
- else
479
- :span
480
- end
481
- element(wrapper, attrs - view_attrs, the_view)
482
- end
483
- end
484
- end
485
- Dryml.last_if = !res.blank?
486
- res
487
- %></def>
488
-
489
- <!-- `<view>` calls this tag when called for a `has_many` collection. By default calls `<links-for-collection/>` -->
490
- <def tag="collection-view" polymorphic><links-for-collection merge-attrs/></def>
491
-
492
- <!-- Renders a comma separated list of links (`<a>`), or "(none)" if the list is empty -->
493
- <def tag="links-for-collection"><%= this.empty? ? "(none)" : context_map { a }.safe_join(", ") %></def>
494
-
495
- <!-- Renders `this` in localized :default format. `format` can be a symbol representing a locale key or a standard format string (see strftime) -->
496
- <def tag="view" for="date" attrs="format"><%= this && (format||= :default) && I18n.backend.localize(I18n.locale, this, format) %></def>
497
-
498
- <!-- Renders `this` in localized :default format. `format` can be a symbol representing a Time::DATE_FORMATS or a standard format string (see strftime) -->
499
- <def tag="view" for="time" attrs="format"><%= this && (format||= :time) && (format.is_a?(Symbol) ? this.to_s(format) : this.strftime(format) ) %></def>
500
-
501
- <!-- Renders `this` in localized :default format. `format` can be a symbol representing a locale key or a standard format string (see strftime) -->
502
- <def tag="view" for="datetime" attrs="format"><%= this && (format||= :default) && I18n.backend.localize(I18n.locale, this, format) %></def>
503
-
504
- <!-- Renders localized `number_with_delimiter this`, or `format % this` if the `format` attribute is given -->
505
- <def tag="view" for="Numeric" attrs="format"><%= format ? format % this : number_with_delimiter(this) %></def>
506
-
507
- <!-- If `this.html_safe?`, returns this unchanged. Otherwise renders `this` with HTML escaping and newlines replaced with `<br>` tags -->
508
- <def tag="view" for="string"><%=
509
- if this.html_safe?
510
- this
511
- elsif !(this.class == String) && this.respond_to?(:to_html) # workaround for Maruku which adds String#to_html : (
512
- this.to_html(scope.xmldoctype)
513
- else
514
- h(this).gsub("\n", "<br#{scope.xmldoctype ? ' /' : ''}>").html_safe
515
- end
516
- %></def>
517
-
518
- <!-- Renders 'Yes' for true and 'No' for false -->
519
- <def tag="view" for="boolean"><%= this ? t('hobo.boolean_yes', :default => 'Yes') : t('hobo.boolean_no', :default => 'No') %></def>
520
-
521
- <!-- Renders a link (`<a>`) to `this` -->
522
- <def tag="view" for="ActiveRecord::Base"><a merge-attrs/></def>
523
-
524
-
525
- <!--
526
- A convenience tag used to output a count summary with a correctly localized and pluralised label. Works with any kind of collection such as an `ActiveRecord` association or an array.
527
-
528
- ### Usage
529
-
530
- <count:comments/> -> <span class="count">1 Comment</span>
531
-
532
- <count:viewings/> -> <span class="count">3 Viewings</span>
533
-
534
- The label can be customised using the `label` attribute, e.g.
535
-
536
- <count:comments label="blog post comment"/> -> <span class="count">12 blog post comments</span>
537
-
538
- You can pass a summary attribute, which will generate a complete localized sentence. It allows 2 options:
539
-
540
- - boolean (e.g. `<count summary/>`): it will lookup the 'tags.count.default' key in the locale file. If the lookup fails, it will fallback to the english default sentences consistent with the count.
541
- - String (e.g. `<count summary="offer"/>`): it will lookup the 'tags.count.offer' key in the locale file. If the lookup fails, it will fallback to the english default sentences consistent with the count.
542
-
543
- ### Examples
544
-
545
- it:
546
- tags:
547
- count:
548
- default:
549
- zero: "Non ci sono {{label}}"
550
- one: "C'è solo 1 {{label}}"
551
- other: "Ci sono {{count}} {{label}}"
552
- choice:
553
- zero: "Non ci sono {{label}} da scegliere"
554
- one: "Puoi scegliere solo una {{label}}"
555
- other: "Puoi scegliere tra {{count}} {{label}}"
556
-
557
- with :en locale and boolean summary (internal defaults)
558
-
559
- <count:comments summary/> -> <span class="count">There is 1 Comment</span>
560
- <count:viewings summary/> -> <span class="count">There are 3 Viewings</span>
561
-
562
- (note: just add the locale english strings to use like the following examples)
563
-
564
- with :it locale and boolean summary (key "tags.count.default")
565
-
566
- <count:comments summary/>
567
- -> count => 0 -> <span class="count">Non ci sono Commenti</span>
568
- -> count => 1 -> <span class="count">C'è solo 1 Commento</span>
569
- -> count => 5 -> <span class="count">Ci sono 5 Commenti</span>
570
-
571
- with :it locale and summary="choice" (key "tags.count.choice")
572
-
573
- <count:comments summary="choice"/>
574
- -> count => 0 -> <span class="count">Non ci sono Commenti da scegliere</span>
575
- -> count => 1 -> <span class="count">Puoi scegliere solo 1 Commento</span>
576
- -> count => 5 -> <span class="count">Puoi scegliere tra 5 Commenti</span>
577
-
578
- ### Additional Notes
579
-
580
- * The `prefix` attribute is deprecated: use summary instead.
581
-
582
- * Use the `lowercase` attribute to force the generated label to be lowercase:
583
-
584
- <count:comments lowercase/> -> <span class="count">1 comment</span>
585
- * Use the `if-any` attribute to output nothing if the count is zero. This can be followed by an `<else>` tag to handle the empty case:
586
-
587
- <count:comments if-any/><else>There are no comments</else>
588
- -->
589
- <def tag="count" attrs="summary, label, if-any, lowercase, prefix"><span class="count"><%=
590
- raise Exception.new("asked for count of a string") if this.is_a?(String)
591
- Rails.logger.warn('The "prefix" attribute is deprecated: please, use a locale string') unless prefix.blank?
592
-
593
- c = collection_count
594
-
595
- # generated label will be pluralized
596
- label ||= case
597
- when this.is_a?(Class)
598
- this.model_name.human(:count=>c)
599
- when (attr = this.try.origin_attribute)
600
- (this_parent || this.origin).class.human_attribute_name(attr, :count=>c)
601
- else
602
- this.member_class.model_name.human(:count=>c)
603
- end
604
-
605
- label = label.downcase if lowercase
606
-
607
- Dryml.last_if = c > 0 if if_any
608
- if if_any && c == 0
609
- ""
610
- else
611
- if summary.blank? # old behaviour
612
- main = label.blank? ? c : "#{c} #{label}"
613
- if prefix.in? %w(are is)
614
- prefix = c == 1 ? "is" : "are"
615
- end
616
- (prefix ? "#{prefix} #{main}" : main.to_s)
617
- else
618
- key = summary.kind_of?(String) ? summary : "default"
619
- default = c == 1 ? "There is 1 #{label}" : "There are #{c} #{label}"
620
- t "tags.count.#{key}", {:count=>c, :label=>label, :default=>default}.merge(attributes)
621
- end
622
- end
623
- %></span></def>
624
-
625
- <!-- Equivalent to `<you capitalize/>`-->
626
- <def tag="You"><you merge capitalize/></def>
627
-
628
- <!-- Renders a `<link rel="Stylesheet" type="text/css">` to include the default stylesheet for the selected theme (select with `<set-theme>`). Included in the default pages.
629
- -->
630
- <def tag="theme-stylesheet" attrs="name">
631
- <% name ||= Hobo.current_theme -%>
632
- <link href="#{base_url}/hobothemes/#{Hobo.current_theme}/stylesheets/#{name}.css"
633
- media="all" rel="Stylesheet" type="text/css" merge/>
634
- </def>
635
-
636
- <!-- Convenience tag to help with the common situation where you need to address the current user as "you", and refer to other users by name
637
-
638
- ### Usage
639
-
640
- The context should be a user object. If `this == current_user` the "you" form is rendered, otherwise the form with the user's name:
641
-
642
- - `<you have/> new mail` -> "you have new mail" or "Jim has new mail"
643
- - `<you are/> now an admin` -> "you are now an admin" or "Jim is now an admin"
644
- - `<you do/>n't want to go there` -> "you don't want to go there" or "Jim doesn't want to go there"
645
-
646
- The tag is also localized in the namespaces "tags.you.current_user" and "tags.you.other_user".
647
- Each namespace can contain the legacy keys "have", "are", "do" used for the respective attributes,
648
- and "nothing" used when no attribute is passed. But you can also use your own keys, providing
649
- that you add the keys in the correct namespaces.
650
-
651
- ### Examples
652
-
653
- it:
654
- tags:
655
- you:
656
- current_user:
657
- nothing: "Tu"
658
- have: "Hai"
659
- are: "sei"
660
- can: "Puoi"
661
- other_user:
662
- nothing: "{{name}}"
663
- have: "{{name}} ha"
664
- are: "{{name}} è"
665
- can: "{{name}} può"
666
-
667
- - `<you have/> un nuovo messaggio.` -> "Hai un nuovo messaggio." or "Jim ha un nuovo messaggio."
668
- - `Adesso <you are/> amministratore.` -> "Adesso sei amministratore." or "Adesso Jim è amministratore."
669
- - `<you can/> scrivere.` -> "Puoi scrivere." or "Jim può scrivere."
670
-
671
- (note: :name is added by default as an interpolable variable)
672
-
673
- ### Attributes
674
-
675
- - capitalize: the first letter of the resulting sentence will be capitalized
676
-
677
-
678
- ### Additional Notes
679
-
680
- The "titleize" attribute is deprecated: use "capitalize" instead.
681
-
682
- -->
683
- <def tag="you" attrs="titleize, capitalize">
684
- <% Rails.logger.warn "'titleize' is a deprecated attribute of the 'you' tag. Please, use 'capitalize' instead." -%>
685
- <% raise ArgumentError, "You can add only one attribute-key to the 'you' tag." if attributes.size > 1 -%>
686
- <% k = case
687
- when attributes[:have] then 'have'
688
- when attributes[:are] then 'are'
689
- when attributes[:do] then 'do'
690
- end -%>
691
- <if test="&this == current_user">
692
- <%= s = t("tags.you.current_user.#{k || attributes.keys.first || 'default'}",:default=>"you #{k}")
693
- (titleize||capitalize) ? s.sub(/^./){|c| c.upcase} : s %>
694
- </if>
695
- <else>
696
- <do param="default">
697
- <%= s = t("tags.you.other_user.#{k || attributes.keys.first || 'default'}", :name=>name(:no_wrapper => true),
698
- :default=>"#{name(:no_wrapper => true)} #{'has' if attributes[:have]}#{'is' if attributes[:are]}#{'does' if attributes[:do]}")
699
- (titleize||capitalize) ? s.sub(/^./){|c| c.upcase} : s %>
700
- </do>
701
- </else>
702
- </def>
703
-
704
- <!-- Equivalent to `<your ... capitalize/>`-->
705
- <def tag="Your"><your merge capitalize/></def>
706
-
707
- <!-- Similar to `<you>`, but renders "Your" or "Fred's" or equivalent localized strings
708
-
709
- ### Attributes
710
-
711
- - capitalize: the first letter of the resulting sentence will be capitalized
712
- - count: used in pluralization. If omitted it will be set to 1.
713
- - key: used to lookup the translation in the locale file. It allows 3 different options:
714
- - single key like 'message': simple translation in 'tags.your.message.current_user'
715
- or 'tags.your.message.other_user'
716
- - composite key like 'any.namespace.message': translation as for the previous case, but it will
717
- translate also the 'any.namespace.message' and will interpolate the variable `<key>` (in this case :message)
718
- in the translation
719
- - when key is omitted it will be set to "default" and will do the translation with that key.
720
- Pass other meaningful attributes to achieve a dynamic usage
721
- - any other attribute passed to the tag will be used as a variable for interpolation
722
-
723
- Notes
724
-
725
- - The :name variable is added by default as an interpolable variable
726
- - If no translation is found an automatic (only english) default is generated:
727
- the Your/Jim's string, joined to the tag content.
728
- If you pass an explicit 'default' attribute you will override the automatic default.
729
-
730
-
731
- ### Examples
732
-
733
- it:
734
- tags:
735
- your:
736
- message:
737
- current_user:
738
- one: "Tuo Messaggio"
739
- other: "Tuoi Messaggi"
740
- other_user:
741
- one: "Messaggio di {{name}}"
742
- other: "Messaggi di {{name}
743
- entry:
744
- current_user:
745
- one: "Tua {{entry}}"
746
- other: "Tue {{entry}}"
747
- other_user: "{{entry}} di {{name}}"
748
-
749
- - `<your key="message" count=>"&messages.count"/>`:
750
-
751
- - count => 1: "Tuo Messaggio" or "Messaggio di Jim"
752
- - count => 5: "Tuoi Messaggi" or "Messaggi di Jim"
753
-
754
- - `<your key="activerecord.models.entry" count=>"&this.entries.count"/>`:
755
-
756
- - count => 1: "Tua Entrata" or "Entrata di Jim"
757
- - count => 5: "Tue Entrate" or "Entrate di Jim"
758
-
759
- - `<your>Posts</your>`: "your Posts" or "Jim's Posts"
760
- -->
761
- <def tag="your" attrs="key, capitalize, name"><%=
762
- key ||= 'default'
763
- name ||= name(:no_wrapper => true)
764
- # prepare symbolized attributes for merging
765
- attrs = {}
766
- attributes.each_pair{|k,v| attrs[k.to_sym] = v}
767
- d = "#{h name}'#{'s' unless name.ends_with?('s')} #{all_parameters.default}"
768
- options = {:default=>[d], :count=>(attrs[:count]||1), :name=>name}
769
- your_key = key.split('.').last
770
- unless key.eql?(your_key) || attrs.has_key?(your_key.to_sym)
771
- options[your_key.to_sym] = t(key, :count=>options[:count], :default=>your_key.titleize)
772
- end
773
- s = if this == current_user
774
- options[:default].unshift :"tags.your.default.current_user"
775
- t("tags.your.#{your_key}.current_user", options.merge(attrs))
776
- else
777
- options[:default].unshift :"tags.your.default.other_user"
778
- t("tags.your.#{your_key}.other_user", options.merge(attrs))
779
- end
780
- (capitalize ? s.sub(/^./){|c| c.upcase} : s).html_safe
781
- %></def>
782
-
783
- <!-- Capitalised version of `<a-or-an>` -->
784
- <def tag="A-or-An" attrs="word"><%=
785
- (word =~ /^[aeiou]/i ? "An " : "A ") + word
786
- %></def>
787
-
788
- <!-- Deprecated. It's harder than you think to do this (e.g. an umbrealla, an user)
789
- -->
790
- <def tag="a-or-an" attrs="word"><%=
791
- (word =~ /^[aeiou]/i ? "an " : "a ") + word
792
- %></def>
793
-
794
-
795
- <!-- Renders a collection of string joined with ", ", or some other string passed in the `join` attribute -->
796
- <def tag="comma-list" attrs="join"><%= this.join(join || ", ") %></def>
797
-
798
-
799
- <!-- Development mode only - a menu to change the `current_user` -->
800
- <def tag="dev-user-changer">
801
- <set user="&Hobo::Model::UserBase.default_user_model"/>
802
- <select-menu if="&user && Rails.env.development?"
803
- first-option="#{t('hobo.dev_user_changer.guest', {:default=>'Guest'})}" options="&user.all(:limit => 30).*.login"
804
- onchange="location.href = '#{dev_support_path :action=>:set_current_user}?login=' + encodeURIComponent(this.options[this.selectedIndex].value)"
805
- selected="#{current_user.login}"
806
- class="dev-user-changer"
807
- merge-attrs/>
808
- </def>