class Voog::Dtk::FileManager

Constants

ASSET_FOLDER_MAP
BOILERPLATE_URL

Attributes

notifier[RW]

Public Class Methods

new(client, opts = {}) click to toggle source
# File lib/voog/dtk/filemanager.rb, line 23
def initialize(client, opts = {})
  @client = client
  @silent = opts.fetch(:silent, false)
  @verbose = opts.fetch(:verbose, false)
  @overwrite = opts.fetch(:overwrite, false)
  @cleanup = opts.fetch(:cleanup, false)
  @notifier = Voog::Dtk::Notifier.new($stderr, @silent)
end

Public Instance Methods

add_files(names) click to toggle source
# File lib/voog/dtk/filemanager.rb, line 861
def add_files(names)
  new_files = add_to_manifest names.map {|n| relative_path_of(n)}
  upload_files new_files.map { |f| f.fetch('file') } unless new_files.empty?
end
add_to_manifest(files = nil) click to toggle source
# File lib/voog/dtk/filemanager.rb, line 95
def add_to_manifest(files = nil)
  return if files.nil?
  @manifest = read_manifest

  new_layouts = []
  new_assets = []

  files = (files.is_a? String) ? [files] : files
  files.uniq.each do |file|
    next if in_manifest?(file, @manifest)

    match = /^(component|layout|image|javascript|asset|stylesheet)s\/(.*)/.match(file)
    next if match.nil?

    type, filename = match[1], match[2]

    unless valid_for_folder?(filename, "#{type}s")
      @notifier.error "Invalid filename '#{filename}' for '#{type}s' folder. Skipping.\n"
      next
    end

    if %w(component layout).include? type
      component = type == 'component'
      name = filename.split('.').first
      title = component ? name : name.gsub('_', ' ').capitalize

      layout = {
        'title' => component ? name : title,
        'layout_name' => name,
        'content_type' => component ? 'component' : 'page',
        'component' => component,
        'file' => file
      }

      new_layouts << layout
    elsif %w(image javascript asset stylesheet).include? type
      asset = {
        'content_type' => begin
          MIME::Types.type_for(filename).first.content_type
        rescue
          'text/unknown'
        end,
        'kind' => "#{type}s",
        'file' => file,
        'filename' => filename
      }

      new_assets << asset
    end

    @notifier.info "Added #{file} to manifest.json"
    @notifier.newline
  end

  new_layouts.map { |l| @manifest['layouts'] << l }
  new_assets.map { |a| @manifest['assets'] << a }

  write_manifest @manifest

  # returns all successfully added files
  new_layouts + new_assets
end
auto_paginate(api_method, opt = {}) click to toggle source
# File lib/voog/dtk/filemanager.rb, line 184
def auto_paginate(api_method, opt = {})
  data = @client.send(api_method, opt)
  last_response = @client.last_response

  while last_response.rels[:next]
    last_response = last_response.rels[:next].get
    data.concat(last_response.data) if last_response.data.is_a?(Array)
  end

  data
end
check() click to toggle source
# File lib/voog/dtk/filemanager.rb, line 536
def check
  ok_char = '.'
  not_ok_char = '!'
  @notifier.info 'Checking manifest.json...'

  if File.exist? 'manifest.json'
    @manifest = read_manifest
    @notifier.success 'OK!'
  else
    @notifier.error 'Manifest file not found! Use the \'manifest\' command to generate one.'
    return false
  end

  # Check for files in manifest
  layouts = @manifest.fetch('layouts', [])
  missing_layouts = %w()

  @notifier.newline
  @notifier.info 'Checking layouts and components'
  layouts.reject(&:nil?).each do |layout|
    if File.exist? layout['file']
      @notifier.success ok_char
    else
      missing_layouts << layout['file']
      @notifier.error not_ok_char
    end
  end

  if missing_layouts.count > 0
    @notifier.error " (#{missing_layouts.count} missing)"
    @notifier.newline
    missing_layouts.each do |a|
      @notifier.normal "    #{a}"
      @notifier.newline
    end if @verbose
  else
    @notifier.success 'OK!'
  end

  assets = @manifest['assets']
  missing_assets = %w()

  @notifier.newline
  @notifier.info 'Checking assets'
  assets.reject(&:nil?).each do |asset|
    if File.exist? asset['file']
      @notifier.success ok_char
    else
      missing_assets << asset['file']
      @notifier.error not_ok_char
    end
  end

  if missing_assets.count > 0
    @notifier.error " (#{missing_assets.count} missing)"
    @notifier.newline
    missing_assets.each do |a|
      @notifier.normal "    #{a}"
      @notifier.newline
    end if @verbose
  else
    @notifier.success 'OK!'
  end

  (missing_assets.count + missing_layouts.count == 0)
