honey-cms 0.1.0
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/cms.rb +13 -0
- data/lib/cms/attribute.rb +86 -0
- data/lib/cms/configuration.rb +40 -0
- data/lib/cms/controller_stub.rb +3 -0
- data/lib/cms/engine.rb +5 -0
- data/lib/cms/form_builder.rb +276 -0
- data/lib/cms/helper.rb +65 -0
- data/lib/cms/orderable.rb +24 -0
- data/lib/cms/routes.rb +14 -0
- data/lib/cms/type.rb +44 -0
- data/lib/cms/uploader.rb +7 -0
- data/lib/cms/view_tags.rb +25 -0
- data/lib/generators/cms/USAGE +8 -0
- data/lib/generators/cms/cms_generator.rb +44 -0
- data/lib/generators/cms/templates/cms_base_controller.rb +78 -0
- data/lib/generators/cms/templates/cms_model.rb +3 -0
- data/lib/generators/cms/templates/migration.rb +19 -0
- data/lib/generators/cms/templates/type_controller.rb +9 -0
- data/lib/generators/cms/templates/type_model.rb +23 -0
- data/lib/generators/cms/templates/views/edit.html.haml +19 -0
- data/lib/generators/cms/templates/views/index.html.haml +34 -0
- data/lib/generators/cms/templates/views/new.html.haml +19 -0
- data/lib/generators/cms/templates/views/show.html.haml +13 -0
- data/lib/honey-cms.rb +2 -0
- metadata +68 -0
data/lib/cms.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
module CMS
|
2
|
+
autoload :Routes, 'cms/routes'
|
3
|
+
autoload :Configuration, 'cms/configuration'
|
4
|
+
autoload :Type, 'cms/type'
|
5
|
+
autoload :Attribute, 'cms/attribute'
|
6
|
+
autoload :Helper, 'cms/helper'
|
7
|
+
autoload :Orderable, 'cms/orderable'
|
8
|
+
autoload :Uploader, 'cms/Uploader'
|
9
|
+
autoload :ViewTags, 'cms/view_tags'
|
10
|
+
autoload :FormBuilder, 'cms/form_builder'
|
11
|
+
end
|
12
|
+
|
13
|
+
Cms = CMS
|
@@ -0,0 +1,86 @@
|
|
1
|
+
class CMS::Attribute
|
2
|
+
attr_accessor :name, :format, :options
|
3
|
+
|
4
|
+
def initialize name, format, options
|
5
|
+
@name = name
|
6
|
+
@format = format
|
7
|
+
@options = options
|
8
|
+
@attr_options = {
|
9
|
+
# null: false
|
10
|
+
}
|
11
|
+
end
|
12
|
+
|
13
|
+
def format
|
14
|
+
@format_enquirer ||= ActiveSupport::StringInquirer.new(@format)
|
15
|
+
end
|
16
|
+
|
17
|
+
def reference? ; format.reference? end
|
18
|
+
def orderable? ; format.orderable? end
|
19
|
+
def file? ; format.file? end
|
20
|
+
|
21
|
+
def has_index?
|
22
|
+
false
|
23
|
+
end
|
24
|
+
|
25
|
+
def has_uniq_index?
|
26
|
+
false
|
27
|
+
end
|
28
|
+
|
29
|
+
def index_name
|
30
|
+
format.reference? ? "#{name}_id" : name
|
31
|
+
end
|
32
|
+
|
33
|
+
def migration_type
|
34
|
+
if format.reference?
|
35
|
+
'belongs_to'
|
36
|
+
elsif format.orderable?
|
37
|
+
'integer'
|
38
|
+
elsif format.file?
|
39
|
+
'string'
|
40
|
+
elsif format.html?
|
41
|
+
'text'
|
42
|
+
else
|
43
|
+
format
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def form_type
|
48
|
+
if format.orderable? || format.reference?
|
49
|
+
'select'
|
50
|
+
elsif format.text? or format.html?
|
51
|
+
'text_area'
|
52
|
+
elsif format.file?
|
53
|
+
'file_field'
|
54
|
+
elsif format.boolean?
|
55
|
+
'check_box'
|
56
|
+
else
|
57
|
+
'text_field'
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def field_name
|
62
|
+
if reference?
|
63
|
+
name + '_id'
|
64
|
+
else
|
65
|
+
name
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def inject_options
|
70
|
+
"".tap { |s| @attr_options.each { |k,v| s << ", :#{k} => #{v.inspect}" } }
|
71
|
+
end
|
72
|
+
|
73
|
+
def inject_index_options
|
74
|
+
has_uniq_index? ? ", :unique => true" : ''
|
75
|
+
end
|
76
|
+
|
77
|
+
def reference_to
|
78
|
+
if reference?
|
79
|
+
@reference_to ||= CMS::Configuration.types.find{|t| t.name == @options['reference_to']}
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def to_s
|
84
|
+
name
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module CMS::Configuration
|
2
|
+
def data
|
3
|
+
@data = YAML.load(File.read(Rails.root.join 'config/cms.yml'))
|
4
|
+
end
|
5
|
+
|
6
|
+
def scoped_types options
|
7
|
+
if options[:only]
|
8
|
+
[types.find{|t| options[:only] == t.name}]
|
9
|
+
else
|
10
|
+
types.reject{|t| options[:except].include?(t.name)}
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def types
|
15
|
+
if defined?(@types) then return @types end
|
16
|
+
|
17
|
+
@types = data['content_types'].map do |name, config|
|
18
|
+
CMS::Type.new(name, config.delete('attributes'), config)
|
19
|
+
end
|
20
|
+
|
21
|
+
@types.each do |type|
|
22
|
+
type.attributes = attributes(type.attributes, type)
|
23
|
+
end
|
24
|
+
|
25
|
+
@types
|
26
|
+
end
|
27
|
+
|
28
|
+
def attributes attributes, type
|
29
|
+
attributes.map do |args|
|
30
|
+
options = args.extract_options!
|
31
|
+
name = args.shift
|
32
|
+
format = args.pop || options.delete('format')
|
33
|
+
attribute = CMS::Attribute.new(name, format, options)
|
34
|
+
attribute.reference_to.references << type if attribute.reference?
|
35
|
+
attribute
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
extend self
|
40
|
+
end
|
data/lib/cms/engine.rb
ADDED
@@ -0,0 +1,276 @@
|
|
1
|
+
class CMS::FormBuilder < ActionView::Helpers::FormBuilder ; end
|
2
|
+
|
3
|
+
ActionView::Base.field_error_proc = Proc.new do |html, instance|
|
4
|
+
if html =~ /<label/
|
5
|
+
html
|
6
|
+
else
|
7
|
+
message = instance.error_message.map{|m| "#{instance.method_name.humanize} #{m}"}.join(', ')
|
8
|
+
"#{html}<div class=\"help-inline\">#{message}</div>".html_safe
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
module CMS::FormBuilder::Fields
|
13
|
+
def radio name, *args
|
14
|
+
args = _apply_field_defaults(args)
|
15
|
+
options = args.extract_options!
|
16
|
+
values = args
|
17
|
+
field_wrapper :radio, name do
|
18
|
+
out = ''.html_safe
|
19
|
+
out.concat label(name) if options[:label]
|
20
|
+
|
21
|
+
value_div = @template.content_tag(:span, class: 'values') do
|
22
|
+
values.map do |value|
|
23
|
+
@template.content_tag :span, class: 'value' do
|
24
|
+
if value.is_a? Array
|
25
|
+
radio_button(name, value[1]) + label(name, value[0], value: value[1])
|
26
|
+
else
|
27
|
+
radio_button(name, value) + label(name, value, value: value)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end.join("\n").html_safe
|
31
|
+
end
|
32
|
+
out.concat value_div
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def location name, options
|
37
|
+
autocomplete name, options.merge(prepend: @template.content_tag(:div, '', class: 'geolocate'))
|
38
|
+
end
|
39
|
+
|
40
|
+
def autocomplete name, *args
|
41
|
+
args = _apply_field_defaults(args)
|
42
|
+
options = args.extract_options!
|
43
|
+
options.reverse_merge!(waiting: 'Searching...')
|
44
|
+
|
45
|
+
option_text = '<div class="options"><div class="inner">'.html_safe
|
46
|
+
option_text.concat @template.content_tag(:div, options[:waiting], class: 'waiting')
|
47
|
+
option_text.concat '<div class="results"></div>'.html_safe
|
48
|
+
option_text.concat '</div></div>'.html_safe
|
49
|
+
|
50
|
+
field_wrapper(:autocomplete, name) do
|
51
|
+
out = ''.html_safe
|
52
|
+
if options[:label] == true
|
53
|
+
out.concat label(name)
|
54
|
+
elsif options[:label]
|
55
|
+
out.concat label(name, options[:label])
|
56
|
+
end
|
57
|
+
out.concat @template.link_to 'learn more', '#', class: 'tipsy', title: send("#{name}_help_text") if options[:help_text]
|
58
|
+
options[:help_text] = false
|
59
|
+
out.concat string(name, options.merge(autocomplete: false, label: false, append: option_text))
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def text_with_counter name, *args
|
64
|
+
args = _apply_field_defaults(args)
|
65
|
+
options = args.extract_options!
|
66
|
+
|
67
|
+
unless options[:length].blank?
|
68
|
+
counter = "<div class=\"counter\"><span class=\"count\">0</span>/<span class=\"maximum\">#{options[:length]}</span></div>".html_safe
|
69
|
+
end
|
70
|
+
|
71
|
+
text(name, options.merge(label: options[:label], append: counter))
|
72
|
+
end
|
73
|
+
|
74
|
+
def boolean *args
|
75
|
+
field :boolean, *_apply_default_options(args, label_first: false)
|
76
|
+
end
|
77
|
+
|
78
|
+
def string *args
|
79
|
+
field :string, *args
|
80
|
+
end
|
81
|
+
|
82
|
+
def search *args
|
83
|
+
field :search, *args
|
84
|
+
end
|
85
|
+
|
86
|
+
def text *args
|
87
|
+
field :text, *args
|
88
|
+
end
|
89
|
+
|
90
|
+
def email *args
|
91
|
+
field :email, *args
|
92
|
+
end
|
93
|
+
|
94
|
+
def password *args
|
95
|
+
field :password, *args
|
96
|
+
end
|
97
|
+
|
98
|
+
def hidden *args
|
99
|
+
field :hidden, *_apply_default_options(args, label: false, wrap_field: false)
|
100
|
+
end
|
101
|
+
|
102
|
+
# slider is weird enough that we will not use the default field helper.
|
103
|
+
# instead, we will construct our own field that looks like a regular field.
|
104
|
+
def slider name, *args
|
105
|
+
args = _apply_field_defaults(args)
|
106
|
+
options = args.extract_options!
|
107
|
+
out = ''.html_safe
|
108
|
+
|
109
|
+
field_wrapper :slider, name do
|
110
|
+
out.concat label(name, options[:label]) if options[:label]
|
111
|
+
out.concat @template.content_tag :div, '', class: 'slider-edit'
|
112
|
+
out.concat hidden(name)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def toggle name, *args
|
117
|
+
args = _apply_field_defaults(args)
|
118
|
+
options = args.extract_options!
|
119
|
+
out = ''.html_safe
|
120
|
+
|
121
|
+
field_wrapper :toggle, name, :'data-default' => options[:default] do
|
122
|
+
out.concat label(name) if options[:label]
|
123
|
+
out.concat(@template.content_tag(:div, class: (object.send(name) ? 'visible' : 'hidden')) do
|
124
|
+
check_box(name) << @template.label_tag(object.send(name) ? 'visible' : 'hidden')
|
125
|
+
end)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def actions options = {}
|
130
|
+
options.reverse_merge! save: 'Save', saving: 'Saving...', class: 'form-actions'
|
131
|
+
@template.content_tag(:div, class: options.delete(:class)) do
|
132
|
+
actions = ''.html_safe
|
133
|
+
actions << submit(options[:save], disable_with: options[:saving], class: 'btn btn-primary')
|
134
|
+
actions << status
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def modal_actions options = {}
|
139
|
+
options.reverse_merge! save: 'Save', saving: 'Saving...', class: 'modal-footer'
|
140
|
+
@template.content_tag(:div, class: options.delete(:class)) do
|
141
|
+
actions = ''.html_safe
|
142
|
+
actions << @template.link_to('Close', '#close', class: 'btn', data: {dismiss: 'modal'})
|
143
|
+
actions << submit(options[:save], disable_with: options[:saving], class: 'btn btn-primary')
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
def status options = {}
|
148
|
+
options.reverse_merge! success: 'Saved!', error: 'Failed!'
|
149
|
+
out = @template.content_tag(:div, class: 'status') do
|
150
|
+
status = ''.html_safe
|
151
|
+
status << @template.content_tag(:div, '', class: 'spinner')
|
152
|
+
# status << @template.content_tag(:div, options[:success], class: 'success')
|
153
|
+
# status << @template.content_tag(:div, options[:error], class: 'error')
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
def field_wrapper type, name, options = {}
|
158
|
+
classes = "field #{type} #{name.to_s.dasherize} control-group"
|
159
|
+
classes << options[:classes] if options[:classes]
|
160
|
+
classes << ' error' if object.errors.include? name
|
161
|
+
options.merge! class: classes
|
162
|
+
@template.content_tag :div, options do
|
163
|
+
yield
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
# def _input_options options
|
168
|
+
# [:autocomplete, :placeholder]
|
169
|
+
# end
|
170
|
+
|
171
|
+
def field *args, &block
|
172
|
+
type, name, options = _extract_field_args(args)
|
173
|
+
out = ''.html_safe
|
174
|
+
|
175
|
+
input_options = {}
|
176
|
+
|
177
|
+
unless options[:autocomplete].nil?
|
178
|
+
options.delete(:autocomplete)
|
179
|
+
input_options[:autocomplete] = 'off'
|
180
|
+
end
|
181
|
+
|
182
|
+
unless options[:placeholder].nil?
|
183
|
+
input_options[:placeholder] = options.delete(:placeholder)
|
184
|
+
end
|
185
|
+
|
186
|
+
unless options[:hidden].nil?
|
187
|
+
input_options[:class] = 'hidden' if options[:hidden] == true
|
188
|
+
end
|
189
|
+
|
190
|
+
unless options[:required].nil?
|
191
|
+
input_options[:required] = 'required' if options[:required] == true
|
192
|
+
end
|
193
|
+
|
194
|
+
out.concat options[:prepend] if options[:prepend]
|
195
|
+
|
196
|
+
|
197
|
+
label_html = label(name, options[:label], class: 'control-label')
|
198
|
+
|
199
|
+
out.concat label_html if options[:label] && options[:label_first]
|
200
|
+
|
201
|
+
if options[:help_text]
|
202
|
+
help_text = send("#{name}_help_text")
|
203
|
+
help_html = "<a class=\"tipsy\" title=\"#{help_text}\" href=\"#\">learn more</a>".html_safe
|
204
|
+
out.concat help_html
|
205
|
+
end
|
206
|
+
|
207
|
+
out.concat(@template.content_tag(:div, class: 'controls') do
|
208
|
+
controls = send(_field_types(type), name, input_options)
|
209
|
+
controls.concat @template.content_tag(:div, options[:help_block], class: 'help-block') if options[:help_block].present?
|
210
|
+
controls
|
211
|
+
end)
|
212
|
+
|
213
|
+
out.concat label_html if options[:label] && !options[:label_first]
|
214
|
+
|
215
|
+
out.concat options[:append] if options[:append]
|
216
|
+
out.concat yield if block_given?
|
217
|
+
|
218
|
+
if options[:wrap_field]
|
219
|
+
field_wrapper(type, name) { out }
|
220
|
+
else
|
221
|
+
out
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
# simple helper method for extracting and applying default options.
|
226
|
+
def _apply_default_options args, defaults
|
227
|
+
options = args.extract_options!
|
228
|
+
args << options.reverse_merge!(defaults)
|
229
|
+
end
|
230
|
+
|
231
|
+
# apply the default options for all fields.
|
232
|
+
def _apply_field_defaults args
|
233
|
+
_apply_default_options args, label: true, wrap_field: true, label_first: true
|
234
|
+
end
|
235
|
+
|
236
|
+
# single use method for parsing options provided by the +field+ helper
|
237
|
+
def _extract_field_args args
|
238
|
+
args = _apply_field_defaults(args)
|
239
|
+
options = args.extract_options!
|
240
|
+
name = args.pop
|
241
|
+
type = args.pop
|
242
|
+
options[:label] = name.to_s.humanize if options[:label].is_a? TrueClass
|
243
|
+
[type, name, options]
|
244
|
+
end
|
245
|
+
|
246
|
+
def _field_types(type)
|
247
|
+
case type
|
248
|
+
when :string, :location
|
249
|
+
:text_field
|
250
|
+
when :search
|
251
|
+
:search_field
|
252
|
+
when :text
|
253
|
+
:text_area
|
254
|
+
when :email
|
255
|
+
:email_field
|
256
|
+
when :password
|
257
|
+
:password_field
|
258
|
+
when :hidden, :slider
|
259
|
+
:hidden_field
|
260
|
+
when :boolean, :radio
|
261
|
+
:radio_button
|
262
|
+
when :boolean, :check
|
263
|
+
:check_box
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
def error_messages
|
268
|
+
if object.errors.any?
|
269
|
+
@template.render partial: 'shared/form/error_messages', object: object.errors
|
270
|
+
end
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
class CMS::FormBuilder
|
275
|
+
include CMS::FormBuilder::Fields
|
276
|
+
end
|
data/lib/cms/helper.rb
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
module CMS::Helper
|
2
|
+
def cms_file name, size = false
|
3
|
+
if file = CMS::FileUpload.find_by_name(name) then return file end
|
4
|
+
opts = {name: name}
|
5
|
+
opts[:description] = (size.sub('x', ' by ') << ' pixels')
|
6
|
+
CMS::FileUpload.create(opts, without_protection: true)
|
7
|
+
end
|
8
|
+
|
9
|
+
def cms_image name, size = false, width = '', height = ''
|
10
|
+
width, height = size.split('x') if size
|
11
|
+
image = cms_file(name, size)
|
12
|
+
|
13
|
+
if image.file?
|
14
|
+
image_tag image.file.url, class: 'cms-image', style: "width: #{width}px ; height: #{height}px"
|
15
|
+
else
|
16
|
+
if current_user && current_user.role.admin?
|
17
|
+
link_to(name, edit_cms_file_upload_path(image), class: 'cms-image missing-cms-image', style: "width: #{width}px ; height: #{height}px ")
|
18
|
+
else
|
19
|
+
content_tag :div, name, class: 'cms-image missing-cms-image', style: "width: #{width}px ; height: #{height}px"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def cms_page_area name, &block
|
25
|
+
page_area = if area = CMS::PageArea.find_by_name(name) then area else CMS::PageArea.new({name: name}, without_protection: true) end
|
26
|
+
|
27
|
+
content_tag :div, role: 'html-editor' do
|
28
|
+
out = content_tag(:div, class: 'cms-page-area', role: 'display') do
|
29
|
+
if page_area.content.present? && !page_area.default
|
30
|
+
content = page_area.content
|
31
|
+
elsif block_given?
|
32
|
+
content = capture(&block)
|
33
|
+
page_area.content = ''.concat(content)
|
34
|
+
page_area.save!
|
35
|
+
end
|
36
|
+
|
37
|
+
display = ''.html_safe
|
38
|
+
display << cms_page_area_edit_link if admin?
|
39
|
+
display << content_tag(:div, class: 'content') do
|
40
|
+
cms_content_parse(content) if content.present?
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
out << content_tag(:div, role: 'editor') do
|
45
|
+
form_for([:cms, page_area], format: 'json', remote: true) do |f|
|
46
|
+
form = f.hidden_field(:name)
|
47
|
+
form << f.hidden_field(:content, class: 'content')
|
48
|
+
form << f.actions(save: 'done')
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
rescue Exception => e
|
53
|
+
if Rails.env.production? then '' else raise e end
|
54
|
+
end
|
55
|
+
|
56
|
+
def cms_page_area_edit_link
|
57
|
+
content_tag :div, class: 'cms-edit-link' do
|
58
|
+
link_to('Edit', '#edit')
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def cms_content_parse content
|
63
|
+
CMS::ViewTags.instance.parse content, context: self
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module CMS::Orderable
|
2
|
+
extend ActiveSupport::Concern
|
3
|
+
|
4
|
+
def order_scope
|
5
|
+
self.class
|
6
|
+
end
|
7
|
+
|
8
|
+
module ClassMethods
|
9
|
+
def orderable name
|
10
|
+
default_scope order(name)
|
11
|
+
after_save :"order_#{name}"
|
12
|
+
|
13
|
+
define_method :"order_#{name}" do
|
14
|
+
order_scope.where("#{name} >= #{send(name)}").where("id != #{id}").select(:id).select(name).inject(send(name)) do |i, record|
|
15
|
+
record.update_column name, (i += 1) ; i
|
16
|
+
end
|
17
|
+
|
18
|
+
order_scope.all.inject(1) do |i, record|
|
19
|
+
record.update_column name, i ; i + 1
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
data/lib/cms/routes.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
class CMS::Routes < SimpleDelegator
|
2
|
+
def draw
|
3
|
+
namespace :cms do
|
4
|
+
get 'description' => 'root#description'
|
5
|
+
get '' => 'root#index'
|
6
|
+
|
7
|
+
CMS::Configuration.types.each do |type|
|
8
|
+
resources type.model_name.route_key
|
9
|
+
end
|
10
|
+
|
11
|
+
resources :topic_datas, :community_maps, :downtowns
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
data/lib/cms/type.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
class CMS::Type
|
2
|
+
attr_accessor :name, :attributes, :options, :references
|
3
|
+
include ActiveModel::Naming # using include instead of extend on purpose
|
4
|
+
def parents ; [] end # quacks like a Module
|
5
|
+
|
6
|
+
def initialize name, attributes, options = {}
|
7
|
+
@name = name
|
8
|
+
@attributes = attributes
|
9
|
+
@options = options.symbolize_keys.merge(timestamps: true, indexes: true)
|
10
|
+
@references = []
|
11
|
+
end
|
12
|
+
|
13
|
+
def accessible_attributes
|
14
|
+
attributes
|
15
|
+
end
|
16
|
+
|
17
|
+
def attributes_with_index
|
18
|
+
attributes.select { |a| a.has_index? || (a.reference? && options[:indexes]) }
|
19
|
+
end
|
20
|
+
|
21
|
+
def orderable_attributes
|
22
|
+
attributes.select(&:orderable?)
|
23
|
+
end
|
24
|
+
|
25
|
+
def file_attributes
|
26
|
+
attributes.select(&:file?)
|
27
|
+
end
|
28
|
+
|
29
|
+
def orderable?
|
30
|
+
!!order_attribute
|
31
|
+
end
|
32
|
+
|
33
|
+
def order_attribute
|
34
|
+
orderable_attributes.first
|
35
|
+
end
|
36
|
+
|
37
|
+
def to_s
|
38
|
+
name
|
39
|
+
end
|
40
|
+
|
41
|
+
def subject
|
42
|
+
options[:subject] || name
|
43
|
+
end
|
44
|
+
end
|
data/lib/cms/uploader.rb
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
class CMS::Uploader < CarrierWave::Uploader::Base
|
2
|
+
def store_dir
|
3
|
+
base = File.join('cms-uploads', model.class.model_name.collection.gsub('_', '-'), model.id.try(:to_s))
|
4
|
+
base = File.join('uploads', base) if storage.is_a? CarrierWave::Storage::File
|
5
|
+
base
|
6
|
+
end
|
7
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
class CMS::ViewTags
|
2
|
+
attr_accessor :context, :controller
|
3
|
+
delegate :current_user, :config, to: :context
|
4
|
+
|
5
|
+
include Singleton
|
6
|
+
include ApplicationHelper
|
7
|
+
include ActionView::Helpers::TextHelper
|
8
|
+
include ActionView::Helpers::UrlHelper
|
9
|
+
include ActionView::Helpers::AssetTagHelper
|
10
|
+
include ApplicationHelper
|
11
|
+
include CMS::Helper
|
12
|
+
include Rails.application.routes.url_helpers
|
13
|
+
|
14
|
+
def setup opts
|
15
|
+
@context = @controller = opts[:context]
|
16
|
+
end
|
17
|
+
|
18
|
+
def parse content, opts = {}
|
19
|
+
setup(opts)
|
20
|
+
content.gsub(/\{\{image [^}}]+\}\}/) do |tag|
|
21
|
+
str, name, size = tag.match(/\{\{image (\w+) (\w+)\}\}/).to_a
|
22
|
+
cms_image(name, size)
|
23
|
+
end.html_safe
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'rails/generators/active_record/migration'
|
2
|
+
|
3
|
+
class CmsGenerator < Rails::Generators::Base
|
4
|
+
source_root File.expand_path('../templates', __FILE__)
|
5
|
+
include Rails::Generators::Migration
|
6
|
+
extend ActiveRecord::Generators::Migration
|
7
|
+
|
8
|
+
class_option :except, type: :array, default: [],
|
9
|
+
desc: 'skip certain types.'
|
10
|
+
|
11
|
+
class_option :only, type: :string, default: false,
|
12
|
+
desc: 'run a specific type generator.'
|
13
|
+
|
14
|
+
class_option :migrate, type: :boolean, default: false,
|
15
|
+
desc: 'generate the migration'
|
16
|
+
|
17
|
+
class_option :controller, type: :boolean, default: false,
|
18
|
+
desc: 'generate the controller'
|
19
|
+
|
20
|
+
def create_migration_file
|
21
|
+
if options[:only].present?
|
22
|
+
migration_template 'migration.rb', "db/migrate/create_#{options[:only].underscore.pluralize}" if options[:migrate]
|
23
|
+
else
|
24
|
+
migration_template 'migration.rb', 'db/migrate/create_cms' if options[:migrate]
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def copy_controller_file
|
29
|
+
template 'cms_base_controller.rb', 'app/controllers/cms/base_controller.rb' if options[:controller]
|
30
|
+
|
31
|
+
empty_directory 'app/controllers/cms'
|
32
|
+
empty_directory 'app/models/cms'
|
33
|
+
|
34
|
+
CMS::Configuration.scoped_types(options).each do |type|
|
35
|
+
@name = (@type = type).model_name
|
36
|
+
template 'type_controller.rb', "app/controllers/cms/#{@name.collection}_controller.rb"
|
37
|
+
template 'type_model.rb', "app/models/cms/#{@name.element}.rb"
|
38
|
+
|
39
|
+
%w(index new show edit).each do |view|
|
40
|
+
template "views/#{view}.html.haml", "app/views/cms/#{@name.collection}/#{view}.html.haml"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
class CMS::BaseController < ApplicationController
|
2
|
+
before_filter :admin!
|
3
|
+
before_filter :set_active
|
4
|
+
layout 'cms'
|
5
|
+
|
6
|
+
before_filter :find_record, only: [:show, :edit, :update, :destroy]
|
7
|
+
before_filter :set_element_variable, only: [:show, :edit, :update, :destroy]
|
8
|
+
respond_to :html, :json
|
9
|
+
|
10
|
+
def index
|
11
|
+
@records = if params[:search].blank?
|
12
|
+
subject.order('id asc') .page(params[:page]).per(100)
|
13
|
+
else
|
14
|
+
subject.search(params[:search]).page(params[:page]).per(100)
|
15
|
+
end
|
16
|
+
|
17
|
+
set_collection_variable
|
18
|
+
respond_with(@records)
|
19
|
+
end
|
20
|
+
|
21
|
+
def new
|
22
|
+
@record = subject.new
|
23
|
+
set_element_variable
|
24
|
+
respond_with(@record)
|
25
|
+
end
|
26
|
+
|
27
|
+
def create
|
28
|
+
@record = subject.create(params[subject.model_name.element])
|
29
|
+
set_element_variable
|
30
|
+
respond_with @record
|
31
|
+
end
|
32
|
+
|
33
|
+
def show
|
34
|
+
respond_with @record
|
35
|
+
end
|
36
|
+
|
37
|
+
def edit
|
38
|
+
respond_with @record
|
39
|
+
end
|
40
|
+
|
41
|
+
def update
|
42
|
+
@record.update_attributes params[subject.model_name.element]
|
43
|
+
respond_with @record
|
44
|
+
end
|
45
|
+
|
46
|
+
def destroy
|
47
|
+
@record.destroy
|
48
|
+
respond_with @record
|
49
|
+
end
|
50
|
+
|
51
|
+
protected
|
52
|
+
|
53
|
+
def respond_with object
|
54
|
+
super [:cms, object]
|
55
|
+
end
|
56
|
+
|
57
|
+
def find_record
|
58
|
+
@record = subject.find(params[:id])
|
59
|
+
end
|
60
|
+
|
61
|
+
def set_collection_variable
|
62
|
+
instance_variable_set :"@#{subject.model_name.collection}", @records
|
63
|
+
end
|
64
|
+
|
65
|
+
def set_element_variable
|
66
|
+
instance_variable_set :"@#{subject.model_name.element}", @record
|
67
|
+
end
|
68
|
+
|
69
|
+
def admin!
|
70
|
+
unless signed_in? && current_user.role.admin?
|
71
|
+
auth_failed
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def set_active
|
76
|
+
@active_page = /cms/
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class <%= migration_class_name %> < ActiveRecord::Migration
|
2
|
+
def change
|
3
|
+
<% CMS::Configuration.scoped_types(options).each do |type| -%>
|
4
|
+
create_table :<%= type.model_name.collection %> do |t|
|
5
|
+
<% type.attributes.each do |attribute| -%>
|
6
|
+
t.<%= attribute.migration_type %> :<%= attribute.name %><%= attribute.inject_options %>
|
7
|
+
<% end -%>
|
8
|
+
<% if type.options[:timestamps] -%>
|
9
|
+
t.timestamps
|
10
|
+
<% end -%>
|
11
|
+
end
|
12
|
+
|
13
|
+
<% type.attributes_with_index.each do |attribute| -%>
|
14
|
+
add_index :<%= type.model_name.collection %>, :<%= attribute.index_name %><%= attribute.inject_index_options %>
|
15
|
+
<% end -%>
|
16
|
+
<% end -%>
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
class CMS::<%= @name %> < ActiveRecord::Base
|
2
|
+
self.table_name = '<%= @name.collection %>'
|
3
|
+
<% @type.attributes.select {|attr| attr.reference? }.each do |attribute| -%>
|
4
|
+
belongs_to :<%= attribute.name %>, class_name: 'CMS::<%= attribute.reference_to %>'
|
5
|
+
<% end -%>
|
6
|
+
<% if @type.references.any? -%>
|
7
|
+
<% @type.references.each do |type| -%>
|
8
|
+
has_many :<%= type.model_name.collection %>, class_name: 'CMS::<%= type %>'
|
9
|
+
<% end -%>
|
10
|
+
<% end -%>
|
11
|
+
attr_accessible <%= @type.accessible_attributes.map {|a| ":#{a.field_name}" }.sort.join(', ') %>
|
12
|
+
<% if @type.orderable? -%>
|
13
|
+
include CMS::Orderable
|
14
|
+
orderable(:<%= @type.order_attribute.name %>)
|
15
|
+
<% end -%>
|
16
|
+
<% @type.file_attributes.each do |attribute| -%>
|
17
|
+
mount_uploader :<%= attribute.name %>, CMS::Uploader
|
18
|
+
<% end -%>
|
19
|
+
|
20
|
+
def self.name
|
21
|
+
'<%= @name %>'
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
%h2 Edit <%= @name.human.downcase %>
|
2
|
+
|
3
|
+
= form_for @<%= @name.element %>, url: cms_<%= @name.element %>_path(@<%= @name.element %>), html: {class: 'form-horizontal'} do |f|
|
4
|
+
<% @type.attributes.each do |attribute| -%>
|
5
|
+
.control-group
|
6
|
+
= f.label :<%= attribute.field_name %>, class: 'control-label'
|
7
|
+
<% if attribute.orderable? -%>
|
8
|
+
.controls
|
9
|
+
= f.<%= attribute.form_type %> :<%= attribute.name %>, 1.upto(CMS::<%= @name %>.count).to_a
|
10
|
+
<% elsif attribute.reference? -%>
|
11
|
+
.controls
|
12
|
+
= f.<%= attribute.form_type %> :<%= attribute.field_name %>, CMS::<%= attribute.reference_to %>.all.collect {|r| [ r.<%= attribute.options['reference_label'] %>, r.id ] }
|
13
|
+
<% else -%>
|
14
|
+
.controls
|
15
|
+
= f.<%= attribute.form_type %> :<%= attribute.name %>
|
16
|
+
<% end -%>
|
17
|
+
<% end -%>
|
18
|
+
.form-actions
|
19
|
+
= f.submit 'Save', class: 'btn btn-primary', :'data-disable-with' => 'Saving...'
|
@@ -0,0 +1,34 @@
|
|
1
|
+
%h2 <%= @name.human.pluralize %>
|
2
|
+
|
3
|
+
= link_to 'New <%= @name.human.downcase %>', new_cms_<%= @name.element %>_path
|
4
|
+
|
5
|
+
%table.table
|
6
|
+
%thead
|
7
|
+
%tr
|
8
|
+
%th Id
|
9
|
+
<% @type.attributes.each do |attribute| -%>
|
10
|
+
%th <%= attribute.name.humanize %>
|
11
|
+
<% end -%>
|
12
|
+
%th
|
13
|
+
|
14
|
+
%tbody
|
15
|
+
- @<%= @name.collection %>.each do |<%= @name.element %>|
|
16
|
+
%tr
|
17
|
+
%td= <%= @name.element %>.id
|
18
|
+
<% @type.attributes.each do |attribute| -%>
|
19
|
+
<% if attribute.reference? -%>
|
20
|
+
%td
|
21
|
+
- if <%= @name.element %>.<%= attribute.name %>.present?
|
22
|
+
= link_to <%= @name.element %>.<%= attribute.name %>.<%= attribute.options['reference_label'] %>, cms_<%= attribute.reference_to.model_name.element %>_path(<%= @name.element %>.<%= attribute.name %>)
|
23
|
+
<% elsif attribute.format.file? -%>
|
24
|
+
%td
|
25
|
+
%img{src: <%= @name.element %>.<%= attribute.name %>}
|
26
|
+
<% else -%>
|
27
|
+
%td= <%= @name.element %>.<%= attribute.name %>
|
28
|
+
<% end -%>
|
29
|
+
<% end -%>
|
30
|
+
%td
|
31
|
+
= link_to 'Edit', edit_cms_<%= @name.element %>_path(<%= @name.element %>)
|
32
|
+
= link_to 'Destroy', cms_<%= @name.element %>_path(<%= @name.element %>), confirm: 'are you sure?', method: 'delete'
|
33
|
+
|
34
|
+
= paginate @<%= @name.collection %>
|
@@ -0,0 +1,19 @@
|
|
1
|
+
%h2 New <%= @name.human.downcase %>
|
2
|
+
|
3
|
+
= form_for @<%= @name.element %>, url: cms_<%= @name.collection %>_path, html: {class: 'form-horizontal'} do |f|
|
4
|
+
<% @type.attributes.each do |attribute| -%>
|
5
|
+
.control-group
|
6
|
+
= f.label :<%= attribute.field_name %>, class: 'control-label'
|
7
|
+
<% if attribute.orderable? -%>
|
8
|
+
.controls
|
9
|
+
= f.<%= attribute.form_type %> :<%= attribute.name %>, 1.upto(CMS::<%= @name %>.count + 1).to_a
|
10
|
+
<% elsif attribute.reference? -%>
|
11
|
+
.controls
|
12
|
+
= f.<%= attribute.form_type %> :<%= attribute.field_name %>, CMS::<%= attribute.reference_to %>.all.collect {|r| [ r.<%= attribute.options['reference_label'] %>, r.id ] }
|
13
|
+
<% else -%>
|
14
|
+
.controls
|
15
|
+
= f.<%= attribute.form_type %> :<%= attribute.name %>
|
16
|
+
<% end -%>
|
17
|
+
<% end -%>
|
18
|
+
.form-actions
|
19
|
+
= f.submit 'Save', class: 'btn btn-primary', :'data-disable-with' => 'Saving...'
|
@@ -0,0 +1,13 @@
|
|
1
|
+
%h2== Showing <%= @name.human.downcase %> ##{@<%= @name.element %>.id}
|
2
|
+
|
3
|
+
%dl
|
4
|
+
<% @type.attributes.each do |attribute| -%>
|
5
|
+
%dt <%= attribute.name.humanize %>
|
6
|
+
<% if attribute.reference? -%>
|
7
|
+
%dd= @<%= @name.element %>.<%= attribute.name %>.try(:<%= attribute.options['reference_label'] %>)
|
8
|
+
<% elsif attribute.format.text? -%>
|
9
|
+
%dd= markdown @<%= @name.element %>.<%= attribute.name %>
|
10
|
+
<% else -%>
|
11
|
+
%dd= @<%= @name.element %>.<%= attribute.name %>
|
12
|
+
<% end -%>
|
13
|
+
<% end -%>
|
data/lib/honey-cms.rb
ADDED
metadata
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: honey-cms
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Quinn Shanahan
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-10-16 00:00:00.000000000 Z
|
13
|
+
dependencies: []
|
14
|
+
description: Some CMS functionality
|
15
|
+
email: quinn@tastehoneyco.com
|
16
|
+
executables: []
|
17
|
+
extensions: []
|
18
|
+
extra_rdoc_files: []
|
19
|
+
files:
|
20
|
+
- lib/cms/attribute.rb
|
21
|
+
- lib/cms/configuration.rb
|
22
|
+
- lib/cms/controller_stub.rb
|
23
|
+
- lib/cms/engine.rb
|
24
|
+
- lib/cms/form_builder.rb
|
25
|
+
- lib/cms/helper.rb
|
26
|
+
- lib/cms/orderable.rb
|
27
|
+
- lib/cms/routes.rb
|
28
|
+
- lib/cms/type.rb
|
29
|
+
- lib/cms/uploader.rb
|
30
|
+
- lib/cms/view_tags.rb
|
31
|
+
- lib/cms.rb
|
32
|
+
- lib/generators/cms/cms_generator.rb
|
33
|
+
- lib/generators/cms/templates/cms_base_controller.rb
|
34
|
+
- lib/generators/cms/templates/cms_model.rb
|
35
|
+
- lib/generators/cms/templates/migration.rb
|
36
|
+
- lib/generators/cms/templates/type_controller.rb
|
37
|
+
- lib/generators/cms/templates/type_model.rb
|
38
|
+
- lib/generators/cms/templates/views/edit.html.haml
|
39
|
+
- lib/generators/cms/templates/views/index.html.haml
|
40
|
+
- lib/generators/cms/templates/views/new.html.haml
|
41
|
+
- lib/generators/cms/templates/views/show.html.haml
|
42
|
+
- lib/generators/cms/USAGE
|
43
|
+
- lib/honey-cms.rb
|
44
|
+
homepage: https://github.com/honeyco/honey-cms
|
45
|
+
licenses: []
|
46
|
+
post_install_message:
|
47
|
+
rdoc_options: []
|
48
|
+
require_paths:
|
49
|
+
- lib
|
50
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
51
|
+
none: false
|
52
|
+
requirements:
|
53
|
+
- - ! '>='
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '0'
|
56
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
requirements: []
|
63
|
+
rubyforge_project:
|
64
|
+
rubygems_version: 1.8.11
|
65
|
+
signing_key:
|
66
|
+
specification_version: 3
|
67
|
+
summary: CMS
|
68
|
+
test_files: []
|