clot 1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/lib/autotest/discover.rb +5 -0
- data/lib/clot.rb +80 -0
- data/lib/clot/active_record/droppable.rb +29 -0
- data/lib/clot/base_drop.rb +116 -0
- data/lib/clot/content_for.rb +35 -0
- data/lib/clot/date_tags.rb +342 -0
- data/lib/clot/deprecated.rb +11 -0
- data/lib/clot/form_filters.rb +130 -0
- data/lib/clot/form_for.rb +279 -0
- data/lib/clot/form_tag.rb +24 -0
- data/lib/clot/if_content_for.rb +32 -0
- data/lib/clot/link_filters.rb +101 -0
- data/lib/clot/model_date_tags.rb +82 -0
- data/lib/clot/model_form_tags.rb +205 -0
- data/lib/clot/mongo_mapper/droppable.rb +29 -0
- data/lib/clot/nav_bar.rb +154 -0
- data/lib/clot/no_model_form_tags.rb +233 -0
- data/lib/clot/tag_helper.rb +22 -0
- data/lib/clot/url_filters.rb +50 -0
- data/lib/clot/yield.rb +59 -0
- metadata +81 -0
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
module Clot
|
|
2
|
+
module FormFilters
|
|
3
|
+
|
|
4
|
+
def form_item(tag, message, required = false)
|
|
5
|
+
tag_id = get_attribute_value("id", tag)
|
|
6
|
+
|
|
7
|
+
form_string = ""
|
|
8
|
+
if tag_id
|
|
9
|
+
form_string = " for=\"#{tag_id}\""
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
if required
|
|
13
|
+
required_string = "<span class=\"required\">*</span>"
|
|
14
|
+
else
|
|
15
|
+
required_string = ""
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
"<p><label#{form_string}>#{message}#{required_string}</label>#{tag}</p>"
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
#################
|
|
22
|
+
|
|
23
|
+
#note - must reconstruct from scratch...
|
|
24
|
+
def input_to_text(input)
|
|
25
|
+
|
|
26
|
+
value_match = /value="([^"]*)"/.match input
|
|
27
|
+
if value_match
|
|
28
|
+
value_text = value_match[1]
|
|
29
|
+
else
|
|
30
|
+
value_text = ""
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
name_match = /name="[^"]*"/.match input
|
|
34
|
+
if name_match
|
|
35
|
+
name_text = " #{name_match[0]}"
|
|
36
|
+
else
|
|
37
|
+
name_text = ""
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
"<textarea#{name_text}>#{value_text}</textarea>"
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def concat(string1, string2)
|
|
44
|
+
"#{string1}#{string2}"
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def get_id_from_name(name)
|
|
49
|
+
name.sub("[", "_").sub("]","")
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def drop_class_to_table_item(clazz)
|
|
53
|
+
match = /_drops/.match clazz.name.tableize
|
|
54
|
+
match.pre_match
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def get_attribute_value(prop, input)
|
|
58
|
+
prop_match = /#{prop}="([^"]*)"/.match input
|
|
59
|
+
if prop_match
|
|
60
|
+
prop_match[1]
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def set_param(tag, key, value)
|
|
66
|
+
match = /#{key}="[^"]*"/.match tag
|
|
67
|
+
if match
|
|
68
|
+
return match.pre_match + "#{key}=\"#{value}\"" + match.post_match
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
match = /(\/>|>)/.match tag
|
|
72
|
+
if match
|
|
73
|
+
match.pre_match + " #{key}=\"#{value}\"" + match.to_s + match.post_match
|
|
74
|
+
else
|
|
75
|
+
tag
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def submit_button(message)
|
|
80
|
+
'<div class="form-submit-button"><input type="submit" value="' + message + '"/></div>'
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def form_input_item(name, value, errors )
|
|
84
|
+
input = "<input type=\"text\" id=\"#{get_id_from_name(name)}\" name=\"#{name}\" value=\"#{value}\"#{get_error_class(errors)}/>"
|
|
85
|
+
input
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def form_text_item(name, value, errors )
|
|
89
|
+
text = "<textarea id=\"#{get_id_from_name(name)}\" name=\"#{name}\"#{get_error_class(errors)}>#{value}</textarea>"
|
|
90
|
+
text
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def form_file_item(name, errors )
|
|
94
|
+
input = "<input type=\"file\" id=\"#{get_id_from_name(name)}\" name=\"#{name}\" />"
|
|
95
|
+
input
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def form_select_item(name, value, collection, errors, blank_option = nil)
|
|
99
|
+
prompt = ""
|
|
100
|
+
if blank_option
|
|
101
|
+
prompt = "<option>#{blank_option}</option>"
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
select = "<select id=\"#{get_id_from_name(name)}\" name=\"#{name}\"#{get_error_class(errors)}>"
|
|
105
|
+
select += prompt
|
|
106
|
+
collection.each do |item|
|
|
107
|
+
@_id = @_label = item.to_s
|
|
108
|
+
if item.respond_to?(:id) && item.respond_to?(:collection_label)
|
|
109
|
+
@_id = item.id
|
|
110
|
+
@_label = item.collection_label
|
|
111
|
+
end
|
|
112
|
+
select += "<option value=\"#{@_id}\"#{get_selection_value(value, item)}>#{@_label}</option>"
|
|
113
|
+
|
|
114
|
+
end
|
|
115
|
+
select += "</select>"
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def get_selection_value(value,item)
|
|
119
|
+
matched_value = item.to_s
|
|
120
|
+
if item.respond_to?(:collection_label)
|
|
121
|
+
matched_value = item.id
|
|
122
|
+
end
|
|
123
|
+
value.to_s == matched_value.to_s ? ' selected="true"' : ''
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def get_error_class(errors)
|
|
127
|
+
errors.blank? ? "" : ' class="error-item"'
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
end
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
require 'clot/url_filters'
|
|
2
|
+
require 'clot/link_filters'
|
|
3
|
+
require 'clot/form_filters'
|
|
4
|
+
require 'clot/tag_helper'
|
|
5
|
+
|
|
6
|
+
module Clot
|
|
7
|
+
class LiquidForm < Liquid::Block
|
|
8
|
+
include UrlFilters
|
|
9
|
+
include LinkFilters
|
|
10
|
+
include FormFilters
|
|
11
|
+
include TagHelper
|
|
12
|
+
|
|
13
|
+
Syntax = /([^\s]+)\s+/
|
|
14
|
+
|
|
15
|
+
def initialize(tag_name, markup, tokens)
|
|
16
|
+
if markup =~ Syntax
|
|
17
|
+
@form_object = $1
|
|
18
|
+
@attributes = {}
|
|
19
|
+
markup.scan(Liquid::TagAttributes) do |key, value|
|
|
20
|
+
@attributes[key] = value
|
|
21
|
+
end
|
|
22
|
+
else
|
|
23
|
+
syntax_error tag_name, markup, tokens
|
|
24
|
+
end
|
|
25
|
+
super
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def render(context)
|
|
29
|
+
set_variables context
|
|
30
|
+
render_form context
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def render_form(context)
|
|
34
|
+
result = get_form_header(context)
|
|
35
|
+
result += get_form_body(context)
|
|
36
|
+
result += get_form_footer
|
|
37
|
+
result
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def syntax_error
|
|
41
|
+
raise SyntaxError.new("Syntax Error in form tag")
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def get_form_body(context)
|
|
45
|
+
context.stack do
|
|
46
|
+
render_all(@nodelist, context) * ""
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def get_form_footer
|
|
51
|
+
"</form>"
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def set_upload
|
|
55
|
+
if @attributes["uploading"] || @attributes["multipart"] == "true"
|
|
56
|
+
@upload_info = ' enctype="multipart/form-data"'
|
|
57
|
+
else
|
|
58
|
+
@upload_info = ''
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def set_variables(context)
|
|
63
|
+
set_controller_action
|
|
64
|
+
set_form_action(context)
|
|
65
|
+
set_class
|
|
66
|
+
set_upload
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class LiquidFormFor < LiquidForm
|
|
73
|
+
|
|
74
|
+
def get_errors(model)
|
|
75
|
+
errors = []
|
|
76
|
+
model.errors.each do |attr,msg|
|
|
77
|
+
errors << attr
|
|
78
|
+
end
|
|
79
|
+
errors
|
|
80
|
+
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def get_form_body(context)
|
|
85
|
+
context.stack do
|
|
86
|
+
context['form_model'] = @model
|
|
87
|
+
context['form_class_name'] = @class_name
|
|
88
|
+
context['form_errors'] = get_errors @model
|
|
89
|
+
return render_all(@nodelist, context)
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
private
|
|
95
|
+
|
|
96
|
+
def set_controller_action
|
|
97
|
+
silence_warnings {
|
|
98
|
+
if @model.nil? || @model.source.nil?
|
|
99
|
+
@activity = "new"
|
|
100
|
+
elsif @model.dropped_class == Searchlogic::Search
|
|
101
|
+
@activity = "search"
|
|
102
|
+
elsif @form_object.include?("_change")
|
|
103
|
+
@activity = "change"
|
|
104
|
+
elsif @model.source.new_record? || @model.source.id.nil?
|
|
105
|
+
@activity = "new"
|
|
106
|
+
elsif @model.dropped_class == NationSignup
|
|
107
|
+
@activity = "new"
|
|
108
|
+
else
|
|
109
|
+
@activity = "edit"
|
|
110
|
+
end
|
|
111
|
+
}
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def set_form_action(context)
|
|
115
|
+
if @activity == "edit"
|
|
116
|
+
@form_action = object_url @model
|
|
117
|
+
elsif @activity == "new"
|
|
118
|
+
@form_action = "/" + @model.dropped_class.to_s.tableize.pluralize
|
|
119
|
+
elsif ['search', 'change'].include?(@activity)
|
|
120
|
+
@form_action = ""
|
|
121
|
+
else
|
|
122
|
+
syntax_error
|
|
123
|
+
end
|
|
124
|
+
if @attributes["parent"]
|
|
125
|
+
@form_action = object_url(context[@attributes["parent"]]) + @form_action
|
|
126
|
+
end
|
|
127
|
+
if context.registers[:controller].params[:controller].split('/').first == 'users' or @form_action == '/accounts'
|
|
128
|
+
@form_action = "/users" + @form_action.gsub("/forms","")
|
|
129
|
+
elsif @activity != 'change'
|
|
130
|
+
@form_action = "/forms" + @form_action if not @form_action[0..5] == '/forms'
|
|
131
|
+
end
|
|
132
|
+
unless @attributes["post_method"].nil?
|
|
133
|
+
@form_action += '/' + @attributes["post_method"]
|
|
134
|
+
@activity = @attributes["post_method"]
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def set_class
|
|
140
|
+
@class_string = ""
|
|
141
|
+
unless @attributes["class"].nil?
|
|
142
|
+
@class_string += 'class="' + @attributes["class"] + '" '
|
|
143
|
+
end
|
|
144
|
+
unless @attributes["autocomplete"].nil?
|
|
145
|
+
@class_string += 'autocomplete="' + @attributes["autocomplete"] + '" '
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
@class_name = drop_class_to_table_item @model.class
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def set_model(context)
|
|
152
|
+
@model = context[@form_object] || nil
|
|
153
|
+
if not @model
|
|
154
|
+
if @form_object.include?("_search")
|
|
155
|
+
search_object_name = @form_object.gsub('_search',"").classify.constantize
|
|
156
|
+
search_object = search_object_name.search
|
|
157
|
+
search_object_name = (search_object_name.to_s + "SearchDrop").constantize
|
|
158
|
+
@model = search_object_name.new(search_object)
|
|
159
|
+
@model.defaults(context)
|
|
160
|
+
context[@form_object] = @model
|
|
161
|
+
elsif @form_object.include?("_change")
|
|
162
|
+
change_object = @form_object.gsub('_change',"").pluralize.classify.constantize
|
|
163
|
+
change_object_name = (change_object.to_s + "ChangeDrop").constantize
|
|
164
|
+
@model = change_object_name.new(change_object.new)
|
|
165
|
+
@model.defaults(context)
|
|
166
|
+
context[@form_object] = @model
|
|
167
|
+
else
|
|
168
|
+
@model = @form_object.classify.constantize.new.to_liquid
|
|
169
|
+
if @model.source.new_record?
|
|
170
|
+
@model.defaults(context)
|
|
171
|
+
context[@form_object] = @model
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def get_form_header(context)
|
|
178
|
+
cs = @class_name + "_form"
|
|
179
|
+
# temporarily taking out 'survey_question_response' from this list because i can't figure out why the javascript
|
|
180
|
+
# response on cagreens survey http://cagreens.nationbuilder.com/ga_dec2011_survey doesn't seem to work (it works everywhere else)
|
|
181
|
+
if ['comment', 'survey_response', 'face_tweet', 'feedback', 'volunteer_signup', 'event_rsvp', 'event_page', 'signup',
|
|
182
|
+
'password_reset', 'password', 'flag', 'nation_signup', 'blog_post_page', 'nation_signin', 'pledge', 'unsubscribe',
|
|
183
|
+
'account', 'petition_signature', 'suggestion_page', 'endorsement'].include?(@class_name) # hacky thing to make ajax forms work
|
|
184
|
+
cs = 'ajaxForm ' + cs
|
|
185
|
+
end
|
|
186
|
+
if ['endorsement', 'suggestion_page', 'petition_signature'].include?(@class_name)
|
|
187
|
+
@upload_info = ' enctype="multipart/form-data"'
|
|
188
|
+
end
|
|
189
|
+
if ['search', 'change'].include?(@activity)
|
|
190
|
+
method_type = "GET"
|
|
191
|
+
else
|
|
192
|
+
method_type = "POST"
|
|
193
|
+
end
|
|
194
|
+
result = '<form class="' + cs + '" method="' + method_type + '" ' + @class_string + 'action="' + @form_action + '"' + @upload_info + '>'
|
|
195
|
+
if @activity == "edit"
|
|
196
|
+
result += '<input type="hidden" name="_method" value="PUT"/>'
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
# this will get replaced with a real authenticity token by rake middleware
|
|
200
|
+
if not ['search', 'change'].include?(@activity)
|
|
201
|
+
result += '<input name="authenticity_token" type="hidden" value="__CROSS_SITE_REQUEST_FORGERY_PROTECTION_TOKEN__"/>'
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
# jim change... add current page
|
|
205
|
+
if context['page'] and not ['search', 'change'].include?(@activity)
|
|
206
|
+
result += '<input name="page_id" type="hidden" value="' + context['page'].id.to_s + '"/>'
|
|
207
|
+
result += '<input name="return_to" type="hidden" value="' + context['page'].full_url + '"/>'
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
if context['signup'] and ['profiles'].include?(context.registers[:controller].controller_name)
|
|
211
|
+
result += '<input name="signup_id" type="hidden" value="' + context['signup'].id.to_s + '"/>'
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
# this is a honeypot field
|
|
215
|
+
result += '<div class="email_address_form" style="display:none;">'
|
|
216
|
+
result += '<p><label for "email_address">Optional email code</label><br/><input name="email_address" type="text" class="text" id="email_address" autocomplete="off"/></p>'
|
|
217
|
+
result += '</div>'
|
|
218
|
+
|
|
219
|
+
result
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
def set_variables(context)
|
|
223
|
+
set_model(context)
|
|
224
|
+
super
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
class ErrorMessagesFor < Liquid::Tag
|
|
231
|
+
|
|
232
|
+
include TagHelper
|
|
233
|
+
def initialize(name, params, tokens)
|
|
234
|
+
@_params = split_params(params)
|
|
235
|
+
super
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def render(context)
|
|
240
|
+
@params = @_params.clone
|
|
241
|
+
@model = context[@params.shift]
|
|
242
|
+
|
|
243
|
+
result = ""
|
|
244
|
+
if @model and @model.errors.count > 0
|
|
245
|
+
@suffix = @model.errors.count > 1 ? "s" : ""
|
|
246
|
+
@default_message = @model.errors.count.to_s + " error#{@suffix} occurred while processing information"
|
|
247
|
+
|
|
248
|
+
@params.each do |pair|
|
|
249
|
+
pair = pair.split /:/
|
|
250
|
+
value = resolve_value(pair[1],context)
|
|
251
|
+
|
|
252
|
+
case pair[0]
|
|
253
|
+
when "header_message" then
|
|
254
|
+
@default_message = value
|
|
255
|
+
end
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
result += '<div class="errorExplanation" id="errorExplanation"><h2>' + @default_message + '</h2><ul>'
|
|
259
|
+
|
|
260
|
+
@model.errors.each do |attr, msg|
|
|
261
|
+
result += "<li>#{error_message(attr, msg)}</li>"
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
result += "</ul></div>"
|
|
265
|
+
end
|
|
266
|
+
result
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
def error_message(attr, msg)
|
|
270
|
+
unless attr == :base
|
|
271
|
+
"#{attr} - #{msg}"
|
|
272
|
+
else
|
|
273
|
+
msg
|
|
274
|
+
end
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
module Clot
|
|
2
|
+
class FormTag < LiquidForm
|
|
3
|
+
|
|
4
|
+
def get_form_header(context)
|
|
5
|
+
"<form action=\"#{resolve_value @form_object,context}\" method=\"#{@http_method}\"#{@upload_info}>"
|
|
6
|
+
end
|
|
7
|
+
def get_form_errors
|
|
8
|
+
""
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def set_variables(context)
|
|
12
|
+
set_method
|
|
13
|
+
set_upload
|
|
14
|
+
#super
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def set_method
|
|
18
|
+
@http_method = @attributes['method'] ||= 'post'
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
module Clot
|
|
2
|
+
class IfContentFor < Liquid::Block
|
|
3
|
+
include Liquid
|
|
4
|
+
|
|
5
|
+
Syntax = /(#{QuotedFragment}+)(\s+(?:with|for)\s+(#{QuotedFragment}+))?/
|
|
6
|
+
|
|
7
|
+
def initialize(tag_name, markup, tokens)
|
|
8
|
+
@blocks = []
|
|
9
|
+
|
|
10
|
+
if markup =~ Syntax
|
|
11
|
+
|
|
12
|
+
@template_name = $1
|
|
13
|
+
@variable_name = $3
|
|
14
|
+
@attributes = {}
|
|
15
|
+
|
|
16
|
+
markup.scan(TagAttributes) do |key, value|
|
|
17
|
+
@attributes[key] = value
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
else
|
|
21
|
+
raise SyntaxError.new("Syntax error in tag 'yield' - Valid syntax: content_for '[template]' (with|for) [object|collection]")
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
super
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def render(context)
|
|
28
|
+
@template_name
|
|
29
|
+
super
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|