end
clone_design(url = BOILERPLATE_URL, dst = 'tmp') click to toggle source
# File lib/voog/dtk/filemanager.rb, line 603
def clone_design(url = BOILERPLATE_URL, dst = 'tmp')
  # Allow only design repositories from Edicy/Voog for now
  pattern = /\Ahttps?:\/\/github.com\/(?:Voog|Edicy)\/design-(\w+)\.git\z/ # HTTPS clone URL
  pattern2 = /\Agit@github.com:(?:Voog|Edicy)\/design-(\w+)\.git\z/ # SSH clone URL

  match = url.match(pattern) || url.match(pattern2)

  unless match.nil?
    @notifier.info "Fetching the #{match[1].capitalize} design..."
    @notifier.newline
  else
    # default to the boilerplate URL if given URL doesn't match the Regex pattern
    @notifier.info 'Fetching design boilerplate...'
    @notifier.newline
    url = BOILERPLATE_URL
  end

  FileUtils.rm_r 'tmp' if Dir.exist? 'tmp'

  begin
    Git.clone url, dst
  rescue
    @notifier.error 'An error occurred!'
    return false
  end

  if Dir.exist? 'tmp'
    Dir.chdir 'tmp'
    @notifier.info 'Copying template files to working directory...'
    @notifier.newline
    Dir.new('.').entries.each do |f|
      unless f =~ /^\..*$/
        if Dir.exist?('../' + f) || File.exist?('../' + f)
          FileUtils.rm_r '../' + f
        end
        FileUtils.mv f, '..'
      end
    end
    Dir.chdir '..'
    FileUtils.rm_r 'tmp'
  end
  @notifier.success 'Done!'
  @notifier.newline
  true
end
content_type_for(file) click to toggle source
# File lib/voog/dtk/filemanager.rb, line 783
def content_type_for(file)
  folder = relative_path_of(file).split('/').first
  if is_editable?(relative_path_of(file))
    if folder == 'stylesheets'
      'text/css'
    elsif folder == 'javascripts'
      'text/javascript'
    end
  else
    if folder == 'images'
      "image/#{relative_path_of(file).split('/').last.split('.').last}"
    elsif folder == 'assets'
      'unknown/unknown'
    end
  end
end
create_asset(asset = nil) click to toggle source
# File lib/voog/dtk/filemanager.rb, line 455
def create_asset(asset = nil)
  valid = asset && asset.respond_to?(:asset_type) \
    && asset.respond_to?(:filename) \
    && asset.respond_to?(:asset_type) \
    && (asset.respond_to?(:public_url) || asset.respond_to?(:data))

  if valid
    folder = ASSET_FOLDER_MAP.fetch(asset.asset_type, 'assets')

    Dir.mkdir(folder) unless Dir.exist?(folder)
    Dir.chdir(folder)

    overwritten = File.exist? asset.filename

    if @verbose
      @notifier.newline
      if overwritten
        @notifier.warning "  + #{folder}/#{asset.filename}"
      else
        @notifier.success "  + #{folder}/#{asset.filename}"
      end
    else
      @notifier.success '.'
    end

    if %w(stylesheet javascript).include? asset.asset_type
      open(asset.filename, 'wb') { |file| file.write(asset.data) }
    else
      url = URI(asset.public_url)
      Net::HTTP.start(url.hostname, url.port, use_ssl: (url.scheme == 'https')) do |http|
        resp = http.get(url.path)
        open(asset.filename, 'wb') { |file| file.write(resp.body) }
      end
    end
    Dir.chdir('..')
  else
    @notifier.error '!' unless @verbose
  end
end
create_assets(ids) click to toggle source
# File lib/voog/dtk/filemanager.rb, line 445
def create_assets(ids)
  @notifier.info "Creating assets#{'...' if @verbose}"
  ids.uniq.each do |id|
    create_asset(get_layout_asset id)
  end
  @notifier.newline if @verbose
  @notifier.success 'Done!'
  @notifier.newline
