refactor locomotive liquid drops + implement the theme or site import page (95% done)
This commit is contained in:
parent
a5ea70229c
commit
eabe4903f3
48
app/controllers/admin/imports_controller.rb
Normal file
48
app/controllers/admin/imports_controller.rb
Normal file
@ -0,0 +1,48 @@
|
||||
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.try(:failed?) } }
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def new; end
|
||||
|
||||
def create
|
||||
if params[:zipfile].blank?
|
||||
@error = t('errors.messages.blank')
|
||||
flash[:alert] = t('flash.admin.imports.create.error')
|
||||
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.alert')
|
||||
|
||||
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
|
@ -33,7 +33,7 @@ class Asset
|
||||
end
|
||||
|
||||
def to_liquid
|
||||
{ :url => self.source.url }.merge(self.attributes)
|
||||
Locomotive::Liquid::Drops::Asset.new(self)
|
||||
end
|
||||
|
||||
end
|
||||
|
@ -23,7 +23,7 @@ class ThemeAssetUploader < AssetUploader
|
||||
|
||||
def store_dir
|
||||
# "sites/#{model.site_id}/themes/#{model.id}"
|
||||
"sites/#{model.site_id}/theme/"
|
||||
"sites/#{model.site_id}/theme/#{model.content_type.pluralize}"
|
||||
end
|
||||
|
||||
def extension_white_list
|
||||
|
@ -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) }
|
||||
- %w(site content_types assets 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| [truncate(site.name, :length => 23), 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'
|
||||
|
@ -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"
|
||||
@ -243,6 +245,20 @@ 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
|
||||
snippets: Snippets
|
||||
pages: Pages
|
||||
|
||||
formtastic:
|
||||
titles:
|
||||
information: General information
|
||||
@ -262,6 +278,7 @@ en:
|
||||
other_fields: Other information
|
||||
presentation: Presentation
|
||||
attributes: Attributes
|
||||
upload: Upload
|
||||
labels:
|
||||
page:
|
||||
raw_template: Template
|
||||
@ -273,6 +290,9 @@ en:
|
||||
custom_fields:
|
||||
field:
|
||||
_alias: Alias
|
||||
import:
|
||||
new:
|
||||
source: File
|
||||
|
||||
hints:
|
||||
page:
|
||||
@ -293,4 +313,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"
|
||||
@ -242,6 +244,20 @@ 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
|
||||
snippets: Snippets
|
||||
pages: Pages
|
||||
|
||||
formtastic:
|
||||
titles:
|
||||
information: Informations générales
|
||||
@ -261,6 +277,7 @@ fr:
|
||||
other_fields: Autres informations
|
||||
presentation: Présentation
|
||||
attributes: Propriétés
|
||||
upload: Envoi au serveur
|
||||
labels:
|
||||
theme_asset:
|
||||
new:
|
||||
@ -270,6 +287,9 @@ fr:
|
||||
custom_fields:
|
||||
field:
|
||||
_alias: Alias
|
||||
import:
|
||||
new:
|
||||
source: Fichier
|
||||
|
||||
hints:
|
||||
page:
|
||||
@ -290,3 +310,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: "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"
|
@ -48,6 +48,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
|
||||
|
16
doc/TODO
16
doc/TODO
@ -19,17 +19,17 @@ BOARD:
|
||||
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 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
|
||||
- global regions: keyword in editable element (http://www.mongodb.org/display/DOCS/Updating)
|
||||
- write my first tutorial about locomotive
|
||||
- asset collections => liquid
|
||||
- apply http://github.com/flori/json/commit/2c0f8d2c9b15a33b8d10ffcb1959aef54d320b57
|
||||
- snippet dependencies => do not work correctly
|
||||
- images tag to write
|
||||
- import tool:
|
||||
x select field (see custom fields and nocoffee theme) ?
|
||||
- disable sub tasks by passing options
|
||||
- exceptions
|
||||
- page to import theme
|
||||
|
||||
- refactor slugify method (use parameterize + create a module)
|
||||
- [content types] the "display column" selector should not include file types
|
||||
|
@ -13,6 +13,7 @@ require 'locomotive/routing'
|
||||
require 'locomotive/regexps'
|
||||
require 'locomotive/render'
|
||||
require 'locomotive/import'
|
||||
require 'locomotive/delayed_job'
|
||||
|
||||
require 'mongo_session_store/mongoid'
|
||||
|
||||
|
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
|
@ -4,15 +4,23 @@ module Locomotive
|
||||
module Import
|
||||
class Job
|
||||
|
||||
# attr_accessor :theme_file, :site, :enabled
|
||||
|
||||
def initialize(theme_file, site = nil, enabled = {})
|
||||
raise "Theme zipfile not found" unless File.exists?(theme_file)
|
||||
|
||||
@theme_file = theme_file
|
||||
@site = site
|
||||
@enabled = Hash.new(true).merge(enabled)
|
||||
@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?
|
||||
@ -21,31 +29,35 @@ module Locomotive
|
||||
:database => @database,
|
||||
:site => @site,
|
||||
:theme_path => @theme_path,
|
||||
:error => nil
|
||||
:error => nil,
|
||||
:worker => @worker
|
||||
}
|
||||
|
||||
begin
|
||||
%w(site content_types assets snippets pages).each do |part|
|
||||
if @enabled[part]
|
||||
"Locomotive::Import::#{part.camelize}".constantize.process(context)
|
||||
else
|
||||
puts "skipping #{part}"
|
||||
end
|
||||
%w(site content_types assets snippets pages).each do |step|
|
||||
if @enabled[step] != false
|
||||
@worker.update_attributes :step => step
|
||||
puts "@worker...#{@worker.failed_at.inspect} / #{@worker.failed?.inspect}"
|
||||
"Locomotive::Import::#{step.camelize}".constantize.process(context)
|
||||
else
|
||||
puts "skipping #{step}"
|
||||
end
|
||||
rescue Exception => e
|
||||
context[:error] = e.message
|
||||
end
|
||||
|
||||
context
|
||||
# Locomotive::Import::Site.process(context)
|
||||
|
||||
# rescue Exception => e
|
||||
# context[:error] = e.message
|
||||
# end
|
||||
#
|
||||
# Locomotive::Import::ContentTypes.process(context)
|
||||
#
|
||||
# Locomotive::Import::Assets.process(context)
|
||||
#
|
||||
# Locomotive::Import::Snippets.process(context)
|
||||
#
|
||||
# Locomotive::Import::Pages.process(context)
|
||||
# context
|
||||
# # Locomotive::Import::Site.process(context)
|
||||
# #
|
||||
# # Locomotive::Import::ContentTypes.process(context)
|
||||
# #
|
||||
# # Locomotive::Import::Assets.process(context)
|
||||
# #
|
||||
# # Locomotive::Import::Snippets.process(context)
|
||||
# #
|
||||
# # Locomotive::Import::Pages.process(context)
|
||||
end
|
||||
|
||||
protected
|
||||
@ -62,7 +74,7 @@ module Locomotive
|
||||
if entry.name =~ /database.yml$/
|
||||
|
||||
@database = YAML.load(zipfile.read(entry.name))
|
||||
@theme_path= File.join(destination_path, entry.name.gsub('database.yml', ''))
|
||||
@theme_path = File.join(destination_path, entry.name.gsub('database.yml', ''))
|
||||
|
||||
next
|
||||
end
|
||||
|
@ -4,7 +4,7 @@ module Locomotive
|
||||
|
||||
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)
|
||||
@ -13,7 +13,7 @@ module Locomotive
|
||||
|
||||
fullpath = template_path.gsub(File.join(theme_path, 'templates'), '').gsub('.liquid', '').gsub(/^\//, '')
|
||||
|
||||
puts "=========== #{fullpath} ================="
|
||||
# puts "=========== #{fullpath} ================="
|
||||
|
||||
next if %w(index 404).include?(fullpath)
|
||||
|
||||
@ -23,28 +23,28 @@ module Locomotive
|
||||
|
||||
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"))
|
||||
|
||||
self.build_parent_template(template, context)
|
||||
|
||||
parent = self.find_parent(fullpath, context)
|
||||
|
||||
puts "updating..... #{fullpath} / #{template}"
|
||||
# puts "updating..... #{fullpath} / #{template}"
|
||||
|
||||
page = site.pages.where(:fullpath => fullpath).first || site.pages.build
|
||||
|
||||
attributes = {
|
||||
|
||||
attributes = {
|
||||
:title => fullpath.split('/').last.humanize,
|
||||
:slug => fullpath.split('/').last,
|
||||
:parent => parent,
|
||||
:raw_template => template
|
||||
:slug => fullpath.split('/').last,
|
||||
:parent => parent,
|
||||
:raw_template => template
|
||||
}.merge(pages[fullpath] || {}).symbolize_keys
|
||||
|
||||
# templatized ?
|
||||
@ -60,37 +60,37 @@ module Locomotive
|
||||
page.save!
|
||||
|
||||
site.reload
|
||||
|
||||
|
||||
context[:done][fullpath] = page
|
||||
|
||||
|
||||
page
|
||||
end
|
||||
|
||||
|
||||
def self.build_parent_template(template, context)
|
||||
puts "building parent_template #{template.blank?}"
|
||||
|
||||
# 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}"
|
||||
|
||||
|
||||
# puts "found parent_template #{fullpath}"
|
||||
|
||||
return if fullpath == 'parent'
|
||||
|
||||
|
||||
self.add_page(fullpath, context)
|
||||
else
|
||||
puts "no parent_template found #{fullpath}"
|
||||
# puts "no parent_template found #{fullpath}"
|
||||
end
|
||||
end
|
||||
|
||||
def self.find_parent(fullpath, context)
|
||||
puts "finding parent for #{fullpath}"
|
||||
|
||||
# puts "finding parent for #{fullpath}"
|
||||
|
||||
site = context[:site]
|
||||
|
||||
|
||||
segments = fullpath.split('/')
|
||||
|
||||
return site.pages.index.first if segments.size == 1
|
||||
@ -110,11 +110,11 @@ module Locomotive
|
||||
|
||||
%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] || {})
|
||||
@ -122,7 +122,7 @@ module Locomotive
|
||||
page.save! rescue nil # TODO better error handling
|
||||
|
||||
site.reload
|
||||
|
||||
|
||||
context[:done][slug] = page
|
||||
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
|
17
lib/locomotive/liquid/drops/asset.rb
Normal file
17
lib/locomotive/liquid/drops/asset.rb
Normal file
@ -0,0 +1,17 @@
|
||||
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
|
||||
|
||||
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)
|
||||
AssetCollection.new(collection)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
class AssetCollection < ::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,19 +0,0 @@
|
||||
module Locomotive
|
||||
module Liquid
|
||||
module Drops
|
||||
class Javascripts < ::Liquid::Drop
|
||||
|
||||
def initialize(site)
|
||||
@site = site
|
||||
end
|
||||
|
||||
def before_method(meth)
|
||||
@context.registers[:theme_uploader].store_path(meth.gsub('__', '.'))
|
||||
# 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,20 +0,0 @@
|
||||
module Locomotive
|
||||
module Liquid
|
||||
module Drops
|
||||
class Stylesheets < ::Liquid::Drop
|
||||
|
||||
def initialize(site)
|
||||
@site = site
|
||||
end
|
||||
|
||||
def before_method(meth)
|
||||
@context.registers[:theme_uploader].store_path(meth.gsub('__', '.'))
|
||||
# asset = @site.theme_assets.where(:content_type => 'stylesheet', :slug => meth.to_s).first
|
||||
# !asset.nil? ? asset.source.url : nil
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
27
lib/locomotive/liquid/drops/theme_assets.rb
Normal file
27
lib/locomotive/liquid/drops/theme_assets.rb
Normal file
@ -0,0 +1,27 @@
|
||||
module Locomotive
|
||||
module Liquid
|
||||
module Drops
|
||||
module ThemeAssets
|
||||
|
||||
class Base < ::Liquid::Drop
|
||||
|
||||
def before_method(meth)
|
||||
content_type = self.class.name.demodulize.underscore.singularize
|
||||
|
||||
asset = ThemeAsset.new(:site => @context.registers[:site], :content_type => content_type)
|
||||
ThemeAssetUploader.new(asset).store_path(meth.gsub('__', '.'))
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
class Images < Base; end
|
||||
|
||||
class Javascripts < Base; end
|
||||
|
||||
class Stylesheets < Base; end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
@ -1,21 +0,0 @@
|
||||
module Locomotive
|
||||
module Liquid
|
||||
module Drops
|
||||
class ThemeImages < ::Liquid::Drop
|
||||
|
||||
def initialize(site)
|
||||
@site = site
|
||||
end
|
||||
|
||||
def before_method(meth)
|
||||
@context.registers[:theme_uploader].store_path(meth.gsub('__', '.'))
|
||||
# asset = @site.theme_assets.where(:content_type => 'image', :slug => meth.to_s).first
|
||||
# !asset.nil? ? asset.source.url : nil
|
||||
# "sites/#{@site.id}/theme/#{meth.gsub('__', '.')}"
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
@ -53,11 +53,11 @@ 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,
|
||||
'stylesheets' => Locomotive::Liquid::Drops::ThemeAssets::Stylesheets.new,
|
||||
'javascripts' => Locomotive::Liquid::Drops::ThemeAssets::Javascripts.new,
|
||||
'images' => Locomotive::Liquid::Drops::ThemeAssets::Images.new,
|
||||
'contents' => Locomotive::Liquid::Drops::Contents.new,
|
||||
'current_page' => self.params[:page]
|
||||
}
|
||||
|
||||
@ -71,7 +71,6 @@ module Locomotive
|
||||
:site => current_site,
|
||||
:page => @page,
|
||||
:inline_editor => self.editing_page?,
|
||||
:theme_uploader => ThemeAssetUploader.new(current_site.theme_assets.build),
|
||||
:current_admin => current_admin
|
||||
}
|
||||
|
||||
|
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 |
@ -128,7 +128,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();
|
||||
} });
|
||||
|
||||
|
25
public/javascripts/admin/import.js
Normal file
25
public/javascripts/admin/import.js
Normal file
@ -0,0 +1,25 @@
|
||||
$(document).ready(function() {
|
||||
|
||||
$('#import-steps').smartupdater({
|
||||
url : $('#import-steps').attr('data-url'),
|
||||
dataType: 'json',
|
||||
minTimeout: 100
|
||||
}, function(data) {
|
||||
var steps = ['site', 'content_types', 'assets', 'snippets', 'pages'];
|
||||
|
||||
var currentIndex = data.step == 'done' ? steps.length - 1 : steps.indexOf(data.step);
|
||||
|
||||
// console.log('data.step = ' + data.step + ', ' + currentIndex + ', ' + data.failed);
|
||||
|
||||
for (var i = 0; i <= currentIndex; i++) {
|
||||
var state = 'done';
|
||||
|
||||
if (i == currentIndex && data.failed) state = 'failed';
|
||||
|
||||
$('#import-steps li:eq(' + i + ')').addClass(state);
|
||||
}
|
||||
|
||||
// if (state == 'failed' || currentIndex == steps.length) # TODO
|
||||
});
|
||||
|
||||
});
|
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")}}}());
|
@ -221,3 +221,44 @@ 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 { background: none; }
|
||||
|
||||
#import-steps li em {
|
||||
background-position: left 0px;
|
||||
}
|
||||
|
||||
#import-steps li strong {
|
||||
margin-left: 18px;
|
||||
display: block;
|
||||
height: 31px;
|
||||
background: transparent url(/images/admin/list/item-right.png) no-repeat right 0;
|
||||
}
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user