hobo_rapid 1.4.0.pre2
Sign up to get free protection for your applications and to get access to all the features.
- data/README.markdown +5 -0
- data/VERSION +1 -0
- data/app/controllers/dev_controller.rb +25 -0
- data/app/helpers/hobo_rapid_helper.rb +197 -0
- data/hobo_rapid.gemspec +26 -0
- data/lib/hobo_rapid/railtie.rb +6 -0
- data/lib/hobo_rapid.rb +13 -0
- data/taglibs/buttons/buttons.dryml +1 -0
- data/taglibs/buttons/create_button.dryml +40 -0
- data/taglibs/buttons/delete-button.dryml +75 -0
- data/taglibs/buttons/remote_method_button.dryml +34 -0
- data/taglibs/buttons/transition_button.dryml +71 -0
- data/taglibs/buttons/transition_link.dryml +22 -0
- data/taglibs/buttons/update_button.dryml +29 -0
- data/taglibs/cache/cache.dryml +1 -0
- data/taglibs/cache/nested_cache.dryml +332 -0
- data/taglibs/cards/card.dryml +7 -0
- data/taglibs/cards/cards.dryml +1 -0
- data/taglibs/cards/search_card.dryml +4 -0
- data/taglibs/editors/click_editor.dryml +60 -0
- data/taglibs/editors/editors.dryml +27 -0
- data/taglibs/editors/live_editor.dryml +37 -0
- data/taglibs/forms/error_messages.dryml +21 -0
- data/taglibs/forms/form.dryml +101 -0
- data/taglibs/forms/formlet.dryml +48 -0
- data/taglibs/forms/forms.dryml +5 -0
- data/taglibs/forms/submit.dryml +14 -0
- data/taglibs/hobo_rapid.dryml +14 -0
- data/taglibs/html/a.dryml +156 -0
- data/taglibs/html/aside.dryml +5 -0
- data/taglibs/html/doctype.dryml +39 -0
- data/taglibs/html/empty_tag.dryml +23 -0
- data/taglibs/html/footer.dryml +5 -0
- data/taglibs/html/header.dryml +5 -0
- data/taglibs/html/html.dryml +18 -0
- data/taglibs/html/if_ie.dryml +11 -0
- data/taglibs/html/image.dryml +11 -0
- data/taglibs/html/javascript.dryml +12 -0
- data/taglibs/html/section.dryml +12 -0
- data/taglibs/html/section_group.dryml +11 -0
- data/taglibs/html/stylesheet.dryml +5 -0
- data/taglibs/html/table.dryml +174 -0
- data/taglibs/i18n/ht.dryml +48 -0
- data/taglibs/i18n/human_attribute_name +18 -0
- data/taglibs/i18n/human_collection_name.dryml +69 -0
- data/taglibs/i18n/i18n.dryml +1 -0
- data/taglibs/i18n/model_name_human.dryml +16 -0
- data/taglibs/i18n/t.dryml +12 -0
- data/taglibs/inputs/after_submit.dryml +32 -0
- data/taglibs/inputs/check_many.dryml +25 -0
- data/taglibs/inputs/collection_input.dryml +10 -0
- data/taglibs/inputs/hidden_field.dryml +48 -0
- data/taglibs/inputs/hot_input.dryml +50 -0
- data/taglibs/inputs/input.dryml +76 -0
- data/taglibs/inputs/input_all.dryml +14 -0
- data/taglibs/inputs/input_for.dryml +38 -0
- data/taglibs/inputs/input_for_date.dryml +86 -0
- data/taglibs/inputs/input_for_enum_string.dryml +26 -0
- data/taglibs/inputs/input_many.dryml +131 -0
- data/taglibs/inputs/inputs.dryml +1 -0
- data/taglibs/inputs/name_one.dryml +74 -0
- data/taglibs/inputs/or_cancel.dryml +8 -0
- data/taglibs/inputs/select_input.dryml +13 -0
- data/taglibs/inputs/select_many.dryml +58 -0
- data/taglibs/inputs/select_menu.dryml +23 -0
- data/taglibs/inputs/select_one.dryml +46 -0
- data/taglibs/inputs/sortable_input_many.dryml +31 -0
- data/taglibs/inputs/sti_type_input.dryml +6 -0
- data/taglibs/lists/collection.dryml +26 -0
- data/taglibs/lists/empty_collection_message.dryml +13 -0
- data/taglibs/lists/feckless_fieldset.dryml +94 -0
- data/taglibs/lists/field_list.dryml +21 -0
- data/taglibs/lists/field_list_v1.dryml +64 -0
- data/taglibs/lists/labelled_item_list.dryml +11 -0
- data/taglibs/lists/lists.dryml +2 -0
- data/taglibs/lists/with_fields.dryml +98 -0
- data/taglibs/pages/account.dryml +30 -0
- data/taglibs/pages/account_disabled.dryml +19 -0
- data/taglibs/pages/flash_message.dryml +23 -0
- data/taglibs/pages/forgot_password.dryml +62 -0
- data/taglibs/pages/login.dryml +57 -0
- data/taglibs/pages/not_found_page.dryml +18 -0
- data/taglibs/pages/page_nav.dryml +17 -0
- data/taglibs/pages/pages.dryml +12 -0
- data/taglibs/pages/permission_denied_page.dryml +24 -0
- data/taglibs/plus/collection_preview.dryml +23 -0
- data/taglibs/plus/filter_menu.dryml +103 -0
- data/taglibs/plus/gravatar.dryml +12 -0
- data/taglibs/plus/live_search.dryml +40 -0
- data/taglibs/plus/plus.dryml +1 -0
- data/taglibs/plus/sortable_collection.dryml +36 -0
- data/taglibs/plus/table_plus.dryml +63 -0
- data/taglibs/views/a_or_an.dryml +10 -0
- data/taglibs/views/collection_name.dryml +28 -0
- data/taglibs/views/comma_list.dryml +2 -0
- data/taglibs/views/count.dryml +99 -0
- data/taglibs/views/links_for_collection.dryml +2 -0
- data/taglibs/views/name.dryml +30 -0
- data/taglibs/views/nil_view.dryml +10 -0
- data/taglibs/views/record_flags.dryml +5 -0
- data/taglibs/views/type_name.dryml +24 -0
- data/taglibs/views/view.dryml +79 -0
- data/taglibs/views/view_for.dryml +42 -0
- data/taglibs/views/views.dryml +1 -0
- data/taglibs/views/you.dryml +150 -0
- data/vendor/assets/javascripts/hobo_rapid.js +1 -0
- data/vendor/assets/stylesheets/feckless-fieldset.css +40 -0
- data/vendor/assets/stylesheets/hobo-rapid.css +94 -0
- data/vendor/assets/stylesheets/hobo_rapid.css +1 -0
- metadata +174 -0
@@ -0,0 +1,62 @@
|
|
1
|
+
<!-- The page that initiates the forgotten password process. Contains a single text-input where the user can provide
|
2
|
+
their email address -->
|
3
|
+
<def tag="forgot-password-page">
|
4
|
+
<simple-page title="#{t 'hobo.forgot_password.title', :default=>['Forgotten Password'] }" merge>
|
5
|
+
|
6
|
+
<body: class="forgot-password-page" param/>
|
7
|
+
|
8
|
+
<content: param>
|
9
|
+
<header param="content-header">
|
10
|
+
<h2 param="heading"><t key="hobo.forgot_password.heading">Forgotten Password</t></h2>
|
11
|
+
<p><t key="hobo.forgot_password.text">Enter the email address you signed up with. If we have it in
|
12
|
+
our records we'll send you an email allowing you to choose a new
|
13
|
+
password.</t></p>
|
14
|
+
</header>
|
15
|
+
|
16
|
+
<section param="content-body">
|
17
|
+
<form action="" class="forgot-password" param>
|
18
|
+
<labelled-item-list param>
|
19
|
+
<labelled-item>
|
20
|
+
<item-label param="email-address-label"><t key="hobo.forgot_password.email_address">Email Address</t></item-label>
|
21
|
+
<item-value>
|
22
|
+
<input type="text" name="email_address" id="email-address" class="email-address" param="email-address-input" />
|
23
|
+
</item-value>
|
24
|
+
</labelled-item>
|
25
|
+
</labelled-item-list>
|
26
|
+
<div param="actions">
|
27
|
+
<submit label="#{t 'hobo.actions.send', :default=>['Send'] }" param/>
|
28
|
+
</div>
|
29
|
+
</form>
|
30
|
+
</section>
|
31
|
+
</content:>
|
32
|
+
|
33
|
+
</simple-page>
|
34
|
+
</def>
|
35
|
+
|
36
|
+
|
37
|
+
<!-- Second page in the forgotten password process. Informs the user that the email has been sent "If the e-mail address you
|
38
|
+
entered is in our records". This is to avoid a privacy concern that the forgotten-password mechanism can be otherwise used to tell
|
39
|
+
if a given email is associated with an account or not. -->
|
40
|
+
<def tag="forgot-password-email-sent-page">
|
41
|
+
<simple-page title="#{t 'hobo.forgot_password_sent.title', :default=>['Forgotten Password - Sent'] }" merge>
|
42
|
+
<body: class="forgot-password-page" param/>
|
43
|
+
|
44
|
+
<content: param>
|
45
|
+
<header param="content-header">
|
46
|
+
<h2 param><t key="hobo.forgot_password_sent.heading">Forgotten Password - Sent</t></h2>
|
47
|
+
</header>
|
48
|
+
|
49
|
+
<section param="content-body">
|
50
|
+
<p param="message">
|
51
|
+
<t key="hobo.forgot_password_sent.text" email-address="#{params[:email_address]}">
|
52
|
+
If the e-mail address you entered, <%= h params[:email_address] %>,
|
53
|
+
is in our records, you will
|
54
|
+
receive an e-mail from us with instructions for resetting your
|
55
|
+
password. If you don't receive this e-mail, please check your
|
56
|
+
junk mail folder.</t></p>
|
57
|
+
<p><a href="#{base_url}/"><t key="hobo.actions.back">Back to</t> <app-name/></a></p>
|
58
|
+
</section>
|
59
|
+
</content:>
|
60
|
+
|
61
|
+
</simple-page>
|
62
|
+
</def>
|
@@ -0,0 +1,57 @@
|
|
1
|
+
<!-- Simple log-in form.
|
2
|
+
|
3
|
+
### Attributes
|
4
|
+
|
5
|
+
- `user-model`: The User class
|
6
|
+
- all other attributes are passed on to `<form>`. You will probably need to set the action attribute: `action="&user_login_path"`.
|
7
|
+
-->
|
8
|
+
<def tag="login-form" attrs="user-model">
|
9
|
+
<% user_model ||= self.try.model -%>
|
10
|
+
<form action="&request.fullpath" class="login" merge>
|
11
|
+
<labelled-item-list param>
|
12
|
+
<labelled-item>
|
13
|
+
|
14
|
+
<item-label param="login-label"><t key="hobo.login.#{user_model.login_attribute}"><%= user_model.login_attribute.to_s.titleize %></t></item-label>
|
15
|
+
<item-value><input type="text" name="login" id="login" class="string" param="login-input" /></item-value>
|
16
|
+
</labelled-item>
|
17
|
+
|
18
|
+
<labelled-item>
|
19
|
+
<item-label param="password-label"><t key="hobo.login.password">Password</t></item-label>
|
20
|
+
<item-value>
|
21
|
+
<input type="password" name="password" id="password" class="string" param="password-input"/>
|
22
|
+
</item-value>
|
23
|
+
</labelled-item>
|
24
|
+
|
25
|
+
<labelled-item param="remember-me">
|
26
|
+
<item-label class="field-label" param="remember-me-label"><t key="hobo.login.remember_me">Remember me:</t></item-label>
|
27
|
+
<item-value>
|
28
|
+
<input type="checkbox" name="remember_me" id="remember-me" param="remember-me-input" checked/>
|
29
|
+
</item-value>
|
30
|
+
</labelled-item>
|
31
|
+
</labelled-item-list>
|
32
|
+
<div param="actions">
|
33
|
+
<submit label="#{t 'hobo.actions.login', :default=>['Log in'] }" param/><if test="&signup_url" class='nav-item'>
|
34
|
+
<t key="hobo.support.or">or</t> <a param="signup" href="&signup_url"><t key="hobo.login.signup">Sign up</t></a></if>
|
35
|
+
</div>
|
36
|
+
</form>
|
37
|
+
</def>
|
38
|
+
|
39
|
+
<!-- Simple log-in page -->
|
40
|
+
<def tag="login-page">
|
41
|
+
<% remember_me = true if remember_me.nil? %>
|
42
|
+
<simple-page title="#{t 'hobo.login.title', :default=>['Log in'] }" merge>
|
43
|
+
|
44
|
+
<body: class="login-page" param/>
|
45
|
+
|
46
|
+
<content: param>
|
47
|
+
<header param="content-header">
|
48
|
+
<h2 param="heading"><t key="hobo.login.heading">Log In</t></h2>
|
49
|
+
</header>
|
50
|
+
|
51
|
+
<section param="content-body">
|
52
|
+
<login-form param="form" user-model="&model"/>
|
53
|
+
<a href="&forgot_password_url" param="forgot-password" if="&forgot_password_url"><t key="hobo.login.forgot_password">Forgot your password?</t></a>
|
54
|
+
</section>
|
55
|
+
</content:>
|
56
|
+
</simple-page>
|
57
|
+
</def>
|
@@ -0,0 +1,18 @@
|
|
1
|
+
<!-- The page rendered by default in the case of a not-found error
|
2
|
+
|
3
|
+
### Attributes
|
4
|
+
|
5
|
+
- `message` - The main message to display. Defaults to "The page you requested cannot be found."
|
6
|
+
|
7
|
+
-->
|
8
|
+
<def tag="not-found-page" attrs="message">
|
9
|
+
<% message ||= t("hobo.messages.not_found", :default=>["The page you requested cannot be found."]) %>
|
10
|
+
<page merge>
|
11
|
+
<body: class="not-found"/>
|
12
|
+
<content: param>
|
13
|
+
<header param="content-header">
|
14
|
+
<h2 param="heading"><message/></h2>
|
15
|
+
</header>
|
16
|
+
</content:>
|
17
|
+
</page>
|
18
|
+
</def>
|
@@ -0,0 +1,17 @@
|
|
1
|
+
<!--
|
2
|
+
A wrapper around the `will_paginate` helper. All [options to
|
3
|
+
`will_paginate`](https://github.com/mislav/will_paginate/wiki/the-will_paginate-view-helper)
|
4
|
+
are available as attributes. If you do not specify `params`, it will
|
5
|
+
be guessed. Standard Ajax form attributes are also supported. See
|
6
|
+
the `<a/>` tag for more information on using AJAX with links.
|
7
|
+
-->
|
8
|
+
<def tag="page-nav">
|
9
|
+
<% ajax_attrs, attributes = attributes.partition_hash(HoboRapidHelper::AJAX_ATTRS) %>
|
10
|
+
<%= will_paginate this, attributes.symbolize_keys.reverse_merge(
|
11
|
+
:inner_window => 2,
|
12
|
+
:previous_label => translate("hobo.actions.previous", :default=>"« Prev"),
|
13
|
+
:next_label =>translate("hobo.actions.next", :default=>"Next »"),
|
14
|
+
:params => recognize_page_path.slice(:controller,:action,:id),
|
15
|
+
:extra_attributes => (ajax_attrs.empty? ? {} : {"data-rapid" => {"a" => {"ajax_attrs" => ajax_attrs}}.to_json}),
|
16
|
+
:ignore_params => [:render, :render_options, :"_"]) %>
|
17
|
+
</def>
|
@@ -0,0 +1,12 @@
|
|
1
|
+
<!-- Rapid-Pages provides tags for working with entire pages, and
|
2
|
+
defines several pages such as the error pages and login pages. -->
|
3
|
+
|
4
|
+
<!-- nodoc. -->
|
5
|
+
<def tag="index-page" polymorphic/>
|
6
|
+
<!-- nodoc. -->
|
7
|
+
<def tag="new-page" polymorphic/>
|
8
|
+
<!-- nodoc. -->
|
9
|
+
<def tag="show-page" polymorphic/>
|
10
|
+
<!-- nodoc. -->
|
11
|
+
<def tag="edit-page" polymorphic/>
|
12
|
+
|
@@ -0,0 +1,24 @@
|
|
1
|
+
<!-- The page rendered by default in the case of a permission-denied error
|
2
|
+
|
3
|
+
### Attributes
|
4
|
+
|
5
|
+
- `message` - The main message to display. Defaults to "That operation is not allowed"
|
6
|
+
|
7
|
+
-->
|
8
|
+
<def tag="permission-denied-page" attrs="message">
|
9
|
+
<% message ||= "That operation is not allowed" %>
|
10
|
+
<page merge>
|
11
|
+
<body: class="permission-denied"/>
|
12
|
+
<content: param>
|
13
|
+
<header param="content-header">
|
14
|
+
<h2 param="heading"><message/></h2>
|
15
|
+
<div class="debug" if="&Rails.env.development?">
|
16
|
+
<h3>Exception:</h3>
|
17
|
+
<pre><%= h @permission_error.pretty_inspect %></pre>
|
18
|
+
<h3>params:</h3>
|
19
|
+
<pre><%= h params.pretty_inspect %></pre>
|
20
|
+
</div>
|
21
|
+
</header>
|
22
|
+
</content:>
|
23
|
+
</page>
|
24
|
+
</def>
|
@@ -0,0 +1,23 @@
|
|
1
|
+
<!-- Deprecated: use collection-preview instead. -->
|
2
|
+
<def tag="preview-with-more"><collection-preview merge/></def>
|
3
|
+
|
4
|
+
|
5
|
+
<!-- Captures the common pattern of a list of "the first few" cards, along with a link to the rest. -->
|
6
|
+
<def tag="collection-preview" attrs="name">
|
7
|
+
<% model_class = this.member_class
|
8
|
+
name ||= model_class.name.downcase.pluralize -%>
|
9
|
+
<section class="#{name.dasherize} collection-preview" param="default">
|
10
|
+
<h3 param="heading">
|
11
|
+
<ht key="#{model_class.to_s.underscore}.collection.heading" count="&this.size">
|
12
|
+
<do param="heading-content"><%= name.pluralize.titleize %></do>
|
13
|
+
</ht>
|
14
|
+
</h3>
|
15
|
+
<a with="&model_class" action="new" if="&can_create?(model_class.new)" param="new-link">
|
16
|
+
<ht key="#{model_class.to_s.underscore}.actions.new">New <%= model_class.model_name.human %></ht>
|
17
|
+
</a>
|
18
|
+
<collection param/>
|
19
|
+
<unless test="&this.empty? || this.size == model_class.count">
|
20
|
+
<a param="show-all"><ht key="#{model_class.to_s.underscore}.actions.show_all" count="100">Show all <%= name.pluralize.titleize %>...</ht></a>
|
21
|
+
</unless>
|
22
|
+
</section>
|
23
|
+
</def>
|
@@ -0,0 +1,103 @@
|
|
1
|
+
<!-- A `<select>` menu intended to act as a filter for index pages.
|
2
|
+
|
3
|
+
### Example
|
4
|
+
|
5
|
+
Filtering on state is a common use. Here's the dryml for Order:
|
6
|
+
|
7
|
+
<filter-menu param-name="state" options="&Order::Lifecycle.states.keys" />
|
8
|
+
|
9
|
+
And the controller action:
|
10
|
+
|
11
|
+
def index
|
12
|
+
# always validate data given in URL's!!!
|
13
|
+
params[:state]=nil unless Order::Lifecycle.states.include?(params[:state]._?.to_sym)
|
14
|
+
finder = params[:state] ? Order.send(params[:state]) : Order
|
15
|
+
hobo_index finder
|
16
|
+
end
|
17
|
+
|
18
|
+
See [Filtering stories by status](/tutorials/agility#filtering_stories_by_status) in the [Agility Tutorial](/tutorials/agility) for an example.
|
19
|
+
|
20
|
+
### Attributes
|
21
|
+
|
22
|
+
Standard AJAX attributes such as 'update' are supported. If any are supplied, filter-menu uses Ajax rather than a page refresh.
|
23
|
+
|
24
|
+
- `param-name` - the name of the HTTP parameter to use for the filter
|
25
|
+
- `options` - an array of options or an array of arrays (useful for localized apps) for the menu.
|
26
|
+
It can be omitted if you provide the options as an array or array of arrays in the locale file.
|
27
|
+
- `no-filter` - The text of the first option which indicates no filter is in effect. Defaults to 'All'
|
28
|
+
- `first-value` - the value to be used with the first option. Typically not used,
|
29
|
+
meaning the option has a blank value.
|
30
|
+
- model - the model name (optional: needed if you use the "activerecord.attributes" namespace.
|
31
|
+
|
32
|
+
### I18n
|
33
|
+
|
34
|
+
It lookups the options attributes in `activerecord.attributes.#{model}.filter_menu.#{param\_name}.options`
|
35
|
+
with fallback to `filter_menu.#{param_name}.options`.
|
36
|
+
The passed options are used as a default in case the lookup fails.
|
37
|
+
Besides the `activerecord.attributes.#{model}.filter_menu.#{param_name}.no_filter` or
|
38
|
+
`tags.filter_menu.default.no_filter` key is used as default of the attribute "no-filter"
|
39
|
+
(or "All" if no default is found)
|
40
|
+
|
41
|
+
### I18n Example
|
42
|
+
|
43
|
+
|
44
|
+
es:
|
45
|
+
activerecord:
|
46
|
+
attributes:
|
47
|
+
<model_name>:
|
48
|
+
filter_menu:
|
49
|
+
period:
|
50
|
+
no_filter: Todos Períodos
|
51
|
+
options:
|
52
|
+
- [ "Hoy", "today" ]
|
53
|
+
- [ "Ayer", "yesterday" ]
|
54
|
+
|
55
|
+
or
|
56
|
+
|
57
|
+
es:
|
58
|
+
tags:
|
59
|
+
filter_menu:
|
60
|
+
period:
|
61
|
+
no_filter: Todos Períodos
|
62
|
+
options:
|
63
|
+
- [ "Hoy", "today" ]
|
64
|
+
- [ "Ayer", "yesterday" ]
|
65
|
+
|
66
|
+
|
67
|
+
TIME_PERIODS = %w[today yesterday]
|
68
|
+
|
69
|
+
<t-filter-menu param-name="period" options="&TIME_PERIODS" no-filter="All Periods"/>
|
70
|
+
|
71
|
+
with I18n.locale == :es
|
72
|
+
|
73
|
+
<select name="period">
|
74
|
+
<option value="">Todos Períodos</option>
|
75
|
+
<option value="today">Hoy</option>
|
76
|
+
<option value="yesterday">Ayer</option>
|
77
|
+
</select>
|
78
|
+
|
79
|
+
with I18n.locale == :en (i.e no locale file)
|
80
|
+
|
81
|
+
<select name="period">
|
82
|
+
<option value="">All Periods</option>
|
83
|
+
<option value="today">today</option>
|
84
|
+
<option value="yesterday">yesterday</option>
|
85
|
+
</select>
|
86
|
+
|
87
|
+
-->
|
88
|
+
<def tag="filter-menu" attrs="model, param-name, options, no-filter, id, first-value">
|
89
|
+
<% options = t("activerecord.attributes.#{model}.filter_menu.#{param_name}.options", :default=>[:"tags.filter_menu.#{param_name}.options", options])
|
90
|
+
raise ArgumentError, %(You must provide an "options" attribute, or set "activerecord.attributes.#{model}.filter_menu.#{param_name}.options" or "tags.filter_menu.#{param_name}.options" to an Array or to an Array of Arrays
|
91
|
+
in your locale file(s)) unless options.is_a?(Array)
|
92
|
+
no_filter = t("activerecord.attributes.#{model}.filter_menu.#{param_name}.no_filter", :default=>[:"tags.filter_menu.#{param_name}.no_filter", :"tags.filter_menu.default.no_filter", no_filter, "All"])
|
93
|
+
%>
|
94
|
+
<form action="&request.path" method="get" class="filter-menu" merge-attrs="&attributes" data-rapid="&data_rapid('filter-menu')">
|
95
|
+
<div>
|
96
|
+
<% opt = options.first.kind_of?(Array) ? options.*.last : options
|
97
|
+
selected = opt.detect {|o| o.to_s==params[param_name.gsub('-', '_')] } %>
|
98
|
+
<select-menu name="¶m_name" options="&options" selected="&selected"
|
99
|
+
first-option="&no_filter" first-value="&first_value" key="¶m_name" merge-params/>
|
100
|
+
<hidden-fields for-query-string skip="page,#{param_name}"/>
|
101
|
+
</div>
|
102
|
+
</form>
|
103
|
+
</def>
|
@@ -0,0 +1,12 @@
|
|
1
|
+
<!-- Renders a gravatar (see [gravatar.com](http://gravatar.com)) image in side a link to `this`. Requires `this` to have an `email_address` field. Normally called with a user record in context.
|
2
|
+
|
3
|
+
### Attributes
|
4
|
+
|
5
|
+
- `size` - Size in pixels of the image. Defaults to 80.
|
6
|
+
- `rating` - The rating allowed. Defaults to 'g'. See [gravatar.com](http://gravatar.com) for information on ratings.
|
7
|
+
|
8
|
+
-->
|
9
|
+
<def tag="gravatar" attrs="size, rating">
|
10
|
+
<% size ||= 80; rating ||= 'g'; digest = Digest::MD5.hexdigest(this.email_address) -%>
|
11
|
+
<a class="gravatar"><img class="gravatar" src="http://www.gravatar.com/avatar/#{digest}?s=#{size}&r=#{rating}" merge-attrs/></a>
|
12
|
+
</def>
|
@@ -0,0 +1,40 @@
|
|
1
|
+
<!-- Provides an ajax-powered *find-as-you-type* live search field which is hooked up to Hobo's site-side search feature.
|
2
|
+
|
3
|
+
Customizing the display of results can be done through customizing the "box" param and the `search-results` tag. The current definition of `<search-results>` expects results to be returned in `@search_results`.
|
4
|
+
|
5
|
+
TODO: currently you have to press 'return' to initiate the search. This should be easy to fix in hjq-live-search.js - the hard part will probably be in doing it in a way that works in all possible browsers.
|
6
|
+
|
7
|
+
### attributes
|
8
|
+
|
9
|
+
- action: the path to send the query. Default is "/search" (actually, site_search_path)
|
10
|
+
- query_param: the name of the param to use for the search. Default is "query"
|
11
|
+
|
12
|
+
All other attributes are merged into the form, so standard form attributes are supported.
|
13
|
+
-->
|
14
|
+
<def tag="live-search" attrs="query-param, panel-tag">
|
15
|
+
<% attributes[:action] ||= site_search_path %>
|
16
|
+
<% attributes[:update] ||= "search-results-part" %>
|
17
|
+
<% attributes[:message] ||= t("hobo.live_search.spinner_message", :default => "Querying...") %>
|
18
|
+
<% query_param ||= "query" %>
|
19
|
+
<div class="search" data-rapid='{"live-search": {}}'>
|
20
|
+
<form merge-attrs param refocus-form>
|
21
|
+
<input type="hidden" value="2" name="search_version"/>
|
22
|
+
<label for="&query_param" param><t key="hobo.live_search.label">Search</t></label><input type="search" name="&query_param" class="live-search" param/>
|
23
|
+
</form>
|
24
|
+
<% panel_tag ||= 'dialog-box' %>
|
25
|
+
<% title = t("hobo.live_search.results_label", :default => "Search Results") %>
|
26
|
+
<call-tag tag="&panel_tag" position="&{:my => 'right top'}" width="&600" height="&800" id="search-results-box" with="&nil" title="&title" param="box">
|
27
|
+
<do part="search-results-part">
|
28
|
+
<search-results/>
|
29
|
+
</do>
|
30
|
+
</call-tag>
|
31
|
+
</div>
|
32
|
+
</def>
|
33
|
+
|
34
|
+
<!-- redefining or extending this tag will allow you to customize the display of search results -->
|
35
|
+
<def tag="search-results">
|
36
|
+
<section with="&@search_results || []" data-rapid='{"search-results": {}}'>
|
37
|
+
<collection/>
|
38
|
+
<else><t key="hobo.live_search.no_results">Your search returned no matches.</t></else>
|
39
|
+
</section>
|
40
|
+
</def>
|
@@ -0,0 +1 @@
|
|
1
|
+
<!-- Tags that define higher level interactive ‘widgets’ -->
|
@@ -0,0 +1,36 @@
|
|
1
|
+
<!-- An enhanced version of Rapid's `<collection>` tag that supports drag-and-drop re-ordering.
|
2
|
+
|
3
|
+
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.
|
4
|
+
|
5
|
+
### Attributes
|
6
|
+
|
7
|
+
`sortable-collection` supports all of the options and events defined by [jQuery UI's Sortable](http://jqueryui.com/demos/sortable/).
|
8
|
+
|
9
|
+
Sortable also supports all of the standard [Hobo AJAX callbacks](http://cookbook.hobocentral.net/api_taglibs/rapid_forms).
|
10
|
+
|
11
|
+
The jQuery UI option `update` is used internally to save the new order via Ajax. If you modify this option, your function may call `$(this).hjq_sortable_collection('update')` to retain this functionality.
|
12
|
+
|
13
|
+
### Controller support
|
14
|
+
|
15
|
+
This tag assumes the controller has a `reorder` action and the model has a `position_column` method. This action is added automatically by Hobo's model-controller if the model declares `acts_as_list`. See also [Drag and Drop Reordering](/manual/controllers#drag_and_drop_reordering) in the [Controllers and Routing](/manual/controllers) chapter of the manual.
|
16
|
+
-->
|
17
|
+
<def tag="sortable-collection">
|
18
|
+
<%
|
19
|
+
options, attrs = attributes.partition_hash(['disabled', 'appendTo', 'axis', 'cancel', 'connectWith', 'containment', 'cursor', 'cursorAt', 'delay', 'distance', 'dropOnEmpty', 'forceHelperSize', 'forcePlaceholderSize', 'grid', 'handle', 'helper', 'items', 'opacity', 'placeholder', 'revert', 'scroll', 'scrollSensitivity', 'scrollSpeed', 'tolerance', 'zIndex'])
|
20
|
+
events, html_attrs = attrs.partition_hash(['create', 'start', 'sort', 'change', 'beforeStop', 'stop', 'update', 'receive', 'remove', 'over', 'out', 'activate', 'deactivate'])
|
21
|
+
ajax_attrs, html_attrs = attrs.partition_hash(HoboRapidHelper::AJAX_CALLBACKS)
|
22
|
+
|
23
|
+
singular_name = this.member_class.name.underscore
|
24
|
+
route_method = subsite ? "#{subsite}_reorder_#{singular_name.pluralize}_url" : "reorder_#{singular_name.pluralize}_url"
|
25
|
+
reorder_url = send(route_method)
|
26
|
+
|
27
|
+
add_classes!(html_attrs, "sortable-collection")
|
28
|
+
add_data_rapid!(html_attrs, "sortable-collection", :options => options, :events => events, :ajax_attrs => ajax_attrs, :reorder_url => reorder_url, :reorder_parameter => "#{singular_name}_ordering")
|
29
|
+
%>
|
30
|
+
<collection class="sortable" merge-attrs="&html_attrs" merge-params>
|
31
|
+
<item: param>
|
32
|
+
<div class="ordering-handle" param="handle" if="&can_edit?(this.position_column)">↑<br/>↓</div>
|
33
|
+
<do param="default"><card param/></do>
|
34
|
+
</item:>
|
35
|
+
</collection>
|
36
|
+
</def>
|
@@ -0,0 +1,63 @@
|
|
1
|
+
<!-- An enhanced version of Rapid's `<table>` that has support for column sorting, searching and pagination.
|
2
|
+
|
3
|
+
This tag calls `<table merge-params>`, so the parameters for `<table>` are also available.
|
4
|
+
|
5
|
+
The enhancements made available in this tag require controller support.
|
6
|
+
|
7
|
+
An [worked example](/tutorials/agility#improve_the_project_page_with_a_searchable_sortable_table) of this tag is available in the [Agility Tutorial](/tutorials/agility)
|
8
|
+
|
9
|
+
### Attributes
|
10
|
+
|
11
|
+
All attributes supported by `<table>` and `<with-fields>` are supported.
|
12
|
+
|
13
|
+
AJAX attributes are passed through to the sort links and the search form.
|
14
|
+
|
15
|
+
sort-field: defaults to @sort_field, if that is available. `parse_sort_params` sets this variable
|
16
|
+
|
17
|
+
sort-direction: 'asc' or 'dec'. defaults to @sort_direction, if that is available. `parse_sort_params` sets this variable
|
18
|
+
|
19
|
+
sort-columns: a hash that allow you to map fields to sortable columns. The default is {"this" => "name"} (or whatever column that has the :name option set on it).
|
20
|
+
|
21
|
+
-->
|
22
|
+
<def tag="table-plus" attrs="sort-field, sort-direction, sort-columns" >
|
23
|
+
<% sort_field ||= @sort_field; sort_direction ||= @sort_direction; sort_columns ||= {} %>
|
24
|
+
<% sort_columns['this'] ||= this.member_class.try.name_attribute %>
|
25
|
+
<% ajax_attrs, attributes = attributes.partition_hash(HoboRapidHelper::AJAX_ATTRS) %>
|
26
|
+
<div class="table-plus" merge-attrs="&attributes - attrs_for(:with_fields) - attrs_for(:table)">
|
27
|
+
<div class="header" param="header">
|
28
|
+
<div class="search">
|
29
|
+
<form param="search-form" method="get" action="" with="&nil" merge-attrs="&ajax_attrs" >
|
30
|
+
<hidden-fields for-query-string skip="page, search"/>
|
31
|
+
<span><t key="hobo.table_plus.search">Search</t></span>
|
32
|
+
<input class="search" type="search" name="search" value="¶ms[:search]"/>
|
33
|
+
<submit label="&t('hobo.table_plus.submit_label', :default=>'Go')" class="search-button" param="search-submit"/>
|
34
|
+
</form>
|
35
|
+
</div>
|
36
|
+
</div>
|
37
|
+
|
38
|
+
<table merge-attrs="&attributes & (attrs_for(:table) + attrs_for(:with_fields))" empty merge-params>
|
39
|
+
<field-heading-row:>
|
40
|
+
<with-field-names merge-attrs="&all_attributes & attrs_for(:with_fields)">
|
41
|
+
<% col = sort_columns[scope.field_path] || scope.field_path
|
42
|
+
sort = sort_field == col && sort_direction == 'asc' ?
|
43
|
+
"-#{col}" : col
|
44
|
+
sort_url = url_for_page_path(query_parameters_filtered('except' => 'page').merge(:sort => sort))
|
45
|
+
col_heading_name = this.member_class.try.human_attribute_name(scope.field_name, :default=> scope.field_name.titleize) %>
|
46
|
+
|
47
|
+
<th param="#{scope.field_name}-heading">
|
48
|
+
<a href="&sort_url" class="column-sort"
|
49
|
+
param="#{scope.field_name}-heading-link" merge-attrs="&ajax_attrs" ><%= col_heading_name %></a>
|
50
|
+
<if test="&col == sort_field">
|
51
|
+
<do param="up-arrow" if="&sort_direction == 'desc'">↑</do>
|
52
|
+
<do param="down-arrow" if="&sort_direction == 'asc'">↓</do>
|
53
|
+
</if>
|
54
|
+
</th>
|
55
|
+
</with-field-names>
|
56
|
+
<th if="&all_parameters[:controls]" class="controls"></th>
|
57
|
+
</field-heading-row>
|
58
|
+
</table>
|
59
|
+
<empty-collection-message param="empty-message"/>
|
60
|
+
|
61
|
+
<page-nav param if="&this.respond_to?(:page_count) || this.respond_to?(:total_pages)" merge-attrs="&ajax_attrs" />
|
62
|
+
</div>
|
63
|
+
</def>
|
@@ -0,0 +1,10 @@
|
|
1
|
+
<!-- Capitalised version of `<a-or-an>` -->
|
2
|
+
<def tag="A-or-An" attrs="word"><%=
|
3
|
+
(word =~ /^[aeiou]/i ? "An " : "A ") + word
|
4
|
+
%></def>
|
5
|
+
|
6
|
+
<!-- Deprecated. It's harder than you think to do this (e.g. an umbrella, an user)
|
7
|
+
-->
|
8
|
+
<def tag="a-or-an" attrs="word"><%=
|
9
|
+
(word =~ /^[aeiou]/i ? "an " : "a ") + word
|
10
|
+
%></def>
|
@@ -0,0 +1,28 @@
|
|
1
|
+
<!-- Renders a human readable name of a collection
|
2
|
+
|
3
|
+
Use this tag for en locale only. Use human-collection-name for i18n.
|
4
|
+
|
5
|
+
### Details
|
6
|
+
|
7
|
+
- Uses `this.origin_attribute` as the name.
|
8
|
+
- Falls back to `<type-name>` otherwise.
|
9
|
+
- By default the name is titleised and plural.
|
10
|
+
|
11
|
+
### Attributes
|
12
|
+
|
13
|
+
- singular: singularise the name
|
14
|
+
- lowercase: render the name in all lower case
|
15
|
+
- dasherize: render the name in lower case with dashes instead of spaces.
|
16
|
+
|
17
|
+
-->
|
18
|
+
<def tag="collection-name" attrs="singular, lowercase, dasherize"><%=
|
19
|
+
if (attr = this.try.origin_attribute)
|
20
|
+
name = attr.to_s
|
21
|
+
name = dasherize ? name.underscore.dasherize : name.titleize
|
22
|
+
name = name.singularize if singular
|
23
|
+
name = name.downcase if lowercase
|
24
|
+
name
|
25
|
+
else
|
26
|
+
type_name(:plural => !singular, :lowercase => lowercase, :dasherize => dasherize)
|
27
|
+
end
|
28
|
+
%></def>
|
@@ -0,0 +1,99 @@
|
|
1
|
+
<!--
|
2
|
+
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.
|
3
|
+
|
4
|
+
### Usage
|
5
|
+
|
6
|
+
<count:comments/> -> <span class="count">1 Comment</span>
|
7
|
+
|
8
|
+
<count:viewings/> -> <span class="count">3 Viewings</span>
|
9
|
+
|
10
|
+
The label can be customised using the `label` attribute, e.g.
|
11
|
+
|
12
|
+
<count:comments label="blog post comment"/> -> <span class="count">12 blog post comments</span>
|
13
|
+
|
14
|
+
You can pass a summary attribute, which will generate a complete localized sentence. It allows 2 options:
|
15
|
+
|
16
|
+
- 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.
|
17
|
+
- 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.
|
18
|
+
|
19
|
+
### Examples
|
20
|
+
|
21
|
+
it:
|
22
|
+
tags:
|
23
|
+
count:
|
24
|
+
default:
|
25
|
+
zero: "Non ci sono {{label}}"
|
26
|
+
one: "C'è solo 1 {{label}}"
|
27
|
+
other: "Ci sono {{count}} {{label}}"
|
28
|
+
choice:
|
29
|
+
zero: "Non ci sono {{label}} da scegliere"
|
30
|
+
one: "Puoi scegliere solo una {{label}}"
|
31
|
+
other: "Puoi scegliere tra {{count}} {{label}}"
|
32
|
+
|
33
|
+
with :en locale and boolean summary (internal defaults)
|
34
|
+
|
35
|
+
<count:comments summary/> -> <span class="count">There is 1 Comment</span>
|
36
|
+
<count:viewings summary/> -> <span class="count">There are 3 Viewings</span>
|
37
|
+
|
38
|
+
(note: just add the locale english strings to use like the following examples)
|
39
|
+
|
40
|
+
with :it locale and boolean summary (key "tags.count.default")
|
41
|
+
|
42
|
+
<count:comments summary/>
|
43
|
+
-> count => 0 -> <span class="count">Non ci sono Commenti</span>
|
44
|
+
-> count => 1 -> <span class="count">C'è solo 1 Commento</span>
|
45
|
+
-> count => 5 -> <span class="count">Ci sono 5 Commenti</span>
|
46
|
+
|
47
|
+
with :it locale and summary="choice" (key "tags.count.choice")
|
48
|
+
|
49
|
+
<count:comments summary="choice"/>
|
50
|
+
-> count => 0 -> <span class="count">Non ci sono Commenti da scegliere</span>
|
51
|
+
-> count => 1 -> <span class="count">Puoi scegliere solo 1 Commento</span>
|
52
|
+
-> count => 5 -> <span class="count">Puoi scegliere tra 5 Commenti</span>
|
53
|
+
|
54
|
+
### Additional Notes
|
55
|
+
|
56
|
+
* The `prefix` attribute is deprecated: use summary instead.
|
57
|
+
|
58
|
+
* Use the `lowercase` attribute to force the generated label to be lowercase:
|
59
|
+
|
60
|
+
<count:comments lowercase/> -> <span class="count">1 comment</span>
|
61
|
+
* 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:
|
62
|
+
|
63
|
+
<count:comments if-any/><else>There are no comments</else>
|
64
|
+
-->
|
65
|
+
<def tag="count" attrs="summary, label, if-any, lowercase, prefix"><span class="count"><%=
|
66
|
+
raise Exception.new("asked for count of a string") if this.is_a?(String)
|
67
|
+
Rails.logger.warn('The "prefix" attribute is deprecated: please, use a locale string') unless prefix.blank?
|
68
|
+
|
69
|
+
c = collection_count
|
70
|
+
|
71
|
+
# generated label will be pluralized
|
72
|
+
label ||= case
|
73
|
+
when this.is_a?(Class)
|
74
|
+
this.model_name.human(:count=>c)
|
75
|
+
when (attr = this.try.origin_attribute)
|
76
|
+
(this_parent || this.origin).class.human_attribute_name(attr, :count=>c)
|
77
|
+
else
|
78
|
+
this.member_class.model_name.human(:count=>c)
|
79
|
+
end
|
80
|
+
|
81
|
+
label = label.downcase if lowercase
|
82
|
+
|
83
|
+
Dryml.last_if = c > 0 if if_any
|
84
|
+
if if_any && c == 0
|
85
|
+
""
|
86
|
+
else
|
87
|
+
if summary.blank? # old behaviour
|
88
|
+
main = label.blank? ? c : "#{c} #{label}"
|
89
|
+
if prefix.in? %w(are is)
|
90
|
+
prefix = c == 1 ? "is" : "are"
|
91
|
+
end
|
92
|
+
(prefix ? "#{prefix} #{main}" : main.to_s)
|
93
|
+
else
|
94
|
+
key = summary.kind_of?(String) ? summary : "default"
|
95
|
+
default = c == 1 ? "There is 1 #{label}" : "There are #{c} #{label}"
|
96
|
+
t "tags.count.#{key}", {:count=>c, :label=>label, :default=>default}.merge(attributes)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
%></span></def>
|