end
create_files(layouts = nil, layout_assets = nil) click to toggle source
# File lib/voog/dtk/filemanager.rb, line 436
def create_files(layouts = nil, layout_assets = nil)
  if layouts.nil? && layout_assets.nil?
    layouts = get_layouts
    layout_assets = get_layout_assets
  end
  create_layouts(layouts.map(&:id))
  create_assets(layout_assets.map(&:id))
end
create_folders() click to toggle source
# File lib/voog/dtk/filemanager.rb, line 427
def create_folders
  @notifier.newline
  @notifier.info 'Creating folder structure...'
  folders = %w(stylesheets images assets javascripts components layouts)
  folders.each { |folder| Dir.mkdir(folder) unless Dir.exist?(folder) }
  @notifier.success 'Done!'
  @notifier.newline
end
create_layout(layout = nil) click to toggle source
# File lib/voog/dtk/filemanager.rb, line 505
def create_layout(layout = nil)
  valid = layout &&
    layout.respond_to?(:component) &&
    layout.respond_to?(:title) &&
    layout.respond_to?(:body)

  if valid
    folder = layout.component ? 'components' : 'layouts'
    filename = "#{layout.title.gsub(/[^\w\.\-]/, '_').downcase}.tpl"
    Dir.chdir(folder)
    overwritten = File.exist? filename

    if @verbose
      @notifier.newline
      if overwritten
        @notifier.warning "  + #{folder}/#{filename}"
      else
        @notifier.success "  + #{folder}/#{filename}"
      end
    else
      @notifier.success '.'
    end

    File.open(filename, 'w') { |file| file.write layout.body }

    Dir.chdir('..')
  else
    @notifier.error '!' unless @verbose
  end
end
create_layouts(ids) click to toggle source
# File lib/voog/dtk/filemanager.rb, line 495
def create_layouts(ids)
  @notifier.info "Creating layouts#{'...' if @verbose}"
  ids.each do |id|
    create_layout get_layout id
  end
  @notifier.newline if @verbose
  @notifier.success 'Done!'
  @notifier.newline
end
create_remote_file(file) click to toggle source
# File lib/voog/dtk/filemanager.rb, line 827
def create_remote_file(file)
  data = {
    filename: file.split('/').last,
    content_type: content_type_for(file)
  }

  if is_editable?(file)
    data[:data] = File.read(file, encoding: 'UTF-8')
  else
    data[:file] = file
  end

  @client.create_layout_asset(data)
end
create_remote_layout(file) click to toggle source
# File lib/voog/dtk/filemanager.rb, line 800
def create_remote_layout(file)
  @manifest = read_manifest if File.exist? 'manifest.json'
  layouts = @manifest.fetch('layouts', []).reject(&:nil?)
  layout = layouts.select { |l| file == l.fetch('file') }.first

  if @manifest && layouts && layout
    data = {
      title: layout.fetch('title'),
      content_type: layout.fetch('content_type'),
      component: layout.fetch('component'),
      body: File.exist?(layout.fetch('file')) ? File.read(layout.fetch('file'), encoding: 'UTF-8') : ''
    }
  else
    name = file.split('/').last.split('.').first
    component = (file.split('/').first =~ /^layouts$/).nil?
    body = File.read(file, encoding: 'UTF-8')
    data = {
      title: component ? name : name.capitalize,
      content_type: 'page',
      component: component,
      body: body
    }
  end

  @client.create_layout(data)
end
delete_layout(id) click to toggle source
# File lib/voog/dtk/filemanager.rb, line 220
def delete_layout(id)
  @client.delete_layout(id)
end
delete_layout_asset(id) click to toggle source
# File lib/voog/dtk/filemanager.rb, line 224
def delete_layout_asset(id)
  @client.delete_layout_asset(id)
end
display_sites(sites) click to toggle source
# File lib/voog/dtk/filemanager.rb, line 985
def display_sites(sites)
  sites.each_with_index do |site, index|
    @notifier.info "#{site.fetch(:name)} #{'(default)' if index == 0}"
    if @verbose
      @notifier.newline
      @notifier.info "  host: #{site.fetch(:host)}"
      @notifier.newline
      @notifier.info "  token: #{site.fetch(:api_token)}"
    end
    @notifier.newline
  end
