siteleaf 0.9.23 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 6f81b5e2b0e30522c443b49419ddc93eaee2ce31
4
- data.tar.gz: 5eb92f3d3bf0e6f555f7620f762462caac9fde80
3
+ metadata.gz: 75e7b85ae46ef7e9a4acb9f91e7cfa6aad12d461
4
+ data.tar.gz: f3fad5f5f09556a6d2e850300ea5f3a5ebb75483
5
5
  SHA512:
6
- metadata.gz: ab5c786334310e8ff5def9532ee81c0dea9b2d29dbeacc5b8352820c6b16cea3770c777cd036ef02ee4c9c70420cf02a0fc3917262e12c24e31737bfdd15cf60
7
- data.tar.gz: 27ac15f55218d9d7227ab0cfdf4fe8ce6c06ef43925c8a99bef7c071fb8bdbbe7ff60dd822ed593197181a9b1bbebd7be6bd15c07d7f859d71ac7b0454514aed
6
+ metadata.gz: ae362f7cba5c44002055e3eb7ac4110003738a34780af46f3e2f0509d33b780e6562f968ab8716e775a79aff119ca2849741aa8b60d4ecd88d7fef47bbb63e88
7
+ data.tar.gz: c19eace8a1bf47b3e4e079cbc2f021bc47e05eb26a8f3148234d75b7f37c30526ab7e774cb051a011f2516c48c9e56cce671dce34699bc340b30e4afecfd46f3
data/.gitignore CHANGED
@@ -17,3 +17,6 @@ test/version_tmp
17
17
  tmp
18
18
  .ruby-version
19
19
  .DS_Store
20
+ source/
21
+ export/
22
+ config.ru
@@ -0,0 +1,2 @@
1
+ EXCON_DEBUG=true
2
+ DEBUG=true
data/README.md CHANGED
@@ -2,12 +2,14 @@ Siteleaf Gem
2
2
  ============
3
3
 
