merging
This commit is contained in:
commit
282fbb7244
4
.gitignore
vendored
4
.gitignore
vendored
@ -25,5 +25,9 @@ tmp/*
|
||||
Capfile
|
||||
config/deploy.rb
|
||||
perf/test.rb
|
||||
<<<<<<< HEAD
|
||||
gem_graph.png
|
||||
sites/
|
||||
=======
|
||||
sites
|
||||
>>>>>>> theme
|
||||
|
50
Gemfile
50
Gemfile
@ -1,28 +1,34 @@
|
||||
# Edit this Gemfile to bundle your application's dependencies.
|
||||
source :rubygems
|
||||
sou rce :rubygems
|
||||
|
||||
# add in all the runtime dependencies
|
||||
|
||||
gem "rails", ">= 3.0.0"
|
||||
gem "locomotive_liquid", ">= 2.1.3"
|
||||
gem "bson_ext", ">= 1.0.8"
|
||||
gem "mongoid", :git => 'http://github.com/mongoid/mongoid.git'
|
||||
gem "mongoid_acts_as_tree", "= 0.1.5"
|
||||
gem "warden"
|
||||
gem "devise", "= 1.1.2"
|
||||
gem "haml", "= 3.0.18"
|
||||
gem "rmagick", "= 2.12.2"
|
||||
gem "aws"
|
||||
gem "mimetype-fu"
|
||||
gem "formtastic", ">= 1.1.0"
|
||||
gem "carrierwave", "0.5.0.beta2"
|
||||
gem "actionmailer-with-request"
|
||||
gem "heroku"
|
||||
gem "httparty", ">= 0.6.1"
|
||||
gem "RedCloth"
|
||||
gem "inherited_resources", ">= 1.1.2"
|
||||
gem "custom_fields", :git => "http://github.com/locomotivecms/custom_fields.git"
|
||||
gem 'rails', '>= 3.0.0'
|
||||
|
||||
gem 'warden'
|
||||
gem 'devise', '= 1.1.3'
|
||||
|
||||
gem 'mongoid', '2.0.0.beta.19'
|
||||
gem 'bson_ext', '1.1'
|
||||
gem 'locomotive_mongoid_acts_as_tree', '0.1.5.1', :require => 'mongoid_acts_as_tree'
|
||||
|
||||
gem 'haml', '= 3.0.18'
|
||||
gem 'locomotive_liquid', '2.2.2', :require => 'liquid'
|
||||
gem 'formtastic', '>= 1.1.0'
|
||||
gem 'inherited_resources', '>= 1.1.2'
|
||||
|
||||
gem 'rmagick', '= 2.12.2'
|
||||
gem 'locomotive_carrierwave', :require => 'carrierwave'
|
||||
|
||||
gem 'custom_fields', '1.0.0.beta'
|
||||
gem 'aws'
|
||||
gem 'mimetype-fu'
|
||||
gem 'actionmailer-with-request'
|
||||
gem 'heroku'
|
||||
gem 'httparty', '>= 0.6.1'
|
||||
gem 'RedCloth'
|
||||
gem 'delayed_job', '2.1.0.pre2'
|
||||
gem 'delayed_job_mongoid', '1.0.0.rc'
|
||||
gem 'rubyzip'
|
||||
|
||||
# The rest of the dependencies are for use when in the locomotive dev environment
|
||||
|
||||
@ -46,7 +52,7 @@ group :test do
|
||||
gem 'capybara'
|
||||
|
||||
gem 'database_cleaner'
|
||||
gem 'cucumber', "0.8.5"
|
||||
gem 'cucumber', '0.8.5'
|
||||
gem 'cucumber-rails'
|
||||
gem 'spork'
|
||||
gem 'launchy'
|
||||
|
104
Gemfile.lock
104
Gemfile.lock
@ -15,25 +15,6 @@ GIT
|
||||
rspec (>= 1.3)
|
||||
yard
|
||||
|
||||
GIT
|
||||
remote: http://github.com/locomotivecms/custom_fields.git
|
||||
revision: eba381b6196c80f2bf8807741297218402df429e
|
||||
specs:
|
||||
custom_fields (0.0.0.3)
|
||||
activesupport (>= 3.0.0)
|
||||
carrierwave
|
||||
mongoid (>= 2.0.0.beta.18)
|
||||
|
||||
GIT
|
||||
remote: http://github.com/mongoid/mongoid.git
|
||||
revision: bc177989f4c4020ec7b16c8ce7cbdea51eba4034
|
||||
specs:
|
||||
mongoid (2.0.0.beta.18)
|
||||
activemodel (~> 3.0)
|
||||
mongo (= 1.0.9)
|
||||
tzinfo (~> 0.3.22)
|
||||
will_paginate (~> 3.0.pre)
|
||||
|
||||
GEM
|
||||
remote: http://rubygems.org/
|
||||
specs:
|
||||
@ -68,14 +49,14 @@ GEM
|
||||
activesupport (3.0.0)
|
||||
arel (1.0.1)
|
||||
activesupport (~> 3.0.0)
|
||||
autotest (4.3.2)
|
||||
autotest (4.4.1)
|
||||
aws (2.3.21)
|
||||
http_connection
|
||||
uuidtools
|
||||
xml-simple
|
||||
bcrypt-ruby (2.1.2)
|
||||
bson (1.0.9)
|
||||
bson_ext (1.0.9)
|
||||
bson (1.1)
|
||||
bson_ext (1.1)
|
||||
builder (2.1.2)
|
||||
capybara (0.3.9)
|
||||
culerity (>= 0.2.4)
|
||||
@ -84,9 +65,11 @@ GEM
|
||||
rack (>= 1.0.0)
|
||||
rack-test (>= 0.5.4)
|
||||
selenium-webdriver (>= 0.0.3)
|
||||
carrierwave (0.5.0.beta2)
|
||||
activesupport (>= 3.0.0.beta4)
|
||||
carrierwave (0.5.0)
|
||||
activesupport (~> 3.0.0)
|
||||
cgi_multipart_eof_fix (2.5.0)
|
||||
childprocess (0.0.7)
|
||||
ffi (~> 0.6.3)
|
||||
columnize (0.3.1)
|
||||
configuration (1.1.0)
|
||||
crack (0.1.8)
|
||||
@ -99,8 +82,18 @@ GEM
|
||||
cucumber-rails (0.3.2)
|
||||
cucumber (>= 0.8.0)
|
||||
culerity (0.2.12)
|
||||
custom_fields (1.0.0.beta)
|
||||
activesupport (>= 3.0.0)
|
||||
carrierwave
|
||||
mongoid (>= 2.0.0.beta.18)
|
||||
daemons (1.1.0)
|
||||
database_cleaner (0.5.2)
|
||||
delayed_job (2.1.0.pre2)
|
||||
activesupport (~> 3.0)
|
||||
daemons
|
||||
delayed_job_mongoid (1.0.0.rc)
|
||||
delayed_job (~> 2.1)
|
||||
mongoid (~> 2.0)
|
||||
devise (1.1.2)
|
||||
bcrypt-ruby (~> 2.1.2)
|
||||
warden (~> 0.10.7)
|
||||
@ -124,7 +117,7 @@ GEM
|
||||
growl-glue (1.0.7)
|
||||
haml (3.0.18)
|
||||
has_scope (0.5.0)
|
||||
heroku (1.10.8)
|
||||
heroku (1.10.14)
|
||||
json_pure (>= 1.2.0, < 1.5.0)
|
||||
launchy (~> 0.3.2)
|
||||
rest-client (>= 1.4.0, < 1.7.0)
|
||||
@ -140,8 +133,13 @@ GEM
|
||||
configuration (>= 0.0.5)
|
||||
rake (>= 0.8.1)
|
||||
linecache (0.43)
|
||||
locomotive_liquid (2.1.3)
|
||||
mail (2.2.6.1)
|
||||
locomotive_carrierwave (0.5.0.1)
|
||||
activesupport (~> 3.0)
|
||||
locomotive_liquid (2.2.2)
|
||||
locomotive_mongoid_acts_as_tree (0.1.5.1)
|
||||
bson (>= 0.20.1)
|
||||
mongoid (<= 2.0.0.beta.19)
|
||||
mail (2.2.7)
|
||||
activesupport (>= 2.3.6)
|
||||
mime-types
|
||||
treetop (>= 1.4.5)
|
||||
@ -149,9 +147,11 @@ GEM
|
||||
mimetype-fu (0.1.2)
|
||||
mongo (1.0.9)
|
||||
bson (>= 1.0.5)
|
||||
mongoid_acts_as_tree (0.1.5)
|
||||
bson (>= 0.20.1)
|
||||
mongoid (<= 2.0.0)
|
||||
mongoid (2.0.0.beta.19)
|
||||
activemodel (~> 3.0)
|
||||
mongo (= 1.0.9)
|
||||
tzinfo (~> 0.3.22)
|
||||
will_paginate (~> 3.0.pre)
|
||||
mongrel (1.1.5)
|
||||
cgi_multipart_eof_fix (>= 2.4)
|
||||
daemons (>= 1.0.3)
|
||||
@ -182,31 +182,32 @@ GEM
|
||||
rest-client (1.6.1)
|
||||
mime-types (>= 1.16)
|
||||
rmagick (2.12.2)
|
||||
rspec (2.0.0.beta.22)
|
||||
rspec-core (= 2.0.0.beta.22)
|
||||
rspec-expectations (= 2.0.0.beta.22)
|
||||
rspec-mocks (= 2.0.0.beta.22)
|
||||
rspec-core (2.0.0.beta.22)
|
||||
rspec-expectations (2.0.0.beta.22)
|
||||
rspec (2.0.0)
|
||||
rspec-core (= 2.0.0)
|
||||
rspec-expectations (= 2.0.0)
|
||||
rspec-mocks (= 2.0.0)
|
||||
rspec-core (2.0.0)
|
||||
rspec-expectations (2.0.0)
|
||||
diff-lcs (>= 1.1.2)
|
||||
rspec-mocks (2.0.0.beta.22)
|
||||
rspec-core (= 2.0.0.beta.22)
|
||||
rspec-expectations (= 2.0.0.beta.22)
|
||||
rspec-rails (2.0.0.beta.22)
|
||||
rspec (= 2.0.0.beta.22)
|
||||
rspec-mocks (2.0.0)
|
||||
rspec-core (= 2.0.0)
|
||||
rspec-expectations (= 2.0.0)
|
||||
rspec-rails (2.0.0)
|
||||
rspec (= 2.0.0)
|
||||
ruby-debug (0.10.3)
|
||||
columnize (>= 0.1)
|
||||
ruby-debug-base (~> 0.10.3.0)
|
||||
ruby-debug-base (0.10.3)
|
||||
linecache (>= 0.3)
|
||||
rubyzip (0.9.4)
|
||||
selenium-webdriver (0.0.28)
|
||||
ffi (>= 0.6.1)
|
||||
selenium-webdriver (0.0.29)
|
||||
childprocess (>= 0.0.7)
|
||||
ffi (~> 0.6.3)
|
||||
json_pure
|
||||
rubyzip
|
||||
spork (0.8.4)
|
||||
term-ansicolor (1.0.5)
|
||||
thor (0.14.2)
|
||||
thor (0.14.3)
|
||||
treetop (1.4.8)
|
||||
polyglot (>= 0.3.1)
|
||||
trollop (1.16.2)
|
||||
@ -226,14 +227,15 @@ DEPENDENCIES
|
||||
actionmailer-with-request
|
||||
autotest
|
||||
aws
|
||||
bson_ext (>= 1.0.8)
|
||||
bson_ext (= 1.1)
|
||||
capybara
|
||||
carrierwave (= 0.5.0.beta2)
|
||||
cgi_multipart_eof_fix
|
||||
cucumber (= 0.8.5)
|
||||
cucumber-rails
|
||||
custom_fields!
|
||||
custom_fields (= 1.0.0.beta)
|
||||
database_cleaner
|
||||
delayed_job (= 2.1.0.pre2)
|
||||
delayed_job_mongoid (= 1.0.0.rc)
|
||||
devise (= 1.1.2)
|
||||
factory_girl_rails
|
||||
fastthread
|
||||
@ -244,16 +246,18 @@ DEPENDENCIES
|
||||
httparty (>= 0.6.1)
|
||||
inherited_resources (>= 1.1.2)
|
||||
launchy
|
||||
locomotive_liquid (>= 2.1.3)
|
||||
locomotive_carrierwave
|
||||
locomotive_liquid (= 2.2.2)
|
||||
locomotive_mongoid_acts_as_tree (= 0.1.5.1)
|
||||
mimetype-fu
|
||||
mocha!
|
||||
mongoid!
|
||||
mongoid_acts_as_tree (= 0.1.5)
|
||||
mongoid (= 2.0.0.beta.19)
|
||||
mongrel
|
||||
pickle!
|
||||
rails (>= 3.0.0)
|
||||
rmagick (= 2.12.2)
|
||||
rspec-rails (>= 2.0.0.beta.18)
|
||||
ruby-debug
|
||||
rubyzip
|
||||
spork
|
||||
warden
|
||||
|
@ -8,7 +8,7 @@ If we have to give only 5 main features to describe our application, there will
|
||||
* nice looking UI (see http://www.locomotiveapp.org for some screenshots)
|
||||
* flexible content types
|
||||
* playing smoothly with Heroku and MongoHQ
|
||||
* inline editing (coming soon)
|
||||
* inline editing (beta)
|
||||
|
||||
h2. Strategy / Development status
|
||||
|
||||
@ -19,12 +19,13 @@ h2. Gems
|
||||
|
||||
Here is a short list of main gems used in the application.
|
||||
|
||||
* Rails 3 RC
|
||||
* Mongoid 2.0.0.beta 16 (with MongoDB 1.6)
|
||||
* Rails 3.0
|
||||
* Mongoid 2.0.0.beta 17 (with MongoDB 1.6)
|
||||
* Liquid
|
||||
* Devise
|
||||
* Carrierwave
|
||||
* Haml
|
||||
* Delayed job
|
||||
|
||||
h2. Installation
|
||||
|
||||
|
@ -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
|
||||
|
@ -3,5 +3,9 @@ module Admin
|
||||
|
||||
sections 'contents'
|
||||
|
||||
def destroy
|
||||
destroy! { admin_pages_url }
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
51
app/controllers/admin/imports_controller.rb
Normal file
51
app/controllers/admin/imports_controller.rb
Normal file
@ -0,0 +1,51 @@
|
||||
module Admin
|
||||
class ImportsController < BaseController
|
||||
|
||||
sections 'settings', 'site'
|
||||
|
||||
actions :show, :new, :create
|
||||
|
||||
def show
|
||||
@job = Delayed::Job.where({ :job_type => 'import', :site_id => current_site.id }).last
|
||||
|
||||
respond_to do |format|
|
||||
format.html do
|
||||
redirect_to new_admin_import_url if @job.nil?
|
||||
end
|
||||
format.json { render :json => {
|
||||
:step => @job.nil? ? 'done' : @job.step,
|
||||
:failed => @job && @job.last_error.present?
|
||||
} }
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def new; end
|
||||
|
||||
def create
|
||||
if params[:zipfile].blank?
|
||||
@error = t('errors.messages.blank')
|
||||
flash[:alert] = t('flash.admin.imports.create.alert')
|
||||
render 'new'
|
||||
else
|
||||
path = self.store_zipfile!
|
||||
|
||||
job = Locomotive::Import::Job.new(path, current_site)
|
||||
Delayed::Job.enqueue job, { :site => current_site, :job_type => 'import' }
|
||||
|
||||
flash[:notice] = t('flash.admin.imports.create.notice')
|
||||
|
||||
redirect_to admin_import_url
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def store_zipfile!
|
||||
file = CarrierWave::SanitizedFile.new(params[:zipfile])
|
||||
file.move_to(File.join(Rails.root, 'tmp', 'files', current_site.id.to_s))
|
||||
file.path
|
||||
end
|
||||
|
||||
end
|
||||
end
|
@ -1,12 +1,14 @@
|
||||
module Admin
|
||||
class SnippetsController < BaseController
|
||||
|
||||
sections 'settings'
|
||||
sections 'settings', 'theme_assets'
|
||||
|
||||
respond_to :json, :only => :update
|
||||
|
||||
def index
|
||||
@snippets = current_site.snippets.order_by([[:name, :asc]])
|
||||
def destroy
|
||||
destroy! do |format|
|
||||
format.html { redirect_to admin_theme_assets_url }
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
@ -4,22 +4,30 @@ module Admin
|
||||
include ActionView::Helpers::SanitizeHelper
|
||||
extend ActionView::Helpers::SanitizeHelper::ClassMethods
|
||||
include ActionView::Helpers::TextHelper
|
||||
include ActionView::Helpers::NumberHelper
|
||||
|
||||
sections 'settings', 'theme_assets'
|
||||
|
||||
respond_to :json, :only => [:create, :update]
|
||||
|
||||
def index
|
||||
assets = current_site.theme_assets.all
|
||||
@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(params[:all]).order_by([[:slug, :asc]])
|
||||
@assets = @assets.group_by { |a| a.folder.split('/').first.to_sym }
|
||||
@js_and_css_assets = (@assets[:javascripts] || []) + (@assets[:stylesheets] || [])
|
||||
|
||||
if request.xhr?
|
||||
@images = @assets[:images]
|
||||
render :action => 'images', :layout => false and return
|
||||
else
|
||||
@snippets = current_site.snippets.order_by([[:name, :asc]]).all.to_a
|
||||
end
|
||||
end
|
||||
|
||||
def edit
|
||||
resource.performing_plain_text = true if resource.stylesheet_or_javascript?
|
||||
edit!
|
||||
end
|
||||
|
||||
def create
|
||||
params[:theme_asset] = { :source => params[:file] } if params[:file]
|
||||
|
||||
@ -27,11 +35,10 @@ module Admin
|
||||
success.json do
|
||||
render :json => {
|
||||
: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
|
||||
:local_path => @theme_asset.local_path(true),
|
||||
:size => number_to_human_size(@theme_asset.size),
|
||||
:date => l(@theme_asset.updated_at, :format => :short)
|
||||
}
|
||||
end
|
||||
failure.json { render :json => { :status => 'error' } }
|
||||
|
@ -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
|
||||
|
||||
|
@ -32,8 +32,12 @@ class Asset
|
||||
end
|
||||
end
|
||||
|
||||
def site_id # needed by the uploader of custom fields
|
||||
self.collection.site_id
|
||||
end
|
||||
|
||||
def to_liquid
|
||||
{ :url => self.source.url }.merge(self.attributes)
|
||||
Locomotive::Liquid::Drops::Asset.new(self)
|
||||
end
|
||||
|
||||
end
|
||||
|
@ -29,6 +29,10 @@ class ContentInstance
|
||||
|
||||
alias :visible? :_visible?
|
||||
|
||||
def site_id # needed by the uploader of custom fields
|
||||
self.content_type.site_id
|
||||
end
|
||||
|
||||
def visible?
|
||||
self._visible || self._visible.nil?
|
||||
end
|
||||
|
@ -26,16 +26,21 @@ class Snippet
|
||||
protected
|
||||
|
||||
def normalize_slug
|
||||
# TODO: refactor it
|
||||
self.slug = self.name.clone if self.slug.blank? && self.name.present?
|
||||
self.slug.slugify!(:without_extension => true, :downcase => true) if self.slug.present?
|
||||
end
|
||||
|
||||
def update_templates
|
||||
return unless (self.site rescue false) # not run if the site is being destroyed
|
||||
|
||||
pages = self.site.pages.any_in(:snippet_dependencies => [self.slug]).to_a
|
||||
|
||||
pages.each do |page|
|
||||
self._change_snippet_inside_template(page.template.root)
|
||||
|
||||
page.instance_variable_set(:@template_changed, true)
|
||||
|
||||
page.send(:_serialize_template) && page.save
|
||||
end
|
||||
end
|
||||
@ -44,11 +49,11 @@ class Snippet
|
||||
case node
|
||||
when Locomotive::Liquid::Tags::Snippet
|
||||
node.refresh(self) if node.slug == self.slug
|
||||
when Locomotive::Liquid::Tags::Block
|
||||
when Locomotive::Liquid::Tags::InheritedBlock
|
||||
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
|
||||
|
@ -2,16 +2,14 @@ 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 :folder, :default => nil
|
||||
field :hidden, :type => Boolean, :default => false
|
||||
mount_uploader :source, ThemeAssetUploader
|
||||
|
||||
## associations ##
|
||||
@ -19,22 +17,25 @@ 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_save :set_slug
|
||||
before_save :sanitize_folder
|
||||
before_save :build_local_path
|
||||
|
||||
## validations ##
|
||||
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]
|
||||
validates_presence_of :plain_text_name, :if => Proc.new { |a| a.performing_plain_text? }
|
||||
validates_uniqueness_of :local_path, :scope => :site_id
|
||||
validates_integrity_of :source
|
||||
validate :content_type_can_not_changed
|
||||
|
||||
## named scopes ##
|
||||
scope :visible, lambda { |all| all ? {} : { :where => { :hidden => false } } }
|
||||
|
||||
## accessors ##
|
||||
attr_accessor :performing_plain_text
|
||||
attr_accessor :plain_text_name, :plain_text, :performing_plain_text
|
||||
|
||||
## methods ##
|
||||
|
||||
@ -48,77 +49,90 @@ 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)
|
||||
def local_path(short = false)
|
||||
if short
|
||||
self.read_attribute(:local_path).gsub(/^#{self.content_type.pluralize}\//, '')
|
||||
else
|
||||
nil
|
||||
self.read_attribute(:local_path)
|
||||
end
|
||||
end
|
||||
|
||||
def plain_text=(source)
|
||||
self.performing_plain_text = true if self.performing_plain_text.nil?
|
||||
write_attribute(:plain_text, source)
|
||||
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
|
||||
end
|
||||
|
||||
def plain_text_name=(name)
|
||||
@plain_text_name_changed = true
|
||||
@plain_text_name = name
|
||||
end
|
||||
|
||||
def plain_text
|
||||
@plain_text ||= self.source.read
|
||||
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) || false
|
||||
end
|
||||
|
||||
def store_plain_text
|
||||
return if self.plain_text.blank?
|
||||
data = self.performing_plain_text? ? self.plain_text : self.source.read
|
||||
|
||||
# replace /theme/<content_type>/<slug> occurences by the real amazon S3 url or local files
|
||||
sanitized_source = self.plain_text.gsub(/(\/theme\/([a-z]+)\/([a-z_\-0-9]+)\.[a-z]{2,3})/) do |url|
|
||||
content_type, slug = url.split('/')[2..-1]
|
||||
return if !self.stylesheet_or_javascript? || self.plain_text_name.blank? || data.blank?
|
||||
|
||||
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
|
||||
sanitized_source = self.escape_shortcut_urls(data)
|
||||
|
||||
self.source = CarrierWave::SanitizedFile.new({
|
||||
:tempfile => StringIO.new(sanitized_source),
|
||||
:filename => "#{self.slug}.#{self.stylesheet? ? 'css' : 'js'}"
|
||||
:filename => "#{self.plain_text_name}.#{self.stylesheet? ? 'css' : 'js'}"
|
||||
})
|
||||
end
|
||||
|
||||
def shortcut_url # ex: /theme/stylesheets/application.css is a shortcut for a theme asset (content_type => stylesheet, slug => 'application')
|
||||
File.join('/theme', self.content_type.pluralize, "#{self.slug}#{File.extname(self.source_filename)}")
|
||||
rescue
|
||||
''
|
||||
end
|
||||
|
||||
def to_liquid
|
||||
{ :url => self.source.url }.merge(self.attributes)
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def sanitize_slug
|
||||
self.slug.slugify!(:underscore => true) if self.slug.present?
|
||||
def safe_source_filename
|
||||
self.source_filename || self.source.send(:original_filename) rescue nil
|
||||
end
|
||||
|
||||
def set_slug
|
||||
if self.slug.blank?
|
||||
self.slug = File.basename(self.source_filename, File.extname(self.source_filename))
|
||||
self.sanitize_slug
|
||||
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(/\/$/, '').downcase
|
||||
|
||||
# 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 extname_can_not_be_changed
|
||||
return if self.new_record?
|
||||
def build_local_path
|
||||
self.local_path = File.join(self.folder, self.safe_source_filename)
|
||||
end
|
||||
|
||||
if File.extname(self.source.file.original_filename) != File.extname(self.source_filename)
|
||||
self.errors.add(:source, :extname_changed)
|
||||
def escape_shortcut_urls(text)
|
||||
return if text.blank?
|
||||
|
||||
text.gsub(/[("'](\/(stylesheets|javascripts|images|media)\/((.+)\/)*([a-z_\-0-9]+)\.[a-z]{2,3})[)"']/) do |path|
|
||||
|
||||
sanitized_path = path.gsub(/[("')]/, '').gsub(/^\//, '')
|
||||
|
||||
if asset = self.site.theme_assets.where(:local_path => sanitized_path).first
|
||||
"#{path.first}#{asset.source.url}#{path.last}"
|
||||
else
|
||||
path
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def content_type_can_not_changed
|
||||
self.errors.add(:source, :extname_changed) if !self.new_record? && self.content_type_changed?
|
||||
end
|
||||
|
||||
end
|
||||
|
@ -31,7 +31,7 @@ class AssetUploader < CarrierWave::Uploader::Base
|
||||
process :set_size
|
||||
process :set_width_and_height
|
||||
|
||||
def set_content_type
|
||||
def set_content_type(*args)
|
||||
value = :other
|
||||
|
||||
content_type = file.content_type == 'application/octet-stream' ? File.mime_type?(original_filename) : file.content_type
|
||||
@ -48,7 +48,7 @@ class AssetUploader < CarrierWave::Uploader::Base
|
||||
model.content_type = value
|
||||
end
|
||||
|
||||
def set_size
|
||||
def set_size(*args)
|
||||
model.size = file.size
|
||||
end
|
||||
|
||||
@ -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
|
||||
|
||||
|
@ -6,27 +6,16 @@ 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
|
||||
|
||||
def store_dir
|
||||
"sites/#{model.site_id}/themes/#{model.id}"
|
||||
File.join('sites', model.site_id.to_s, 'theme', model.folder_was || model.folder)
|
||||
end
|
||||
|
||||
def stale_model?
|
||||
!model.new_record? && model.folder_changed?
|
||||
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
|
||||
|
@ -4,6 +4,7 @@
|
||||
= render 'admin/shared/menu/settings'
|
||||
|
||||
- content_for :buttons do
|
||||
= admin_button_tag :import, new_admin_import_url, :class => 'new'
|
||||
= admin_button_tag t('.new_membership'), new_admin_membership_url, :class => 'new'
|
||||
|
||||
%p!= t('.help')
|
||||
|
21
app/views/admin/imports/new.html.haml
Normal file
21
app/views/admin/imports/new.html.haml
Normal file
@ -0,0 +1,21 @@
|
||||
- title t('.title')
|
||||
|
||||
- content_for :submenu do
|
||||
= render 'admin/shared/menu/settings'
|
||||
|
||||
%p!= t('.help')
|
||||
|
||||
= form_tag admin_import_url, :multipart => true, :class => 'formtastic import' do
|
||||
|
||||
%fieldset.inputs
|
||||
%legend
|
||||
%span= t('formtastic.titles.upload')
|
||||
%ol
|
||||
%li{ :class => "file #{'error' if @error} required" }
|
||||
= label_tag 'zipfile', t('formtastic.labels.import.new.source')
|
||||
= file_field_tag 'zipfile'
|
||||
- if @error
|
||||
%p.inline-errors= @error
|
||||
%p.inline-hints= t('formtastic.hints.import.source')
|
||||
|
||||
= render 'admin/shared/form_actions', :button_label => :send
|
19
app/views/admin/imports/show.html.haml
Normal file
19
app/views/admin/imports/show.html.haml
Normal file
@ -0,0 +1,19 @@
|
||||
- content_for :head do
|
||||
= javascript_include_tag 'admin/plugins/json2', 'admin/plugins/smartupdater', 'admin/import'
|
||||
|
||||
- title t('.title')
|
||||
|
||||
- content_for :submenu do
|
||||
= render 'admin/shared/menu/settings'
|
||||
|
||||
%p!= t('.help')
|
||||
|
||||
%ul{ :id => 'import-steps', :class => 'list', :'data-url' => admin_import_url(:json), :'data-success-message' => t('.message.success'), :'data-failure-message' => t('.message.failure') }
|
||||
- %w(site content_types assets asset_collections snippets pages).each do |step|
|
||||
%li{ :id => "#{step}-step" }
|
||||
%em
|
||||
%strong
|
||||
= link_to t(".steps.#{step}"), '#'
|
||||
.more
|
||||
.states
|
||||
|
@ -1,7 +1,7 @@
|
||||
%h1
|
||||
- if current_admin.sites.size > 1
|
||||
= form_tag new_admin_cross_domain_session_url, :method => 'get' do
|
||||
= select_tag 'target_id', options_for_select(current_admin.sites.collect { |site| [site.name, site.id] }, current_site.id), :id => 'site-selector'
|
||||
= select_tag 'target_id', options_for_select(current_admin.sites.collect { |site| [truncate(site.name, :length => 32), site.id] }, current_site.id), :id => 'site-selector'
|
||||
= submit_tag 'Switch', :style => 'display: none'
|
||||
- else
|
||||
= link_to current_site.name, admin_root_url, :class => 'single'
|
||||
|
@ -1,5 +1,5 @@
|
||||
%ul
|
||||
= admin_submenu_item 'site', edit_admin_current_site_url
|
||||
= admin_submenu_item 'snippets', admin_snippets_url
|
||||
/ = admin_submenu_item 'snippets', admin_snippets_url
|
||||
= admin_submenu_item 'theme_assets', admin_theme_assets_url
|
||||
= admin_submenu_item 'account', edit_admin_my_account_url
|
@ -1,4 +1,5 @@
|
||||
%li
|
||||
%em
|
||||
%strong= link_to snippet.name, edit_admin_snippet_path(snippet)
|
||||
.more
|
||||
%span!= t('.updated_at')
|
||||
|
@ -12,4 +12,4 @@
|
||||
|
||||
= render 'form', :f => form
|
||||
|
||||
= render 'admin/shared/form_actions', :back_url => admin_snippets_url, :button_label => :update
|
||||
= render 'admin/shared/form_actions', :back_url => admin_theme_assets_url, :button_label => :update
|
@ -9,4 +9,4 @@
|
||||
|
||||
= render 'form', :f => form
|
||||
|
||||
= render 'admin/shared/form_actions', :back_url => admin_snippets_url, :button_label => :create
|
||||
= render 'admin/shared/form_actions', :back_url => admin_theme_assets_url, :button_label => :create
|
@ -1,12 +1,13 @@
|
||||
- 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'} #{'hidden' if asset.hidden?}" }
|
||||
%em
|
||||
%strong= link_to asset.local_path(!edit), edit ? edit_admin_theme_asset_path(asset) : asset.source.url, :'data-local-path' => asset.local_path
|
||||
.more
|
||||
%span.size= number_to_human_size(asset.size)
|
||||
—
|
||||
%span!= t('.updated_at')
|
||||
%span.date= l asset.updated_at, :format => :short
|
||||
|
||||
- if edit
|
||||
= link_to image_tag('admin/list/icons/trash.png'), admin_theme_asset_path(asset), :class => 'remove', :confirm => t('admin.messages.confirm'), :method => :delete
|
||||
|
@ -4,24 +4,23 @@
|
||||
|
||||
= f.hidden_field :performing_plain_text
|
||||
|
||||
#file-selector{ :class => "selector #{'hidden' if @theme_asset.performing_plain_text?}" }
|
||||
#file-selector{ :class => "selector #{'hidden' if @theme_asset.stylesheet_or_javascript?}" }
|
||||
= f.inputs :name => :information do
|
||||
= f.input :source
|
||||
= f.input :slug
|
||||
|
||||
- if @theme_asset.new_record? || @theme_asset.stylesheet_or_javascript?
|
||||
%span.alt
|
||||
!= t('admin.theme_assets.form.choose_plain_text')
|
||||
|
||||
- if allow_plain_text_editing?(@theme_asset)
|
||||
#text-selector{ :class => "selector #{'hidden' if !@theme_asset.performing_plain_text?}", :style => "#{'display: none' if !@theme_asset.performing_plain_text?}" }
|
||||
#text-selector{ :class => "selector #{'hidden' if !@theme_asset.stylesheet_or_javascript?}", :style => "#{'display: none' if !@theme_asset.stylesheet_or_javascript?}" }
|
||||
= f.inputs :name => :code, :class => 'inputs code' do
|
||||
|
||||
- if @theme_asset.new_record?
|
||||
= f.input :slug
|
||||
= f.input :plain_text_name
|
||||
|
||||
= f.custom_input :content_type do
|
||||
= f.select :content_type, ["stylesheet", "javascript"]
|
||||
= f.select :content_type, %w(stylesheet javascript)
|
||||
|
||||
= f.custom_input :plain_text, :css => 'full', :with_label => false do
|
||||
%code{ :class => (@theme_asset.size && @theme_asset.size > 40000 ? 'nude' : (@theme_asset.content_type || 'stylesheet')) }
|
||||
@ -32,9 +31,7 @@
|
||||
%span.alt
|
||||
!= t('admin.theme_assets.form.choose_file')
|
||||
|
||||
- if @theme_asset.image?
|
||||
= f.foldable_inputs :name => "#{t('formtastic.titles.preview')} #{image_dimensions_and_size(@theme_asset)}", :class => 'preview' do
|
||||
%li
|
||||
.image
|
||||
.inside
|
||||
= image_tag(@theme_asset.source.url(:preview))
|
||||
= f.foldable_inputs :name => :options do
|
||||
= f.input :folder
|
||||
= f.custom_input :hidden, :css => 'toggle' do
|
||||
= f.check_box :hidden
|
||||
|
@ -6,7 +6,7 @@
|
||||
- content_for :buttons do
|
||||
= admin_button_tag t('admin.theme_assets.index.new'), new_admin_theme_asset_url, :class => 'new'
|
||||
|
||||
%p!= t('.help', :url => @theme_asset.source.url, :shortcut_url => @theme_asset.shortcut_url)
|
||||
%p!= t('.help', :url => @theme_asset.source.url)
|
||||
|
||||
= semantic_form_for @theme_asset, :url => admin_theme_asset_url(@theme_asset), :html => { :multipart => true, :class => 'save-with-shortcut' } do |form|
|
||||
|
||||
|
@ -4,12 +4,12 @@
|
||||
.actions
|
||||
= admin_button_tag t('admin.theme_assets.index.new'), admin_theme_assets_url(:json), :class => 'button small add', :id => 'upload-link'
|
||||
|
||||
- if @image_assets.empty?
|
||||
- if @images.empty?
|
||||
%p.no-items!= t('.no_items')
|
||||
|
||||
%ul.assets
|
||||
= render 'asset', :asset => current_site.theme_assets.build, :edit => false
|
||||
%ul.list.theme-assets
|
||||
= render 'asset', :asset => current_site.theme_assets.build(:updated_at => Time.now, :local_path => 'images/new.jpg', :content_type => 'image'), :edit => false
|
||||
|
||||
= render :partial => 'asset', :collection => @image_assets, :locals => { :per_row => 3, :edit => false }
|
||||
= render :partial => 'asset', :collection => @images, :locals => { :edit => false }
|
||||
|
||||
%li.clear
|
@ -4,33 +4,48 @@
|
||||
= render 'admin/shared/menu/settings'
|
||||
|
||||
- content_for :buttons do
|
||||
= admin_button_tag t('admin.theme_assets.index.all'), all_admin_theme_assets_url, :class => 'show'
|
||||
= admin_button_tag t('admin.snippets.index.new'), new_admin_snippet_url, :class => 'new'
|
||||
= admin_button_tag :new, new_admin_theme_asset_url, :class => 'new'
|
||||
|
||||
%p!= t('.help')
|
||||
|
||||
%h3!= t('.snippets')
|
||||
- if @snippets.empty?
|
||||
%p.no-items!= t('.no_items', :url => new_admin_snippet_url)
|
||||
- else
|
||||
%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]
|
||||
|
||||
|
@ -46,5 +46,7 @@ module Locomotive
|
||||
|
||||
# Configure sensitive parameters which will be filtered from the log file.
|
||||
config.filter_parameters << :password
|
||||
|
||||
config.middleware.insert_after ::ActionDispatch::Static, '::Locomotive::Middlewares::Fonts', :path => %r{^/fonts}
|
||||
end
|
||||
end
|
||||
|
1
config/initializers/haml.rb
Normal file
1
config/initializers/haml.rb
Normal file
@ -0,0 +1 @@
|
||||
Haml::Template.options[:ugly] = true # improve performance in dev
|
@ -8,3 +8,8 @@
|
||||
# inflect.irregular 'person', 'people'
|
||||
# inflect.uncountable %w( fish sheep )
|
||||
# end
|
||||
|
||||
ActiveSupport::Inflector.inflections do |inflect|
|
||||
inflect.irregular 'media', 'media'
|
||||
end
|
||||
|
||||
|
@ -1,5 +1,4 @@
|
||||
require File.dirname(__FILE__) + '/../../lib/locomotive.rb'
|
||||
require File.dirname(__FILE__) + '/../../lib/core_ext.rb'
|
||||
|
||||
Locomotive.configure do |config|
|
||||
# if not defined, locomotive will use example.com as main domain name. Remove prefix www from your domain name.
|
||||
|
@ -30,6 +30,7 @@ en:
|
||||
back: Back without saving
|
||||
create: Create
|
||||
update: Update
|
||||
send: Send
|
||||
|
||||
errors:
|
||||
"500":
|
||||
@ -131,6 +132,7 @@ en:
|
||||
|
||||
current_sites:
|
||||
edit:
|
||||
import: import
|
||||
new_membership: add account
|
||||
help: "The site name can be updated by clicking it."
|
||||
ask_for_name: "Please type the new site name"
|
||||
@ -156,19 +158,23 @@ en:
|
||||
theme_assets:
|
||||
index:
|
||||
title: Listing theme files
|
||||
help: "Theme assets represent files needed by layouts and snippets. If you need to manage an image gallery, go to the Assets section instead."
|
||||
help: "The theme files section is the place where you manage the files needed by your layout, ...etc. If you need to manage an image gallery, go to the Assets section instead."
|
||||
all: all assets
|
||||
new: new file
|
||||
snippets: Snippets
|
||||
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."
|
||||
edit:
|
||||
title: "Editing %{file}"
|
||||
help: "You can insert the following shortcut url in your stylesheets: <strong>%{shortcut_url}</strong> OR use the direct url <strong>%{url}</strong>"
|
||||
help: "This asset is accessible from the following url: <strong>%{url}</strong>"
|
||||
form:
|
||||
picker_link: Insert a file into the code
|
||||
choose_file: Choose file
|
||||
@ -242,6 +248,24 @@ en:
|
||||
title: Cross-domain authentication
|
||||
notice: You will be redirected to the website in a couple of seconds.
|
||||
|
||||
imports:
|
||||
new:
|
||||
title: Import
|
||||
help: "Be careful when you upload a new theme for your existing website, your current data could be modified or even removed."
|
||||
show:
|
||||
title: Import in progress
|
||||
help: "Your site is being updated from the theme zip file you have just uploaded. It lasts a couple of seconds."
|
||||
steps:
|
||||
site: Site information
|
||||
content_types: Custom content types
|
||||
assets: Theme files
|
||||
asset_collections: Asset collections
|
||||
snippets: Snippets
|
||||
pages: Pages
|
||||
messages:
|
||||
success: "Your site was successfully updated."
|
||||
failure: "The import did not work."
|
||||
|
||||
formtastic:
|
||||
titles:
|
||||
information: General information
|
||||
@ -261,6 +285,7 @@ en:
|
||||
other_fields: Other information
|
||||
presentation: Presentation
|
||||
attributes: Attributes
|
||||
upload: Upload
|
||||
labels:
|
||||
page:
|
||||
raw_template: Template
|
||||
@ -272,6 +297,9 @@ en:
|
||||
custom_fields:
|
||||
field:
|
||||
_alias: Alias
|
||||
import:
|
||||
new:
|
||||
source: File
|
||||
|
||||
hints:
|
||||
page:
|
||||
@ -292,4 +320,6 @@ en:
|
||||
field:
|
||||
_alias: "Property available in liquid templates"
|
||||
hint: "Text displayed in the model form just below the field"
|
||||
import:
|
||||
source: "A zipfile containing a database.yml along with assets and templates"
|
||||
|
||||
|
@ -40,6 +40,7 @@ fr:
|
||||
back: Retour sans sauvegarder
|
||||
create: Créer
|
||||
update: Mettre à jour
|
||||
send: Envoyer
|
||||
|
||||
custom_fields:
|
||||
edit:
|
||||
@ -131,6 +132,7 @@ fr:
|
||||
|
||||
current_sites:
|
||||
edit:
|
||||
import: importer
|
||||
new_membership: ajouter compte
|
||||
help: "Le nom du site est modifiable en cliquant dessus."
|
||||
ask_for_name: "Veuillez entrer le nouveau nom"
|
||||
@ -156,13 +158,17 @@ fr:
|
||||
theme_assets:
|
||||
index:
|
||||
title: Liste des fichiers du thème
|
||||
help: "Les fichiers du thème sont utilisés par les gabarits et les snippets. Si vous avez besoin d'une galerie d'images, la section Média est plus adéquate."
|
||||
help: "Les fichiers du thème sont utilisés pour construire le gabarit de vos pages. Si vous avez besoin d'une galerie d'images, la section Média est plus adéquate."
|
||||
all: tous les fichiers
|
||||
new: nouveau fichier
|
||||
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."
|
||||
@ -241,6 +247,24 @@ fr:
|
||||
title: Transfert vers un autre site
|
||||
notice: Vous allez être redirigé(e) vers le site dans quelques secondes.
|
||||
|
||||
imports:
|
||||
new:
|
||||
title: Import
|
||||
help: "Faites attention quand vous envoyez un nouveau theme sur votre site, les données de celui-ci pourront être modifiées voire même supprimées."
|
||||
show:
|
||||
title: Import en cours
|
||||
help: "Votre site est en train d'être mis à jour à partir du fichier zip précédemment envoyé sur le serveur. Cette opération peut durer quelques secondes."
|
||||
steps:
|
||||
site: Informations sur le site
|
||||
content_types: Modèles de données personnalisés
|
||||
assets: Fichiers du thème
|
||||
asset_collections: Collections de média
|
||||
snippets: Snippets
|
||||
pages: Pages
|
||||
messages:
|
||||
success: "Votre site a été mis à jour avec succès."
|
||||
failure: "L'import n'a pas fonctionné."
|
||||
|
||||
formtastic:
|
||||
titles:
|
||||
information: Informations générales
|
||||
@ -260,6 +284,7 @@ fr:
|
||||
other_fields: Autres informations
|
||||
presentation: Présentation
|
||||
attributes: Propriétés
|
||||
upload: Envoi au serveur
|
||||
labels:
|
||||
theme_asset:
|
||||
new:
|
||||
@ -269,6 +294,9 @@ fr:
|
||||
custom_fields:
|
||||
field:
|
||||
_alias: Alias
|
||||
import:
|
||||
new:
|
||||
source: Fichier
|
||||
|
||||
hints:
|
||||
page:
|
||||
@ -289,3 +317,5 @@ fr:
|
||||
field:
|
||||
_alias: "Champ utilisable dans les templates liquid"
|
||||
hint: "Texte affiché dans le formulaire de l'élément juste en dessous du champ."
|
||||
import:
|
||||
source: "Un fichier zip contenant database.yml, les fichiers du thème et les templates de page"
|
||||
|
@ -107,4 +107,9 @@ en:
|
||||
|
||||
cross_domain_sessions:
|
||||
create:
|
||||
alert: "You need to sign in"
|
||||
alert: "You need to sign in"
|
||||
|
||||
imports:
|
||||
create:
|
||||
notice: "Your site is being updated."
|
||||
alert: "The import was not done."
|
@ -108,3 +108,8 @@ fr:
|
||||
cross_domain_sessions:
|
||||
create:
|
||||
alert: "Vous devez vous authentifier"
|
||||
|
||||
imports:
|
||||
create:
|
||||
notice: "Votre site est en train d'être mis à jour"
|
||||
alert: "L'import n'a pas pu se faire"
|
@ -29,7 +29,9 @@ Rails.application.routes.draw do
|
||||
|
||||
resources :memberships
|
||||
|
||||
resources :theme_assets
|
||||
resources :theme_assets do
|
||||
get :all, :action => 'index', :on => :collection, :defaults => { :all => true }
|
||||
end
|
||||
|
||||
resources :asset_collections
|
||||
|
||||
@ -48,6 +50,8 @@ Rails.application.routes.draw do
|
||||
resources :custom_fields, :path => 'custom/:parent/:slug/fields'
|
||||
|
||||
resources :cross_domain_sessions, :only => [:new, :create]
|
||||
|
||||
resource :import, :only => [:new, :show, :create]
|
||||
end
|
||||
|
||||
# sitemap
|
||||
|
64
doc/TODO
64
doc/TODO
@ -1,27 +1,17 @@
|
||||
BOARD:
|
||||
|
||||
- inline editing (http://www.aloha-editor.com/wiki/index.php/Aloha_PHP_Example)
|
||||
x spinner
|
||||
x save automatically (callback) => store modifications
|
||||
x admin buttons
|
||||
x edit page
|
||||
x save / cancel
|
||||
x back to back-office => admin settings of the page
|
||||
(- duplicate page ?)
|
||||
(- super bonus statistics)
|
||||
x locale
|
||||
x store page toolbar status in cookie
|
||||
x trim short text content
|
||||
x namespace js functions
|
||||
- html view in the aloha popup
|
||||
- editable elements should wrap a tag: div, h1, ...etc (default span)
|
||||
- edit images (upload new ones, ...etc) => wait for aloha or send them an email ?
|
||||
x customize tinyMCE: no html popup => div popup, nice icons
|
||||
x add images / files inside long text element (back-office side at first ?)
|
||||
|
||||
- import tool:
|
||||
- add samples option
|
||||
- remove existing pages / contents option
|
||||
|
||||
- global regions: keyword in editable element (http://www.mongodb.org/display/DOCS/Updating)
|
||||
- create a repo for a tool "a la" vision
|
||||
- write my first tutorial about locomotive
|
||||
- asset collections => liquid
|
||||
|
||||
|
||||
- refactor slugify method (use parameterize + create a module)
|
||||
- [content types] the "display column" selector should not include file types
|
||||
@ -42,6 +32,7 @@ BUGS:
|
||||
- custom fields: accepts_nested_attributes weird behaviour when creating new content type + adding random fields
|
||||
|
||||
NICE TO HAVE:
|
||||
- import / export site
|
||||
- asset collections: custom resizing if image
|
||||
- super_finder
|
||||
- better icons for mime type
|
||||
@ -51,7 +42,6 @@ NICE TO HAVE:
|
||||
- page with regexp url ?
|
||||
- page redirection (option)
|
||||
- automatic update !
|
||||
- import / export site
|
||||
- page not found (front) => if logged in, link to create the page
|
||||
|
||||
DONE:
|
||||
@ -107,4 +97,42 @@ x editable_file tag
|
||||
x stylish file field
|
||||
x remove not used editable element all in once
|
||||
x default content from parent editable element
|
||||
x unable to upload/remove editable file
|
||||
x unable to upload/remove editable file
|
||||
x customize tinyMCE: no html popup => div popup, nice icons
|
||||
x add images / files inside long text element (back-office side at first ?)
|
||||
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 snippet dependencies => do not work correctly
|
||||
? google analytics tag
|
||||
x mask internal asset_collections
|
||||
x refactor ui for the theme assets page
|
||||
x fix assets liquid tags / filters
|
||||
x upload and insert new images in a css or js from the ui is broken
|
||||
x proxy for fonts (http://markevans.github.com/dragonfly/file.Rails3.html)
|
||||
x order yaml file (http://www.ruby-forum.com/topic/120295)
|
||||
x fix tests
|
||||
x inline editing (http://www.aloha-editor.com/wiki/index.php/Aloha_PHP_Example)
|
||||
x spinner
|
||||
x save automatically (callback) => store modifications
|
||||
x admin buttons
|
||||
x edit page
|
||||
x save / cancel
|
||||
x back to back-office => admin settings of the page
|
||||
(- duplicate page ?)
|
||||
(- super bonus statistics)
|
||||
x locale
|
||||
x store page toolbar status in cookie
|
||||
x trim short text content
|
||||
x namespace js functions
|
||||
x 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 contents: group_by, oder_by, api_enabled
|
||||
x folders for theme assets
|
||||
x theme assets whitelist
|
||||
x fonts
|
||||
x asset collections
|
||||
|
@ -1,7 +1,6 @@
|
||||
require 'mimetype_fu'
|
||||
|
||||
# require 'locomotive/patches'
|
||||
require 'locomotive/version'
|
||||
require 'locomotive/core_ext'
|
||||
require 'locomotive/configuration'
|
||||
require 'locomotive/logger'
|
||||
require 'locomotive/liquid'
|
||||
@ -15,6 +14,9 @@ require 'locomotive/admin_responder'
|
||||
require 'locomotive/routing'
|
||||
require 'locomotive/regexps'
|
||||
require 'locomotive/render'
|
||||
require 'locomotive/import'
|
||||
require 'locomotive/delayed_job'
|
||||
require 'locomotive/middlewares'
|
||||
|
||||
require 'locomotive/session_store'
|
||||
|
||||
|
@ -3,6 +3,5 @@ require 'locomotive/carrierwave/base'
|
||||
require 'locomotive/carrierwave/patches'
|
||||
|
||||
# register missing mime types
|
||||
|
||||
# what does this do?
|
||||
# EXTENSIONS[:eot] = 'application/vnd.ms-fontobject'
|
||||
EXTENSIONS[:eot] = 'application/vnd.ms-fontobject'
|
||||
EXTENSIONS[:woff] = 'application/x-woff'
|
||||
|
@ -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
|
||||
|
@ -29,6 +29,10 @@ class String
|
||||
replace(self.slugify(options))
|
||||
end
|
||||
|
||||
def parameterize!(sep = '_')
|
||||
replace(self.parameterize(sep))
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
## Hash
|
@ -5,7 +5,8 @@ module CustomFields
|
||||
class FileUploader < ::CarrierWave::Uploader::Base
|
||||
|
||||
def store_dir
|
||||
"sites/#{model.content_type.site_id}/contents/#{model.id}/files"
|
||||
puts
|
||||
"sites/#{model.site_id}/contents/#{model.class.model_name.underscore}/#{model.id}/files"
|
||||
end
|
||||
|
||||
def cache_dir
|
||||
|
47
lib/locomotive/delayed_job.rb
Normal file
47
lib/locomotive/delayed_job.rb
Normal file
@ -0,0 +1,47 @@
|
||||
require 'delayed_job'
|
||||
|
||||
module Delayed
|
||||
module Backend
|
||||
module Base
|
||||
module ClassMethods
|
||||
# Add a job to the queue
|
||||
def enqueue(*args)
|
||||
object = args.shift
|
||||
unless object.respond_to?(:perform)
|
||||
raise ArgumentError, 'Cannot enqueue items which do not respond to perform'
|
||||
end
|
||||
|
||||
attributes = {
|
||||
:job_type => object.class.name.demodulize.underscore,
|
||||
:payload_object => object,
|
||||
:priority => Delayed::Worker.default_priority,
|
||||
:run_at => nil
|
||||
}
|
||||
|
||||
if args.first.respond_to?(:[])
|
||||
attributes.merge!(args.first)
|
||||
else
|
||||
attributes.merge!({
|
||||
:priority => args.first || Delayed::Worker.default_priority,
|
||||
:run_at => args[1]
|
||||
})
|
||||
end
|
||||
|
||||
self.create(attributes)
|
||||
end
|
||||
end
|
||||
|
||||
def failed?
|
||||
failed_at.present?
|
||||
end
|
||||
end
|
||||
|
||||
module Mongoid
|
||||
class Job
|
||||
field :job_type
|
||||
field :step
|
||||
referenced_in :site
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
7
lib/locomotive/import.rb
Normal file
7
lib/locomotive/import.rb
Normal file
@ -0,0 +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'
|
53
lib/locomotive/import/asset_collections.rb
Normal file
53
lib/locomotive/import/asset_collections.rb
Normal file
@ -0,0 +1,53 @@
|
||||
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_with_index do |data, position|
|
||||
name, data = data.keys.first, data.values.first
|
||||
|
||||
attributes = { :_alias => name, :label => name.humanize, :kind => 'string', :position => position }.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
|
73
lib/locomotive/import/assets.rb
Normal file
73
lib/locomotive/import/assets.rb
Normal file
@ -0,0 +1,73 @@
|
||||
module Locomotive
|
||||
module Import
|
||||
module Assets
|
||||
|
||||
def self.process(context)
|
||||
site, theme_path = context[:site], context[:theme_path]
|
||||
|
||||
whitelist = self.build_regexps_in_withlist(context[:database]['site']['assets']['whitelist']) rescue nil
|
||||
|
||||
self.add_theme_assets(site, theme_path, whitelist)
|
||||
|
||||
self.add_other_assets(site, theme_path)
|
||||
end
|
||||
|
||||
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)
|
||||
|
||||
visible = self.check_against_whitelist(whitelist, asset_path.gsub(File.join(theme_path, 'public'), ''))
|
||||
|
||||
folder = asset_path.gsub(File.join(theme_path, 'public'), '').gsub(File.basename(asset_path), '').gsub(/^\//, '').gsub(/\/$/, '')
|
||||
|
||||
asset = site.theme_assets.where(:local_path => File.join(folder, File.basename(asset_path))).first
|
||||
|
||||
asset ||= site.theme_assets.build(:folder => folder)
|
||||
|
||||
asset.attributes = { :source => File.open(asset_path), :performing_plain_text => false, :hidden => !visible }
|
||||
asset.save!
|
||||
|
||||
site.reload
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def self.add_other_assets(site, theme_path)
|
||||
collection = AssetCollection.find_or_create_internal(site)
|
||||
|
||||
Dir[File.join(theme_path, 'public', 'samples', '*')].each do |asset_path|
|
||||
|
||||
next if File.directory?(asset_path)
|
||||
|
||||
name = File.basename(asset_path, File.extname(asset_path)).parameterize('_')
|
||||
|
||||
collection.assets.create! :name => name, :source => File.open(asset_path)
|
||||
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
|
84
lib/locomotive/import/content_types.rb
Normal file
84
lib/locomotive/import/content_types.rb
Normal file
@ -0,0 +1,84 @@
|
||||
module Locomotive
|
||||
module Import
|
||||
module ContentTypes
|
||||
|
||||
def self.process(context)
|
||||
site, database = context[:site], context[:database]
|
||||
|
||||
content_types = database['site']['content_types']
|
||||
|
||||
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.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.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) }
|
||||
|
||||
site.content_types.build(attributes)
|
||||
end
|
||||
|
||||
def self.add_or_update_fields(content_type, fields)
|
||||
fields.each_with_index do |data, position|
|
||||
name, data = data.keys.first, data.values.first
|
||||
|
||||
attributes = { :_alias => name, :label => name.humanize, :kind => 'string', :position => position }.merge(data).symbolize_keys
|
||||
|
||||
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
|
73
lib/locomotive/import/job.rb
Normal file
73
lib/locomotive/import/job.rb
Normal file
@ -0,0 +1,73 @@
|
||||
require 'zip/zipfilesystem'
|
||||
|
||||
module Locomotive
|
||||
module Import
|
||||
class Job
|
||||
|
||||
def initialize(theme_file, site = nil, enabled = {})
|
||||
raise "Theme zipfile not found" unless File.exists?(theme_file)
|
||||
|
||||
@theme_file = theme_file
|
||||
@site = site
|
||||
@enabled = enabled
|
||||
end
|
||||
|
||||
def before(worker)
|
||||
@worker = worker
|
||||
end
|
||||
|
||||
def perform
|
||||
puts "theme_file = #{@theme_file} / #{@site.present?} / #{@enabled.inspect}"
|
||||
|
||||
self.unzip!
|
||||
|
||||
raise "No database.yml found in the theme zipfile" if @database.nil?
|
||||
|
||||
context = {
|
||||
:database => @database,
|
||||
:site => @site,
|
||||
:theme_path => @theme_path,
|
||||
:error => nil,
|
||||
:worker => @worker
|
||||
}
|
||||
|
||||
%w(site content_types assets asset_collections snippets pages).each do |step|
|
||||
if @enabled[step] != false
|
||||
"Locomotive::Import::#{step.camelize}".constantize.process(context)
|
||||
@worker.update_attributes :step => step if @worker
|
||||
else
|
||||
puts "skipping #{step}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def unzip!
|
||||
Zip::ZipFile.open(@theme_file) do |zipfile|
|
||||
destination_path = File.join(Rails.root, 'tmp', 'themes', @site.id.to_s)
|
||||
|
||||
FileUtils.rm_r destination_path, :force => true
|
||||
|
||||
zipfile.each do |entry|
|
||||
next if entry.name =~ /__MACOSX/
|
||||
|
||||
if entry.name =~ /database.yml$/
|
||||
|
||||
@database = YAML.load(zipfile.read(entry.name))
|
||||
@theme_path = File.join(destination_path, entry.name.gsub('database.yml', ''))
|
||||
|
||||
next
|
||||
end
|
||||
|
||||
FileUtils.mkdir_p(File.dirname(File.join(destination_path, entry.name)))
|
||||
|
||||
zipfile.extract(entry, File.join(destination_path, entry.name))
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
132
lib/locomotive/import/pages.rb
Normal file
132
lib/locomotive/import/pages.rb
Normal file
@ -0,0 +1,132 @@
|
||||
module Locomotive
|
||||
module Import
|
||||
module Pages
|
||||
|
||||
def self.process(context)
|
||||
site, pages, theme_path = context[:site], context[:database]['pages'], context[:theme_path]
|
||||
|
||||
context[:done] = {} # initialize the hash storing pages already processed
|
||||
|
||||
self.add_index_and_404(context)
|
||||
|
||||
Dir[File.join(theme_path, 'templates', '**/*')].each do |template_path|
|
||||
|
||||
fullpath = template_path.gsub(File.join(theme_path, 'templates'), '').gsub('.liquid', '').gsub(/^\//, '')
|
||||
|
||||
# puts "=========== #{fullpath} ================="
|
||||
|
||||
next if %w(index 404).include?(fullpath)
|
||||
|
||||
self.add_page(fullpath, context)
|
||||
end
|
||||
end
|
||||
|
||||
def self.add_page(fullpath, context)
|
||||
puts "....adding #{fullpath}"
|
||||
|
||||
page = context[:done][fullpath]
|
||||
|
||||
return page if page # already added, so skip it
|
||||
|
||||
site, pages, theme_path = context[:site], context[:database]['site']['pages'], context[:theme_path]
|
||||
|
||||
template = File.read(File.join(theme_path, 'templates', "#{fullpath}.liquid")) rescue "Unable to find #{fullpath}.liquid"
|
||||
|
||||
self.build_parent_template(template, context)
|
||||
|
||||
parent = self.find_parent(fullpath, context)
|
||||
|
||||
# puts "updating..... #{fullpath} / #{template}"
|
||||
|
||||
page = site.pages.where(:fullpath => fullpath).first || site.pages.build
|
||||
|
||||
attributes = {
|
||||
:title => fullpath.split('/').last.humanize,
|
||||
:slug => fullpath.split('/').last,
|
||||
:parent => parent,
|
||||
:raw_template => template
|
||||
}.merge(pages[fullpath] || {}).symbolize_keys
|
||||
|
||||
# templatized ?
|
||||
if content_type_slug = attributes.delete(:content_type)
|
||||
attributes[:content_type] = site.content_types.where(:slug => content_type_slug).first
|
||||
end
|
||||
|
||||
page.attributes = attributes
|
||||
|
||||
# do not parse liquid templates now
|
||||
# page.instance_variable_set(:@template_changed, false)
|
||||
|
||||
page.save!
|
||||
|
||||
site.reload
|
||||
|
||||
context[:done][fullpath] = page
|
||||
|
||||
page
|
||||
end
|
||||
|
||||
def self.build_parent_template(template, context)
|
||||
# puts "building parent_template #{template.blank?}"
|
||||
|
||||
# just check if the template contains the extends keyword
|
||||
# template
|
||||
fullpath = template.scan(/\{% extends (\w+) %\}/).flatten.first
|
||||
|
||||
if fullpath # inheritance detected
|
||||
fullpath.gsub!("'", '')
|
||||
|
||||
# puts "found parent_template #{fullpath}"
|
||||
|
||||
return if fullpath == 'parent'
|
||||
|
||||
self.add_page(fullpath, context)
|
||||
else
|
||||
# puts "no parent_template found #{fullpath}"
|
||||
end
|
||||
end
|
||||
|
||||
def self.find_parent(fullpath, context)
|
||||
# puts "finding parent for #{fullpath}"
|
||||
|
||||
site = context[:site]
|
||||
|
||||
segments = fullpath.split('/')
|
||||
|
||||
return site.pages.index.first if segments.size == 1
|
||||
|
||||
segments.pop
|
||||
|
||||
parent_fullpath = segments.join('/').gsub(/^\//, '')
|
||||
|
||||
# look for a local index page in db
|
||||
parent = site.pages.where(:fullpath => parent_fullpath).first
|
||||
|
||||
parent || self.add_page(parent_fullpath, context)
|
||||
end
|
||||
|
||||
def self.add_index_and_404(context)
|
||||
site, pages, theme_path = context[:site], context[:database]['site']['pages'], context[:theme_path]
|
||||
|
||||
%w(index 404).each do |slug|
|
||||
page = site.pages.where({ :slug => slug, :depth => 0 }).first
|
||||
|
||||
# puts "building system page (#{slug}) => #{page.inspect}"
|
||||
|
||||
page ||= sites.pages.build(:slug => slug, :parent => nil)
|
||||
|
||||
template = File.read(File.join(theme_path, 'templates', "#{slug}.liquid"))
|
||||
|
||||
page.attributes = { :raw_template => template }.merge(pages[slug] || {})
|
||||
|
||||
page.save! rescue nil # TODO better error handling
|
||||
|
||||
site.reload
|
||||
|
||||
context[:done][slug] = page
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
17
lib/locomotive/import/site.rb
Normal file
17
lib/locomotive/import/site.rb
Normal file
@ -0,0 +1,17 @@
|
||||
module Locomotive
|
||||
module Import
|
||||
module Site
|
||||
|
||||
def self.process(context)
|
||||
site, database = context[:site], context[:database]
|
||||
|
||||
attributes = database['site'].clone.delete_if { |name, value| %w{pages assets content_types asset_collections}.include?(name) }
|
||||
|
||||
site.attributes = attributes
|
||||
|
||||
site.save!
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
23
lib/locomotive/import/snippets.rb
Normal file
23
lib/locomotive/import/snippets.rb
Normal file
@ -0,0 +1,23 @@
|
||||
module Locomotive
|
||||
module Import
|
||||
module Snippets
|
||||
|
||||
def self.process(context)
|
||||
site, theme_path = context[:site], context[:theme_path]
|
||||
|
||||
Dir[File.join(theme_path, 'snippets', '*')].each do |snippet_path|
|
||||
|
||||
name = File.basename(snippet_path, File.extname(snippet_path)).parameterize('_')
|
||||
|
||||
snippet = site.snippets.where(:slug => name).first || site.snippets.build(:name => name)
|
||||
|
||||
snippet.template = File.read(snippet_path) # = site.snippets.create! :name => name, :template =>
|
||||
|
||||
snippet.save!
|
||||
# puts "snippet = #{snippet.inspect}"
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
@ -1,3 +1,5 @@
|
||||
require 'locomotive/liquid/drops/base'
|
||||
|
||||
%w{. tags drops filters}.each do |dir|
|
||||
Dir[File.join(File.dirname(__FILE__), 'liquid', dir, '*.rb')].each { |lib| require lib }
|
||||
end
|
21
lib/locomotive/liquid/drops/asset.rb
Normal file
21
lib/locomotive/liquid/drops/asset.rb
Normal file
@ -0,0 +1,21 @@
|
||||
module Locomotive
|
||||
module Liquid
|
||||
module Drops
|
||||
class Asset < Base
|
||||
|
||||
def before_method(meth)
|
||||
return '' if @source.nil?
|
||||
|
||||
if not @@forbidden_attributes.include?(meth.to_s)
|
||||
value = @source.send(meth)
|
||||
end
|
||||
end
|
||||
|
||||
def url
|
||||
@source.source.url
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
@ -4,12 +4,47 @@ module Locomotive
|
||||
|
||||
class AssetCollections < ::Liquid::Drop
|
||||
|
||||
def initialize(site)
|
||||
@site = site
|
||||
def before_method(meth)
|
||||
collection = @context.registers[:site].asset_collections.where(:slug => meth.to_s).first
|
||||
AssetCollectionProxy.new(collection)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
class AssetCollectionProxy < ::Liquid::Drop
|
||||
|
||||
def initialize(collection)
|
||||
@collection = collection
|
||||
end
|
||||
|
||||
def first
|
||||
@collection.assets.first
|
||||
end
|
||||
|
||||
def last
|
||||
@collection.assets.last
|
||||
end
|
||||
|
||||
def each(&block)
|
||||
@collection.assets.each(&block)
|
||||
end
|
||||
|
||||
def paginate(options = {})
|
||||
paginated_collection = @collection.assets.paginate(options)
|
||||
{
|
||||
:collection => paginated_collection,
|
||||
:current_page => paginated_collection.current_page,
|
||||
:previous_page => paginated_collection.previous_page,
|
||||
:next_page => paginated_collection.next_page,
|
||||
:total_entries => paginated_collection.total_entries,
|
||||
:total_pages => paginated_collection.total_pages,
|
||||
:per_page => paginated_collection.per_page
|
||||
}
|
||||
end
|
||||
|
||||
def before_method(meth)
|
||||
@site.asset_collections.where(:slug => meth.to_s)
|
||||
return '' if @collection.nil?
|
||||
@collection.send(meth)
|
||||
end
|
||||
|
||||
end
|
||||
|
@ -3,21 +3,16 @@ module Locomotive
|
||||
module Drops
|
||||
class Contents < ::Liquid::Drop
|
||||
|
||||
def initialize(site)
|
||||
@site = site
|
||||
end
|
||||
|
||||
def before_method(meth)
|
||||
type = @site.content_types.where(:slug => meth.to_s).first
|
||||
ProxyCollection.new(@site, type)
|
||||
type = @context.registers[:site].content_types.where(:slug => meth.to_s).first
|
||||
ProxyCollection.new(type)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
class ProxyCollection < ::Liquid::Drop
|
||||
|
||||
def initialize(site, content_type)
|
||||
@site = site
|
||||
def initialize(content_type)
|
||||
@content_type = content_type
|
||||
@collection = nil
|
||||
end
|
||||
|
@ -1,18 +0,0 @@
|
||||
module Locomotive
|
||||
module Liquid
|
||||
module Drops
|
||||
class Javascripts < ::Liquid::Drop
|
||||
|
||||
def initialize(site)
|
||||
@site = site
|
||||
end
|
||||
|
||||
def before_method(meth)
|
||||
asset = @site.theme_assets.where(:content_type => 'javascript', :slug => meth.to_s).first
|
||||
!asset.nil? ? asset.source.url : nil
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
@ -1,19 +0,0 @@
|
||||
module Locomotive
|
||||
module Liquid
|
||||
module Drops
|
||||
class Stylesheets < ::Liquid::Drop
|
||||
|
||||
def initialize(site)
|
||||
@site = site
|
||||
end
|
||||
|
||||
def before_method(meth)
|
||||
asset = @site.theme_assets.where(:content_type => 'stylesheet', :slug => meth.to_s).first
|
||||
!asset.nil? ? asset.source.url : nil
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
@ -1,19 +0,0 @@
|
||||
module Locomotive
|
||||
module Liquid
|
||||
module Drops
|
||||
class ThemeImages < ::Liquid::Drop
|
||||
|
||||
def initialize(site)
|
||||
@site = site
|
||||
end
|
||||
|
||||
def before_method(meth)
|
||||
asset = @site.theme_assets.where(:content_type => 'image', :slug => meth.to_s).first
|
||||
!asset.nil? ? asset.source.url : nil
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
@ -7,7 +7,19 @@ module Locomotive
|
||||
# input: url of the css file
|
||||
def stylesheet_tag(input)
|
||||
return '' if input.nil?
|
||||
|
||||
unless input =~ /^(\/|http:)/
|
||||
segments = "stylesheets/#{input}".split('/')
|
||||
|
||||
filename, folder = segments.pop, segments.join('/')
|
||||
|
||||
stylesheet = ThemeAsset.new(:site => @context.registers[:site], :folder => folder)
|
||||
|
||||
input = '/' + ThemeAssetUploader.new(stylesheet).store_path(filename)
|
||||
end
|
||||
|
||||
input = "#{input}.css" unless input.ends_with?('.css')
|
||||
|
||||
%{<link href="#{input}" media="screen" rel="stylesheet" type="text/css" />}
|
||||
end
|
||||
|
||||
@ -15,16 +27,41 @@ module Locomotive
|
||||
# input: url of the javascript file
|
||||
def javascript_tag(input)
|
||||
return '' if input.nil?
|
||||
|
||||
unless input =~ /^(\/|http:)/
|
||||
segments = "javascripts/#{input}".split('/')
|
||||
|
||||
filename, folder = segments.pop, segments.join('/')
|
||||
|
||||
javascript = ThemeAsset.new(:site => @context.registers[:site], :folder => folder)
|
||||
|
||||
input = '/' + ThemeAssetUploader.new(javascript).store_path(filename)
|
||||
end
|
||||
|
||||
input = "#{input}.js" unless input.ends_with?('.js')
|
||||
|
||||
%{<script src="#{input}" type="text/javascript"></script>}
|
||||
end
|
||||
|
||||
def theme_image_url(input)
|
||||
return '' if input.nil?
|
||||
|
||||
input = "images/#{input}" unless input.starts_with?('/')
|
||||
|
||||
segments = input.split('/')
|
||||
|
||||
filename, folder = segments.pop, segments.join('/')
|
||||
|
||||
image = ThemeAsset.new(:site => @context.registers[:site], :folder => folder)
|
||||
|
||||
'/' + ThemeAssetUploader.new(image).store_path(filename)
|
||||
end
|
||||
|
||||
# Write an image tag
|
||||
# input: url of the image OR asset drop
|
||||
def image_tag(input, *args)
|
||||
image_options = inline_options(args_to_options(args))
|
||||
"<img src=\"#{File.join('/', get_path_from_asset(input))}\" #{image_options}/>"
|
||||
"<img src=\"#{File.join('/', get_url_from_asset(input))}\" #{image_options}/>"
|
||||
end
|
||||
|
||||
# Embed a flash movie into a page
|
||||
@ -32,7 +69,7 @@ module Locomotive
|
||||
# width: width (in pixel or in %) of the embedded movie
|
||||
# height: height (in pixel or in %) of the embedded movie
|
||||
def flash_tag(input, *args)
|
||||
path = get_path_from_asset(input)
|
||||
path = get_url_from_asset(input)
|
||||
embed_options = inline_options(args_to_options(args))
|
||||
%{
|
||||
<object #{embed_options}>
|
||||
@ -100,9 +137,9 @@ module Locomotive
|
||||
(options.stringify_keys.to_a.collect { |a, b| "#{a}=\"#{b}\"" }).join(' ') << ' '
|
||||
end
|
||||
|
||||
# Get the path to be used in html tags such as image_tag, flash_tag, ...etc
|
||||
# Get the url to be used in html tags such as image_tag, flash_tag, ...etc
|
||||
# input: url (String) OR asset drop
|
||||
def get_path_from_asset(input)
|
||||
def get_url_from_asset(input)
|
||||
input.respond_to?(:url) ? input.url : input
|
||||
end
|
||||
end
|
||||
|
@ -5,7 +5,7 @@ module Locomotive
|
||||
#
|
||||
# Usage:
|
||||
#
|
||||
# {% consume blog from 'http://nocoffee.tumblr.com/api/read.json?num=3' username: 'john', password: 'easy', format: 'json' %}
|
||||
# {% consume blog from 'http://nocoffee.tumblr.com/api/read.json?num=3' username: 'john', password: 'easy', format: 'json', expires_in: 3000 %}
|
||||
# {% for post in blog.posts %}
|
||||
# {{ post.title }}
|
||||
# {% endfor %}
|
||||
@ -23,6 +23,8 @@ module Locomotive
|
||||
markup.scan(::Liquid::TagAttributes) do |key, value|
|
||||
@options[key] = value if key != 'http'
|
||||
end
|
||||
@expires_in = (@options.delete('expires_in') || 0).to_i
|
||||
@cache_key = Digest::SHA1.hexdigest(@target)
|
||||
else
|
||||
raise ::Liquid::SyntaxError.new("Syntax Error in 'consume' - Valid syntax: consume <var> from \"<url>\" [username: value, password: value]")
|
||||
end
|
||||
@ -31,10 +33,18 @@ module Locomotive
|
||||
end
|
||||
|
||||
def render(context)
|
||||
context.stack do
|
||||
context.scopes.last[@target.to_s] = Locomotive::Httparty::Webservice.consume(@url, @options.symbolize_keys)
|
||||
render_all_and_cache_it(context)
|
||||
end
|
||||
|
||||
render_all(@nodelist, context)
|
||||
protected
|
||||
|
||||
def render_all_and_cache_it(context)
|
||||
Rails.cache.fetch(@cache_key, :expires_in => @expires_in) do
|
||||
context.stack do
|
||||
context.scopes.last[@target.to_s] = Locomotive::Httparty::Webservice.consume(@url, @options.symbolize_keys)
|
||||
|
||||
render_all(@nodelist, context)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
39
lib/locomotive/liquid/tags/google_analytics.rb
Normal file
39
lib/locomotive/liquid/tags/google_analytics.rb
Normal 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
|
@ -5,7 +5,7 @@ module Locomotive
|
||||
|
||||
def end_tag
|
||||
super
|
||||
|
||||
|
||||
if !self.contains_super?(@nodelist) # then disable all editable_elements coming from the parent block too and not used
|
||||
@context[:page].disable_parent_editable_elements(@name)
|
||||
end
|
||||
@ -17,7 +17,7 @@ module Locomotive
|
||||
nodelist.any? do |node|
|
||||
if node.is_a?(::Liquid::Variable) && node.name == 'block.super'
|
||||
true
|
||||
elsif node.respond_to?(:nodelist) && !node.is_a?(Locomotive::Liquid::Tags::InheritedBlock)
|
||||
elsif node.respond_to?(:nodelist) && !node.nodelist.nil? && !node.is_a?(Locomotive::Liquid::Tags::InheritedBlock)
|
||||
contains_super?(node.nodelist)
|
||||
end
|
||||
end
|
||||
|
1
lib/locomotive/middlewares.rb
Normal file
1
lib/locomotive/middlewares.rb
Normal file
@ -0,0 +1 @@
|
||||
require 'locomotive/middlewares/fonts'
|
44
lib/locomotive/middlewares/fonts.rb
Normal file
44
lib/locomotive/middlewares/fonts.rb
Normal file
@ -0,0 +1,44 @@
|
||||
require 'rack/utils'
|
||||
|
||||
module Locomotive
|
||||
module Middlewares
|
||||
class Fonts
|
||||
include Rack::Utils
|
||||
|
||||
def initialize(app, opts = {})
|
||||
@app = app
|
||||
@path_regexp = opts[:path] || %r{^/fonts/}
|
||||
@file_server = ::Rack::File.new(opts[:root] || "#{Rails.root}/public")
|
||||
@expires_in = opts[:expires_in] || 24.hour
|
||||
end
|
||||
|
||||
def call(env)
|
||||
if env["PATH_INFO"] =~ @path_regexp
|
||||
site = fetch_site(env['SERVER_NAME'])
|
||||
|
||||
if site.nil?
|
||||
@app.call(env)
|
||||
else
|
||||
env["PATH_INFO"] = ::File.join('/', 'sites', site.id.to_s, 'theme', env["PATH_INFO"])
|
||||
|
||||
response = @file_server.call(env)
|
||||
|
||||
response[1]['Cache-Control'] = "public; max-age=#{@expires_in}" # varnish
|
||||
|
||||
response
|
||||
end
|
||||
else
|
||||
@app.call(env)
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def fetch_site(domain_name)
|
||||
Rails.cache.fetch(domain_name, :expires_in => @expires_in) do
|
||||
Site.match_domain(domain_name).first
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
@ -1,8 +1,8 @@
|
||||
require 'mongoid'
|
||||
# require 'mongoid/document'
|
||||
|
||||
## various patches
|
||||
module Mongoid #:nodoc:
|
||||
|
||||
module Document
|
||||
|
||||
def update_child_with_noname(child, clear = false)
|
||||
@ -13,9 +13,21 @@ module Mongoid #:nodoc:
|
||||
|
||||
alias_method_chain :update_child, :noname
|
||||
|
||||
# module ClassMethods
|
||||
#
|
||||
# def instantiate(attrs = nil, allocating = false) # used by carrierwave to back up the original file
|
||||
# document = super
|
||||
# document.send(:run_callbacks, :initialize) do
|
||||
# document
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
module Mongoid #:nodoc:
|
||||
module Validations #:nodoc:
|
||||
class AssociatedValidator < ActiveModel::EachValidator
|
||||
|
@ -53,11 +53,8 @@ module Locomotive
|
||||
assigns = {
|
||||
'site' => current_site,
|
||||
'page' => @page,
|
||||
'asset_collections' => Locomotive::Liquid::Drops::AssetCollections.new(current_site),
|
||||
'stylesheets' => Locomotive::Liquid::Drops::Stylesheets.new(current_site),
|
||||
'javascripts' => Locomotive::Liquid::Drops::Javascripts.new(current_site),
|
||||
'images' => Locomotive::Liquid::Drops::ThemeImages.new(current_site),
|
||||
'contents' => Locomotive::Liquid::Drops::Contents.new(current_site),
|
||||
'asset_collections' => Locomotive::Liquid::Drops::AssetCollections.new,
|
||||
'contents' => Locomotive::Liquid::Drops::Contents.new,
|
||||
'current_page' => self.params[:page]
|
||||
}
|
||||
|
||||
|
@ -81,6 +81,9 @@ end
|
||||
# # empty page (imac 27'): User System Total Real
|
||||
# Rendering page 10k times 21.390000 1.820000 23.210000 ( 24.120529)
|
||||
|
||||
# # empty page (mac mini core 2 duo / 2Go): User System Total Real
|
||||
# Rendering a simple page 10k times 17.130000 0.420000 17.550000 ( 19.459768)
|
||||
|
||||
# # page with inherited template (imac 27'): User System Total Real
|
||||
# Rendering page 10k times 85.840000 7.600000 93.440000 ( 97.841248)
|
||||
|
||||
@ -89,3 +92,8 @@ end
|
||||
|
||||
# # with locomotive liquid (imac 27'): User System Total Real
|
||||
# Rendering page 10k times 38.750000 3.050000 41.800000 ( 42.880022)
|
||||
|
||||
# # with locomotive liquid (mac mini core 2 duo / 2Go): User System Total Real
|
||||
# Rendering a complex page 10k times 30.840000 0.530000 31.370000 ( 32.847565)
|
||||
|
||||
|
||||
|
BIN
public/images/admin/list/icons/states.png
Normal file
BIN
public/images/admin/list/icons/states.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.0 KiB |
@ -1,4 +1,3 @@
|
||||
var foo = null;
|
||||
var I18nLocale = null;
|
||||
var CodeMirrorEditors = [];
|
||||
|
||||
@ -128,7 +127,7 @@ $(document).ready(function() {
|
||||
$('code.html textarea').each(function() { addCodeMirrorEditor('liquid', $(this)); });
|
||||
|
||||
// site selector
|
||||
$('#site-selector').selectmenu({ style: 'dropdown', width: 300, offsetTop: 8, change: function(event, ui) {
|
||||
$('#site-selector').selectmenu({ style: 'dropdown', width: 395, offsetTop: 8, change: function(event, ui) {
|
||||
$('#site-selector').parent().submit();
|
||||
} });
|
||||
|
||||
|
32
public/javascripts/admin/import.js
Normal file
32
public/javascripts/admin/import.js
Normal file
@ -0,0 +1,32 @@
|
||||
$(document).ready(function() {
|
||||
|
||||
$('#import-steps').smartupdater({
|
||||
url : $('#import-steps').attr('data-url'),
|
||||
dataType: 'json',
|
||||
minTimeout: 100
|
||||
}, function(data) {
|
||||
var steps = ['site', 'content_types', 'assets', 'asset_collections', 'snippets', 'pages'];
|
||||
|
||||
var currentIndex = data.step == 'done' ? steps.length - 1 : steps.indexOf(data.step);
|
||||
|
||||
for (var i = 0; i < steps.length; i++) {
|
||||
var state = null;
|
||||
|
||||
if (i <= currentIndex) state = 'done';
|
||||
if (i == currentIndex + 1 && data.failed) state = 'failed';
|
||||
|
||||
if (state != null)
|
||||
$('#import-steps li:eq(' + i + ')').addClass(state);
|
||||
}
|
||||
|
||||
if (data.step == 'done')
|
||||
$.growl('notice', $('#import-steps').attr('data-success-message'));
|
||||
|
||||
if (data.failed)
|
||||
$.growl('alert', $('#import-steps').attr('data-failure-message'));
|
||||
|
||||
if (data.step == 'done' || data.failed)
|
||||
$('#import-steps').smartupdaterStop();
|
||||
});
|
||||
|
||||
});
|
@ -80,7 +80,7 @@ $(document).ready(function() {
|
||||
if (typeof $.fn.imagepicker != 'undefined')
|
||||
$('a#image-picker-link').imagepicker({
|
||||
insertFn: function(link) {
|
||||
return "{{ theme_images." + link.attr('data-slug') + " }}";
|
||||
return "{{ '" + link.attr('data-local-path') + "' | theme_image_url }}";
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -47,13 +47,13 @@ $.fn.imagepicker = function(options) {
|
||||
.insertBefore($('.asset-picker ul li.clear'))
|
||||
.addClass('asset');
|
||||
|
||||
asset.find('h4 a').attr('href', json.url)
|
||||
.attr('data-slug', json.slug)
|
||||
.attr('data-shortcut-url', json.shortcut_url)
|
||||
.html(json.name).bind('click', function(e) {
|
||||
asset.find('strong a').attr('href', json.url)
|
||||
.attr('data-local-path', json.local_path)
|
||||
.html(json.local_path).bind('click', function(e) {
|
||||
copyLinkToEditor($(this), e);
|
||||
});
|
||||
asset.find('.image .inside img').attr('src', json.vignette_url);
|
||||
asset.find('.more .size').html(json.size);
|
||||
asset.find('.more .date').html(json.date);
|
||||
|
||||
if ($('.asset-picker ul li.asset').length % 3 == 0)
|
||||
asset.addClass('last');
|
||||
@ -74,7 +74,7 @@ $.fn.imagepicker = function(options) {
|
||||
'onComplete': function() {
|
||||
setupUploader();
|
||||
|
||||
$('ul.assets h4 a').bind('click', function(e) { copyLinkToEditor($(this), e); });
|
||||
$('ul.theme-assets strong a').bind('click', function(e) { copyLinkToEditor($(this), e); });
|
||||
}
|
||||
});
|
||||
});
|
||||
|
155
public/javascripts/admin/plugins/smartupdater.js
Normal file
155
public/javascripts/admin/plugins/smartupdater.js
Normal file
@ -0,0 +1,155 @@
|
||||
/**
|
||||
* smartupdater - jQuery Plugin
|
||||
*
|
||||
* Version - 2.0.0
|
||||
*
|
||||
* Copyright (c) 2010 Vadim Kiryukhin
|
||||
* vkiryukhin@gmail.com
|
||||
*
|
||||
* Dual licensed under the MIT and GPL licenses:
|
||||
* http://www.opensource.org/licenses/mit-license.php
|
||||
* http://www.gnu.org/licenses/gpl.html
|
||||
*
|
||||
* Based on the work done by Terry M. Schmidt and the jQuery ekko plugin.
|
||||
*
|
||||
* USAGE:
|
||||
*
|
||||
* $("#myObject").smartupdater({
|
||||
* url : "demo.php",
|
||||
* minTimeout : 60000
|
||||
* }, function (data) {
|
||||
* //process data here;
|
||||
* }
|
||||
* );
|
||||
*
|
||||
* Public functions:
|
||||
* $("#myObject").smartupdaterStop();
|
||||
* $("#myObject").smartupdaterRestart();
|
||||
* $("#myObject").smartupdaterSetTimeout();
|
||||
*
|
||||
* Public Attributes:
|
||||
* var smStatus = $("#myObject")[0].smartupdaterStatus.state; // "ON" | "OFF" | "undefined"
|
||||
* var smTimeout = $("#myObject")[0].smartupdaterStatus.timeout; // current timeout
|
||||
*
|
||||
**/
|
||||
|
||||
(function(jQuery) {
|
||||
jQuery.fn.smartupdater = function (options, callback) {
|
||||
|
||||
return this.each(function () {
|
||||
var elem = this;
|
||||
|
||||
elem.settings = jQuery.extend({
|
||||
url : '', // see jQuery.ajax for details
|
||||
type : 'get', // see jQuery.ajax for details
|
||||
data : '', // see jQuery.ajax for details
|
||||
dataType : 'text', // see jQuery.ajax for details
|
||||
|
||||
minTimeout : 60000, // Starting value for the timeout in milliseconds; default 1 minute.
|
||||
maxTimeout : ((1000 * 60) * 60), // Default 1 hour.
|
||||
multiplier : 2, //if set to 2, interval will double each time the response hasn't changed.
|
||||
maxFailedRequests : 10 // smartupdater stops after this number of consecutive ajax failures;
|
||||
|
||||
}, options);
|
||||
|
||||
elem.smartupdaterStatus = {};
|
||||
elem.smartupdaterStatus.state = '';
|
||||
elem.smartupdaterStatus.timeout = 0;
|
||||
|
||||
var es = elem.settings;
|
||||
|
||||
es.prevContent = '';
|
||||
es.originalMinTimeout = es.minTimeout;
|
||||
es.failedRequests = 0;
|
||||
es.response = '';
|
||||
|
||||
function start() {
|
||||
$.ajax({url: es.url,
|
||||
type: es.type,
|
||||
data: es.data,
|
||||
dataType: es.dataType,
|
||||
success: function (data) {
|
||||
|
||||
if(es.dataType == 'json') {
|
||||
es.response = JSON.stringify(data);
|
||||
if ( data.smartupdater) {
|
||||
es.originalMinTimeout = data.smartupdater;
|
||||
}
|
||||
} else {
|
||||
es.response = data;
|
||||
}
|
||||
|
||||
if (es.prevContent != es.response) {
|
||||
es.prevContent = es.response;
|
||||
es.minTimeout = es.originalMinTimeout;
|
||||
es.periodicalUpdater = setTimeout(start, es.minTimeout);
|
||||
elem.smartupdaterStatus.timeout = es.minTimeout;
|
||||
callback(data);
|
||||
} else if (es.multiplier > 1) {
|
||||
es.minTimeout = (es.minTimeout < es.maxTimeout) ? Math.round(es.minTimeout * es.multiplier) : es.maxTimeout;
|
||||
es.periodicalUpdater = setTimeout(start, es.minTimeout);
|
||||
elem.smartupdaterStatus.timeout = es.minTimeout;
|
||||
}
|
||||
|
||||
es.failedRequests = 0;
|
||||
elem.smartupdaterStatus.state = 'ON';
|
||||
},
|
||||
|
||||
error: function() {
|
||||
if ( ++es.failedRequests < es.maxFailedRequests ) {
|
||||
es.periodicalUpdater = setTimeout(start, es.minTimeout);
|
||||
elem.smartupdaterStatus.timeout = es.minTimeout;
|
||||
} else {
|
||||
clearTimeout(es.periodicalUpdater);
|
||||
elem.smartupdaterStatus.state = 'OFF';
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
es.fnStart = start;
|
||||
start();
|
||||
});
|
||||
};
|
||||
|
||||
jQuery.fn.smartupdaterStop = function () {
|
||||
return this.each(function () {
|
||||
var elem = this;
|
||||
clearTimeout(elem.settings.periodicalUpdater);
|
||||
elem.smartupdaterStatus.state = 'OFF';
|
||||
});
|
||||
};
|
||||
|
||||
jQuery.fn.smartupdaterRestart = function () {
|
||||
return this.each(function () {
|
||||
var elem = this;
|
||||
clearTimeout(elem.settings.periodicalUpdater);
|
||||
elem.settings.minTimeout = elem.settings.originalMinTimeout;
|
||||
elem.settings.fnStart();
|
||||
});
|
||||
};
|
||||
|
||||
jQuery.fn.smartupdaterSetTimeout = function (period) {
|
||||
return this.each(function () {
|
||||
var elem = this;
|
||||
clearTimeout(elem.settings.periodicalUpdater);
|
||||
this.settings.originalMinTimeout = period;
|
||||
this.settings.fnStart();
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
})(jQuery);
|
||||
|
||||
|
||||
/******************************************************
|
||||
* http://www.JSON.org/json2.js
|
||||
* 2010-03-20
|
||||
*
|
||||
* Public Domain.
|
||||
*
|
||||
* NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
|
||||
*
|
||||
* See http://www.JSON.org/js.html
|
||||
*********************************************************/
|
||||
if(!this.JSON){this.JSON={}}(function(){function f(n){return n<10?"0"+n:n}if(typeof Date.prototype.toJSON!=="function"){Date.prototype.toJSON=function(key){return isFinite(this.valueOf())?this.getUTCFullYear()+"-"+f(this.getUTCMonth()+1)+"-"+f(this.getUTCDate())+"T"+f(this.getUTCHours())+":"+f(this.getUTCMinutes())+":"+f(this.getUTCSeconds())+"Z":null};String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(key){return this.valueOf()}}var cx=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,escapable=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,gap,indent,meta={"\b":"\\b","\t":"\\t","\n":"\\n","\f":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"},rep;function quote(string){escapable.lastIndex=0;return escapable.test(string)?'"'+string.replace(escapable,function(a){var c=meta[a];return typeof c==="string"?c:"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)})+'"':'"'+string+'"'}function str(key,holder){var i,k,v,length,mind=gap,partial,value=holder[key];if(value&&typeof value==="object"&&typeof value.toJSON==="function"){value=value.toJSON(key)}if(typeof rep==="function"){value=rep.call(holder,key,value)}switch(typeof value){case"string":return quote(value);case"number":return isFinite(value)?String(value):"null";case"boolean":case"null":return String(value);case"object":if(!value){return"null"}gap+=indent;partial=[];if(Object.prototype.toString.apply(value)==="[object Array]"){length=value.length;for(i=0;i<length;i+=1){partial[i]=str(i,value)||"null"}v=partial.length===0?"[]":gap?"[\n"+gap+partial.join(",\n"+gap)+"\n"+mind+"]":"["+partial.join(",")+"]";gap=mind;return v}if(rep&&typeof rep==="object"){length=rep.length;for(i=0;i<length;i+=1){k=rep[i];if(typeof k==="string"){v=str(k,value);if(v){partial.push(quote(k)+(gap?": ":":")+v)}}}}else{for(k in value){if(Object.hasOwnProperty.call(value,k)){v=str(k,value);if(v){partial.push(quote(k)+(gap?": ":":")+v)}}}}v=partial.length===0?"{}":gap?"{\n"+gap+partial.join(",\n"+gap)+"\n"+mind+"}":"{"+partial.join(",")+"}";gap=mind;return v}}if(typeof JSON.stringify!=="function"){JSON.stringify=function(value,replacer,space){var i;gap="";indent="";if(typeof space==="number"){for(i=0;i<space;i+=1){indent+=" "}}else{if(typeof space==="string"){indent=space}}rep=replacer;if(replacer&&typeof replacer!=="function"&&(typeof replacer!=="object"||typeof replacer.length!=="number")){throw new Error("JSON.stringify")}return str("",{"":value})}}if(typeof JSON.parse!=="function"){JSON.parse=function(text,reviver){var j;function walk(holder,key){var k,v,value=holder[key];if(value&&typeof value==="object"){for(k in value){if(Object.hasOwnProperty.call(value,k)){v=walk(value,k);if(v!==undefined){value[k]=v}else{delete value[k]}}}}return reviver.call(holder,key,value)}text=String(text);cx.lastIndex=0;if(cx.test(text)){text=text.replace(cx,function(a){return"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)})}if(/^[\],:{}\s]*$/.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]").replace(/(?:^|:|,)(?:\s*\[)+/g,""))){j=eval("("+text+")");return typeof reviver==="function"?walk({"":j},""):j}throw new SyntaxError("JSON.parse")}}}());
|
@ -37,7 +37,7 @@ $(document).ready(function() {
|
||||
|
||||
$('a#image-picker-link').imagepicker({
|
||||
insertFn: function(link) {
|
||||
return link.attr('data-shortcut-url');
|
||||
return link.attr('href');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
@ -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; }
|
||||
@ -104,6 +111,12 @@ 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; }
|
||||
|
||||
ul.theme-assets li.hidden strong a { font-style: italic; color: #8B8D9A; font-weight: normal; }
|
||||
|
||||
/* ___ contents ___ */
|
||||
|
||||
#contents-list li { background: none; }
|
||||
@ -120,6 +133,7 @@ div#uploadAssetsInputQueue { display: none; }
|
||||
background: transparent url(/images/admin/list/item-right.png) no-repeat right 0;
|
||||
}
|
||||
|
||||
/* ___ snippets ___ */
|
||||
|
||||
/* ___ pages ___ */
|
||||
|
||||
@ -176,6 +190,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; }
|
||||
@ -205,3 +220,31 @@ div#uploadAssetsInputQueue { display: none; }
|
||||
#progressbar-wrapper { margin: 40px 0; height: 30px; }
|
||||
#progressbar-wrapper #progressbar { height: 100%; }
|
||||
|
||||
/* ___ import steps ___ */
|
||||
|
||||
#import-steps { margin: 0px 200px; }
|
||||
|
||||
#import-steps li strong a { color: #b7baca; }
|
||||
|
||||
#import-steps li .more .states {
|
||||
position: relative;
|
||||
top: 4px;
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
background: transparent url(/images/admin/list/icons/states.png) no-repeat 0 0;
|
||||
}
|
||||
|
||||
#import-steps li.done .more .states {
|
||||
background-position: 0 -16px;
|
||||
}
|
||||
|
||||
#import-steps li.failed .more .states {
|
||||
background-position: 0 -32px;
|
||||
}
|
||||
|
||||
#import-steps li.done strong a {
|
||||
color: #1F82BC;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -49,7 +49,7 @@
|
||||
|
||||
/* ___ asset picker ___ */
|
||||
|
||||
div.asset-picker { width: 470px; position: relative; }
|
||||
div.asset-picker { width: 720px; position: relative; }
|
||||
div.asset-picker .actions { position: absolute; right: 4px; top: 0px; }
|
||||
|
||||
div.asset-picker p.no-items { background-image: url("/images/admin/list/none-small.png"); }
|
||||
@ -57,6 +57,9 @@ div.asset-picker p.no-items { background-image: url("/images/admin/list/none-sma
|
||||
div.asset-picker ul { overflow: auto; height: 471px; }
|
||||
div.asset-picker ul li.new-asset { display: none; }
|
||||
|
||||
div.asset-picker ul { margin: 0px; }
|
||||
div.asset-picker ul li .more { top: 8px; }
|
||||
|
||||
/* ___ custom fields ___ */
|
||||
|
||||
#edit-custom-field {
|
||||
|
@ -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; }
|
||||
|
@ -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 {
|
||||
|
@ -24,10 +24,10 @@ describe Locomotive::Liquid::Drops::Contents do
|
||||
|
||||
def render_template(template = '', assigns = {})
|
||||
assigns = {
|
||||
'contents' => Locomotive::Liquid::Drops::Contents.new(@site)
|
||||
'contents' => Locomotive::Liquid::Drops::Contents.new
|
||||
}.merge(assigns)
|
||||
|
||||
Liquid::Template.parse(template).render assigns
|
||||
Liquid::Template.parse(template).render(::Liquid::Context.new({}, assigns, { :site => @site }))
|
||||
end
|
||||
|
||||
end
|
||||
|
@ -4,20 +4,47 @@ describe Locomotive::Liquid::Filters::Html do
|
||||
|
||||
include Locomotive::Liquid::Filters::Html
|
||||
|
||||
before(:each) do
|
||||
@context = build_context
|
||||
end
|
||||
|
||||
it 'should return a link tag for a stylesheet file' do
|
||||
result = "<link href=\"main.css\" media=\"screen\" rel=\"stylesheet\" type=\"text/css\" />"
|
||||
result = "<link href=\"/sites/42/theme/stylesheets/main.css\" media=\"screen\" rel=\"stylesheet\" type=\"text/css\" />"
|
||||
stylesheet_tag('main.css').should == result
|
||||
stylesheet_tag('main').should == result
|
||||
stylesheet_tag(nil).should == ''
|
||||
end
|
||||
|
||||
it 'should return a link tag for a stylesheet file with folder' do
|
||||
result = "<link href=\"/sites/42/theme/stylesheets/trash/main.css\" media=\"screen\" rel=\"stylesheet\" type=\"text/css\" />"
|
||||
stylesheet_tag('trash/main.css').should == result
|
||||
end
|
||||
|
||||
it 'should return a link tag for a stylesheet file without touching the url' do
|
||||
result = "<link href=\"/trash/main.css\" media=\"screen\" rel=\"stylesheet\" type=\"text/css\" />"
|
||||
stylesheet_tag('/trash/main.css').should == result
|
||||
stylesheet_tag('/trash/main').should == result
|
||||
end
|
||||
|
||||
it 'should return a script tag for a javascript file' do
|
||||
result = %{<script src="main.js" type="text/javascript"></script>}
|
||||
result = %{<script src="/sites/42/theme/javascripts/main.js" type="text/javascript"></script>}
|
||||
javascript_tag('main.js').should == result
|
||||
javascript_tag('main').should == result
|
||||
javascript_tag(nil).should == ''
|
||||
end
|
||||
|
||||
it 'should return a script tag for a javascript file with folder' do
|
||||
result = %{<script src="/sites/42/theme/javascripts/trash/main.js" type="text/javascript"></script>}
|
||||
javascript_tag('trash/main.js').should == result
|
||||
javascript_tag('trash/main').should == result
|
||||
end
|
||||
|
||||
it 'should return a script tag for a javascript file without touching the url' do
|
||||
result = %{<script src="/trash/main.js" type="text/javascript"></script>}
|
||||
javascript_tag('/trash/main.js').should == result
|
||||
javascript_tag('/trash/main').should == result
|
||||
end
|
||||
|
||||
it 'should return an image tag without paramaters' do
|
||||
image_tag('foo.jpg').should == "<img src=\"/foo.jpg\" />"
|
||||
end
|
||||
@ -79,4 +106,15 @@ describe Locomotive::Liquid::Filters::Html do
|
||||
html.should == ''
|
||||
end
|
||||
|
||||
def build_context
|
||||
Site.any_instance.stubs(:id).returns(42)
|
||||
klass = Class.new
|
||||
klass.class_eval do
|
||||
def registers
|
||||
{ :site => Factory.build(:site) }
|
||||
end
|
||||
end
|
||||
klass.new
|
||||
end
|
||||
|
||||
end
|
||||
|
@ -23,10 +23,28 @@ describe ThemeAsset do
|
||||
@asset.height.should == 32
|
||||
end
|
||||
|
||||
it 'should have a slug' do
|
||||
end
|
||||
|
||||
describe 'local path and folder' do
|
||||
|
||||
it 'should set the local path based on the content type' do
|
||||
@asset.source = FixturedAsset.open('5k.png')
|
||||
@asset.save
|
||||
@asset.slug.should == '5k'
|
||||
@asset.local_path.should == 'images/5k.png'
|
||||
end
|
||||
|
||||
it 'should set the local path based on the folder' do
|
||||
@asset.folder = 'trash'
|
||||
@asset.source = FixturedAsset.open('5k.png')
|
||||
@asset.save
|
||||
@asset.local_path.should == 'images/trash/5k.png'
|
||||
end
|
||||
|
||||
it 'should set sanitize the local path' do
|
||||
@asset.folder = '/images/à la poubelle'
|
||||
@asset.source = FixturedAsset.open('5k.png')
|
||||
@asset.save
|
||||
@asset.local_path.should == 'images/a_la_poubelle/5k.png'
|
||||
end
|
||||
|
||||
end
|
||||
@ -64,10 +82,12 @@ describe ThemeAsset do
|
||||
|
||||
before(:each) do
|
||||
ThemeAsset.any_instance.stubs(:site_id).returns('test')
|
||||
@asset = Factory.build(:theme_asset, :site => Factory.build(:site))
|
||||
@asset.performing_plain_text = true
|
||||
@asset.slug = 'a file'
|
||||
@asset.plain_text = "Lorem ipsum"
|
||||
@asset = Factory.build(:theme_asset, {
|
||||
:site => Factory.build(:site),
|
||||
:plain_text_name => 'test',
|
||||
:plain_text => 'Lorem ipsum',
|
||||
:performing_plain_text => true
|
||||
})
|
||||
end
|
||||
|
||||
it 'should handle stylesheet' do
|
||||
@ -84,24 +104,6 @@ describe ThemeAsset do
|
||||
@asset.source.should_not be_nil
|
||||
end
|
||||
|
||||
context 'shortcut urls' do
|
||||
|
||||
before(:each) do
|
||||
@image = Factory.build(:theme_asset, :source => FixturedAsset.open('5k.png'))
|
||||
@image.source.stubs(:url).returns('5k.png')
|
||||
@asset.stubs(:stylesheet?).returns(true)
|
||||
@asset.site.theme_assets.stubs(:where).returns([@image])
|
||||
@asset.plain_text = 'body { background-image: url("/theme/images/5k.png"); } h1 { background-image: url("/images/5k.png"); }'
|
||||
@asset.store_plain_text
|
||||
end
|
||||
|
||||
it 'replaces shortcut url if present' do
|
||||
@asset.plain_text.should == 'body { background-image: url("/theme/images/5k.png"); } h1 { background-image: url("/images/5k.png"); }'
|
||||
@asset.source.read.should == 'body { background-image: url("5k.png"); } h1 { background-image: url("/images/5k.png"); }'
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
Loading…
Reference in New Issue
Block a user