end
find_assets(names) click to toggle source
# File lib/voog/dtk/filemanager.rb, line 945
def find_assets(names)
  assets = get_layout_assets
  results = []
  names.each do |name|
    case name
    when /\Aassets\/?\Z/
      results << assets.select { |a| a.asset_type == 'asset' }.map(&:id)
    when /\Aimages\/?\Z/
      results << assets.select { |a| a.asset_type == 'image' }.map(&:id)
    when /\Ajavascripts\/?\Z/
      results << assets.select { |a| a.asset_type == 'javascript' }.map(&:id)
    when /\Astylesheets\/?\Z/
      results << assets.select { |a| a.asset_type == 'stylesheet' }.map(&:id)
    else
      results << assets.select { |a| a.filename == name.split('/').last }.map(&:id)
    end
  end
  results.flatten
end
find_layouts(names) click to toggle source
# File lib/voog/dtk/filemanager.rb, line 925
def find_layouts(names)
  layouts = get_layouts
  results = []

  names.each do |name|
    case name
    when /\Alayouts\/?\Z/
      results << layouts.select { |l| !l.component }.map(&:id)
    when /\Acomponents\/?\Z/
      results << layouts.select { |l| l.component }.map(&:id)
    else
      type, name = name.gsub('.tpl', '').split('/')
      results << layouts.select do |l|
        (type == 'layouts' ? !l.component : l.component) && l.title.gsub(/[^\w\.]/, '_').downcase == name
      end.map(&:id)
    end
  end
  results.flatten
end
generate_local_manifest(verbose = false, silent=false) click to toggle source
# File lib/voog/dtk/filemanager.rb, line 258
def generate_local_manifest(verbose = false, silent=false)
  unless %w(layouts components).map { |f| Dir.exist? f }.all?
    @notifier.error 'Missing local layout folders! (See `kit help init`)'
    return false
  end

  begin
    @old_manifest = JSON.parse(File.read('manifest.json', encoding: 'UTF-8')).to_h if File.exist? 'manifest.json'
  rescue JSON::ParserError
    @notifier.error 'Invalid JSON in current manifest file!'
    @notifier.newline
  end

  @notifier.info 'Reading local files...'
  layouts_dir = Dir.new('layouts')
  layouts = layouts_dir.entries.select do |file|
    (!File.directory?(File.join(layouts_dir, file)) && valid_for_folder?(file, 'layouts'))
  end
  layouts = layouts.map do |l|
    attrs = {
      'content_type' =>  'page',
      'component' => false,
      'file' => "layouts/#{l}",
      'layout_name' => 'page_default',
      'title' => l.split('.').first.gsub('_', ' ').capitalize
    }
    if @old_manifest && @old_manifest.fetch('layouts')
      old_layout = @old_manifest.fetch('layouts').select { |ol| ol.fetch('file').gsub('layouts/', '') == l }.first || {}
      attrs.merge! old_layout
    end
    attrs
  end
  components_dir = Dir.new('components')
  components = components_dir.entries.select do |file|
    (!File.directory?(File.join(components_dir, file)) && valid_for_folder?(file, 'components'))
  end
  components = components.map do |c|
    name = c.split('.').first.gsub('_', ' ')
    attrs = {
      'content_type' => 'component',
      'component' => true,
      'file' => "components/#{c}",
      'layout_name' => name,
      'title' => name
    }
    if @old_manifest && @old_manifest.fetch('layouts')
      old_component = @old_manifest.fetch('layouts').select { |ol| ol.fetch('file').gsub('components/', '') == c }.first || {}
      attrs.merge! old_component
    end
    attrs
  end
  assets = []
  asset_dirs = %w(assets images javascripts stylesheets)
  asset_dirs.each do |dir|
    next unless Dir.exist? dir
    current_dir = Dir.new(dir)
    current_dir.entries.each do |file|
      next unless !File.directory?(File.join(current_dir, file)) && valid_for_folder?(file, dir)
      attrs = {
        'content_type' => begin
          MIME::Types.type_for(file).first.content_type
        rescue
          'text/unknown'
        end,
        'file' => "#{dir}/#{file}",
        'kind' => dir,
        'filename' => file
      }
      if @old_manifest && @old_manifest.fetch('assets')
        old_asset = @old_manifest.fetch('assets').select { |ol| ol.fetch('filename') == file }.first || {}
        attrs.merge! old_asset
      end
      assets << attrs
    end
  end

  manifest = {
    'description' => "New design",
    'name' => "New design",
    'preview_medium' => "",
    'preview_small' => "",
    'author' => "",
    'layouts' => sort_layouts_by_content_type(layouts + components),
    'assets' => assets
  }
  if @old_manifest
    old_meta = @old_manifest.tap{ |m| m.delete('assets') }.tap{ |m| m.delete('layouts') }
    manifest.merge! old_meta
  end
  @notifier.newline
  @notifier.info 'Writing layout files to new manifest.json file...'
  write_manifest(manifest)
  @notifier.success 'Done!'
  @notifier.newline
  true
