From ecfa466074e3d3b585682069f291eac915e9200c Mon Sep 17 00:00:00 2001 From: dinedine Date: Thu, 7 Oct 2010 02:45:41 +0200 Subject: [PATCH] add missing asset collections import module + add google analytics import + work on simplifying the theme asset logic --- app/controllers/admin/assets_controller.rb | 4 +- .../admin/content_types_controller.rb | 4 + .../admin/theme_assets_controller.rb | 13 +- app/helpers/admin/custom_fields_helper.rb | 6 +- app/models/snippet.rb | 2 +- app/models/theme_asset.rb | 205 ++++++++++++------ app/uploaders/asset_uploader.rb | 4 +- app/uploaders/font_uploader.rb | 13 ++ app/uploaders/theme_asset_uploader.rb | 46 ++-- app/views/admin/snippets/_snippet.html.haml | 11 - app/views/admin/theme_assets/_asset.html.haml | 34 ++- app/views/admin/theme_assets/index.html.haml | 34 +-- config/initializers/inflections.rb | 5 + config/locales/admin_ui_en.yml | 4 +- config/locales/admin_ui_fr.yml | 4 +- doc/TODO | 15 +- lib/locomotive/carrierwave.rb | 1 + lib/locomotive/carrierwave/patches.rb | 12 +- lib/locomotive/import.rb | 1 + lib/locomotive/import/asset_collections.rb | 51 +++++ lib/locomotive/import/assets.rb | 68 +++++- lib/locomotive/import/content_types.rb | 45 +++- lib/locomotive/import/job.rb | 6 +- lib/locomotive/import/pages.rb | 6 +- lib/locomotive/liquid/drops/theme_assets.rb | 2 +- .../liquid/tags/google_analytics.rb | 39 ++++ public/stylesheets/admin/application.css | 74 ++++++- public/stylesheets/admin/inline_editor.css | 2 + public/stylesheets/admin/layout.css | 1 + 29 files changed, 545 insertions(+), 167 deletions(-) create mode 100644 app/uploaders/font_uploader.rb create mode 100644 lib/locomotive/import/asset_collections.rb create mode 100644 lib/locomotive/liquid/tags/google_analytics.rb diff --git a/app/controllers/admin/assets_controller.rb b/app/controllers/admin/assets_controller.rb index 2a7ad567..35e371b1 100644 --- a/app/controllers/admin/assets_controller.rb +++ b/app/controllers/admin/assets_controller.rb @@ -22,8 +22,8 @@ module Admin end def set_collections_and_current_collection - @asset_collections = current_site.asset_collections - @asset_collection = @asset_collections.find(params[:collection_id]) + @asset_collections = current_site.asset_collections.not_internal.order_by([[:name, :asc]]) + @asset_collection = current_site.asset_collections.find(params[:collection_id]) end end diff --git a/app/controllers/admin/content_types_controller.rb b/app/controllers/admin/content_types_controller.rb index 09627739..1cc9582d 100644 --- a/app/controllers/admin/content_types_controller.rb +++ b/app/controllers/admin/content_types_controller.rb @@ -3,5 +3,9 @@ module Admin sections 'contents' + def destroy + destroy! { admin_pages_url } + end + end end diff --git a/app/controllers/admin/theme_assets_controller.rb b/app/controllers/admin/theme_assets_controller.rb index e4f79bd6..cbdbcd78 100644 --- a/app/controllers/admin/theme_assets_controller.rb +++ b/app/controllers/admin/theme_assets_controller.rb @@ -10,10 +10,11 @@ module Admin respond_to :json, :only => [:create, :update] def index - assets = current_site.theme_assets.all.to_a - @non_image_assets = assets.find_all { |a| a.stylesheet? || a.javascript? } - @image_assets = assets.find_all { |a| a.image? } - @flash_assets = assets.find_all { |a| a.movie? } + @assets = current_site.theme_assets.visible.all.order_by([[:slug, :asc]]).group_by { |a| a.folder.split('/').first.to_sym } + @js_and_css_assets = (@assets[:javascripts] || []) + (@assets[:stylesheets] || []) + # @non_image_assets = assets.find_all { |a| a.stylesheet? || a.javascript? } + # @image_assets = assets.find_all { |a| a.image? } + # @flash_assets = assets.find_all { |a| a.movie? } if request.xhr? render :action => 'images', :layout => false and return @@ -31,9 +32,7 @@ module Admin :status => 'success', :name => truncate(@theme_asset.slug, :length => 22), :slug => @theme_asset.slug, - :url => @theme_asset.source.url, - :vignette_url => @theme_asset.vignette_url, - :shortcut_url => @theme_asset.shortcut_url + :url => @theme_asset.source.url } end failure.json { render :json => { :status => 'error' } } diff --git a/app/helpers/admin/custom_fields_helper.rb b/app/helpers/admin/custom_fields_helper.rb index 4c541d76..2ae107f9 100644 --- a/app/helpers/admin/custom_fields_helper.rb +++ b/app/helpers/admin/custom_fields_helper.rb @@ -1,8 +1,8 @@ module Admin::CustomFieldsHelper - def options_for_field_kind(selected = nil) - options = %w{String Text Category Boolean Date File}.map do |kind| - [t("admin.custom_fields.kind.#{kind.downcase}"), kind] + def options_for_field_kind + options = %w{string text category boolean date file}.map do |kind| + [t("admin.custom_fields.kind.#{kind}"), kind] end end diff --git a/app/models/snippet.rb b/app/models/snippet.rb index 1da66f92..a37f092b 100644 --- a/app/models/snippet.rb +++ b/app/models/snippet.rb @@ -53,7 +53,7 @@ class Snippet self._change_snippet_inside_template(node.parent) if node.parent else if node.respond_to?(:nodelist) - node.nodelist.each do |child| + (node.nodelist || []).each do |child| self._change_snippet_inside_template(child) end end diff --git a/app/models/theme_asset.rb b/app/models/theme_asset.rb index e962b597..79c412a9 100644 --- a/app/models/theme_asset.rb +++ b/app/models/theme_asset.rb @@ -2,16 +2,15 @@ class ThemeAsset include Locomotive::Mongoid::Document - ## Extensions ## - include Models::Extensions::Asset::Vignette - ## fields ## - field :slug + field :local_path field :content_type field :width, :type => Integer field :height, :type => Integer field :size, :type => Integer - field :plain_text + # field :plain_text + field :folder, :default => nil + field :hidden, :type => Boolean, :default => false mount_uploader :source, ThemeAssetUploader ## associations ## @@ -19,26 +18,39 @@ class ThemeAsset ## indexes ## index :site_id - index [[:content_type, Mongo::ASCENDING], [:slug, Mongo::ASCENDING], [:site_id, Mongo::ASCENDING]] + index [[:site_id, Mongo::ASCENDING], [:local_path, Mongo::ASCENDING]] ## callbacks ## - before_validation :sanitize_slug before_validation :store_plain_text + before_validation :sanitize_folder + before_validation :build_local_path + # before_validation :sanitize_slug # before_validation :escape_shortcut_urls - before_save :set_slug + # before_save :set_slug ## validations ## - validate :extname_can_not_be_changed + # validate :extname_can_not_be_changed validates_presence_of :site, :source - validates_presence_of :slug, :if => Proc.new { |a| a.new_record? && a.performing_plain_text? } - validates_uniqueness_of :slug, :scope => [:site_id, :content_type] + # validate :plain_text_name_is_required_if_no_given_source + validates_presence_of :plain_text_name, :if => Proc.new { |a| a.performing_plain_text? } + # validates_presence_of :slug, :if => Proc.new { |a| a.new_record? && a.performing_plain_text? } + # validates_uniqueness_of :slug, :scope => [:site_id, :content_type, :folder] + validates_uniqueness_of :local_path, :scope => :site_id validates_integrity_of :source + ## named scopes ## + scope :visible, :where => { :hidden => false } + ## accessors ## - attr_accessor :performing_plain_text + attr_accessor :plain_text_name, :plain_text, :performing_plain_text ## methods ## + # def source=(new_file) + # @new_source = true + # super + # end + %w{movie image stylesheet javascript font}.each do |type| define_method("#{type}?") do self.content_type == type @@ -49,46 +61,69 @@ class ThemeAsset self.stylesheet? || self.javascript? end - def plain_text - if self.stylesheet_or_javascript? - self.plain_text = self.source.read if read_attribute(:plain_text).blank? - read_attribute(:plain_text) - else - nil + def plain_text_name + if not @plain_text_name_changed + @plain_text_name ||= self.safe_source_filename end + @plain_text_name.gsub(/(\.[a-z0-9A-Z]+)$/, '') rescue nil + # + # @plain_text_name ||= self.safe_source_filename rescue nil if + # + # if @plain_text_name_changed + # + # + # @plain_text_name_changed ? @plain_text_name : + # (@plain_text_name || self.safe_source_filename).gsub(/(\.[a-z0-9A-Z]+)$/, '') rescue nil + end + + def plain_text_name=(name) + @plain_text_name_changed = true + @plain_text_name = name + end + + def plain_text + if not @plain_text_changed + @plain_text ||= self.source.read rescue nil + end + @plain_text + # # self.plain_text = self.source.read if read_attribute(:plain_text).blank? + # # read_attribute(:plain_text) + # else + # nil + # end end def plain_text=(source) - self.performing_plain_text = true if self.performing_plain_text.nil? - write_attribute(:plain_text, source) + # @new_source = true + @plain_text_changed = true + self.performing_plain_text = true #if self.performing_plain_text.nil? + @plain_text = source + # write_attribute(:plain_text, source) end def performing_plain_text? - return true if !self.new_record? && self.stylesheet_or_javascript? && self.errors.empty? - - !(self.performing_plain_text.blank? || self.performing_plain_text == 'false' || self.performing_plain_text == false) + Boolean.set(self.performing_plain_text) + # self.performing_plain_text == true || self.performing_plain_text == '1' || self.performing_plain_text == + # return true if !self.new_record? && self.stylesheet_or_javascript? && self.errors.empty? + # !(self.performing_plain_text.blank? || self.performing_plain_text == 'false' || self.performing_plain_text == false) end def store_plain_text - return if !self.stylesheet_or_javascript? || self.plain_text.blank? + return if !self.stylesheet_or_javascript? || self.plain_text_name.blank? || self.plain_text.blank? sanitized_source = self.escape_shortcut_urls(self.plain_text) - if self.source.nil? - self.source = CarrierWave::SanitizedFile.new({ - :tempfile => StringIO.new(sanitized_source), - :filename => "#{self.slug}.#{self.stylesheet? ? 'css' : 'js'}" - }) - else - self.source.file.instance_variable_set(:@file, StringIO.new(sanitized_source)) - end + self.source = CarrierWave::SanitizedFile.new({ + :tempfile => StringIO.new(sanitized_source), + :filename => "#{self.plain_text_name}.#{self.stylesheet? ? 'css' : 'js'}" + }) end - def shortcut_url # ex: /stylesheets/application.css is a shortcut for a theme asset (content_type => stylesheet, slug => 'application') - File.join(self.content_type.pluralize, "#{self.slug}#{File.extname(self.source_filename)}") - rescue - '' - end + # def shortcut_url # ex: /stylesheets/application.css is a shortcut for a theme asset (content_type => stylesheet, slug => 'application') + # File.join(self.content_type.pluralize, "#{self.slug}#{File.extname(self.source_filename)}") + # rescue + # '' + # end def to_liquid { :url => self.source.url }.merge(self.attributes) @@ -96,41 +131,83 @@ class ThemeAsset protected - def escape_shortcut_urls(text) # replace // occurences by the real amazon S3 url or local files + # def new_source? + # @new_source == true + # end + + def safe_source_filename + self.source_filename || self.source.send(:original_filename) rescue nil + end + + def sanitize_folder + self.folder = self.content_type.pluralize if self.folder.blank? + + # no accents, no spaces, no leading and ending trails + self.folder = ActiveSupport::Inflector.transliterate(self.folder).gsub(/(\s)+/, '_').gsub(/^\//, '').gsub(/\/$/, '') + + # folder should begin by a root folder + if (self.folder =~ /^(stylesheets|javascripts|images|media|fonts)/).nil? + self.folder = File.join(self.content_type.pluralize, self.folder) + end + end + + def build_local_path + puts "self.source_filename = #{self.source_filename} / #{self.safe_source_filename} / #{File.join(self.folder, self.safe_source_filename)}" + self.local_path = File.join(self.folder, self.safe_source_filename) + end + + def escape_shortcut_urls(text) return if text.blank? - text.gsub(/(\/(stylesheets|javascripts|images)\/([a-z_\-0-9]+)\.[a-z]{2,3})/) do |url| - content_type, slug = url.split('/')[1..-1] + text.gsub(/[("'](\/(stylesheets|javascripts|images|media)\/((.+)\/)*([a-z_\-0-9]+)\.[a-z]{2,3})[)"']/) do |path| + sanitized_path = path.gsub(/[("')]/, '').gsub(/^\//, '') - content_type = content_type.singularize - slug = slug.split('.').first + # puts "\t\t\tfound path = #{sanitized_path}" - if asset = self.site.theme_assets.where(:content_type => content_type, :slug => slug).first - asset.source.url + if asset = self.site.theme_assets.where(:local_path => sanitized_path).first + "#{path.first}#{asset.source.url}#{path.last}" else - url + path end + + # content_type, slug = url.split('/')[1..-1] + + # content_type = content_type.singularize + # slug = slug.split('.').first + + # if asset = self.site.theme_assets.where(:content_type => content_type, :slug => slug).first + # asset.source.url + # else + # url + # end end end - def sanitize_slug - self.slug.parameterize! if self.slug.present? - end + # def plain_text_name_is_required_if_no_given_source + # puts "@performing_plain_text = #{self.performing_plain_text?} / #{@plain_text_name} / new_file = #{self.source.inspect}" + # if self.performing_plain_text? && @plain_text_name.blank? + # self.errors.add(:plain_text_name, :blank) + # end + # end - def set_slug - if self.slug.blank? - self.slug = File.basename(self.source_filename, File.extname(self.source_filename)) - self.sanitize_slug - end - end - - def extname_can_not_be_changed - return if self.new_record? || self.source.file.original_filename.nil? - - puts "filename => #{self.source_filename} / source file => #{self.source.file.inspect}" - - if File.extname(self.source.file.original_filename) != File.extname(self.source_filename) - self.errors.add(:source, :extname_changed) - end - end + # def sanitize_slug + # self.slug.parameterize! if self.slug.present? + # end + # + # def set_slug + # if self.slug.blank? + # self.slug = File.basename(self.source_filename, File.extname(self.source_filename)) + # self.sanitize_slug + # end + # end + # + # def extname_can_not_be_changed + # return if self.new_record? || self.source.file.original_filename.nil? + # + # puts "filename => #{self.source_filename} / source file => #{self.source.file.inspect}" + # + # if File.extname(self.source.file.original_filename) != File.extname(self.source_filename) + # self.errors.add(:source, :extname_changed) + # end + # end end diff --git a/app/uploaders/asset_uploader.rb b/app/uploaders/asset_uploader.rb index 41eaae9e..5be936b7 100644 --- a/app/uploaders/asset_uploader.rb +++ b/app/uploaders/asset_uploader.rb @@ -61,12 +61,12 @@ class AssetUploader < CarrierWave::Uploader::Base def self.content_types { :image => ['image/jpeg', 'image/pjpeg', 'image/gif', 'image/png', 'image/x-png', 'image/jpg'], - :movie => [/^video/, 'application/x-shockwave-flash', 'application/x-swf'], + :video => [/^video/, 'application/x-shockwave-flash', 'application/x-swf'], :audio => [/^audio/, 'application/ogg', 'application/x-mp3'], :pdf => ['application/pdf', 'application/x-pdf'], :stylesheet => ['text/css'], :javascript => ['text/javascript', 'text/js', 'application/x-javascript', 'application/javascript'], - :font => ['application/x-font-ttf', 'application/vnd.ms-fontobject'] + :font => ['application/x-font-ttf', 'application/vnd.ms-fontobject', 'image/svg+xml', 'application/x-woff'] } end diff --git a/app/uploaders/font_uploader.rb b/app/uploaders/font_uploader.rb new file mode 100644 index 00000000..40fdd4d2 --- /dev/null +++ b/app/uploaders/font_uploader.rb @@ -0,0 +1,13 @@ +# encoding: utf-8 + +class FontUploader < CarrierWave::Uploader::Base + + def store_dir + "sites/#{model.id}/theme/fonts" + end + + def cache_dir + "#{Rails.root}/tmp/uploads" + end + +end diff --git a/app/uploaders/theme_asset_uploader.rb b/app/uploaders/theme_asset_uploader.rb index 52198195..58c9b2f9 100644 --- a/app/uploaders/theme_asset_uploader.rb +++ b/app/uploaders/theme_asset_uploader.rb @@ -6,28 +6,40 @@ class ThemeAssetUploader < AssetUploader process :set_size process :set_width_and_height - version :thumb do - process :resize_to_fill => [50, 50] - process :convert => 'png' - end - - version :medium do - process :resize_to_fill => [80, 80] - process :convert => 'png' - end - - version :preview do - process :resize_to_fit => [880, 1100] - process :convert => 'png' - end + # version :thumb do + # process :resize_to_fill => [50, 50] + # process :convert => 'png' + # end + # + # version :medium do + # process :resize_to_fill => [80, 80] + # process :convert => 'png' + # end + # + # version :preview do + # process :resize_to_fit => [880, 1100] + # process :convert => 'png' + # end def store_dir - # "sites/#{model.site_id}/themes/#{model.id}" - "sites/#{model.site_id}/theme/#{model.content_type.pluralize}" + File.join('sites', model.site_id.to_s, 'theme', model.folder) + # File.join('sites', model.site_id.to_s, 'theme', model.content_type.pluralize, model.subfolder || '') + + # base = File.join('sites', model.site_id.to_s, 'theme') + # # puts "base = #{base} / #{model.subfolder.inspect}" + # if model.subfolder.blank? + # File.join(base, model.content_type.pluralize) + # else + # File.join(base, model.subfolder) + # end + + # + # # "sites/#{model.site_id}/themes/#{model.id}/#{model.content_type.pluralize}" + # File.join("sites/#{model.site_id}/theme", model.subfolder || '', model.content_type.pluralize) end def extension_white_list - %w(jpg jpeg gif png css js swf flv) + %w(jpg jpeg gif png css js swf flv eot svg ttf woff) end end diff --git a/app/views/admin/snippets/_snippet.html.haml b/app/views/admin/snippets/_snippet.html.haml index 7a389608..821fac37 100644 --- a/app/views/admin/snippets/_snippet.html.haml +++ b/app/views/admin/snippets/_snippet.html.haml @@ -1,14 +1,3 @@ -/ - per_row = local_assigns[:per_row] || 6 -/ - snippet_counter = local_assigns[:snippet_counter] || 0 -/ -/ %li{ :class => "asset #{'last' if (snippet_counter + 1) % per_row == 0}"} -/ %h4= link_to truncate(snippet.slug, :length => 18), edit_admin_snippet_path(snippet) -/ .image -/ .inside -/ / = vignette_tag(asset) -/ .actions -/ = link_to image_tag('admin/list/icons/cross.png'), admin_snippet_path(snippet), :class => 'remove', :confirm => t('admin.messages.confirm'), :method => :delete - %li %em %strong= link_to snippet.name, edit_admin_snippet_path(snippet) diff --git a/app/views/admin/theme_assets/_asset.html.haml b/app/views/admin/theme_assets/_asset.html.haml index 6d30d0a8..87b24fce 100644 --- a/app/views/admin/theme_assets/_asset.html.haml +++ b/app/views/admin/theme_assets/_asset.html.haml @@ -1,12 +1,24 @@ -- per_row = local_assigns[:per_row] || 6 -- asset_counter = local_assigns[:asset_counter] || 0 -- edit = local_assigns.key?(:edit) ? edit : true +/ - per_row = local_assigns[:per_row] || 6 +/ - asset_counter = local_assigns[:asset_counter] || 0 +/ - edit = local_assigns.key?(:edit) ? edit : true +/ +/ %li{ :class => "#{asset.new_record? ? 'new-asset' : 'asset'} #{'last' if (asset_counter + 1) % per_row == 0}"} +/ %h4= link_to truncate(asset.slug, :length => 18), edit ? edit_admin_theme_asset_path(asset) : asset.source.url, :"data-slug" => asset.slug, :"data-shortcut-url" => asset.shortcut_url +/ .image +/ .inside +/ = vignette_tag(asset) +/ - if edit +/ .actions +/ = link_to image_tag('admin/list/icons/cross.png'), admin_theme_asset_path(asset), :class => 'remove', :confirm => t('admin.messages.confirm'), :method => :delete -%li{ :class => "#{asset.new_record? ? 'new-asset' : 'asset'} #{'last' if (asset_counter + 1) % per_row == 0}"} - %h4= link_to truncate(asset.slug, :length => 18), edit ? edit_admin_theme_asset_path(asset) : asset.source.url, :"data-slug" => asset.slug, :"data-shortcut-url" => asset.shortcut_url - .image - .inside - = vignette_tag(asset) - - if edit - .actions - = link_to image_tag('admin/list/icons/cross.png'), admin_theme_asset_path(asset), :class => 'remove', :confirm => t('admin.messages.confirm'), :method => :delete \ No newline at end of file + +%li + %em + %strong= link_to asset.local_path, edit_admin_theme_asset_path(asset) + .more + %span.size= number_to_human_size(asset.size) + — + %span!= t('.updated_at') + = l asset.updated_at, :format => :short + + = link_to image_tag('admin/list/icons/trash.png'), admin_theme_asset_path(asset), :class => 'remove', :confirm => t('admin.messages.confirm'), :method => :delete diff --git a/app/views/admin/theme_assets/index.html.haml b/app/views/admin/theme_assets/index.html.haml index 6607930b..b0ad7309 100644 --- a/app/views/admin/theme_assets/index.html.haml +++ b/app/views/admin/theme_assets/index.html.haml @@ -13,34 +13,38 @@ - if @snippets.empty? %p.no-items!= t('.no_items', :url => new_admin_snippet_url) - else - %ul#snippets-list.list + %ul.list.theme-assets = render @snippets %br %h3!= t('.css_and_js') -- if @non_image_assets.empty? +- if @js_and_css_assets.empty? %p.no-items!= t('.no_items', :url => new_admin_theme_asset_url) - else - %ul.assets - = render :partial => 'asset', :collection => @non_image_assets - %li.clear + %ul.list.theme-assets + = render :partial => 'asset', :collection => @js_and_css_assets %br %h3!= t('.images') -- if @image_assets.empty? +- if @assets[:images].nil? %p.no-items!= t('.no_items', :url => new_admin_theme_asset_url) - else - %ul.assets - = render :partial => 'asset', :collection => @image_assets - %li.clear + %ul.list.theme-assets + = render :partial => 'asset', :collection => @assets[:images] - -- if not @flash_assets.empty? +- if @assets[:fonts] %br - %h3!= t('.flashes') - %ul.assets - = render :partial => 'asset', :collection => @flash_assets - %li.clear \ No newline at end of file + %h3!= t('.fonts') + %ul.list.theme-assets + = render :partial => 'asset', :collection => @assets[:fonts] + +- if @assets[:media] + %br + + %h3!= t('.media') + %ul.list.theme-assets + = render :partial => 'asset', :collection => @assets[:media] + diff --git a/config/initializers/inflections.rb b/config/initializers/inflections.rb index 9e8b0131..37e167a4 100644 --- a/config/initializers/inflections.rb +++ b/config/initializers/inflections.rb @@ -8,3 +8,8 @@ # inflect.irregular 'person', 'people' # inflect.uncountable %w( fish sheep ) # end + +ActiveSupport::Inflector.inflections do |inflect| + inflect.irregular 'media', 'media' +end + diff --git a/config/locales/admin_ui_en.yml b/config/locales/admin_ui_en.yml index db87dc88..6778d5ea 100644 --- a/config/locales/admin_ui_en.yml +++ b/config/locales/admin_ui_en.yml @@ -164,8 +164,10 @@ en: css_and_js: Style and javascript fonts: Fonts images: Images - flashes: Flash + media: Media no_items: "There are no files for now. Just click here to create the first one." + asset: + updated_at: Updated at new: title: New file help: "You have the choice to either upload any file or to copy/paste a stylesheet or a javascript in plain text." diff --git a/config/locales/admin_ui_fr.yml b/config/locales/admin_ui_fr.yml index e9fa9931..8d859898 100644 --- a/config/locales/admin_ui_fr.yml +++ b/config/locales/admin_ui_fr.yml @@ -163,9 +163,11 @@ fr: snippets: Snippets css_and_js: Style et javascript images: Images - flashes: Flash + media: Media fonts: Polices no_items: "Il n'existe pas de fichiers. Vous pouvez commencer par créer un ici." + asset: + updated_at: Mis à jour le new: title: Nouveau fichier help: "Vous avez le choix de soit uploader n'importe quel fichier ou bien soit de copier/coller du code css ou javascript." diff --git a/doc/TODO b/doc/TODO index ce01edf2..8cd74651 100644 --- a/doc/TODO +++ b/doc/TODO @@ -22,12 +22,23 @@ x create a repo for a tool "a la" vision x asset collections => liquid x images tag to write ! apply http://github.com/flori/json/commit/2c0f8d2c9b15a33b8d10ffcb1959aef54d320b57 -x import tool: +x snippet dependencies => do not work correctly +? google analytics tag +- import tool: x select field (see custom fields and nocoffee theme) ? x disable sub tasks by passing options x exceptions x page to import theme -x snippet dependencies => do not work correctly + x contents: group_by, oder_by, api_enabled + ? asset collections + ? fonts + ? folders for theme assets + ? theme assets whitelist + - add samples ? +x mask internal asset_collections +- refactor ui for the theme assets page +- proxy for fonts +- order yaml file (http://www.ruby-forum.com/topic/120295) - global regions: keyword in editable element (http://www.mongodb.org/display/DOCS/Updating) - write my first tutorial about locomotive diff --git a/lib/locomotive/carrierwave.rb b/lib/locomotive/carrierwave.rb index 9c8ab72a..04c4983d 100644 --- a/lib/locomotive/carrierwave.rb +++ b/lib/locomotive/carrierwave.rb @@ -4,3 +4,4 @@ require 'locomotive/carrierwave/patches' # register missing mime types EXTENSIONS[:eot] = 'application/vnd.ms-fontobject' +EXTENSIONS[:woff] = 'application/x-woff' diff --git a/lib/locomotive/carrierwave/patches.rb b/lib/locomotive/carrierwave/patches.rb index 7d852fe8..1df1cb2e 100644 --- a/lib/locomotive/carrierwave/patches.rb +++ b/lib/locomotive/carrierwave/patches.rb @@ -39,17 +39,21 @@ module CarrierWave module Mongoid def validates_integrity_of(*attrs) options = attrs.last.is_a?(Hash) ? attrs.last : {} - options[:message] ||= I18n.t('carrierwave.errors.integrity', :default => 'is not an allowed type of file.') validates_each(*attrs) do |record, attr, value| - record.errors.add attr, options[:message] if record.send("#{attr}_integrity_error") + if record.send("#{attr}_integrity_error") + message = options[:message] || I18n.t('carrierwave.errors.integrity', :default => 'is not an allowed type of file.') + record.errors.add attr, message + end end end def validates_processing_of(*attrs) options = attrs.last.is_a?(Hash) ? attrs.last : {} - options[:message] ||= I18n.t('carrierwave.errors.processing', :default => 'failed to be processed.') validates_each(*attrs) do |record, attr, value| - record.errors.add attr, options[:message] if record.send("#{attr}_processing_error") + if record.send("#{attr}_processing_error") + message = options[:message] || I18n.t('carrierwave.errors.processing', :default => 'failed to be processed.') + record.errors.add attr, message + end end end end diff --git a/lib/locomotive/import.rb b/lib/locomotive/import.rb index 19f1b3a5..82d6140b 100644 --- a/lib/locomotive/import.rb +++ b/lib/locomotive/import.rb @@ -1,6 +1,7 @@ require 'locomotive/import/job' require 'locomotive/import/site' require 'locomotive/import/assets' +require 'locomotive/import/asset_collections' require 'locomotive/import/content_types' require 'locomotive/import/snippets' require 'locomotive/import/pages' \ No newline at end of file diff --git a/lib/locomotive/import/asset_collections.rb b/lib/locomotive/import/asset_collections.rb new file mode 100644 index 00000000..7319f6ff --- /dev/null +++ b/lib/locomotive/import/asset_collections.rb @@ -0,0 +1,51 @@ +module Locomotive + module Import + module AssetCollections + + def self.process(context) + site, database = context[:site], context[:database] + + asset_collections = database['site']['asset_collections'] + + return if asset_collections.nil? + + asset_collections.each do |name, attributes| + puts "....asset_collection = #{attributes['slug']}" + + asset_collection = site.asset_collections.where(:slug => attributes['slug']).first + + asset_collection ||= self.build_asset_collection(site, attributes.merge(:name => name)) + + self.add_or_update_fields(asset_collection, attributes['fields']) + + asset_collection.save! + + site.reload + end + end + + def self.build_asset_collection(site, data) + attributes = { :internal => false }.merge(data) + + attributes.delete_if { |name, value| %w{fields assets}.include?(name) } + + site.asset_collections.build(attributes) + end + + def self.add_or_update_fields(asset_collection, fields) + fields.each do |name, data| + attributes = { :_alias => name, :label => name.humanize, :kind => 'string' }.merge(data).symbolize_keys + + field = asset_collection.asset_custom_fields.detect { |f| f._alias == attributes[:_alias] } + + field ||= asset_collection.asset_custom_fields.build(attributes) + + field.send(:set_unique_name!) if field.new_record? + + field.attributes = attributes + end + end + + end + end +end \ No newline at end of file diff --git a/lib/locomotive/import/assets.rb b/lib/locomotive/import/assets.rb index a65c4884..04c15fda 100644 --- a/lib/locomotive/import/assets.rb +++ b/lib/locomotive/import/assets.rb @@ -5,26 +5,45 @@ module Locomotive def self.process(context) site, theme_path = context[:site], context[:theme_path] - self.add_theme_assets(site, theme_path) + whitelist = self.build_regexps_in_withlist(context[:database]['site']['assets']['whitelist']) rescue nil + + puts "whitelist = #{whitelist.inspect}" + + self.add_theme_assets(site, theme_path, whitelist) + + # self.add_font_assets(site, theme_path) self.add_other_assets(site, theme_path) end - def self.add_theme_assets(site, theme_path) - %w(images stylesheets javascripts).each do |kind| - Dir[File.join(theme_path, 'public', kind, '*')].each do |asset_path| + def self.add_theme_assets(site, theme_path, whitelist) + %w(images media fonts javascripts stylesheets).each do |kind| + Dir[File.join(theme_path, 'public', kind, '**/*')].each do |asset_path| next if File.directory?(asset_path) - slug = File.basename(asset_path, File.extname(asset_path)).parameterize('_') + visible = self.check_against_whitelist(whitelist, asset_path.gsub(File.join(theme_path, 'public'), '')) - asset = site.theme_assets.where(:content_type => kind.singularize, :slug => slug).first - - asset ||= site.theme_assets.build + folder = asset_path.gsub(File.join(theme_path, 'public'), '').gsub(File.basename(asset_path), '').gsub(/^\//, '').gsub(/\/$/, '') + # folder = nil if folder.blank? || folder == kind - asset.attributes = { :source => File.open(asset_path), :performing_plain_text => false } + puts "folder = #{folder} / #{visible.inspect} / #{asset_path.gsub(File.join(theme_path, 'public'), '')} / local_path = #{File.join(folder, File.basename(asset_path))}" + + # slug = File.basename(asset_path, File.extname(asset_path)).parameterize('_') + + # asset = site.theme_assets.where(:content_type => kind.singularize, :folder => folder, :slug => slug).first + + asset = site.theme_assets.where(:local_path => File.join(folder, File.basename(asset_path))).first + + puts "found asset ! #{!asset.nil?} / #{File.join(folder, File.basename(asset_path))} / #{kind.singularize}" + + asset ||= site.theme_assets.build(:folder => folder) + + asset.attributes = { :source => File.open(asset_path), :performing_plain_text => false, :hidden => !visible } asset.save! + puts "--------------------" + site.reload # asset.reload # @@ -36,6 +55,16 @@ module Locomotive end end + # def self.add_font_assets(site, theme_path) + # uploader = FontUploader.new(site) + # + # Dir[File.join(theme_path, 'public', 'fonts', '*')].each do |asset_path| + # next if File.directory?(asset_path) + # puts "font file = #{asset_path}" + # uploader.store!(File.open(asset_path)) + # end + # end + def self.add_other_assets(site, theme_path) collection = AssetCollection.find_or_create_internal(site) @@ -49,6 +78,27 @@ module Locomotive end end + def self.build_regexps_in_withlist(rules) + rules.collect do |rule| + if rule.start_with?('^') + Regexp.new(rule.gsub('/', '\/')) + else + rule + end + end + end + + def self.check_against_whitelist(whitelist, path) + (whitelist || []).each do |rule| + case rule + when Regexp + return true if path =~ rule + when String + return true if path == rule + end + end + false + end end end end \ No newline at end of file diff --git a/lib/locomotive/import/content_types.rb b/lib/locomotive/import/content_types.rb index 5fd84621..8ebccc33 100644 --- a/lib/locomotive/import/content_types.rb +++ b/lib/locomotive/import/content_types.rb @@ -10,20 +10,28 @@ module Locomotive return if content_types.nil? content_types.each do |name, attributes| + puts "....content_type = #{attributes['slug']}" + content_type = site.content_types.where(:slug => attributes['slug']).first - content_type ||= self.create_content_type(site, attributes.merge(:name => name)) + content_type ||= self.build_content_type(site, attributes.merge(:name => name)) self.add_or_update_fields(content_type, attributes['fields']) + self.set_highlighted_field_name(content_type) + + self.set_order_by_value(content_type) + + self.set_group_by_value(content_type) + content_type.save! site.reload end end - def self.create_content_type(site, data) - attributes = { :order_by => 'manually' }.merge(data) + def self.build_content_type(site, data) + attributes = { :order_by => '_position_in_list', :group_by_field_name => data.delete('group_by') }.merge(data) attributes.delete_if { |name, value| %w{fields contents}.include?(name) } @@ -32,16 +40,43 @@ module Locomotive def self.add_or_update_fields(content_type, fields) fields.each do |name, data| - attributes = { :label => name.humanize, :_name => name, :kind => 'String' }.merge(data).symbolize_keys + attributes = { :_alias => name, :label => name.humanize, :kind => 'string' }.merge(data).symbolize_keys - field = content_type.content_custom_fields.detect { |f| f._name == attributes[:_name] } + field = content_type.content_custom_fields.detect { |f| f._alias == attributes[:_alias] } field ||= content_type.content_custom_fields.build(attributes) + field.send(:set_unique_name!) if field.new_record? + field.attributes = attributes end end + def self.set_highlighted_field_name(content_type) + field = content_type.content_custom_fields.detect { |f| f._alias == content_type.highlighted_field_name } + + content_type.highlighted_field_name = field._name if field + end + + def self.set_order_by_value(content_type) + order_by = (case content_type.order_by + when 'manually', '_position_in_list' then '_position_in_list' + when 'date', 'updated_at' then 'updated_at' + else + content_type.content_custom_fields.detect { |f| f._alias == content_type.order_by }._name rescue nil + end) + + content_type.order_by = order_by || '_position_in_list' + end + + def self.set_group_by_value(content_type) + return if content_type.group_by_field_name.blank? + + field = content_type.content_custom_fields.detect { |f| f._alias == content_type.group_by_field_name } + + content_type.group_by_field_name = field._name if field + end + end end end \ No newline at end of file diff --git a/lib/locomotive/import/job.rb b/lib/locomotive/import/job.rb index 0d24f384..d7344b46 100644 --- a/lib/locomotive/import/job.rb +++ b/lib/locomotive/import/job.rb @@ -33,10 +33,10 @@ module Locomotive :worker => @worker } - %w(site content_types assets snippets pages).each do |step| + %w(site content_types assets asset_collections snippets pages).each do |step| if @enabled[step] != false - @worker.update_attributes :step => step - puts "@worker...#{@worker.failed_at.inspect} / #{@worker.failed?.inspect}" + @worker.update_attributes :step => step if @worker + # puts "@worker...#{@worker.failed_at.inspect} / #{@worker.failed?.inspect}" "Locomotive::Import::#{step.camelize}".constantize.process(context) else puts "skipping #{step}" diff --git a/lib/locomotive/import/pages.rb b/lib/locomotive/import/pages.rb index 7829fba8..e7fe592b 100644 --- a/lib/locomotive/import/pages.rb +++ b/lib/locomotive/import/pages.rb @@ -30,7 +30,7 @@ module Locomotive site, pages, theme_path = context[:site], context[:database]['site']['pages'], context[:theme_path] - template = File.read(File.join(theme_path, 'templates', "#{fullpath}.liquid")) + template = File.read(File.join(theme_path, 'templates', "#{fullpath}.liquid")) rescue "Unable to find #{fullpath}.liquid" self.build_parent_template(template, context) @@ -95,9 +95,9 @@ module Locomotive return site.pages.index.first if segments.size == 1 - (segments.last == 'index' ? 2 : 1).times { segments.pop } + segments.pop - parent_fullpath = File.join(segments.join('/'), 'index').gsub(/^\//, '') + parent_fullpath = segments.join('/').gsub(/^\//, '') # look for a local index page in db parent = site.pages.where(:fullpath => parent_fullpath).first diff --git a/lib/locomotive/liquid/drops/theme_assets.rb b/lib/locomotive/liquid/drops/theme_assets.rb index a69efa49..b69c173b 100644 --- a/lib/locomotive/liquid/drops/theme_assets.rb +++ b/lib/locomotive/liquid/drops/theme_assets.rb @@ -9,7 +9,7 @@ module Locomotive content_type = self.class.name.demodulize.underscore.singularize asset = ThemeAsset.new(:site => @context.registers[:site], :content_type => content_type) - ThemeAssetUploader.new(asset).store_path(meth.gsub('__', '.')) + '/' + ThemeAssetUploader.new(asset).store_path(meth.gsub('__', '.')) end end diff --git a/lib/locomotive/liquid/tags/google_analytics.rb b/lib/locomotive/liquid/tags/google_analytics.rb new file mode 100644 index 00000000..fd89d24f --- /dev/null +++ b/lib/locomotive/liquid/tags/google_analytics.rb @@ -0,0 +1,39 @@ +module Liquid + module Locomotive + module Tags + class GoogleAnalytics < ::Liquid::Tag + + Syntax = /(#{::Liquid::Expression}+)?/ + + def initialize(tag_name, markup, tokens, context) + if markup =~ Syntax + @account_id = $1.gsub('\'', '') + else + raise ::Liquid::SyntaxError.new("Syntax Error in 'google_analytics' - Valid syntax: google_analytics ") + end + + super + end + + def render(context) + %{ + } + end + end + + ::Liquid::Template.register_tag('google_analytics', GoogleAnalytics) + end + end +end diff --git a/public/stylesheets/admin/application.css b/public/stylesheets/admin/application.css index 45229900..03dd4128 100644 --- a/public/stylesheets/admin/application.css +++ b/public/stylesheets/admin/application.css @@ -53,7 +53,6 @@ ul.list li { margin-bottom: 10px; position: relative; clear: both; - background: transparent url(/images/admin/list/item.png) no-repeat 0 0; } ul.list li em { @@ -64,6 +63,13 @@ ul.list li em { width: 18px; } +ul.list li strong { + margin-left: 18px; + display: block; + height: 31px; + background: transparent url(/images/admin/list/item-right.png) no-repeat right 0; +} + ul.list li strong a { position: relative; top: 2px; @@ -71,6 +77,7 @@ ul.list li strong a { text-decoration: none; color: #1f82bc; font-size: 0.9em; + text-shadow: 1px 1px 1px #fff; } ul.list.sortable li strong a { left: 10px; } @@ -98,12 +105,68 @@ ul.list li span.handle { cursor: move; } + +/*ul.list li { + height: 31px; + margin-bottom: 10px; + position: relative; + clear: both; + background: transparent url(/images/admin/list/item.png) no-repeat 0 0; +} + +ul.list li em { + display: block; + float: left; + background: transparent url(/images/admin/list/item-left.png) no-repeat left 0; + height: 31px; + width: 18px; +} + +ul.list li strong a { + position: relative; + top: 2px; + left: 15px; + text-decoration: none; + color: #1f82bc; + font-size: 0.9em; + text-shadow: 1px 1px 1px #fff; +} + +ul.list.sortable li strong a { left: 10px; } + +ul.list li strong a:hover { text-decoration: underline; } + +ul.list li div.more { + position: absolute; + top: 3px; + right: 15px; + font-size: 0.7em; + color: #8b8d9a; +} + +ul.list li div.more a { + margin-left: 10px; + position: relative; + top: 4px; +} + +ul.list li span.handle { + position: relative; + top: 5px; + margin: 0 0 0 15px; + cursor: move; +} +*/ /* ___ asset collections ___ */ div#asset-uploader { display: inline-block; margin-left: 10px; } div#asset-uploader span.spinner { position: relative; top: -3px; display: none; } div#uploadAssetsInputQueue { display: none; } +/* ___ theme assets ___ */ + +ul.theme-assets { margin-left: 40px; } + /* ___ contents ___ */ #contents-list li { background: none; } @@ -122,7 +185,7 @@ div#uploadAssetsInputQueue { display: none; } /* ___ snippets ___ */ -#snippets-list { margin-left: 40px; } +/*#snippets-list { margin-left: 40px; } #snippets-list li { background: none; } @@ -136,7 +199,7 @@ div#uploadAssetsInputQueue { display: none; } height: 31px; background: transparent url(/images/admin/list/item-right.png) no-repeat right 0; } - +*/ /* ___ pages ___ */ #pages-list { @@ -192,6 +255,7 @@ div#uploadAssetsInputQueue { display: none; } color: #1f82bc; font-size: 0.9em; padding-left: 6px; + text-shadow: 1px 1px 1px #fff; } #pages-list li strong a:hover { text-decoration: underline; } @@ -225,7 +289,7 @@ div#uploadAssetsInputQueue { display: none; } #import-steps { margin: 0px 200px; } -#import-steps li { background: none; } +/*#import-steps li { background: none; } #import-steps li em { background-position: left 0px; @@ -236,7 +300,7 @@ div#uploadAssetsInputQueue { display: none; } display: block; height: 31px; background: transparent url(/images/admin/list/item-right.png) no-repeat right 0; -} +}*/ #import-steps li strong a { color: #b7baca; } diff --git a/public/stylesheets/admin/inline_editor.css b/public/stylesheets/admin/inline_editor.css index ecb10d2e..5c611b93 100644 --- a/public/stylesheets/admin/inline_editor.css +++ b/public/stylesheets/admin/inline_editor.css @@ -7,6 +7,7 @@ padding: 0 10px; font-family: Helvetica; -webkit-box-shadow: -3px 3px 12px #818181; + z-index: 999; } #page-toolbar ul { @@ -26,6 +27,7 @@ padding-left: 24px; text-decoration: none; color: #fff; + outline: none; } #page-toolbar ul li.link a:hover span { text-decoration: underline; } diff --git a/public/stylesheets/admin/layout.css b/public/stylesheets/admin/layout.css index 51464f83..f667603e 100644 --- a/public/stylesheets/admin/layout.css +++ b/public/stylesheets/admin/layout.css @@ -97,6 +97,7 @@ body { font-weight: bold; color: #1e1f26; padding: 7px 0 10px 20px; + text-shadow: 1px 1px 1px #fff; } #content div.inner p {