diff --git a/app/assets/javascripts/locomotive/views/import/show_view.js.coffee b/app/assets/javascripts/locomotive/views/import/show_view.js.coffee new file mode 100644 index 00000000..0ad96d89 --- /dev/null +++ b/app/assets/javascripts/locomotive/views/import/show_view.js.coffee @@ -0,0 +1,47 @@ +Locomotive.Views.Import ||= {} + +class Locomotive.Views.Import.ShowView extends Backbone.View + + el: '#content' + + render: -> + super + + @enable_updating() + + enable_updating: -> + @updater = @$('#import-steps').smartupdater + url : @$('#import-steps').attr('data-url') + dataType: 'json' + minTimeout: 300, + @refresh_steps + + refresh_steps: (data) => + console.log 'refresh_steps: ....' + window.foo = data + window.bar = @ + + if data.step == 'done' + $('#import-steps li').addClass('done') + $.growl 'notice', @$('#import-steps').attr('data-success-message') + else + steps = ['site', 'content_types', 'assets', 'snippets', 'pages'] + current_index = steps.indexOf(data.step) || 0 + + _.each steps, (step, index) => + state = null + + if index == current_index + 1 && data.failed then state = 'failed' + if (index <= current_index) then state = 'done' + + @$("#import-steps li:eq(#{index})").addClass(state) if state? + + if data.failed + $.growl 'alert', @$('#import-steps').attr('data-failure-message') + + if data.step == 'done' || data.failed + @updater.smartupdater('stop') + + remove: -> + super + @updater.smartupdater('stop') if @updater? \ No newline at end of file diff --git a/app/assets/stylesheets/locomotive/application.scss b/app/assets/stylesheets/locomotive/application.scss index f3765b47..40656ec2 100644 --- a/app/assets/stylesheets/locomotive/application.scss +++ b/app/assets/stylesheets/locomotive/application.scss @@ -325,18 +325,21 @@ ul.list { margin: 0px 200px; li { - strong a { color: #b7baca; } + strong { + margin-left: 18px; + color: #b7baca; + } .more .states { position: relative; - top: 4px; + top: 7px; height: 16px; width: 16px; background: transparent image-url("locomotive/list/icons/states.png") no-repeat 0 0; } &.done { - strong a { color: #1F82BC; } + strong { color: #1F82BC; } .more .states { background-position: 0 -16px; diff --git a/app/controllers/locomotive/import_controller.rb b/app/controllers/locomotive/import_controller.rb index 06ce9c2e..c2d4aeec 100644 --- a/app/controllers/locomotive/import_controller.rb +++ b/app/controllers/locomotive/import_controller.rb @@ -7,6 +7,8 @@ module Locomotive before_filter :authorize_import + respond_to :json, :only => :show + def show @import = Locomotive::Import::Model.current(current_site) respond_with @import @@ -29,37 +31,4 @@ module Locomotive end end -end - - -# begin -# Locomotive::Import::Job.run!(params[:zipfile], current_site, { -# :samples => Boolean.set(params[:samples]), -# :reset => Boolean.set(params[:reset]) -# }) -# -# flash[:notice] = t("fash.locomotive.import.create.#{Locomotive.config.delayed_job ? 'notice' : 'done'}") -# -# redirect_to Locomotive.config.delayed_job ? import_url : new_import_url -# rescue Exception => e -# logger.error "[Locomotive import] #{e.message} / #{e.backtrace}" -# -# @error = e.message -# flash[:alert] = t('fash.locomotive.import.create.alert') -# -# render 'new' -# end - -# 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_import_url if @job.nil? -# end -# format.json { render :json => { -# :step => @job.nil? ? 'done' : @job.step, -# :failed => @job && @job.last_error.present? -# } } -# end -# end \ No newline at end of file +end \ No newline at end of file diff --git a/app/controllers/locomotive/sites_controller.rb b/app/controllers/locomotive/sites_controller.rb index 5473b140..0186f210 100644 --- a/app/controllers/locomotive/sites_controller.rb +++ b/app/controllers/locomotive/sites_controller.rb @@ -3,7 +3,7 @@ module Locomotive sections 'settings' - respond_to :json, :only => [:create] + respond_to :json, :only => [:create, :destroy] def new @site = Site.new diff --git a/app/views/locomotive/import/show.html.haml b/app/views/locomotive/import/show.html.haml index d0f76644..13f7c209 100644 --- a/app/views/locomotive/import/show.html.haml +++ b/app/views/locomotive/import/show.html.haml @@ -1,6 +1,3 @@ -- content_for :head do - = include_javascripts :import - - title t('.title') - content_for :submenu do @@ -10,7 +7,7 @@ %ul{ :id => 'import-steps', :class => 'list', :'data-url' => import_url(:json), :'data-success-message' => t('.messages.success'), :'data-failure-message' => t('.messages.failure') } - %w(site content_types assets snippets pages).each do |step| - %li{ :id => "#{step}-step" } + %li.item{ :id => "#{step}-step" } %strong= t(".steps.#{step}") .more .states diff --git a/doc/TODO b/doc/TODO index 71a007c5..dc91debf 100644 --- a/doc/TODO +++ b/doc/TODO @@ -29,9 +29,8 @@ x edit my site x snippets x delete in ajax x upload many files at once - - import/export - - export - - replace DJ by QU + x import/export + x export - site picker - content types - change in main menu diff --git a/lib/locomotive/import/job.rb b/lib/locomotive/import/job.rb index 9809b86d..db602d3d 100644 --- a/lib/locomotive/import/job.rb +++ b/lib/locomotive/import/job.rb @@ -47,15 +47,14 @@ module Locomotive self.reset! if @options[:reset] - # %w(site content_types content_assets snippets pages).each do |step| - %w(site assets snippets pages).each do |step| + # %w(site content_types assets snippets pages).each do |step| + %w(site assets snippets pages).each do |step| # TODO (Did): unable content_types for now, waiting for its refactoring if @options[:enabled][step] != false "Locomotive::Import::#{step.camelize}".constantize.process(context, @options) @worker.update_attributes :step => step if @worker else self.log "skipping #{step}" end - sleep 10 # FIXME DEBUG PURPOSE end end diff --git a/lib/locomotive/import/model.rb b/lib/locomotive/import/model.rb index 50a1234d..fe800550 100644 --- a/lib/locomotive/import/model.rb +++ b/lib/locomotive/import/model.rb @@ -41,7 +41,6 @@ module Locomotive ## class methods ## def self.create(attributes = {}) - puts attributes.inspect new(attributes).tap do |job| if job.valid? begin @@ -61,6 +60,15 @@ module Locomotive } end + def self.current(site) + job = Delayed::Job.where({ :job_type => 'import', :site_id => site.id }).last + + { + :step => job.nil? ? 'done' : job.step, + :failed => job && job.last_error.present? + } + end + def self.name 'Import' end diff --git a/vendor/assets/javascripts/smartupdater.js b/vendor/assets/javascripts/smartupdater.js new file mode 100644 index 00000000..f2120057 --- /dev/null +++ b/vendor/assets/javascripts/smartupdater.js @@ -0,0 +1,296 @@ +/** +* smartupdater - jQuery Plugin +* +* Version - 4.0.beta +* Copyright (c) 2010 - 2011 Vadim Kiryukhin +* vkiryukhin @ gmail.com +* +* http://www.eslinstructor.net/smartupdater/ +* +* Dual licensed under the MIT and GPL licenses: +* http://www.opensource.org/licenses/mit-license.php +* http://www.gnu.org/licenses/gpl.html +* +* USAGE: +* +* $("#myObject").smartupdater({ +* url : "foo.php" +* }, function (data) { +* //process data here; +* } +* ); +* +* Public functions: +* $("#myObject").smartupdater("stop") +* $("#myObject").smartupdater("restart"); +* $("#myObject").smartupdater("setTimeout",timeout); +* $("#myObject").smartupdater("alterUrl"[,"foo.php"[,data]]); +* $("#myObject").smartupdater("alterCallback"[, foo]); +* +* Public Attributes: +* var status = $("#myObject").smartupdater("getState"); +* var timeout = $("#myObject").smartupdater("getTimeout"); +* +**/ + +(function($) { + + + var methods = { + + init : function( options, callback) { + return this.each(function () { + var elem = this, + es = {}; + + elem.settings = jQuery.extend(true,{ + 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, // 1 minute + maxFailedRequests : 10, // max. number of consecutive ajax failures + maxFailedRequestsCb : false, // falure callback function + httpCache : false, // http cache + rCallback : false, // remote callback functions + selfStart : true, // start automatically after initializing + smartStop : { active: false, //disabled by default + monitorTimeout: 2500, // 2.5 seconds + minHeight: 1, // 1px + minWidth: 1 // 1px + } + + }, options); + + elem.smartupdaterStatus = {state:'',timeout:0}; + + es = elem.settings; + + es.prevContent = ''; + es.failedRequests = 0; + es.etag = '0'; + es.lastModified = '0'; + es.callback = callback; + es.origReq = {url:es.url,data:es.data,callback:callback}; + es.stopFlag = false; + + + function start() { + + /* check if element has been deleted and clean it up */ + if(!$(elem).parents('body').length) { + clearInterval(elem.smartupdaterStatus.smartStop); + clearTimeout(elem.settings.h); + elem = {}; + return; + } + + $.ajax({ + url : es.url, + type : es.type, + data : es.data, + dataType: es.dataType, + cache : false, // MUST be set to false to prevent IE caching issue. + + success: function (data, statusText, xhr) { + + var dataNotModified = false, + rCallback = false, + xSmart = jQuery.parseJSON(xhr.getResponseHeader("X-Smartupdater")), + xhrEtag, xhrLM; + + if(xSmart) { // remote control + + /* remote timeout */ + es.minTimeout = xSmart.timeout ? xSmart.timeout : es.minTimeout; + + /* remote callback */ + rCallback = xSmart.callback ? xSmart.callback : false; + } + + if(es.httpCache) { // http cache process here + + xhrEtag = xhr.getResponseHeader("ETag"); + xhrLM = xhr.getResponseHeader("Last-Modified"); + + dataNotModified = (es.etag == xhrEtag || es.lastModified == xhrLM) ? true : false; + es.etag = xhrEtag ? xhrEtag : es.etag; + es.lastModified = xhrLM ? xhrLM : es.lastModified; + } + + if ( dataNotModified || + es.prevContent == xhr.responseText || + xhr.status == 304 ) { // data is not changed + + if(!es.stopFlag) { + clearTimeout(es.h); + es.h = setTimeout(start, es.minTimeout); + } + + } else { // data is changed + + /* cache response data */ + es.prevContent = xhr.responseText; + + /* reset timeout */ + if(!es.stopFlag) { + clearTimeout(es.h); + es.h = setTimeout(start, es.minTimeout); + } + + /* run callback function */ + if(es.rCallback && rCallback && es.rCallback.search(rCallback) != -1) { + window[rCallback](data); + } else { + es.callback(data); + } + } + + elem.smartupdaterStatus.timeout = es.minTimeout; + es.failedRequests = 0; + }, + + error: function(xhr, textStatus, errorThrown) { + if ( ++es.failedRequests < es.maxFailedRequests ) { + + /* increment falure counter and reset timeout */ + if(!es.stopFlag) { + clearTimeout(es.h); + es.h = setTimeout(start, es.minTimeout); + elem.smartupdaterStatus.timeout = es.minTimeout; + } + + } else { + + /* stop smartupdater */ + clearTimeout(es.h); + elem.smartupdaterStatus.state = 'OFF'; + if( typeof(es.maxFailedRequestsCb)==='function') { + es.maxFailedRequestsCb(xhr, textStatus, errorThrown); + } + } + }, + + beforeSend: function(xhr, settings) { + + if(es.httpCache) { + + /* set http cache-related headers */ + xhr.setRequestHeader("If-None-Match", es.etag ); + xhr.setRequestHeader("If-Modified-Since", es.lastModified ); + } + + /* Feedback: Smartupdater sends it's current timeout to server */ + xhr.setRequestHeader("X-Smartupdater", '{"timeout":"'+elem.smartupdaterStatus.timeout+'"}'); + } + }); + + elem.smartupdaterStatus.state = 'ON'; + } + + es.fnStart = start; + + if(es.selfStart) { + start(); + } + + if(es.smartStop.active) { + + elem.smartupdaterStatus.smartStop = setInterval(function(){ + + // check if object has been deleted + if(!$(elem).parents('body').length) { + clearInterval(elem.smartupdaterStatus.smartStop); + clearTimeout(elem.settings.h); + elem = {}; + return; + } + + var $elem = $(elem); + var width = $elem.width(), + height = $elem.height(), + hidden = $elem.is(":hidden"); + + //element has been expanded, so smartupdater should be re-started. + if(!hidden && height > es.smartStop.minHeight && width > es.smartStop.minWidth + && elem.smartupdaterStatus.state=="OFF") { + $elem.smartupdater("restart"); + } else + //element has been minimized, so smartupdater should be stopped. + if( (hidden || height <= es.smartStop.minHeight || width <= es.smartStop.minWidth) + && elem.smartupdaterStatus.state=="ON") { + $elem.smartupdater("stop"); + + } + + },es.smartStop.monitorTimeout); + } + + }); + + },// init() + + stop : function () { + return this.each(function () { + this.settings.stopFlag = true; + clearTimeout(this.settings.h); + this.smartupdaterStatus.state = 'OFF'; + }); + }, + + restart : function () { + return this.each(function () { + this.settings.stopFlag = false; + clearTimeout(this.settings.h); + this.settings.failedRequests = 0; + this.settings.etag = "0"; + this.settings.lastModified = "0"; + this.settings.fnStart(); + }) + }, + + setTimeout : function (period) { + return this.each(function () { + clearTimeout(this.settings.h); + this.settings.minTimeout = period; + this.settings.fnStart(); + }); + }, + + alterCallback : function (callback) { + return this.each(function () { + this.settings.callback = callback ? callback : this.settings.origReq.callback; + }); + }, + + alterUrl : function (url,data) { + return this.each(function () { + this.settings.url = url ? url : this.settings.origReq.url; + this.settings.data = data ? data : this.settings.origReq.data; + }); + }, + + getTimeout : function () { + return this[0].smartupdaterStatus.timeout; + }, + + getState : function () { + return this[0].smartupdaterStatus.state; + } + + }; //methods + + jQuery.fn.smartupdater = function (options, callback) { + + if ( methods[options] ) { + return methods[ options ].apply( this, Array.prototype.slice.call( arguments, 1 )); + } else if ( typeof options === 'object' || ! method ) { + return methods.init.apply( this, arguments ); + } else { + $.error( 'Method ' + options + ' does not exist on jQuery.tooltip' ); + } + }; + + +})(jQuery);