end
generate_manifest(layouts = nil, layout_assets = nil) click to toggle source
# File lib/voog/dtk/filemanager.rb, line 368
def generate_manifest(layouts = nil, layout_assets = nil)
  layouts ||= get_layouts
  layout_assets ||= get_layout_assets

  # type->folder map for layout assets
  asset_folders = {
    'asset' => 'assets',
    'javascript' => 'javascripts',
    'stylesheet' => 'stylesheets',
    'image' => 'images'
  }

  if (layouts.empty? && layout_assets.empty?)
    @notifier.error 'No remote layouts found to generate manifest from!'
    @notifier.newline
    return false
  end

  unless valid?(layouts) && valid?(layout_assets)
    @notifier.error 'No valid layouts found to generate manifest from!'
    @notifier.newline
    return false
  end

  @notifier.info 'Writing remote layouts to new manifest.json file...'

  manifest = {}

  manifest[:layouts] = layouts.inject(Array.new) do |memo, l|
    memo << {
      'title' => l.title,
      'layout_name' => l.title.gsub(/[^\w\.\-]/, '_').downcase,
      'content_type' => l.content_type,
      'component' => l.component,
      'file' => "#{(l.component ? 'components' : 'layouts')}/#{l.title.gsub(/[^\w\.\-]/, '_').downcase}.tpl"
    }
  end

  manifest[:layouts] = sort_layouts_by_content_type(manifest[:layouts])

  manifest[:assets] = layout_assets.inject(Array.new) do |memo, a|

    # kind is same as asset_type for kinds that are represented in the asset_folders hash, defaults to 'asset'
    kind = asset_folders.key?(a.asset_type.to_s) ? a.asset_type : 'asset'
    folder = asset_folders.fetch(kind, 'assets')

    memo << {
      'kind' => kind,
      'filename' => a.filename,
      'file' => "#{folder}/#{a.filename}",
      'content_type' => a.content_type
    }
  end

  write_manifest(manifest)
  @notifier.success 'Done!'
  @notifier.newline
end
generate_remote_manifest() click to toggle source
# File lib/voog/dtk/filemanager.rb, line 355
def generate_remote_manifest
  generate_manifest get_layouts, get_layout_assets
end
get_layout(id) click to toggle source
# File lib/voog/dtk/filemanager.rb, line 204
def get_layout(id)
  @client.layout id
end
get_layout_asset(id) click to toggle source
# File lib/voog/dtk/filemanager.rb, line 208
def get_layout_asset(id)
  @client.layout_asset id
end
get_layout_assets() click to toggle source
# File lib/voog/dtk/filemanager.rb, line 200
def get_layout_assets
  auto_paginate(:layout_assets, per_page: 250)
end
get_layouts() click to toggle source
# File lib/voog/dtk/filemanager.rb, line 196
def get_layouts
  auto_paginate(:layouts, per_page: 250)
end
in_manifest?(file, manifest = nil) click to toggle source
# File lib/voog/dtk/filemanager.rb, line 45
def in_manifest?(file, manifest = nil)
  @manifest = manifest || read_manifest
  filenames = @manifest['layouts'].map { |l| l.fetch('file', '') }
  filenames += @manifest['assets'].map { |a| a.fetch('filename', '') }
  filenames.include? file
end
is_asset?(filename) click to toggle source
# File lib/voog/dtk/filemanager.rb, line 842
def is_asset?(filename)
  asset_folders = %w(assets images stylesheets javascripts)
  asset_folders.include?(filename.split('/').first)
end
is_editable?(file) click to toggle source
# File lib/voog/dtk/filemanager.rb, line 776
def is_editable?(file)
  folder = relative_path_of(file).split('/').first
  extension = relative_path_of(file).split('/').last.split('.').last

  (%w(stylesheets javascripts).include? folder) && (%w(js css).include? extension)
