class Inkcite::Uploader

Constants

IMAGE_PATH

Public Class Methods

upload(email, opts) click to toggle source
# File lib/inkcite/uploader.rb, line 7
def self.upload email, opts

  # True if we're only uploading images.
  images_only = !!opts[:images]

  # Check to see if forcing the upload is specified.  If not, check to see
  # when the most recent file was updated and
  force = !!opts[:force]
  unless force

    times = []

    unless images_only
      Dir.glob(File.join(email.path, '*.{html,tsv,txt,yml}')).each do |file|
        times << File.mtime(file).to_i
      end
    end

    local_images = email.image_dir
    if File.exist?(local_images)
      Dir.foreach(local_images) do |file|
        times << File.mtime(File.join(local_images, file)).to_i unless file.starts_with?('.')
      end
    end

    # Get the most recently updated file.
    last_update = times.max

    # Determine when the last upload was completed.
    last_upload = email.meta(:last_upload).to_i

    return unless last_update > last_upload

  end

  self.do_upload(email, force, images_only)

end

Private Class Methods

copy!(email, sftp, local, remote, force=true) click to toggle source
# File lib/inkcite/uploader.rb, line 50
def self.copy! email, sftp, local, remote, force=true

  # Nothing to copy unless the local directory exists (e.g. some emails don't
  # have an images directory.)
  return unless File.exist?(local)

  last_upload = email.meta(:last_upload).to_i

  Dir.foreach(local) do |file|
    next if file.starts_with?('.')

    local_file = File.join(local, file)
    unless File.directory?(local_file)

      remote_file = File.join(remote, file)

      unless force
        next unless File.mtime(local_file).to_i > last_upload
      end

      puts "Uploading #{local_file} -> #{remote_file} ..."
      sftp.upload!(local_file, remote_file)

    end

  end
end
do_upload(email, force, images_only) click to toggle source

Internal method responsive for doing the actual upload and forcing (if necessary) the update of the graphics.

# File lib/inkcite/uploader.rb, line 80
    def self.do_upload email, force, images_only

      # The preview version defines the configuration for the server to which
      # the files will be sftp'd.
      config = email.config[:sftp]
      if config.nil? || config.blank?
        puts "Unable to upload assets to CDN ('sftp:' section not found in config.yml)"
        return
      end

      # TODO: Verify SFTP configuration
      host = config[:host]
      path = config[:path]
      username = config[:username]
      password = config[:password]

      # Pre-optimize images before we upload them to the CDN.
      email.optimize_images

      # This is the directory from which images will be uploaded.
      # The email provides us with the correct directory based on
      # whether or not image optimization is enabled.
      local_images = email.optimized_image_dir

      # This is the last location of image upload.  If we're working
      # on multiple versions but the images all point to the same
      # location, it isn't necessary to re-upload images each time.
      last_remote_root = nil

      puts "Uploading to #{host} ..."

      begin

        # Get a local handle on the litmus configuration.
        Net::SFTP.start(host, username, :password => password) do |sftp|

          # Upload each version of the email.
          email.versions.each do |version|

            view = email.view(:preview, :email, version)

            # Need to pass the upload path through the renderer to ensure
            # that embedded tags will be converted into data.
            remote_root = Inkcite::Renderer.render(path, view)

            # Recursively ensure that the full directory structure necessary for
            # the content and images is present.
            mkdir! sftp, remote_root

            # Upload the images to the remote directory.  We use the last_remote_root
            # to ensure that we're not repeatedly uploading the same images over and
            # over when force is enabled -- but will re-upload images to distinct
            # remote roots.
            if last_remote_root != remote_root
              copy! email, sftp, local_images, remote_root, force
              last_remote_root = remote_root
            end

            # Nothing left to do if this is an image-only upload
            next if images_only

            # Check to see if we're creating an in-browser version of the email.
            next unless email.formats.include?(:browser)

            browser_view = email.view(:preview, :browser, version)

            # Check to see if there is a HTML version of this preview.  Some emails
            # do not have a hosted version and so it is not necessary to upload the
            # HTML version of the email - but this is a bad practice.
            file_name = browser_view.file_name
            next if file_name.blank?

            remote_file_name = File.join(remote_root, file_name)
            puts "Uploading #{remote_file_name}"

            # We need to use StringIO to write the email to a buffer in order to upload
            # the email's content in binary so that its encoding is honored.  SFTP defaults
            # to ASCII-8bit in non-binary mode, so it was blowing up on UTF-8 special
            # characters (e.g. "Mäkinen").
            # http://stackoverflow.com/questions/9439289/netsftp-transfer-mode-binary-vs-text
            io = StringIO.new(browser_view.render!)
            sftp.upload!(io, remote_file_name)

          end

        end

      rescue SocketError => e
        abort <<-USAGE.strip_heredoc

          Oops! There was an unexpected error trying to upload to your
          CDN or image host:

            #{e.message}

          Please check that the sftp section of config.yml is correct:

            sftp:
              host: '#{host}'
              path: '#{path}'
              username: '#{username}'
              password: '#{password.gsub(/./, '*')}'

        USAGE

      end


      # Timestamp to indicate we uploaded now
      email.set_meta :last_upload, Time.now.to_i

      true
    end
mkdir!(sftp, path) click to toggle source
# File lib/inkcite/uploader.rb, line 194
def self.mkdir! sftp, path

  _path = File::SEPARATOR

  path.split(File::SEPARATOR).each do |dir|

    # Add the child directory on to the path.
    _path = File.join(_path, dir)

    begin
      sftp.stat!(_path).directory?
    rescue Net::SFTP::StatusException
      begin
        puts "Creating directory: #{_path}"
        sftp.mkdir!(_path)
      rescue
        raise "Error creating #{_path}: #{$!}"
      end
    end
  end

end