add missing asset collections import module + add google analytics import + work on simplifying the theme asset logic

This commit is contained in:
dinedine 2010-10-07 02:45:41 +02:00
parent dd0a09ec4d
commit ecfa466074
29 changed files with 545 additions and 167 deletions

View File

@ -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

View File

@ -3,5 +3,9 @@ module Admin
sections 'contents'
def destroy
destroy! { admin_pages_url }
end
end
end

View File

@ -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' } }

View File

@ -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

View File

@ -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

View File

@ -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 /<content_type>/<slug> 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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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
%li
%em
%strong= link_to asset.local_path, edit_admin_theme_asset_path(asset)
.more
%span.size= number_to_human_size(asset.size)
&mdash;
%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

View File

@ -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
%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]

View File

@ -8,3 +8,8 @@
# inflect.irregular 'person', 'people'
# inflect.uncountable %w( fish sheep )
# end
ActiveSupport::Inflector.inflections do |inflect|
inflect.irregular 'media', 'media'
end

View File

@ -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 <a href=\"%{url}\">here</a> 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."

View File

@ -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 <a href='%{url}'>ici</a>."
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."

View File

@ -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

View File

@ -4,3 +4,4 @@ require 'locomotive/carrierwave/patches'
# register missing mime types
EXTENSIONS[:eot] = 'application/vnd.ms-fontobject'
EXTENSIONS[:woff] = 'application/x-woff'

View File

@ -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

View File

@ -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'

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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}"

View File

@ -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

View File

@ -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

View File

@ -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 <account_id>")
end
super
end
def render(context)
%{
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', '#{@account_id}']);
_gaq.push(['_trackPageview']);
(function() \{
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
\})();
</script>}
end
end
::Liquid::Template.register_tag('google_analytics', GoogleAnalytics)
end
end
end

View File

@ -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; }

View File

@ -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; }

View File

@ -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 {