end
is_layout?(filename) click to toggle source
# File lib/voog/dtk/filemanager.rb, line 847
def is_layout?(filename)
  layout_folders = %w(components layouts)
  layout_folders.include?(filename.split('/').first)
end
layout_asset_id_map(assets=nil) click to toggle source

Returns filename=>id hash for layout assets

# File lib/voog/dtk/filemanager.rb, line 668
def layout_asset_id_map(assets=nil)
  assets ||= get_layout_assets
  assets.inject(Hash.new) do |memo, a|
    filename = "#{ASSET_FOLDER_MAP.fetch(a.asset_type, 'assets')}/#{a.filename}"
    memo[filename] = a.id
    memo
  end
end
layout_id_map(layouts = nil) click to toggle source

Returns filename=>id hash for layout files

# File lib/voog/dtk/filemanager.rb, line 650
def layout_id_map(layouts = nil)
  layouts ||= get_layouts
  remote_layouts = layouts.inject(Hash.new) do |memo, l|
    memo[l.title.downcase] = l.id
    memo
  end

  @manifest = read_manifest
  fail 'Manifest not found! (See `kit help push` for more info)'.red unless @manifest
  layouts = @manifest.fetch('layouts').reject(&:nil?)
  layouts.inject(Hash.new) do |memo, l|
    remote_exist = remote_layouts.key?(l.fetch('title').downcase)
    memo[l.fetch('file')] = remote_layouts.fetch(l.fetch('title').downcase, nil) if remote_exist
    memo
  end
end
pull_files(names) click to toggle source
# File lib/voog/dtk/filemanager.rb, line 965
def pull_files(names)
  layout_ids = find_layouts(names)
  asset_ids = find_assets(names)

  found = layout_ids.length + asset_ids.length

  unless found
    @notifier.error "Unable to find any files matching the given pattern#{'s' if names.length > 1}!"
    @notifier.newline
    ret = false
  else
    ret = true
  end

  create_layouts(layout_ids) unless layout_ids.empty?
  create_assets(asset_ids) unless asset_ids.empty?

  ret
end
read_manifest() click to toggle source
# File lib/voog/dtk/filemanager.rb, line 32
def read_manifest
  JSON.parse(File.read('manifest.json', encoding: 'UTF-8')).to_h
end
relative_path_of(file) click to toggle source
# File lib/voog/dtk/filemanager.rb, line 83
def relative_path_of(file)
  if file.include? Dir.pwd
    pwd = Pathname.new Dir.pwd
    file_path = Pathname.new file
    file_path.relative_path_from(pwd).to_s
  else
    file
  end
rescue
  file
end
remove_files(names) click to toggle source
# File lib/voog/dtk/filemanager.rb, line 852
def remove_files(names)
  names.each do |name|
    remove_local_file(name) if File.file?(name)
    remove_remote_file(name)
    remove_from_manifest(name)
    @notifier.newline
  end
end
remove_from_manifest(files = nil) click to toggle source
# File lib/voog/dtk/filemanager.rb, line 158
def remove_from_manifest(files = nil)
  return if files.nil?
  @manifest = read_manifest
  files = (files.is_a? String) ? [relative_path_of(files)] : files.map{ |f| relative_path_of(f) }
  files.uniq.each do |file|
    match = /^(component|layout|image|javascript|asset|stylesheet)s\/(.*)/.match(file)
    next if match.nil?
    type, filename = match[1], match[2]
    if %w(component layout).include? type
      @manifest['layouts'].delete_if do |layout|
        match = layout.fetch('file', nil) == file
        @notifier.info "Removed #{file} from manifest.json" if match
        match
      end
    elsif %w(image javascript asset stylesheet).include? type
      @manifest['assets'].delete_if do |asset|
        match = asset.fetch('file', nil) == file
        @notifier.info "Removed #{file} from manifest.json" if match
        match
      end
    end
    @notifier.newline
  end
  write_manifest @manifest
end
remove_local_file(file) click to toggle source
# File lib/voog/dtk/filemanager.rb, line 866
def remove_local_file(file)
  if File.exist?(file) && File.delete(file)
    @notifier.info "Removed local file #{file}." unless @silent
    @notifier.newline
    return true
  else
    @notifier.error "Failed to remove file #{file}." unless @silent
    @notifier.newline
    return false
  end
