liquid_cms 0.3.0.10 → 0.3.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.rdoc +12 -0
- data/app/controllers/cms/assets_controller.rb +32 -10
- data/app/controllers/cms/components_controller.rb +3 -3
- data/app/controllers/cms/main_controller.rb +2 -0
- data/app/controllers/cms/pages_controller.rb +2 -2
- data/app/helpers/cms/pages_helper.rb +0 -12
- data/app/liquid/cms_paperclip_extension.rb +1 -1
- data/app/liquid/drops/cms_asset_drop.rb +15 -0
- data/app/liquid/filters/cms_filters.rb +1 -1
- data/app/liquid/tags/asset_search_tag.rb +26 -0
- data/app/liquid/tags/cms/tag_common.rb +29 -0
- data/app/liquid/tags/data_tag.rb +59 -0
- data/app/models/cms/asset.rb +109 -2
- data/app/models/cms/component.rb +21 -17
- data/app/models/cms/page.rb +1 -3
- data/app/models/cms/tag.rb +21 -0
- data/app/models/cms/taggable.rb +78 -0
- data/app/models/cms/tagging.rb +6 -0
- data/app/views/cms/assets/_asset.html.erb +2 -3
- data/app/views/cms/assets/_form.html.erb +53 -2
- data/app/views/cms/assets/_list.html.erb +16 -1
- data/app/views/cms/assets/_meta_field.html.erb +15 -0
- data/app/views/cms/documentation/_cms_drops.html.erb +18 -0
- data/app/views/cms/documentation/_cms_tags.html.erb +13 -0
- data/app/views/cms/pages/_page.html.erb +1 -3
- data/app/views/cms/shared/_sidebar.html.erb +30 -28
- data/config/initializers/cms/liquid_cms.rb +8 -0
- data/config/locales/cms/en.yml +5 -0
- data/generators/liquid_cms/templates/migration_rev1.rb +38 -0
- data/lib/generators/liquid_cms/install_generator.rb +7 -9
- data/lib/generators/liquid_cms/templates/migration_rev1.rb +38 -0
- data/lib/generators/liquid_cms/templates/public/cms/stylesheets/sidebar.css +25 -7
- data/lib/generators/liquid_cms/templates/public/cms/stylesheets/simple_form.css +79 -4
- data/lib/generators/liquid_cms/templates/public/cms/stylesheets/styles.css +0 -8
- data/lib/generators/liquid_cms/templates/public/cms/stylesheets/themes/dark.css +3 -0
- data/lib/liquid_cms/configuration.rb +12 -0
- data/lib/liquid_cms/version.rb +1 -1
- data/test/functional/assets_controller_test.rb +64 -16
- data/test/functional/components_controller_test.rb +90 -1
- data/test/functional/main_controller_test.rb +21 -0
- data/test/integration/pages_test.rb +123 -0
- data/test/integration/pages_test_no_context.rb +57 -0
- data/test/rails_app/db/migrate/20110329201435_create_liquid_cms_upgrade_rev1.rb +38 -0
- data/test/test_helper.rb +2 -0
- data/test/test_helpers/asset_helpers.rb +6 -4
- data/test/test_helpers/component_helpers.rb +37 -0
- data/test/test_helpers/file_helpers.rb +11 -0
- data/test/unit/asset_test.rb +114 -8
- data/test/unit/component_test.rb +64 -1
- data/test/unit/helpers/cms/common_helper_test.rb +4 -0
- metadata +37 -6
- data/app/views/cms/assets/destroy.js.rjs +0 -2
- data/test/rails_app/config/initializers/cms/vestal_versions.rb +0 -11
data/CHANGELOG.rdoc
CHANGED
@@ -1,3 +1,15 @@
|
|
1
|
+
== 0.3.1.0
|
2
|
+
|
3
|
+
* Enhancements
|
4
|
+
* Many improvements to assets including the addition of meta data fields, tags and the ability to specify custom image sizes.
|
5
|
+
* Assets can now be searched for using the liquid 'asset_data' tag. {% asset_data tag:'test' %}
|
6
|
+
See the built-in docs for more details on how to use "asset_data" and asset drops.
|
7
|
+
* Asset drops (accessible via the asset_data tag) give you access to the meta data and custom image information.
|
8
|
+
* limit and random ordering for collection retrieved from the asset_data tag.
|
9
|
+
|
10
|
+
* Fixes
|
11
|
+
* Optimize asset image file sizes with -strip
|
12
|
+
|
1
13
|
== 0.3.0.10
|
2
14
|
|
3
15
|
* Enhancements
|
@@ -6,11 +6,13 @@ class Cms::AssetsController < Cms::MainController
|
|
6
6
|
end
|
7
7
|
|
8
8
|
def new
|
9
|
-
@asset =
|
9
|
+
@asset = create_tagged_asset
|
10
10
|
end
|
11
11
|
|
12
12
|
def create
|
13
|
-
@asset = @context.assets.build
|
13
|
+
@asset = @context.assets.build
|
14
|
+
@asset.assign_ordered_attributes params[:cms_asset]
|
15
|
+
|
14
16
|
if @asset.save
|
15
17
|
flash[:notice] = t('assets.flash.created')
|
16
18
|
redirect_to cms_root_path
|
@@ -25,14 +27,9 @@ class Cms::AssetsController < Cms::MainController
|
|
25
27
|
|
26
28
|
def update
|
27
29
|
@asset = @context.assets.find params[:id]
|
30
|
+
@asset.assign_ordered_attributes params[:cms_asset]
|
28
31
|
|
29
|
-
|
30
|
-
@asset.write params[:file_content]
|
31
|
-
else
|
32
|
-
@asset.update_attributes params[:cms_asset]
|
33
|
-
end
|
34
|
-
|
35
|
-
if success
|
32
|
+
if @asset.save
|
36
33
|
flash[:notice] = t('assets.flash.updated')
|
37
34
|
redirect_to cms_root_path
|
38
35
|
else
|
@@ -48,7 +45,32 @@ class Cms::AssetsController < Cms::MainController
|
|
48
45
|
|
49
46
|
respond_to do |format|
|
50
47
|
format.html { redirect_to cms_root_path }
|
51
|
-
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
protected
|
52
|
+
# pre-populate an asset with tagged data and meta fields if a tag param is present
|
53
|
+
def create_tagged_asset
|
54
|
+
asset = Cms::Asset.new
|
55
|
+
curr_tag = (params[:tag] || '').strip
|
56
|
+
|
57
|
+
if curr_tag.present?
|
58
|
+
asset.tag_list = curr_tag
|
59
|
+
|
60
|
+
meta_asset = @context.assets.tagged_with(curr_tag).first :conditions => 'meta_data is not null'
|
61
|
+
if meta_asset
|
62
|
+
# remove meta values since we only want the key names
|
63
|
+
# new values will be provided for the new asset
|
64
|
+
asset.meta_data = meta_asset.meta_data.collect{|m| {:name => m[:name], :value => ''}}
|
65
|
+
|
66
|
+
# assign custom dims
|
67
|
+
asset.custom_width = meta_asset.custom_width
|
68
|
+
asset.custom_height = meta_asset.custom_height
|
69
|
+
end
|
70
|
+
|
71
|
+
asset
|
72
|
+
else
|
73
|
+
asset
|
52
74
|
end
|
53
75
|
end
|
54
76
|
end
|
@@ -17,10 +17,10 @@ class Cms::ComponentsController < Cms::MainController
|
|
17
17
|
@component = Cms::Component.new(@context, @path)
|
18
18
|
@component.write params[:file_content]
|
19
19
|
|
20
|
-
flash[:notice] = "
|
20
|
+
flash[:notice] = "The component file has been updated."
|
21
21
|
redirect_to cms_root_path
|
22
22
|
else
|
23
|
-
flash[:error] = "Not an editable
|
23
|
+
flash[:error] = "Not an editable component."
|
24
24
|
redirect_to :controller => 'cms/components', :action => 'edit', :url => @path
|
25
25
|
end
|
26
26
|
end
|
@@ -39,7 +39,7 @@ class Cms::ComponentsController < Cms::MainController
|
|
39
39
|
component_zip = params[:zip_file]
|
40
40
|
|
41
41
|
if component_zip.present? && File.extname(component_zip.original_filename) == '.zip'
|
42
|
-
Cms::Component.
|
42
|
+
Cms::Component.expand @context, component_zip.path
|
43
43
|
flash[:notice] = 'The component has been uploaded.'
|
44
44
|
else
|
45
45
|
flash[:error] = 'The component file must be a zip archive.'
|
@@ -10,7 +10,7 @@ class Cms::PagesController < Cms::MainController
|
|
10
10
|
end
|
11
11
|
|
12
12
|
def create
|
13
|
-
@page = @context.pages.build(
|
13
|
+
@page = @context.pages.build(params[:cms_page])
|
14
14
|
if @page.save
|
15
15
|
flash[:notice] = t('pages.flash.created')
|
16
16
|
redirect_to cms_root_path
|
@@ -25,7 +25,7 @@ class Cms::PagesController < Cms::MainController
|
|
25
25
|
|
26
26
|
def update
|
27
27
|
@page = @context.pages.find params[:id]
|
28
|
-
if @page.update_attributes(
|
28
|
+
if @page.update_attributes(params[:cms_page])
|
29
29
|
flash[:notice] = t('pages.flash.updated')
|
30
30
|
redirect_to edit_cms_page_path(@page)
|
31
31
|
else
|
@@ -3,18 +3,6 @@ module Cms::PagesHelper
|
|
3
3
|
page.new_record? ? @context.pages.layouts : @context.pages.layouts.reject{|pg| pg == page}
|
4
4
|
end
|
5
5
|
|
6
|
-
def delete_page_link(page)
|
7
|
-
options = {:method => :delete, :confirm => "Are you sure you want to delete the \"#{page}\" page?"}
|
8
|
-
|
9
|
-
# use a remote link if there are no children since if we remove the current page list item, all the children items get removed (in the UI) as well
|
10
|
-
# it's easier to just remove the item otherwise with a normal post and refresh the page
|
11
|
-
if page.content_pages.empty?
|
12
|
-
link_to cms_icon('delete.png', :title => 'Delete'), cms_page_path(page), {:remote => true, :indicator => dom_id(page, 'progress')}.merge(options)
|
13
|
-
else
|
14
|
-
link_to cms_icon('delete.png', :title => 'Delete'), cms_page_path(page), options
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
6
|
# find the # of term matches in each page and sorts the pages by the match count (highest to lowest)
|
19
7
|
# only shows the first SEARCH_LIMIT pages
|
20
8
|
def page_match_order(pages, term)
|
@@ -7,7 +7,7 @@ module Paperclip
|
|
7
7
|
all_styles = self.styles.keys + ['original']
|
8
8
|
all_styles.each do |style|
|
9
9
|
g = Paperclip::Geometry.from_file(self.path(style)) rescue nil
|
10
|
-
h[style] = {'width' => g.width.to_i, 'height' => g.height.to_i, 'url' => self.url(style)} unless g.nil?
|
10
|
+
h[style.to_s] = {'width' => g.width.to_i, 'height' => g.height.to_i, 'url' => self.url(style)} unless g.nil?
|
11
11
|
end
|
12
12
|
end
|
13
13
|
end
|
@@ -114,7 +114,7 @@ module CmsFilters
|
|
114
114
|
|
115
115
|
def component_url(path)
|
116
116
|
context = @context.registers[:context]
|
117
|
-
"/" +
|
117
|
+
"/" + Cms::Component.base_path(context).join(path).to_s
|
118
118
|
end
|
119
119
|
end
|
120
120
|
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# Syntax
|
2
|
+
# {% asset_data tag'test', as:'sale_vehicles' %}
|
3
|
+
|
4
|
+
class AssetDataTag < Cms::DataTag
|
5
|
+
def get_data
|
6
|
+
raise Liquid::ArgumentError.new("The required 'tag' parameter is missing.") if options[:tag].blank?
|
7
|
+
|
8
|
+
collection = uses_random do |random_func|
|
9
|
+
assets = context_object.assets.tagged_with(options[:tag])
|
10
|
+
|
11
|
+
assets = if options[:random] == true
|
12
|
+
assets.order(random_func)
|
13
|
+
else
|
14
|
+
assets.order('cms_assets.created_at ASC')
|
15
|
+
end
|
16
|
+
|
17
|
+
assets = assets.limit(options[:limit]) if options[:limit]
|
18
|
+
|
19
|
+
assets.all
|
20
|
+
end
|
21
|
+
|
22
|
+
yield 'assets', collection
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
Liquid::Template.register_tag('asset_data', AssetDataTag)
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Cms::TagCommon
|
2
|
+
extend ActiveSupport::Memoizable
|
3
|
+
|
4
|
+
HyphenatedTagAttributes = /([\w-]+)\s*\:\s*(#{Liquid::QuotedFragment})/
|
5
|
+
|
6
|
+
def parse_options(context, markup)
|
7
|
+
begin
|
8
|
+
options = HashWithIndifferentAccess.new
|
9
|
+
return options if markup.blank?
|
10
|
+
|
11
|
+
markup.scan(HyphenatedTagAttributes) do |key, value|
|
12
|
+
options[key.to_sym] = context[value]
|
13
|
+
end
|
14
|
+
|
15
|
+
options
|
16
|
+
rescue ArgumentError => e
|
17
|
+
raise SyntaxError.new("Syntax Error in 'tag options' - Valid syntax: name:value")
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def context_object(context)
|
22
|
+
context.registers[:context].object
|
23
|
+
end
|
24
|
+
memoize :context_object
|
25
|
+
|
26
|
+
def params(context)
|
27
|
+
context.registers[:controller].params.except(:controller, :action)
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
class Cms::DataTag < Liquid::Tag
|
2
|
+
module TagMethods
|
3
|
+
extend Cms::TagCommon
|
4
|
+
end
|
5
|
+
|
6
|
+
attr_reader :context
|
7
|
+
attr_reader :options
|
8
|
+
|
9
|
+
def initialize(tag_name, markup, tokens)
|
10
|
+
@markup = markup
|
11
|
+
super
|
12
|
+
end
|
13
|
+
|
14
|
+
def context_object
|
15
|
+
TagMethods.context_object(@context)
|
16
|
+
end
|
17
|
+
|
18
|
+
def params
|
19
|
+
TagMethods.params(@context)
|
20
|
+
end
|
21
|
+
|
22
|
+
def render(context)
|
23
|
+
@context = context
|
24
|
+
@options = TagMethods.parse_options(context, @markup)
|
25
|
+
|
26
|
+
get_data do |name, data|
|
27
|
+
context[@options[:as] || name] = data
|
28
|
+
end
|
29
|
+
''
|
30
|
+
end
|
31
|
+
|
32
|
+
def uses_random(&block)
|
33
|
+
collection = []
|
34
|
+
|
35
|
+
# random sql func supported by postgresql and sqlite (perhaps others)
|
36
|
+
random_func = "random()"
|
37
|
+
|
38
|
+
begin
|
39
|
+
collection = yield random_func
|
40
|
+
rescue ActiveRecord::StatementInvalid => e
|
41
|
+
if options[:random] == true
|
42
|
+
# the random function used was invalid, so we'll try an alternative syntax for mysql (perhaps others)
|
43
|
+
mysql_func = "rand()"
|
44
|
+
|
45
|
+
if random_func != mysql_func
|
46
|
+
random_func = mysql_func
|
47
|
+
else
|
48
|
+
# set random to false and just use the default order since the alt didn't work either
|
49
|
+
options[:random] = false
|
50
|
+
end
|
51
|
+
|
52
|
+
# retry the query
|
53
|
+
retry
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
collection
|
58
|
+
end
|
59
|
+
end
|
data/app/models/cms/asset.rb
CHANGED
@@ -1,22 +1,76 @@
|
|
1
1
|
module Cms
|
2
2
|
class Asset < ActiveRecord::Base
|
3
|
+
unloadable
|
4
|
+
|
3
5
|
set_table_name 'cms_assets'
|
4
6
|
|
7
|
+
include Cms::Taggable
|
8
|
+
define_tag_association :assets
|
9
|
+
|
10
|
+
def self.tags_for_context(context)
|
11
|
+
common_options = {:order => 'name ASC'}
|
12
|
+
context.object ? Cms::Tag.all({:include => :assets, :conditions => {'cms_assets.context_id' => context.object.id}}.merge(common_options)) : Cms::Tag.all(common_options)
|
13
|
+
end
|
14
|
+
|
15
|
+
class Meta
|
16
|
+
attr_reader :name, :value, :errors
|
17
|
+
|
18
|
+
def initialize(data)
|
19
|
+
@name, @value = data[:name], data[:value]
|
20
|
+
@errors = ActiveModel::Errors.new(self)
|
21
|
+
end
|
22
|
+
|
23
|
+
def valid?
|
24
|
+
validate
|
25
|
+
errors.empty?
|
26
|
+
end
|
27
|
+
|
28
|
+
def validate
|
29
|
+
errors.clear
|
30
|
+
if @name.blank?
|
31
|
+
errors.add :name, 'must be set'
|
32
|
+
else
|
33
|
+
errors.add :name, "is an invalid format" if (@name =~ /^[a-z]+[a-z0-9_]*$/).nil?
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
serialize :meta_data
|
39
|
+
|
5
40
|
has_attached_file :asset,
|
6
|
-
:styles => { :tiny => '50x50>', :thumb => '100x100>'
|
41
|
+
:styles => { :tiny => '50x50>', :thumb => '100x100>', :large => '200x200>', :custom => Proc.new{|instance| custom_dimensions(instance)} },
|
42
|
+
:convert_options => {:all => '-strip -quality 90'},
|
7
43
|
:path => ":rails_root/public/cms/assets/:id/:style/:filename",
|
8
44
|
:url => "/cms/assets/:id/:style/:filename"
|
9
45
|
|
10
46
|
validates_attachment_presence :asset
|
47
|
+
validate :meta_data_check
|
11
48
|
|
12
49
|
scope :ordered, :order => 'asset_file_name ASC'
|
13
50
|
|
51
|
+
after_save :reprocess_custom_dimensions
|
14
52
|
before_post_process :process_check
|
15
53
|
|
16
54
|
def to_s
|
17
55
|
asset_file_name
|
18
56
|
end
|
19
57
|
|
58
|
+
def assign_ordered_attributes(params)
|
59
|
+
# force the custom dimensions to be assigned first so that when the asset is assigned, the custom dims are present
|
60
|
+
# if the custom dims aren't set before the asset is assigned, the custom size won't be generated properly
|
61
|
+
# this can occur if the attribute hash is iterated with the asset coming before the dims
|
62
|
+
self.attributes = params.slice(:custom_height, :custom_width)
|
63
|
+
self.attributes = params
|
64
|
+
end
|
65
|
+
|
66
|
+
def self.context_tags(context)
|
67
|
+
if context
|
68
|
+
Tag.all :joins => 'inner join taggings on taggings.tag_id = tags.id inner join cms_assets on taggings.taggable_id = cms_assets.id', :conditions => {'cms_assets.context_id' => context.id}
|
69
|
+
else
|
70
|
+
Tag.all
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
20
74
|
def image?
|
21
75
|
!(asset_content_type =~ /^image.*/).blank?
|
22
76
|
end
|
@@ -30,6 +84,40 @@ module Cms
|
|
30
84
|
!(asset_content_type =~ /(javascript|css|xml|html)$/).blank?
|
31
85
|
end
|
32
86
|
|
87
|
+
def file_content
|
88
|
+
read
|
89
|
+
end
|
90
|
+
|
91
|
+
def file_content=(content)
|
92
|
+
write content
|
93
|
+
end
|
94
|
+
|
95
|
+
def meta
|
96
|
+
return @_meta if @_meta.present?
|
97
|
+
@_meta = (meta_data || []).collect{|m| Meta.new(m)}
|
98
|
+
end
|
99
|
+
|
100
|
+
def meta=(data)
|
101
|
+
# reset the cached meta collection
|
102
|
+
@_meta = nil
|
103
|
+
|
104
|
+
# data ex:
|
105
|
+
# {"new_1301457489798"=>{"name"=>"test1", "value"=>"test1"}, "new_1301457493800"=>{"name"=>"test2", "value"=>"test2"}}
|
106
|
+
# converted to:
|
107
|
+
# [{"name"=>"test1", "value"=>"test1"}, {"name"=>"test2", "value"=>"test2"}]
|
108
|
+
# strip spaces of name and value
|
109
|
+
temp_data = data.to_a.sort{|a,b| a.first <=> b.first}.collect{|a| h = a[1]; {:name => h[:name].strip, :value => h[:value].strip} }
|
110
|
+
# remove any elements that have both name and value blank
|
111
|
+
temp_data = temp_data.reject{|d| d[:name].blank? && d[:value].blank?}
|
112
|
+
|
113
|
+
self.meta_data = temp_data
|
114
|
+
end
|
115
|
+
|
116
|
+
def to_liquid
|
117
|
+
Cms::AssetDrop.new(self)
|
118
|
+
end
|
119
|
+
|
120
|
+
protected
|
33
121
|
def read
|
34
122
|
return '' if !editable?
|
35
123
|
asset.to_file(:original).read
|
@@ -46,9 +134,28 @@ module Cms
|
|
46
134
|
end
|
47
135
|
end
|
48
136
|
|
49
|
-
|
137
|
+
def meta_data_check
|
138
|
+
if !meta.find_all{|m| !m.valid?}.empty?
|
139
|
+
errors.add :meta_data, "is invalid"
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
def self.custom_dimensions(record)
|
144
|
+
if !record.custom_width.to_i.zero? && !record.custom_height.to_i.zero?
|
145
|
+
"#{record.custom_width}x#{record.custom_height}>"
|
146
|
+
else
|
147
|
+
''
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
50
151
|
def process_check
|
51
152
|
image? && !icon?
|
52
153
|
end
|
154
|
+
|
155
|
+
def reprocess_custom_dimensions
|
156
|
+
if custom_height_changed? || custom_width_changed?
|
157
|
+
asset.reprocess!
|
158
|
+
end
|
159
|
+
end
|
53
160
|
end
|
54
161
|
end
|