4
4
  - [Installation](#installation)
5
- - [Testing sites locally](#testing-sites-locally)
5
+ - [Using the CLI](#using-the-cli)
6
6
  - [Using this gem in your application](#using-this-gem-in-your-application)
7
7
  - [Using the API](#using-the-api)
8
+ - [Exporting your site](#exporting-your-site)
8
9
  - [Troubleshooting](#troubleshooting)
9
10
  - [Contributing](#contributing)
10
11
 
12
+
11
13
  Installation
12
14
  ------------
13
15
 
@@ -16,8 +18,10 @@ The Siteleaf gem is available for installation on [Rubygems](https://rubygems.or
16
18
  gem install siteleaf
17
19
 
18
20
 
19
- Testing sites locally
20
- ---------------------
21
+ Using the CLI
22
+ -------------
23
+
24
+ Important: if using a Gemfile, make sure to prepend all commands with bundle exec (e.g. bundle exec siteleaf auth).
21
25
 
22
26
  The Siteleaf gem allows you to test and develop your sites locally. If using [Pow](http://pow.cx) or [Anvil](http://anvilformac.com), your local website will be automatically set up and can be accessed at `http://yoursite.dev`.
23
27
 
@@ -86,6 +90,7 @@ To use this gem in your application, add the following to your Gemfile:
86
90
  gem 'siteleaf', :git => 'git://github.com/siteleaf/siteleaf-gem.git'
87
91
 
88
92
 
93
+
89
94
  Using the API
90
95
  -------------
91
96
 
@@ -166,6 +171,19 @@ post.delete
166
171
  Siteleaf::Page.delete('519719ddcc85910626000001')
167
172
  ```
168
173
 
174
+
175
+ Exporting your site
176
+ ---------------------------------
177
+
178
+ You can backup or export your Siteleaf content to Jekyll-compatible format by running the following command:
179
+
180
+ siteleaf export
181
+
182
+ You will be asked to enter your main posts path, this is typically `posts` (default) or `blog` (if your posts are located at `/blog/hello-world` for example). If your site does not have any posts, leave blank to use the default.
183
+
184
+ All content including Pages, Posts, Site Metadata, and Assets will be exported to a sub-folder called `export`. Theme files are not included in the export.
185
+
186
+
169
187
  Troubleshooting
170
188
  ------------
171
189
 
@@ -17,6 +17,7 @@ Commands:
17
17
  pull theme Pulls theme files for configured site from Siteleaf
18
18
  push theme Pushes all files in dir as theme to configured site
19
19
  publish Publish website to hosting provider
20
+ export Exports site content to Jekyll-compatible format
20
21
  help Prints this help document
21
22
  version Prints the siteleaf gem version
22
23
 
@@ -88,13 +89,13 @@ def get_theme_assets(site_id)
88
89
  if assets = Siteleaf::Theme.assets_by_site_id(site_id)
89
90
  updated_count = 0
90
91
  assets.each do |asset|
91
- if File.exist?(asset.filename) && (asset.checksum == Digest::MD5.hexdigest(File.read(asset.filename)))
92
+ if File.exist?(asset.filename) && (asset.checksum == Digest::MD5.hexdigest(IO.binread(asset.filename)))
92
93
  # file is up to date
93
94
  else
94
95
  print "Downloading #{asset.filename}..."
95
- file = open(asset.file['url'], /^1\.8/.match(RUBY_VERSION) ? 'r' : 'r:UTF-8') { |f| f.read }
96
+ file = open(asset.file['url'], 'r:UTF-8') { |f| f.read }
96
97
  FileUtils.mkdir_p(File.dirname(asset.filename))
97
- File.open(asset.filename, /^1\.8/.match(RUBY_VERSION) ? 'w' : 'w:UTF-8') { |f| f.write(file) }
98
+ File.open(asset.filename, 'w:UTF-8') { |f| f.write(file) }
98
99
  updated_count += 1
99
100
  print "complete.\n"
100
101
  end
@@ -109,13 +110,13 @@ def put_theme_assets(site_id)
109
110
  updated_count = 0
110
111
  ignore_paths = ['config.ru', '.*']
111
112
  ignore_paths += File.read('.siteleafignore').split(/\r?\n/) if File.exists?('.siteleafignore')
112
-
113
+
113
114
  # upload files
114
115
  paths = Dir.glob("**/*")
115
116
  paths.each do |path|
116
117
  if !File.directory?(path) && !ignore_paths.any?{|i| File.fnmatch?(i, path, File::FNM_CASEFOLD) || File.fnmatch?(i, File.basename(path), File::FNM_CASEFOLD) }
117
118
  asset = assets.find{|a| a.filename == path }
118
- if asset.nil? || (asset && asset.checksum != Digest::MD5.hexdigest(File.read(path)))
119
+ if asset.nil? || (asset && asset.checksum != Digest::MD5.hexdigest(IO.binread(path)))
119
120
  print "Uploading #{path}..."
120
121
  asset.delete if asset
121
122
  if response = Siteleaf::Asset.create({:site_id => site_id, :theme_id => theme.id, :file => File.new(path), :filename => path})
@@ -128,10 +129,10 @@ def put_theme_assets(site_id)
128
129
  end
129
130
  end
130
131
  end
131
-
132
+
132
133
  # check for old files
133
134
  missing_assets = []
134
- assets.each do |asset|
135
+ assets.each do |asset|
135
136
  missing_assets << asset if !paths.include?(asset.filename)
136
137
  end
137
138
  if missing_assets.empty?
@@ -171,6 +172,90 @@ def publish(site_id, quiet = true)
171
172
  end
172
173
  end
173
174
 
175
+ def export(site_id, posts_path = "posts")
176
+ FileUtils.mkdir_p 'export'
177
+ posts_path = "posts" if posts_path == ""
178
+
179
+ site = Siteleaf::Site.find(site_id)
180
+ site.posts_path = posts_path
181
+
182
+ export_uploads(site)
183
+ export_content(site)
184
+ export_config(site)
185
+ end
186
+
187
+ def export_config(site)
188
+ filename = site.filename
189
+ print "Exporting #{filename}..."
190
+
191
+ filename = File.join("export", filename)
192
+ open(filename, 'w:UTF-8') do |f|
193
+ f.puts site.to_file
194
+ end
195
+
196
+ print "complete.\n"
197
+ end
198
+
199
+ def export_content(site)
200
+ pages = site.pages
201
+
202
+ pages.each do |page|
203
+ # export non-blank pages
204
+ unless page.body.to_s == "" and page.meta.empty?
205
+ filename = page.filename
206
+ print "Exporting #{filename}..."
207
+
208
+ filename = File.join("export", filename)
209
+ FileUtils.mkdir_p File.dirname(filename)
210
+ open(filename, 'w:UTF-8') do |f|
211
+ f.puts page.to_file
212
+ end
213
+
214
+ print "complete.\n"
215
+ end
216
+
217
+ page.posts.each do |post|
218
+ filename = post.filename(site.posts_path)
219
+ print "Exporting #{filename}..."
220
+
221
+ filename = File.join("export", filename)
222
+ FileUtils.mkdir_p File.dirname(filename)
223
+ open(filename, 'w:UTF-8') do |f|
224
+ f.puts post.to_file
225
+ end
226
+
227
+ print "complete.\n"
228
+ end
229
+ end
230
+ end
231
+
232
+ def export_uploads(site)
233
+ FileUtils::mkdir_p 'export/_uploads'
234
+
235
+ if assets = site.assets
236
+ assets.each do |asset|
237
+ filename = File.join("export", "_uploads", asset.filename)
238
+ arr_path = filename.split('/')
239
+
240
+ # Make subdirectories if needed
241
+ if arr_path.length > 3
242
+ subdir = arr_path[0..(arr_path.length - 2)].join('/')
243
+ FileUtils::mkdir_p subdir
244
+ end
245
+
246
+ if !File.exist?(filename) or (asset.checksum != Digest::MD5.hexdigest(IO.binread(filename)))
247
+ print "Exporting #{filename}..."
248
+
249
+ file = open(asset.file['url'], 'r:UTF-8') { |f| f.read }
250
+ FileUtils.mkdir_p(File.dirname(filename))
251
+ File.open(filename, 'w:UTF-8') { |f| f.write(file) }
252
+
253
+ print "complete.\n"
254
+ end
255
+ end
256
+ end
257
+ end
258
+
174
259
  case ARGV[0]
175
260
  when '-v', '--version', 'version'
176
261
  puts Siteleaf::VERSION
@@ -239,6 +324,16 @@ when 'publish'
239
324
  puts "Site not configured, run `siteleaf config yoursite.com`.\n"
240
325
  end
241
326
  end
327
+ when 'export'
328
+ if auth != false
329
+ if site_id = get_site_id
330
+ print 'Enter your main posts path (default "posts"): '
331
+ posts_path = $stdin.gets.chomp
332
+ export(site_id, posts_path)
333
+ else
334
+ puts "Site not configured, run `siteleaf config yoursite.com`.\n"
335
+ end
336
+ end
242
337
  else
243
338
  puts "`#{ARGV[0]}` command not found.\n"
244
339
  puts help
@@ -0,0 +1,12 @@
1
+ class Time
2
+ def encode_with(coder)
3
+ label =
4
+ if utc?
5
+ usec.zero? ? '%Y-%m-%d %H:%M:%S Z' : '%Y-%m-%d %H:%M:%S.%9N Z'
6
+ else
7
+ usec.zero? ? '%Y-%m-%d %H:%M:%S %:z' : '%Y-%m-%d %H:%M:%S.%9N %:z'
8
+ end
9
+
10
+ coder.represent_scalar(nil, strftime(label))
11
+ end
12
+ end
@@ -12,7 +12,10 @@ require 'siteleaf/server'
12
12
  require 'siteleaf/site'
13
13
  require 'siteleaf/theme'
14
14
  require 'siteleaf/user'
15
+ require 'patches/time_with_zone_encode_with'
15
16
  require 'rbconfig'
17
+ require 'uri'
18
+ require 'yaml'
16
19
 
17
20
  module Siteleaf
18
21
 
@@ -31,5 +31,41 @@ module Siteleaf
31
31
  Page.find(self.parent_id) if self.parent_id
32
32
  end
33
33
 
34
+ def draft?
35
+ visibility == 'draft'
36
+ end
37
+
38
+ def published?
39
+ visibility == 'visible'
40
+ end
41
+
42
+ def filename
43
+ "#{url.sub('/','')}.markdown"
44
+ end
45
+
46
+ def to_file
47
+ assets = Dir.glob("export/_uploads/**/*").each_with_object({}) { |var, hash| hash[var.sub('export/_uploads','/assets')] = var.sub('export/_uploads','/uploads') }
48
+ (frontmatter + "---\n\n".freeze + body.to_s).gsub(Regexp.union(assets.keys), assets)
49
+ end
50
+
51
+ protected
52
+
53
+ def frontmatter
54
+ attrs = {}
55
+ attrs['title'] = title
56
+ attrs['date'] = Time.parse(published_at).utc
57
+ attrs['published'] = false if !published?
58
+
59
+ meta.each{|m| attrs[m['key']] = m['value'].to_s.gsub("\r\n","\n")} unless meta.nil?
60
+
61
+ if defined?(taxonomy) && !taxonomy.nil?
62
+ taxonomy.each do |t|
63
+ attrs[t['key']] = t['values'].map{|v| v['value']} unless t['values'].empty?
64
+ end
65
+ end
66
+
67
+ attrs.empty? ? "---\n".freeze : attrs.to_yaml
68
+ end
69
+
34
70
  end
35
71
  end
@@ -16,5 +16,21 @@ module Siteleaf
16
16
  result.map { |r| Asset.new(r) } if result
17
17
  end
18
18
 
19
+ def filename(posts_path = 'posts')
20
+ paths = url.sub('/','').split('/')
21
+ basename = "#{paths.pop}.markdown"
22
+ path = paths.join('_')
23
+ if path == posts_path
24
+ if draft?
25
+ path = 'drafts'
26
+ else
27
+ path = 'posts'
28
+ date = Time.parse(published_at).strftime('%Y-%m-%d')
29
+ basename = "#{date}-#{basename}"
30
+ end
31
+ end
32
+ "_#{path}/#{basename}"
33
+ end
34
+
19
35
  end
20
36
  end
@@ -1,7 +1,7 @@
1
1
  module Siteleaf
2
2
  class Site < Entity
3
3
 
4
- attr_accessor :title, :domain, :timezone, :meta
4
+ attr_accessor :title, :domain, :timezone, :meta, :posts_path
5
5
  attr_reader :id, :user_id, :created_at, :updated_at
6
6
 
7
7
  def self.find_by_domain(domain)
@@ -27,6 +27,11 @@ module Siteleaf
27
27
  result.map { |r| Page.new(r) } if result
28
28
  end
29
29
 
30
+ def posts
31
+ result = Client.get "sites/#{self.id}/posts"
32
+ result.map { |r| Post.new(r) } if result
33
+ end
34
+
30
35
  def resolve(url = '/')
31
36
  Client.get "sites/#{self.id}/resolve", {"url" => url}
32
37
  end
@@ -40,5 +45,68 @@ module Siteleaf
40
45
  Job.new(id: result.parsed_response["job_id"]) if result
41
46
  end
42
47
 
48
+ def posts_path
49
+ @posts_path || 'posts'
50
+ end
51
+
52
+ def filename
53
+ "_config.yml"
54
+ end
55
+
56
+ def to_file
57
+ assets = Dir.glob("export/_uploads/**/*").each_with_object({}) { |var, hash| hash[var.sub('export/_uploads','/assets')] = var.sub('export/_uploads','/uploads') }
58
+ config.gsub(Regexp.union(assets.keys), assets)
59
+ end
60
+
61
+ protected
62
+
63
+ def config
64
+ attrs = {}
65
+ attrs['title'] = title
66
+ attrs['url'] = "http://#{domain}"
67
+
68
+ meta.each{|m| attrs[m['key']] = m['value'].to_s.gsub("\r\n","\n")} unless meta.nil?
69
+
70
+ attrs['timezone'] = timezone
71
+ attrs['permalink'] = 'pretty'
72
+
73
+ # output uploads using v1 /assets path
74
+ attrs['collections'] = {
75
+ 'uploads' => {
76
+ 'title' => 'Uploads',
77
+ 'output' => true
78
+ }
79
+ }
80
+
81
+ # use collections for any set of posts not called "posts"
82
+ pages.each do |page|
83
+ path = page.url.sub('/','').gsub('/','_')
84
+ if path != posts_path && page.posts.size > 0
85
+ attrs['collections'][path] = {'output' => true}
86
+ # output permalink for non-standard urls (e.g. posts inside sub-pages)
87
+ attrs['collections'][path]['permalink'] = "#{page.url}/:path" if path != page.slug
88
+ end
89
+ end
90
+
91
+ # set permalink style for posts
92
+ attrs['defaults'] = {
93
+ 'scope' => {
94
+ 'path' => '',
95
+ 'type' => 'posts'
96
+ },
97
+ 'values' => {
98
+ 'permalink' => "/#{posts_path}/:title/"
99
+ }
100
+ }
101
+
102
+ # markdown defaults to match v1 rendering
103
+ attrs['markdown'] = 'redcarpet'
104
+ attrs['redcarpet'] = {
105
+ 'extensions' => ['autolink', 'fenced_code_blocks', 'lax_spacing', 'strikethrough', 'superscript', 'tables', 'footnotes', 'highlight']
106
+ }
107
+
108
+ attrs.empty? ? "---\n".freeze : attrs.to_yaml
109
+ end
110
+
43
111
  end
44
112
  end
@@ -1,3 +1,3 @@
1
1
  module Siteleaf
2
- VERSION = "0.9.23"
2
+ VERSION = "1.0.0"
3
3
  end
metadata CHANGED
@@ -1,55 +1,55 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: siteleaf
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.23
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Siteleaf
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-06-24 00:00:00.000000000 Z
11
+ date: 2015-10-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: httparty
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - '>='
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
19
  version: 0.13.3
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - '>='
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: 0.13.3
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: httmultiparty
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - '>='
31
+ - - ">="
32
32
  - !ruby/object:Gem::Version
33
33
  version: 0.3.13
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - '>='
38
+ - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: 0.3.13
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: rack
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - '>='
45
+ - - ">="
46
46
  - !ruby/object:Gem::Version
47
47
  version: '0'
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - '>='
52
+ - - ">="
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
55
  description: A Ruby interface and command line utility for the Siteleaf API.
@@ -60,12 +60,14 @@ executables:
60
60
  extensions: []
61
61
  extra_rdoc_files: []
62
62
  files:
63
- - .gitignore
63
+ - ".gitignore"
64
+ - ".rbenv-vars"
64
65
  - Gemfile
65
66
  - LICENSE.txt
66
67
  - README.md
67
68
  - Rakefile
68
69
  - bin/siteleaf
70
+ - lib/patches/time_with_zone_encode_with.rb
69
71
  - lib/siteleaf.rb
70
72
  - lib/siteleaf/asset.rb
71
73
  - lib/siteleaf/client.rb
@@ -89,12 +91,12 @@ require_paths:
89
91
  - lib
90
92
  required_ruby_version: !ruby/object:Gem::Requirement
91
93
  requirements:
92
- - - '>='
94
+ - - ">="
93
95
  - !ruby/object:Gem::Version
94
96
  version: 1.9.3
95
97
  required_rubygems_version: !ruby/object:Gem::Requirement
96
98
  requirements:
97
- - - '>='
99
+ - - ">="
98
100
  - !ruby/object:Gem::Version
99
101
  version: '0'
100
102
  requirements: []