end
remove_remote_file(file) click to toggle source
# File lib/voog/dtk/filemanager.rb, line 878
def remove_remote_file(file)
  relative_filename = relative_path_of(file)
  folder, filename = relative_filename.split('/')
  return unless (folder && filename)

  asset_ids = layout_asset_id_map
  layout_ids = layout_id_map

  if is_asset? relative_filename
    id = asset_ids.fetch(relative_filename, nil)

    unless id.nil?
      if delete_layout_asset(id)
        @notifier.info "Removed remote asset '#{filename}'." unless @silent
      else
        @notifier.error "Failed to remove remote asset '#{filename}'!" unless @silent
      end
    end
  elsif is_layout? relative_filename
    filename = filename.gsub('.tpl', '')
    id = layout_ids.fetch(relative_filename, nil)

    unless id.nil?
      if delete_layout(id)
        @notifier.info "Removed remote layout '#{filename}'." unless @silent
      else
        @notifier.error "Failed to remove remote layout '#{relative_filename}'!" unless @silent
      end
    end
  else
    @notifier.error "Invalid filename: '#{relative_filename}'"
  end
  @notifier.newline
end
sort_layouts_by_content_type(layouts) click to toggle source
# File lib/voog/dtk/filemanager.rb, line 359
def sort_layouts_by_content_type(layouts)
  # make sure that 'blog' is before 'blog_article' and 'elements' is before 'element'
  preferred_order = %w(page blog blog_article elements element error_401 error_404 photoset component)

  layouts.sort do |a, b|
    preferred_order.index(a.fetch('content_type')) <=> preferred_order.index(b.fetch('content_type'))
  end
end
update_layout(id, data) click to toggle source
# File lib/voog/dtk/filemanager.rb, line 212
def update_layout(id, data)
  @client.update_layout(id, body: data)
end
update_layout_asset(id, data) click to toggle source
# File lib/voog/dtk/filemanager.rb, line 216
def update_layout_asset(id, data)
  @client.update_layout_asset(id, data: data)
end
upload_files(files) click to toggle source
# File lib/voog/dtk/filemanager.rb, line 677
def upload_files(files)
  if files.length == 0
    @notifier.info "Pushing all files...\n"
    files = %w(layouts components stylesheets javascripts images assets)
  end

  layout_assets = layout_asset_id_map
  layouts = layout_id_map

  # Find if provided file is a directory instead
  files.each_with_index do |file, index|
    next if file.is_a? Array
    if Dir.exist? relative_path_of(file)
      subfiles = Dir.new(relative_path_of(file)).entries.reject{|e| e =~ /^(\.|\.\.)$/ } # Keep only normal subfiles
      subfiles.map! { |subfile| subfile = "#{relative_path_of(file)[/[^\/]*/]}/#{subfile}" } # Prepend folder name
      files[index] = subfiles # Insert as Array so sub-subfolders won't get processed again
    end
  end
  files.flatten! # If every folder is processed, flatten the array

  @manifest = read_manifest
  local_layouts = @manifest.fetch('layouts', []).reject(&:nil?).map{ |l| l.fetch('file','') }
  local_assets = @manifest.fetch('assets', []).reject(&:nil?).map{ |a| a.fetch('file','') }

  files.each_with_index do |file, index|
    @notifier.newline if index > 0
    relative_filename = relative_path_of(file)
    if File.exist?(relative_filename)
      if uploadable?(relative_filename)
        if relative_filename =~ /^(layout|component)s\/[^\/]+\.tpl$/ # if layout/component
          if local_layouts.include?(relative_filename)
            if layouts.key?(relative_filename)
              @notifier.info "Updating layout file #{relative_filename}..."
              if update_layout(layouts[relative_filename], File.read(relative_filename, encoding: 'UTF-8'))
                @notifier.success 'OK!'
              else
                @notifier.error "Cannot update layout file #{relative_filename}!"
              end
            else
              @notifier.warning "Remote file #{relative_filename} not found!"
              @notifier.info "\nTrying to create layout file #{relative_filename}..."
              if create_remote_layout(relative_filename)
                @notifier.success 'OK!'
              else
                @notifier.error "Unable to create layout file #{relative_filename}!"
              end
            end
          else
            @notifier.warning "Layout file #{relative_filename} not found in manifest! Skipping."
          end
        elsif relative_filename =~ /^(asset|image|stylesheet|javascript)s\/[^\/]+\..+$/ # if other asset
          if local_assets.include? relative_filename
            if layout_assets.key? relative_filename
              if is_editable?(relative_filename)
                @notifier.info "Updating layout asset file #{relative_filename}..."
                if update_layout_asset(layout_assets[relative_filename], File.read(relative_filename, encoding: 'UTF-8'))
                  @notifier.success 'OK!'
                else
                  @notifier.error "Unable to update file #{relative_filename}!"
                end
              else
                if @overwrite
                  @notifier.info "Re-uploading file #{relative_filename}..."
                  if delete_layout_asset(layout_assets[relative_filename]) && create_remote_file(relative_filename)
                    @notifier.success 'OK!'
                  else
                    @notifier.error "Unable to update file #{relative_filename}!"
                  end
                else
                  @notifier.warning "Not allowed to update file #{relative_filename}!"
                end
              end
            else
              @notifier.warning "Remote file #{relative_filename} not found!"
              @notifier.info "\nTrying to create file #{relative_filename}..."
              if create_remote_file(relative_filename)
                @notifier.success 'OK!'
              else
                @notifier.error "Unable to create file #{relative_filename}!"
              end
            end
          else
            @notifier.warning "Asset file #{relative_filename} not found in manifest! Skipping."
          end
        elsif Dir.exist? relative_filename
          @notifier.warning "Not allowed to push subfolder #{relative_filename}!"
        else
          @notifier.warning "Not allowed to push file #{relative_filename}!"
        end
      else
        @notifier.error "Cannot upload file #{relative_filename}!"
      end
    else
      @notifier.error "File #{relative_filename} not found!"
    end
  end
  @notifier.newline
