voog-kit 0.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/.rspec +3 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.markdown +142 -0
- data/Rakefile +1 -0
- data/bin/kit +212 -0
- data/lib/voog/dtk/filemanager.rb +784 -0
- data/lib/voog/dtk/guard.rb +147 -0
- data/lib/voog/dtk/notifier.rb +34 -0
- data/lib/voog/dtk/version.rb +5 -0
- data/lib/voog/dtk.rb +131 -0
- data/spec/fixtures/.DS_Store +0 -0
- data/spec/fixtures/.voog +3 -0
- data/spec/fixtures/.voog2 +7 -0
- data/spec/fixtures/images/bullet.png +0 -0
- data/spec/fixtures/layout.json +12 -0
- data/spec/fixtures/layout_asset.json +15 -0
- data/spec/fixtures/layout_assets.json +7 -0
- data/spec/fixtures/layouts.json +9 -0
- data/spec/fixtures/manifest.json +17 -0
- data/spec/models/dtk/config_spec.rb +82 -0
- data/spec/models/dtk/filemanager_spec.rb +524 -0
- data/spec/spec_helper.rb +38 -0
- data/voog-kit.gemspec +34 -0
- metadata +238 -0
@@ -0,0 +1,784 @@
|
|
1
|
+
require 'voog_api'
|
2
|
+
require 'net/http'
|
3
|
+
require 'json'
|
4
|
+
require 'fileutils'
|
5
|
+
require 'git'
|
6
|
+
require 'mime/types'
|
7
|
+
|
8
|
+
module Voog::Dtk
|
9
|
+
class FileManager
|
10
|
+
attr_accessor :notifier
|
11
|
+
def initialize(client, verbose=false, silent=false)
|
12
|
+
@notifier = Voog::Dtk::Notifier.new($stderr, silent)
|
13
|
+
@client = client
|
14
|
+
@verbose = verbose
|
15
|
+
end
|
16
|
+
|
17
|
+
def read_manifest
|
18
|
+
JSON.parse(File.read('manifest.json')).to_h
|
19
|
+
end
|
20
|
+
|
21
|
+
def write_manifest(manifest)
|
22
|
+
File.open('manifest.json', 'w+') do |file|
|
23
|
+
file << JSON.pretty_generate(manifest)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def in_manifest?(file, manifest=nil)
|
28
|
+
@manifest = manifest || read_manifest
|
29
|
+
filenames = @manifest['layouts'].map{|l| l.fetch('file', '')}
|
30
|
+
filenames += @manifest['assets'].map{|a| a.fetch('filename', '')}
|
31
|
+
filenames.include? file
|
32
|
+
end
|
33
|
+
|
34
|
+
def add_to_manifest(files = nil)
|
35
|
+
return if files.nil?
|
36
|
+
@manifest = read_manifest
|
37
|
+
files = (files.is_a? String) ? [files] : files
|
38
|
+
files.uniq.each do |file|
|
39
|
+
next if in_manifest?(file, @manifest)
|
40
|
+
match = /^(component|layout|image|javascript|asset|stylesheet)s\/(.*)/.match(file)
|
41
|
+
next if match.nil?
|
42
|
+
type, filename = match[1], match[2]
|
43
|
+
if %w(component layout).include? type
|
44
|
+
component = type == 'component'
|
45
|
+
name = filename.split('.').first
|
46
|
+
title = component ? name : name.gsub('_', ' ').capitalize
|
47
|
+
layout = {
|
48
|
+
'title' => component ? name : title,
|
49
|
+
'layout_name' => name,
|
50
|
+
'content_type' => component ? 'component' : 'page',
|
51
|
+
'component' => component,
|
52
|
+
'file' => file
|
53
|
+
}
|
54
|
+
@manifest['layouts'] << layout
|
55
|
+
elsif %w(image javascript asset stylesheet).include? type
|
56
|
+
asset = {
|
57
|
+
'content_type' => begin
|
58
|
+
MIME::Types.type_for(filename).first.content_type
|
59
|
+
rescue
|
60
|
+
'text/unknown'
|
61
|
+
end,
|
62
|
+
'kind' => "#{type}s",
|
63
|
+
'file' => file,
|
64
|
+
'filename' => filename
|
65
|
+
}
|
66
|
+
@manifest['assets'] << asset
|
67
|
+
end
|
68
|
+
@notifier.newline
|
69
|
+
@notifier.info "Added #{file} to manifest.json"
|
70
|
+
end
|
71
|
+
write_manifest @manifest
|
72
|
+
@notifier.newline
|
73
|
+
end
|
74
|
+
|
75
|
+
def remove_from_manifest(files = nil)
|
76
|
+
return if files.nil?
|
77
|
+
@manifest = read_manifest
|
78
|
+
files = (files.is_a? String) ? [files] : files
|
79
|
+
files.uniq.each do |file|
|
80
|
+
match = /^(component|layout|image|javascript|asset|stylesheet)s\/(.*)/.match(file)
|
81
|
+
next if match.nil?
|
82
|
+
type, filename = match[1], match[2]
|
83
|
+
if %w(component layout).include? type
|
84
|
+
@manifest['layouts'].delete_if do |layout|
|
85
|
+
match = layout.fetch('file', nil) == file
|
86
|
+
@notifier.info "Removed #{file} from manifest.json\n" if match
|
87
|
+
match
|
88
|
+
end
|
89
|
+
elsif %w(image javascript asset stylesheet).include? type
|
90
|
+
@manifest['assets'].delete_if do |asset|
|
91
|
+
match = asset.fetch('file', nil) == file
|
92
|
+
@notifier.info "Removed #{file} from manifest.json\n" if match
|
93
|
+
match
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
write_manifest @manifest
|
98
|
+
end
|
99
|
+
|
100
|
+
def delete_remote_files(files)
|
101
|
+
return if files.nil?
|
102
|
+
files = (files.is_a? String) ? [files] : files
|
103
|
+
layouts = layout_id_map
|
104
|
+
assets = asset_id_map
|
105
|
+
files.uniq.each do |file|
|
106
|
+
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def get_layouts
|
111
|
+
@client.layouts(per_page: 10000)
|
112
|
+
end
|
113
|
+
|
114
|
+
def get_layout_assets
|
115
|
+
@client.layout_assets(per_page: 10000)
|
116
|
+
end
|
117
|
+
|
118
|
+
def get_layout(id)
|
119
|
+
@client.layout id
|
120
|
+
end
|
121
|
+
|
122
|
+
def get_layout_asset(id)
|
123
|
+
@client.layout_asset id
|
124
|
+
end
|
125
|
+
|
126
|
+
def update_layout(id, data)
|
127
|
+
@client.update_layout(id, body: data)
|
128
|
+
end
|
129
|
+
|
130
|
+
def update_layout_asset(id, data)
|
131
|
+
@client.update_layout_asset(id, data: data)
|
132
|
+
end
|
133
|
+
|
134
|
+
def valid?(item)
|
135
|
+
if item.is_a? String
|
136
|
+
begin
|
137
|
+
valid? JSON.parse(item)
|
138
|
+
rescue
|
139
|
+
false
|
140
|
+
end
|
141
|
+
else
|
142
|
+
if item.is_a? Array
|
143
|
+
item.each do |subitem|
|
144
|
+
return valid? subitem
|
145
|
+
end
|
146
|
+
else
|
147
|
+
if item.respond_to?('[]') && item.respond_to?('key?')
|
148
|
+
return ( %w(title content_type component).map do |key|
|
149
|
+
(item.key? key) || (item.key? key.to_sym)
|
150
|
+
end.all? || %w(asset_type content_type filename).map do |key|
|
151
|
+
(item.key? key) || (item.key? key.to_sym)
|
152
|
+
end.all?)
|
153
|
+
else
|
154
|
+
return ( %i(title content_type component).map do |key|
|
155
|
+
item.respond_to?(key)
|
156
|
+
end.all? || %i(asset_type content_type filename).map do |key|
|
157
|
+
item.respond_to?(key)
|
158
|
+
end.all?)
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
def generate_local_manifest(verbose=false, silent=false)
|
165
|
+
unless %w(layouts components).map { |f| Dir.exists? f }.all?
|
166
|
+
@notifier.error 'Cannot find any local layout files! (See `kit help init`)'
|
167
|
+
return false
|
168
|
+
end
|
169
|
+
|
170
|
+
begin
|
171
|
+
@old_manifest = JSON.parse(File.read('manifest.json', :encoding => 'UTF-8')).to_h if File.exists? 'manifest.json'
|
172
|
+
rescue JSON::ParserError => e
|
173
|
+
@notifier.error "Invalid JSON in current manifest file!"
|
174
|
+
@notifier.newline
|
175
|
+
end
|
176
|
+
|
177
|
+
@notifier.info 'Reading local files...'
|
178
|
+
layouts_dir = Dir.new('layouts')
|
179
|
+
layouts = layouts_dir.entries.select do |file|
|
180
|
+
file =~ /(.*)\.tpl/
|
181
|
+
end
|
182
|
+
layouts = layouts.map do |l|
|
183
|
+
attrs = {
|
184
|
+
"content_type" => "page",
|
185
|
+
"component" => false,
|
186
|
+
"file" => "layouts/#{l}",
|
187
|
+
"layout_name" => "page_default",
|
188
|
+
"title" => l.split(".").first.gsub('_', " ").capitalize
|
189
|
+
}
|
190
|
+
if @old_manifest && @old_manifest.fetch('layouts')
|
191
|
+
old_layout = @old_manifest.fetch('layouts').reject(&:nil?).select { |ol| ol.fetch('file').include? l}.first || {}
|
192
|
+
attrs.merge! old_layout
|
193
|
+
end
|
194
|
+
attrs
|
195
|
+
end
|
196
|
+
components_dir = Dir.new('components')
|
197
|
+
components = components_dir.entries.select do |file|
|
198
|
+
file =~/(.*)\.tpl/
|
199
|
+
end
|
200
|
+
components = components.map do |c|
|
201
|
+
name = c.split(".").first.gsub('_', ' ')
|
202
|
+
attrs = {
|
203
|
+
"content_type" => "component",
|
204
|
+
"component" => true,
|
205
|
+
"file" => "components/#{c}",
|
206
|
+
"layout_name" => name,
|
207
|
+
"title" => name
|
208
|
+
}
|
209
|
+
if @old_manifest && @old_manifest.fetch('layouts')
|
210
|
+
old_component = @old_manifest.fetch('layouts').reject(&:nil?).select { |ol| ol.fetch('file').include? c}.first || {}
|
211
|
+
attrs.merge! old_component
|
212
|
+
end
|
213
|
+
attrs
|
214
|
+
end
|
215
|
+
assets = []
|
216
|
+
asset_dirs = %w(assets images javascripts stylesheets)
|
217
|
+
asset_dirs.each do |dir|
|
218
|
+
next unless Dir.exists? dir
|
219
|
+
current_dir = Dir.new(dir)
|
220
|
+
current_dir.entries.each do |file|
|
221
|
+
next if file =~ /^\.\.?$/
|
222
|
+
attrs = {
|
223
|
+
"content_type" => begin
|
224
|
+
MIME::Types.type_for(file).first.content_type
|
225
|
+
rescue
|
226
|
+
'text/unknown'
|
227
|
+
end,
|
228
|
+
"file" => "#{dir}/#{file}",
|
229
|
+
"kind" => dir,
|
230
|
+
"filename" => file
|
231
|
+
}
|
232
|
+
if @old_manifest && @old_manifest.fetch('assets')
|
233
|
+
old_asset = @old_manifest.fetch('assets').reject(&:nil?).select { |ol| ol.fetch('file').include? file}.first || {}
|
234
|
+
attrs.merge! old_asset
|
235
|
+
end
|
236
|
+
assets << attrs
|
237
|
+
end
|
238
|
+
end
|
239
|
+
manifest = {
|
240
|
+
"description" => "New design",
|
241
|
+
"name" => "New design",
|
242
|
+
"preview_medium" => "",
|
243
|
+
"preview_small" => "",
|
244
|
+
"author" => "",
|
245
|
+
"layouts" => layouts + components,
|
246
|
+
"assets" => assets
|
247
|
+
}
|
248
|
+
if @old_manifest
|
249
|
+
old_meta = @old_manifest.tap{ |m| m.delete("assets") }.tap{ |m| m.delete("layouts") }
|
250
|
+
manifest.merge! old_meta
|
251
|
+
end
|
252
|
+
@notifier.newline
|
253
|
+
@notifier.info 'Writing layout files to new manifest.json file...'
|
254
|
+
write_manifest(manifest)
|
255
|
+
@notifier.success 'Done!'
|
256
|
+
@notifier.newline
|
257
|
+
return true
|
258
|
+
end
|
259
|
+
|
260
|
+
def generate_remote_manifest
|
261
|
+
generate_manifest get_layouts, get_layout_assets
|
262
|
+
end
|
263
|
+
|
264
|
+
def generate_manifest(layouts = nil, layout_assets = nil)
|
265
|
+
layouts = layouts || get_layouts
|
266
|
+
layout_assets = layout_assets || get_layout_assets
|
267
|
+
|
268
|
+
unless (layouts && layout_assets && !layouts.empty? && !layout_assets.empty?)
|
269
|
+
@notifier.error 'No remote layouts found to generate manifest from!'
|
270
|
+
@notifier.newline
|
271
|
+
return false
|
272
|
+
end
|
273
|
+
|
274
|
+
unless valid?(layouts) && valid?(layout_assets)
|
275
|
+
@notifier.error 'No valid layouts found to generate manifest from!'
|
276
|
+
@notifier.newline
|
277
|
+
return false
|
278
|
+
end
|
279
|
+
|
280
|
+
@notifier.info 'Writing remote layouts to new manifest.json file...'
|
281
|
+
manifest = Hash.new
|
282
|
+
manifest[:layouts] = layouts.inject(Array.new) do |memo, l|
|
283
|
+
memo << {
|
284
|
+
title: l.title,
|
285
|
+
layout_name: l.title.gsub(/[^\w\.\-]/, '_').downcase,
|
286
|
+
content_type: l.content_type,
|
287
|
+
component: l.component,
|
288
|
+
file: "#{(l.component ? 'components' : 'layouts')}/#{l.title.gsub(/[^\w\.\-]/, '_').downcase}.tpl"
|
289
|
+
}
|
290
|
+
end
|
291
|
+
|
292
|
+
manifest[:assets] = layout_assets.inject(Array.new) do |memo, a|
|
293
|
+
folder = if %w(unknown font).include? a.asset_type
|
294
|
+
"assets"
|
295
|
+
else
|
296
|
+
"#{a.asset_type}s"
|
297
|
+
end
|
298
|
+
memo << {
|
299
|
+
kind: a.asset_type,
|
300
|
+
filename: a.filename,
|
301
|
+
file: "#{folder}/#{a.filename}",
|
302
|
+
content_type: a.content_type
|
303
|
+
}
|
304
|
+
end
|
305
|
+
|
306
|
+
write_manifest(manifest)
|
307
|
+
@notifier.success 'Done!'
|
308
|
+
@notifier.newline
|
309
|
+
end
|
310
|
+
|
311
|
+
def create_folders
|
312
|
+
@notifier.newline
|
313
|
+
@notifier.info 'Creating folder structure...'
|
314
|
+
folders = %w(stylesheets images assets javascripts components layouts)
|
315
|
+
folders.each { |folder| Dir.mkdir(folder) unless Dir.exists?(folder) }
|
316
|
+
@notifier.success 'Done!'
|
317
|
+
@notifier.newline
|
318
|
+
end
|
319
|
+
|
320
|
+
def create_files(layouts = nil, layout_assets = nil)
|
321
|
+
if layouts.nil? && layout_assets.nil?
|
322
|
+
layouts = get_layouts
|
323
|
+
layout_assets = get_layout_assets
|
324
|
+
end
|
325
|
+
create_layouts(layouts.map(&:id))
|
326
|
+
create_assets(layout_assets.map(&:id))
|
327
|
+
end
|
328
|
+
|
329
|
+
def create_assets(ids)
|
330
|
+
@notifier.info "Creating assets#{'...' if @verbose}"
|
331
|
+
ids.uniq.each do |id|
|
332
|
+
create_asset(get_layout_asset id)
|
333
|
+
end
|
334
|
+
@notifier.newline if @verbose
|
335
|
+
@notifier.success 'Done!'
|
336
|
+
@notifier.newline
|
337
|
+
end
|
338
|
+
|
339
|
+
def create_asset(asset = nil)
|
340
|
+
valid = asset && asset.respond_to?(:asset_type) \
|
341
|
+
&& asset.respond_to?(:filename) \
|
342
|
+
&& asset.respond_to?(:asset_type) \
|
343
|
+
&& (asset.respond_to?(:public_url) || asset.respond_to?(:data))
|
344
|
+
|
345
|
+
folder_names = {
|
346
|
+
'image' => 'images',
|
347
|
+
'stylesheet' => 'stylesheets',
|
348
|
+
'javascript' => 'javascripts',
|
349
|
+
'font' => 'assets',
|
350
|
+
'unknown' => 'assets'
|
351
|
+
}
|
352
|
+
|
353
|
+
if valid
|
354
|
+
folder = folder_names.fetch(asset.asset_type, 'assets')
|
355
|
+
|
356
|
+
Dir.mkdir(folder) unless Dir.exists?(folder)
|
357
|
+
Dir.chdir(folder)
|
358
|
+
|
359
|
+
overwritten = File.exists? asset.filename
|
360
|
+
|
361
|
+
if @verbose
|
362
|
+
@notifier.newline
|
363
|
+
if overwritten
|
364
|
+
@notifier.warning " + #{folder}/#{asset.filename}"
|
365
|
+
else
|
366
|
+
@notifier.success " + #{folder}/#{asset.filename}"
|
367
|
+
end
|
368
|
+
else
|
369
|
+
@notifier.success '.'
|
370
|
+
end
|
371
|
+
|
372
|
+
if %w(stylesheet javascript).include? asset.asset_type
|
373
|
+
open(asset.filename, 'wb') { |file| file.write(asset.data) }
|
374
|
+
else
|
375
|
+
url = URI(asset.public_url)
|
376
|
+
Net::HTTP.start(url.hostname) do |http|
|
377
|
+
resp = http.get(url.path)
|
378
|
+
open(asset.filename, 'wb') { |file| file.write(resp.body) }
|
379
|
+
end
|
380
|
+
end
|
381
|
+
Dir.chdir('..')
|
382
|
+
else
|
383
|
+
unless @verbose
|
384
|
+
@notifier.error '!'
|
385
|
+
end
|
386
|
+
end
|
387
|
+
end
|
388
|
+
|
389
|
+
def create_layouts(ids)
|
390
|
+
@notifier.info "Creating layouts#{'...' if @verbose}"
|
391
|
+
ids.each do |id|
|
392
|
+
create_layout get_layout id
|
393
|
+
end
|
394
|
+
@notifier.newline if @verbose
|
395
|
+
@notifier.success 'Done!'
|
396
|
+
@notifier.newline
|
397
|
+
end
|
398
|
+
|
399
|
+
def create_layout(layout = nil)
|
400
|
+
valid = layout &&
|
401
|
+
layout.respond_to?(:component) &&
|
402
|
+
layout.respond_to?(:title) &&
|
403
|
+
layout.respond_to?(:body)
|
404
|
+
|
405
|
+
if valid
|
406
|
+
folder = layout.component ? 'components' : 'layouts'
|
407
|
+
filename = "#{layout.title.gsub(/[^\w\.\-]/, '_').downcase}.tpl"
|
408
|
+
Dir.chdir(folder)
|
409
|
+
overwritten = File.exists? filename
|
410
|
+
|
411
|
+
if @verbose
|
412
|
+
@notifier.newline
|
413
|
+
if overwritten
|
414
|
+
@notifier.warning " + #{folder}/#{filename}"
|
415
|
+
else
|
416
|
+
@notifier.success " + #{folder}/#{filename}"
|
417
|
+
end
|
418
|
+
else
|
419
|
+
@notifier.success '.'
|
420
|
+
end
|
421
|
+
|
422
|
+
File.open(filename, 'w') { |file| file.write layout.body }
|
423
|
+
|
424
|
+
Dir.chdir('..')
|
425
|
+
else
|
426
|
+
unless @verbose
|
427
|
+
@notifier.error '!'
|
428
|
+
end
|
429
|
+
end
|
430
|
+
end
|
431
|
+
|
432
|
+
def check
|
433
|
+
ok_char = "."
|
434
|
+
not_ok_char = "!"
|
435
|
+
@notifier.info "Checking manifest.json..."
|
436
|
+
|
437
|
+
# Check for manifest
|
438
|
+
if File.exists? 'manifest.json'
|
439
|
+
@manifest = read_manifest
|
440
|
+
@notifier.success 'OK!'
|
441
|
+
else
|
442
|
+
@notifier.error 'Manifest file not found! Use the \'manifest\' command to generate one.'
|
443
|
+
return false
|
444
|
+
end
|
445
|
+
|
446
|
+
# Check for files in manifest
|
447
|
+
layouts = @manifest.fetch('layouts', [])
|
448
|
+
missing_layouts = %w()
|
449
|
+
|
450
|
+
@notifier.newline
|
451
|
+
@notifier.info "Checking layouts and components"
|
452
|
+
layouts.reject(&:nil?).each do |layout|
|
453
|
+
if File.exists? layout['file']
|
454
|
+
@notifier.success ok_char
|
455
|
+
else
|
456
|
+
missing_layouts << layout['file']
|
457
|
+
@notifier.error not_ok_char
|
458
|
+
end
|
459
|
+
end
|
460
|
+
|
461
|
+
if missing_layouts.count > 0
|
462
|
+
@notifier.error " (#{missing_layouts.count} missing)"
|
463
|
+
@notifier.newline
|
464
|
+
missing_layouts.each do |a|
|
465
|
+
@notifier.normal " #{a}"
|
466
|
+
@notifier.newline
|
467
|
+
end if @verbose
|
468
|
+
else
|
469
|
+
@notifier.success 'OK!'
|
470
|
+
end
|
471
|
+
|
472
|
+
assets = @manifest['assets']
|
473
|
+
missing_assets = %w()
|
474
|
+
|
475
|
+
@notifier.newline
|
476
|
+
@notifier.info "Checking assets"
|
477
|
+
assets.reject(&:nil?).each do |asset|
|
478
|
+
if File.exists? asset['file']
|
479
|
+
@notifier.success ok_char
|
480
|
+
else
|
481
|
+
missing_assets << asset['file']
|
482
|
+
@notifier.error not_ok_char
|
483
|
+
end
|
484
|
+
end
|
485
|
+
|
486
|
+
if missing_assets.count > 0
|
487
|
+
@notifier.error " (#{missing_assets.count} missing)"
|
488
|
+
@notifier.newline
|
489
|
+
missing_assets.each do |a|
|
490
|
+
@notifier.normal " #{a}"
|
491
|
+
@notifier.newline
|
492
|
+
end if @verbose
|
493
|
+
else
|
494
|
+
@notifier.success 'OK!'
|
495
|
+
end
|
496
|
+
end
|
497
|
+
|
498
|
+
def fetch_boilerplate(dst='tmp')
|
499
|
+
@notifier.info 'Fetching design boilerplate...'
|
500
|
+
@notifier.newline
|
501
|
+
|
502
|
+
FileUtils.rm_r 'tmp' if Dir.exists? 'tmp'
|
503
|
+
|
504
|
+
begin
|
505
|
+
Git.clone 'git@github.com:Edicy/design-boilerplate.git', dst
|
506
|
+
rescue
|
507
|
+
@notifier.error 'An error occurred!'
|
508
|
+
return false
|
509
|
+
end
|
510
|
+
|
511
|
+
if Dir.exists? 'tmp'
|
512
|
+
Dir.chdir 'tmp'
|
513
|
+
@notifier.info 'Copying boilerplate files to working directory...'
|
514
|
+
@notifier.newline
|
515
|
+
Dir.new('.').entries.each do |f|
|
516
|
+
unless f =~ /^\..*$/
|
517
|
+
if Dir.exists?('../' + f) || File.exists?('../' + f)
|
518
|
+
FileUtils.rm_r '../' + f
|
519
|
+
end
|
520
|
+
FileUtils.mv f, '..'
|
521
|
+
end
|
522
|
+
end
|
523
|
+
Dir.chdir '..'
|
524
|
+
FileUtils.rm_r 'tmp'
|
525
|
+
end
|
526
|
+
@notifier.success 'Done!'
|
527
|
+
@notifier.newline
|
528
|
+
return true
|
529
|
+
end
|
530
|
+
|
531
|
+
# Returns filename=>id hash for layout files
|
532
|
+
def layout_id_map(layouts=nil)
|
533
|
+
layouts ||= get_layouts
|
534
|
+
remote_layouts = layouts.inject(Hash.new) do |memo, l|
|
535
|
+
memo[l.title.downcase] = l.id
|
536
|
+
memo
|
537
|
+
end
|
538
|
+
|
539
|
+
@manifest = read_manifest
|
540
|
+
fail "Manifest not found! (See `kit help push` for more info)".red unless @manifest
|
541
|
+
layouts = @manifest.fetch('layouts').reject(&:nil?)
|
542
|
+
layouts.inject(Hash.new) do |memo, l|
|
543
|
+
remote_exists = remote_layouts.key?(l.fetch('title').downcase)
|
544
|
+
memo[l.fetch('file')] = remote_layouts.fetch(l.fetch('title').downcase, nil) if remote_exists
|
545
|
+
memo
|
546
|
+
end
|
547
|
+
end
|
548
|
+
|
549
|
+
# Returns filename=>id hash for layout assets
|
550
|
+
def layout_asset_id_map(assets=nil)
|
551
|
+
assets ||= get_layout_assets
|
552
|
+
assets.inject(Hash.new) do |memo, a|
|
553
|
+
memo[a.public_url.gsub("http://#{@client.host}/", '')] = a.id
|
554
|
+
memo
|
555
|
+
end
|
556
|
+
end
|
557
|
+
|
558
|
+
def upload_files(files)
|
559
|
+
if files.length == 0
|
560
|
+
@notifier.info "Pushing all files...\n"
|
561
|
+
files = %w(layouts components stylesheets javascripts images assets)
|
562
|
+
end
|
563
|
+
|
564
|
+
layout_assets = layout_asset_id_map
|
565
|
+
layouts = layout_id_map
|
566
|
+
|
567
|
+
# Find if provided file is a directory instead
|
568
|
+
files.each_with_index do |file, index|
|
569
|
+
next if file.is_a? Array
|
570
|
+
if Dir.exists? file
|
571
|
+
subfiles = Dir.new(file).entries.reject{|e| e =~ /^(\.|\.\.)$/ } # Keep only normal subfiles
|
572
|
+
subfiles.map!{ |subfile| subfile = "#{file[/[^\/]*/]}/#{subfile}"} # Prepend folder name
|
573
|
+
files[index] = subfiles # Insert as Array so sub-subfolders won't get processed again
|
574
|
+
end
|
575
|
+
end
|
576
|
+
files.flatten! # If every folder is processed, flatten the array
|
577
|
+
|
578
|
+
@manifest = read_manifest
|
579
|
+
local_layouts = @manifest.fetch('layouts', []).reject(&:nil?).map{ |l| l.fetch('file','') }
|
580
|
+
local_assets = @manifest.fetch('assets', []).reject(&:nil?).map{ |a| a.fetch('file','') }
|
581
|
+
|
582
|
+
files.each_with_index do |file, index|
|
583
|
+
@notifier.newline if index > 0
|
584
|
+
if File.exist?(file)
|
585
|
+
if uploadable?(file)
|
586
|
+
if file =~ /^(layout|component)s\/[^\s\/]+\.tpl$/ # if layout/component
|
587
|
+
if local_layouts.include?(file)
|
588
|
+
if layouts.key?(file)
|
589
|
+
@notifier.info "Updating layout file #{file}..."
|
590
|
+
if update_layout(layouts[file], File.read(file, :encoding => 'UTF-8'))
|
591
|
+
@notifier.success 'OK!'
|
592
|
+
else
|
593
|
+
@notifier.error "Cannot update layout file #{file}!"
|
594
|
+
end
|
595
|
+
else
|
596
|
+
@notifier.warning "Remote file #{file} not found!"
|
597
|
+
@notifier.info "\nTrying to create layout file #{file}..."
|
598
|
+
if create_remote_layout(file)
|
599
|
+
@notifier.success 'OK!'
|
600
|
+
else
|
601
|
+
@notifier.error "Unable to create layout file #{file}!"
|
602
|
+
end
|
603
|
+
end
|
604
|
+
else
|
605
|
+
@notifier.warning "Layout file #{file} not found in manifest! Skipping."
|
606
|
+
end
|
607
|
+
elsif file =~ /^(asset|image|stylesheet|javascript)s\/[^\s\/]+\..+$/ # if other asset
|
608
|
+
if local_assets.include? file
|
609
|
+
if layout_assets.key? file
|
610
|
+
if is_editable?(file)
|
611
|
+
@notifier.info "Updating layout asset file #{file}..."
|
612
|
+
if update_layout_asset(layout_assets[file], File.read(file, :encoding => 'UTF-8'))
|
613
|
+
@notifier.success 'OK!'
|
614
|
+
else
|
615
|
+
@notifier.error "Unable to update file #{file}!"
|
616
|
+
end
|
617
|
+
else
|
618
|
+
@notifier.warning "Not allowed to update file #{file}! Skipping."
|
619
|
+
end
|
620
|
+
else
|
621
|
+
@notifier.warning "Remote file #{file} not found!"
|
622
|
+
@notifier.info "\nTrying to create file #{file}..."
|
623
|
+
if create_remote_file(file)
|
624
|
+
@notifier.success 'OK!'
|
625
|
+
else
|
626
|
+
@notifier.error "Unable to create file #{file}!"
|
627
|
+
end
|
628
|
+
end
|
629
|
+
else
|
630
|
+
@notifier.warning "Asset file #{file} not found in manifest! Skipping."
|
631
|
+
end
|
632
|
+
elsif Dir.exists? file
|
633
|
+
@notifier.warning "Not allowed to push subfolder #{file}!"
|
634
|
+
else
|
635
|
+
@notifier.warning "Not allowed to push file #{file}!"
|
636
|
+
end
|
637
|
+
else
|
638
|
+
@notifier.error "Cannot upload file #{file}!"
|
639
|
+
end
|
640
|
+
else
|
641
|
+
@notifier.error "File #{file} not found!"
|
642
|
+
end
|
643
|
+
end
|
644
|
+
@notifier.newline
|
645
|
+
end
|
646
|
+
|
647
|
+
def is_editable?(file)
|
648
|
+
folder = file.split('/').first
|
649
|
+
extension = file.split('/').last.split('.').last
|
650
|
+
|
651
|
+
(%w(stylesheets javascripts).include? folder) && (%w(js css).include? extension)
|
652
|
+
end
|
653
|
+
|
654
|
+
def content_type_for(file)
|
655
|
+
folder = file.split('/').first
|
656
|
+
if is_editable?(file)
|
657
|
+
if folder == 'stylesheets'
|
658
|
+
'text/css'
|
659
|
+
elsif folder == 'javascripts'
|
660
|
+
'text/javascript'
|
661
|
+
end
|
662
|
+
else
|
663
|
+
if folder == 'images'
|
664
|
+
"image/#{file.split("/").last.split(".").last}"
|
665
|
+
elsif folder == 'assets'
|
666
|
+
'unknown/unknown'
|
667
|
+
end
|
668
|
+
end
|
669
|
+
end
|
670
|
+
|
671
|
+
def create_remote_layout(file)
|
672
|
+
@manifest = read_manifest if File.exists? 'manifest.json'
|
673
|
+
layouts = @manifest.fetch('layouts', []).reject(&:nil?)
|
674
|
+
layout = layouts.select { |l| file == l.fetch('file') }.first
|
675
|
+
|
676
|
+
if @manifest && layouts && layout
|
677
|
+
data = {
|
678
|
+
title: layout.fetch('title'),
|
679
|
+
content_type: layout.fetch('content_type'),
|
680
|
+
component: layout.fetch('component'),
|
681
|
+
body: File.exists?(layout.fetch('file')) ? File.read(layout.fetch('file'), :encoding => 'UTF-8') : ''
|
682
|
+
}
|
683
|
+
else
|
684
|
+
name = file.split('/').last.split('.').first
|
685
|
+
component = (file.split('/').first =~ /^layouts$/).nil?
|
686
|
+
body = File.read(file, :encoding => 'UTF-8')
|
687
|
+
data = {
|
688
|
+
title: component ? name : name.capitalize,
|
689
|
+
content_type: 'page',
|
690
|
+
component: component,
|
691
|
+
body: body
|
692
|
+
}
|
693
|
+
end
|
694
|
+
|
695
|
+
@client.create_layout(data)
|
696
|
+
end
|
697
|
+
|
698
|
+
def create_remote_file(file)
|
699
|
+
data = {
|
700
|
+
filename: file.split('/').last,
|
701
|
+
content_type: content_type_for(file)
|
702
|
+
}
|
703
|
+
|
704
|
+
if is_editable?(file)
|
705
|
+
data[:data] = File.read(file, :encoding => 'UTF-8')
|
706
|
+
else
|
707
|
+
data[:file] = file
|
708
|
+
end
|
709
|
+
|
710
|
+
@client.create_layout_asset(data)
|
711
|
+
end
|
712
|
+
|
713
|
+
def uploadable?(file)
|
714
|
+
if file.is_a? String
|
715
|
+
!(file =~ /^(component|layout|image|asset|javascript|stylesheet)s\/([^\s]+)/).nil?
|
716
|
+
else
|
717
|
+
begin
|
718
|
+
uploadable? file.try(:to_s)
|
719
|
+
rescue
|
720
|
+
fail "Cannot upload file '#{file}'!".red
|
721
|
+
end
|
722
|
+
end
|
723
|
+
end
|
724
|
+
|
725
|
+
def find_layouts(names)
|
726
|
+
layouts = get_layouts
|
727
|
+
@manifest = read_manifest if File.exist? 'manifest.json'
|
728
|
+
results = []
|
729
|
+
|
730
|
+
names.each do |name|
|
731
|
+
name = name.split('/').last.split('.').first
|
732
|
+
if @manifest
|
733
|
+
layout = @manifest.fetch('layouts', []).reject(&:nil?).find{ |l| l['file'].split('/').last.split('.').first == name }
|
734
|
+
if layout # layout file is in manifest
|
735
|
+
layout = layouts.find{ |l| l.title == layout['title'] }
|
736
|
+
else # not found in manifest
|
737
|
+
layout = layouts.find{ |l| l.title == name }
|
738
|
+
end
|
739
|
+
id = layout.id if layout
|
740
|
+
else
|
741
|
+
layout = layouts.find{ |l| l.title.gsub(/[^\w\.]/, '_').downcase == name}
|
742
|
+
id = layout['id'] if layout
|
743
|
+
end
|
744
|
+
results << id if id
|
745
|
+
end
|
746
|
+
|
747
|
+
results
|
748
|
+
end
|
749
|
+
|
750
|
+
def find_assets(names)
|
751
|
+
assets = get_layout_assets
|
752
|
+
results = []
|
753
|
+
names.each do |name|
|
754
|
+
name = name.split('/').last
|
755
|
+
layout = assets.find{ |l| l.filename == name }
|
756
|
+
results << layout.id if layout
|
757
|
+
end
|
758
|
+
results
|
759
|
+
end
|
760
|
+
|
761
|
+
def pull_files(names)
|
762
|
+
# TODO: pull whole folders, e.g "pull images"
|
763
|
+
layout_ids = find_layouts(names)
|
764
|
+
asset_ids = find_assets(names)
|
765
|
+
|
766
|
+
found = layout_ids.length + asset_ids.length
|
767
|
+
if found > 0 && found < names.length
|
768
|
+
@notifier.warning "Unable to find some specified files!"
|
769
|
+
@notifier.newline
|
770
|
+
ret = true
|
771
|
+
elsif found == names.length
|
772
|
+
ret = true
|
773
|
+
elsif found == 0
|
774
|
+
@notifier.error "Unable to find any specified files!"
|
775
|
+
ret = false
|
776
|
+
end
|
777
|
+
|
778
|
+
create_layouts(layout_ids) unless layout_ids.empty?
|
779
|
+
create_assets(asset_ids) unless asset_ids.empty?
|
780
|
+
|
781
|
+
ret
|
782
|
+
end
|
783
|
+
end
|
784
|
+
end
|