refactor locomotive liquid drops + implement the theme or site import page (95% done)

This commit is contained in:
dinedine 2010-09-29 00:08:11 +02:00
parent a5ea70229c
commit eabe4903f3
31 changed files with 581 additions and 140 deletions

View 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

View File

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

View File

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

View File

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

View 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

View 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
&nbsp;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View 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

View File

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

View File

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

View File

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

View 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

View File

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

View File

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

View File

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

View File

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

View 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

View File

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

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

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

View 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
});
});

View 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")}}}());

View File

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