liquid_cms 0.2.0.13 → 0.2.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.
Files changed (55) hide show
  1. data/CHANGELOG.rdoc +12 -0
  2. data/app/controllers/cms/assets_controller.rb +32 -10
  3. data/app/controllers/cms/components_controller.rb +3 -3
  4. data/app/controllers/cms/main_controller.rb +2 -0
  5. data/app/controllers/cms/pages_controller.rb +2 -2
  6. data/app/helpers/cms/pages_helper.rb +0 -12
  7. data/app/liquid/cms_paperclip_extension.rb +1 -1
  8. data/app/liquid/drops/cms_asset_drop.rb +15 -0
  9. data/app/liquid/filters/cms_filters.rb +1 -1
  10. data/app/liquid/tags/asset_search_tag.rb +26 -0
  11. data/app/liquid/tags/cms/tag_common.rb +29 -0
  12. data/app/liquid/tags/data_tag.rb +59 -0
  13. data/app/models/cms/asset.rb +109 -2
  14. data/app/models/cms/component.rb +21 -17
  15. data/app/models/cms/page.rb +1 -3
  16. data/app/models/cms/tag.rb +21 -0
  17. data/app/models/cms/taggable.rb +78 -0
  18. data/app/models/cms/tagging.rb +6 -0
  19. data/app/views/cms/assets/_asset.html.erb +2 -3
  20. data/app/views/cms/assets/_form.html.erb +53 -3
  21. data/app/views/cms/assets/_list.html.erb +16 -1
  22. data/app/views/cms/assets/_meta_field.html.erb +15 -0
  23. data/app/views/cms/documentation/_cms_drops.html.erb +18 -0
  24. data/app/views/cms/documentation/_cms_tags.html.erb +13 -0
  25. data/app/views/cms/pages/_page.html.erb +1 -3
  26. data/app/views/cms/shared/_sidebar.html.erb +30 -29
  27. data/generators/liquid_cms/lib/insert_commands.rb +14 -0
  28. data/generators/liquid_cms/liquid_cms_generator.rb +5 -1
  29. data/generators/liquid_cms/templates/config/initializers/cms/liquid_cms.rb +8 -0
  30. data/generators/liquid_cms/templates/config/locales/cms/en.yml +5 -0
  31. data/generators/liquid_cms/templates/migration_rev1.rb +38 -0
  32. data/generators/liquid_cms/templates/public/cms/stylesheets/sidebar.css +25 -7
  33. data/generators/liquid_cms/templates/public/cms/stylesheets/simple_form.css +79 -4
  34. data/generators/liquid_cms/templates/public/cms/stylesheets/styles.css +0 -8
  35. data/generators/liquid_cms/templates/public/cms/stylesheets/themes/dark.css +3 -0
  36. data/lib/liquid_cms/configuration.rb +12 -0
  37. data/lib/liquid_cms/version.rb +1 -1
  38. data/rails/init.rb +0 -1
  39. data/test/functional/assets_controller_test.rb +64 -16
  40. data/test/functional/components_controller_test.rb +90 -1
  41. data/test/functional/main_controller_test.rb +21 -0
  42. data/test/integration/pages_test.rb +124 -0
  43. data/test/integration/pages_test_no_context.rb +57 -0
  44. data/test/rails_app/db/migrate/20110329201435_create_liquid_cms_upgrade_rev1.rb +38 -0
  45. data/test/test_helper.rb +2 -0
  46. data/test/test_helpers/asset_helpers.rb +6 -4
  47. data/test/test_helpers/component_helpers.rb +35 -0
  48. data/test/test_helpers/file_helpers.rb +11 -0
  49. data/test/unit/asset_test.rb +114 -8
  50. data/test/unit/component_test.rb +65 -2
  51. data/test/unit/helpers/cms/common_helper_test.rb +4 -0
  52. metadata +35 -7
  53. data/app/views/cms/assets/destroy.js.rjs +0 -2
  54. data/generators/liquid_cms/templates/config/initializers/cms/vestal_versions.rb +0 -9
  55. data/test/rails_app/config/initializers/cms/vestal_versions.rb +0 -9
data/CHANGELOG.rdoc CHANGED
@@ -1,3 +1,15 @@
1
+ == Master
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.2.0.13
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 = Cms::Asset.new
9
+ @asset = create_tagged_asset
10
10
  end
11
11
 
12
12
  def create
13
- @asset = @context.assets.build params[:cms_asset]
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
- success = if params[:file_content].present?
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
- format.js
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] = "Component file updated."
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 file."
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.new(@context).expand component_zip.path
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.'
@@ -19,5 +19,7 @@ protected
19
19
  @cms_pages = @context.pages.ordered.all(:conditions => {:layout_page_id => nil})
20
20
  @cms_assets = @context.assets.ordered
21
21
  @cms_components = @context.components
22
+
23
+ @context_asset_tags = Cms::Asset.tags_for_context(@context)
22
24
  end
23
25
  end
@@ -10,7 +10,7 @@ class Cms::PagesController < Cms::MainController
10
10
  end
11
11
 
12
12
  def create
13
- @page = @context.pages.build({:updated_by => current_user}.merge(params[:cms_page] || {}))
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({:updated_by => current_user}.merge(params[:cms_page] || {}))
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_remote cms_icon('delete.png', :title => 'Delete'), {:url => cms_page_path(page), :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
@@ -0,0 +1,15 @@
1
+ require 'cms_common_drop'
2
+
3
+ class Cms::AssetDrop < Cms::CommonDrop
4
+ def meta
5
+ {}.tap do |h|
6
+ (@record.meta_data || []).each do |mh|
7
+ h[mh[:name]] = mh[:value]
8
+ end
9
+ end
10
+ end
11
+
12
+ def image
13
+ @record.asset
14
+ end
15
+ end
@@ -88,7 +88,7 @@ module CmsFilters
88
88
 
89
89
  def component_url(path)
90
90
  context = @context.registers[:context]
91
- "/" + File.join(Cms::Component.base_path(context), Cms::Component.component_path(context, path))
91
+ "/" + Cms::Component.base_path(context).join(path).to_s
92
92
  end
93
93
  end
94
94
 
@@ -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.scoped(:order => random_func)
13
+ else
14
+ assets.scoped(:order => 'cms_assets.created_at ASC')
15
+ end
16
+
17
+ assets = assets.scoped(: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
@@ -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 = ActiveRecord::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>' }, #:custom => Proc.new { |instance| "#{instance.photo_width}x#{instance.photo_height}>" } },
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
  named_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
- protected
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