end
uploadable?(file) click to toggle source
# File lib/voog/dtk/filemanager.rb, line 913
def uploadable?(file)
  if file.is_a? String
    !(relative_path_of(file) =~ /(component|layout|image|asset|javascript|stylesheet)s\/(.+)/).nil?
  else
    begin
      uploadable? (relative_path_of(file).is_a? String) ? relative_path_of(file).try(:to_s) : relative_path_of(file)
    rescue
      raise "Cannot upload file '#{relative_path_of(file)}'!".red
    end
  end
end
valid?(item) click to toggle source
# File lib/voog/dtk/filemanager.rb, line 228
def valid?(item)
  if item.is_a? String
    begin
      valid? JSON.parse(item)
    rescue
      false
    end
  else
    if item.is_a? Array
      item.each do |subitem|
        return valid? subitem
      end
    else
      if item.respond_to?('[]') && item.respond_to?('key?')
        return ( %w(title content_type component).map do |key|
          (item.key? key) || (item.key? key.to_sym)
        end.all? || %w(asset_type content_type filename).map do |key|
          (item.key? key) || (item.key? key.to_sym)
        end.all?)
      else
        return ( %i(title content_type component).map do |key|
          item.respond_to?(key)
        end.all? || %i(asset_type content_type filename).map do |key|
          item.respond_to?(key)
        end.all?)
      end
    end
  end
end
valid_for_folder?(filename, folder) click to toggle source
# File lib/voog/dtk/filemanager.rb, line 52
def valid_for_folder?(filename, folder)
  return false unless (filename && folder)

  # discard dotfiles
  return false if filename.match(/\A[\.]{1}.+\z/)

  mimetype = MIME::Types.of(filename).first
  media_type = mimetype.media_type if mimetype
  sub_type = mimetype.sub_type if mimetype

  case folder
  when 'images'
    # SVG files are assets, not images
    (media_type == 'image') && (sub_type != 'svg+xml')
  when 'javascripts'
    # Allow only pure JS files
    (media_type == 'application') && (sub_type == 'javascript')
  when 'stylesheets'
    # Only pure CSS files, not SCSS/LESS etc.
    (media_type == 'text') && (sub_type == 'css')
  when 'layouts'
    # Allow only files with .tpl extension
    /\A[^\.]+\.tpl\z/.match(filename) && true
  when 'components'
    # Allow only files with .tpl extension
    /\A[^\.]+\.tpl\z/.match(filename) && true
  else
    true
  end
end
write_manifest(manifest) click to toggle source
# File lib/voog/dtk/filemanager.rb, line 36
def write_manifest(manifest)
  if manifest["layouts"].is_a?(Array)
    manifest["layouts"].sort! { |a, b| a["file"] <=> b["file"]}
  end
  File.open('manifest.json', 'w+', encoding: 'UTF-8') do |file|
    file << JSON.pretty_generate(manifest)
  end
end