Thimblr 0.6.12 → 0.8.0

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.
@@ -2,12 +2,6 @@
2
2
  Tumblr:
3
3
  PostsPerPage: 10
4
4
 
5
- ## Settings affecting Thimblr
6
- # The location of your theme files
7
- ThemesLocation: themes
8
- # The location of your data files
9
- DataLocation: data
10
-
11
5
  # This allows the thimblr server to open your chosen editor to edit
12
6
  # the theme you're currently viewing. There is a (very small!) risk
13
7
  # that this could do malicious things, so you can disable it if you
@@ -5,7 +5,9 @@ require 'digest/md5'
5
5
  require 'pathname'
6
6
  require 'launchy'
7
7
  require 'thimblr/parser'
8
+ require 'thimblr/importer'
8
9
  require 'rbconfig'
10
+ require 'fileutils'
9
11
 
10
12
  class Thimblr::Application < Sinatra::Base
11
13
  Editors = {
@@ -13,10 +15,10 @@ class Thimblr::Application < Sinatra::Base
13
15
  'bbedit' => {'command' => "bbedit",'platform' => 'mac','name' => "BBEdit"},
14
16
  'textedit' => {'command' => "open -a TextEdit.app",'platform' => 'mac','name' => "TextEdit"}
15
17
  }
16
- Locations = [
17
- {"dir" => "~/Library/Application Support/Thimblr/", 'name' => "Application Support", 'platform' => "mac"},
18
- {'dir' => "~/.thimblr/",'name' => "Home directory", 'platform' => "nix"}
19
- ]
18
+ Locations = {
19
+ "mac" => {"dir" => "~/Library/Application Support/Thimblr/", 'name' => "Application Support", 'platform' => "mac"},
20
+ "nix" => {'dir' => "~/.thimblr/",'name' => "Home directory", 'platform' => "nix"}
21
+ }
20
22
 
21
23
  case RbConfig::CONFIG['target_os']
22
24
  when /darwin/i
@@ -28,8 +30,8 @@ class Thimblr::Application < Sinatra::Base
28
30
  end
29
31
 
30
32
  def self.parse_config(s)
31
- set :themes, File.expand_path((File.directory? s['ThemesLocation']) ? s['ThemesLocation'] : "./themes")
32
- set :data, File.expand_path((File.directory? s['DataLocation'] || "") ? s['DataLocation'] : "")
33
+ set :themes, File.expand_path(File.join(Locations[Platform]['dir'],"themes"))
34
+ set :data, File.expand_path(File.join(Locations[Platform]['dir'],"data"))
33
35
  set :allowediting, (s['AllowEditing']) ? true : false
34
36
  set :editor, s['Editor'] if s['Editor']
35
37
  set :tumblr, Thimblr::Parser::Defaults.merge(s['Tumblr'] || {})
@@ -40,10 +42,29 @@ class Thimblr::Application < Sinatra::Base
40
42
  set :root, File.join(File.dirname(__FILE__),"..")
41
43
  Dir.chdir root
42
44
  set :config, File.join(root,'config')
45
+ set :settingsfile, File.expand_path(File.join(Locations[Platform]['dir'],'settings.yaml'))
46
+
47
+ # Generate Data & Theme directories if required
48
+ if not File.directory?(File.expand_path(File.join(Locations[Platform]['dir'],"themes")))
49
+ FileUtils.cp_r(File.join(root,'themes'),File.expand_path(Locations[Platform]['dir']))
50
+ end
51
+
52
+ if not File.directory?(File.expand_path(File.join(Locations[Platform]['dir'],"data")))
53
+ FileUtils.mkdir_p(File.expand_path(File.join(Locations[Platform]['dir'],"data")))
54
+ FileUtils.cp(File.join(config,'demo.yaml'),File.expand_path(File.join(Locations[Platform]['dir'],'data','demo.yml')))
55
+ end
56
+
57
+ begin # Try to load the settings file, if it's crap then overwrite it with the defaults
58
+ s.parse_config(YAML::load(open(settingsfile)))
59
+ rescue
60
+ FileUtils.cp(File.join(config,'settings.default.yaml'),settingsfile)
61
+ retry
62
+ end
43
63
 
44
- s.parse_config(YAML::load(open(File.join(config,'settings.yaml'))))
45
64
  enable :sessions
46
65
  set :bind, '127.0.0.1'
66
+
67
+ Launchy.open("http://localhost:#{port}")
47
68
  end
48
69
 
49
70
  helpers do
@@ -53,7 +74,11 @@ class Thimblr::Application < Sinatra::Base
53
74
  end
54
75
 
55
76
  get '/' do
56
- erb :index
77
+ redirect 'index.html'
78
+ end
79
+
80
+ get '/menu' do
81
+ erb :menu
57
82
  end
58
83
 
59
84
  get '/help' do
@@ -77,7 +102,7 @@ class Thimblr::Application < Sinatra::Base
77
102
  end
78
103
 
79
104
  get '/data.set' do
80
- if File.exists?(File.join(settings.data,"#{params['data']}.yml")) or params['data'] == 'demo'
105
+ if File.exists?(File.join(settings.data,"#{params['data']}.yml"))
81
106
  response.set_cookie('data',params['data'])
82
107
  else
83
108
  halt 404, "Not found"
@@ -89,7 +114,6 @@ class Thimblr::Application < Sinatra::Base
89
114
  Dir.glob("#{settings.data}/*.yml").collect do |datum|
90
115
  data[File.basename(datum,".yml")] = Digest::MD5.hexdigest(open(datum).read)
91
116
  end
92
- data['demo'] = Digest::MD5.hexdigest(open(File.join(settings.config,"demo.yml")).read)
93
117
  data.to_json
94
118
  end
95
119
 
@@ -118,12 +142,11 @@ class Thimblr::Application < Sinatra::Base
118
142
  get %r{/(tumblr)?settings.set} do |tumblr|
119
143
  halt 501 if tumblr == "tumblr" # TODO: Tumblr settings save
120
144
 
145
+ params['AllowEditing'] = (params['AllowEditing'] == "on")
121
146
  settings.parse_config(params)
122
- open(File.join(settings.config,"settings.yaml"),"w") do |f|
147
+ open(settings.settingsfile,"w") do |f|
123
148
  f.write YAML.dump({
124
149
  "Tumblr" => settings.tumblr,
125
- "ThemesLocation" => get_relative(settings.themes),
126
- "DataLocation" => get_relative(settings.data),
127
150
  "AllowEditing" => settings.allowediting,
128
151
  "Editor" => settings.editor,
129
152
  "Port" => settings.port
@@ -133,9 +156,17 @@ class Thimblr::Application < Sinatra::Base
133
156
  "Settings saved"
134
157
  end
135
158
 
136
- # TODO: Downloads feed data from a tumblr site
137
- get '/import' do
138
- halt 501, "Sorry, I haven't written this bit yet!"
159
+ # Downloads feed data from a tumblr site
160
+ get %r{/import/([a-zA-Z0-9-]+)} do |username|
161
+ begin
162
+ data = Thimblr::Import.username(username)
163
+ open(File.join(settings.data,"#{username}.yml"),'w') do |f|
164
+ f.write data
165
+ end
166
+ rescue Exception => e
167
+ halt 404, e.message
168
+ end
169
+ "Imported as '#{username}'"
139
170
  end
140
171
 
141
172
  before do
@@ -179,7 +210,4 @@ class Thimblr::Application < Sinatra::Base
179
210
  get '/thimblr/*' do
180
211
  @parser.render_page(params[:splat])
181
212
  end
182
-
183
- # TODO: Only if Sinatra runs successfully
184
- Launchy.open("http://localhost:#{port}")
185
213
  end
@@ -0,0 +1,89 @@
1
+ require 'open-uri'
2
+ require 'nokogiri'
3
+ require 'yaml'
4
+ require 'uri'
5
+
6
+ module Thimblr
7
+ class Import
8
+ def self.username(username)
9
+ data = {}
10
+ begin
11
+ xml = Nokogiri::XML(open("http://#{username}.tumblr.com/api/read"))
12
+ rescue OpenURI::HTTPError
13
+ raise "Username not found"
14
+ end
15
+
16
+ data['Title'] = xml.search('tumblelog')[0]['title']
17
+ data['Description'] = xml.search('tumblelog')[0].content
18
+
19
+ data['Posts'] = []
20
+ xml.search('posts post').each do |xpost|
21
+ post = {
22
+ 'PostId' => xpost['id'].to_i,
23
+ 'Permalink' => xpost['url'],
24
+ 'Type' => xpost['type'].capitalize,
25
+ 'Timestamp' => xpost['unix-timestamp'].to_i,
26
+ 'Tags' => xpost.search('tag').collect{ |tag| tag.content }
27
+ }
28
+
29
+ post['Type'] = "Text" if post['Type'] == "Regular"
30
+ post['Type'] = "Chat" if post['Type'] == "Conversation"
31
+
32
+ post.store('Title', xpost.search("#{xpost['type']}-title")[0].content) rescue nil
33
+
34
+ post.store('Caption',xpost.search("#{xpost['type']}-caption")[0].content) rescue nil
35
+
36
+ post.store('LinkURL',xpost.search("#{xpost['type']}-link-url")[0].content) rescue nil
37
+
38
+ post.store('Source',xpost.search("#{xpost['type']}-source")[0].content) rescue nil
39
+
40
+ case post['Type']
41
+ when "Photo"
42
+ xpost.search('photo-url').each do |photo|
43
+ post["PhotoURL-#{photo['max-width']}"] = photo.content
44
+ end
45
+ when "Link"
46
+ begin
47
+ post['Name'] = xpost.search("link-text")[0].content
48
+ rescue
49
+ end
50
+ when "Video"
51
+ post['Player'] = xpost.search("video-player")[0].content
52
+ when "Text"
53
+ post['Body'] = xpost.search("regular-body")[0].content
54
+ end
55
+
56
+ data['Posts'].push post
57
+ end
58
+
59
+ # Pages
60
+ begin
61
+ xml = Nokogiri::XML(open("http://#{username}.tumblr.com/api/pages"))
62
+ data['Pages'] = []
63
+
64
+ xml.search('pages').children.each do |re|
65
+ case re.name
66
+ when "redirect"
67
+ data['Pages'].push({
68
+ "Label" => re['link-title'],
69
+ "URL" => re['redirect-to']
70
+ })
71
+ when "page"
72
+ data['Pages'].push({
73
+ "Label" => re['link-title'],
74
+ "URL" => URI.split(re['url'])[5],
75
+ "Title" => re['title'],
76
+ "InTheme" => (re['render-in-theme'] == "true"),
77
+ "Body" => re.content
78
+ })
79
+ end
80
+ end
81
+ # Do pages
82
+ rescue OpenURI::HTTPError
83
+ # No pages
84
+ end
85
+
86
+ YAML::dump(data)
87
+ end
88
+ end
89
+ end
@@ -41,9 +41,9 @@ module Thimblr
41
41
  'Pagination' => (@posts.length > @settings['PostsPerPage'].to_i),
42
42
  'SubmissionsEnabled' => template['SubmissionsEnabled'],
43
43
  'AskEnabled' => !@constants['AskLabel'].empty?,
44
- 'HasPages' => @pages.length > 0,
45
- 'Following' => @following.length > 0,
46
- 'Followed' => @followed.length > 0,
44
+ 'HasPages' => (@pages.length > 0 rescue false),
45
+ 'Following' => (@following.length > 0 rescue false),
46
+ 'Followed' => (@followed.length > 0 rescue false),
47
47
  'More' => true
48
48
  }
49
49
 
@@ -16,12 +16,32 @@ $(document).ready(function(){
16
16
  e.preventDefault();
17
17
  })
18
18
 
19
+ $('#import').bind('submit',function(e) {
20
+ $('#TumblrUser').disabled = true
21
+ $.ajax({
22
+ url:'/import/'+$('#TumblrUser').val(),
23
+ success: function(d) {
24
+ $('#TumblrUser,#importinput').effect("highlight", {color:'#00ff00'}, 1000)
25
+ $('#TumblrUser').val('')
26
+ $('#TumblrUser').disabled = false
27
+ },
28
+ error: function(d) {
29
+ $('#TumblrUser,#importinput').effect("highlight", {color:'#ff0000'}, 2000)
30
+ $('#TumblrUser').val('')
31
+ $('#TumblrUser').disabled = false
32
+ }
33
+ })
34
+ })
35
+
19
36
  $('table.settings a.preset').bind('click',function(e) {
20
37
  e.preventDefault();
21
38
  $('#'+$(this).parent().attr('rel')).val(this.rel).effect("highlight", {}, 500);
22
39
  })
23
40
 
24
41
  $('#settings input').bind('change',function(e) {
25
- $.get($('#settings').attr('action'),$('#settings').serialize())
42
+ $.get($('#settings').attr('action'),$('#settings').serialize(),function(d,status) {
43
+ $('#saved').effect("highlight", {}, 500);
44
+ $('#saved em').text(d)
45
+ })
26
46
  })
27
47
  })
@@ -11,7 +11,7 @@ $(document).ready(function(){
11
11
 
12
12
  $('#theme-select').bind('submit',function(e){
13
13
  $.get('/theme.set',{'theme':$('#theme-selector').children(':selected').val()},function() {
14
- $('#theme-preview').attr('src','/thimblr');
14
+ parent.tumblr.location.href = '/thimblr'
15
15
  $('#theme-selector').children(':selected').removeClass('altered')
16
16
  })
17
17
  return false;
@@ -27,14 +27,13 @@ $(document).ready(function(){
27
27
  $('#data-select').bind('submit',function(e){
28
28
  currtheme = $('#data-selector').children(':selected').val();
29
29
  $.get('/data.set',{'data':currtheme},function() {
30
- $('#data-preview').attr('src','/thimblr');
30
+ parent.tumblr.location.href = '/thimblr'
31
31
  $('#data-selector').children(':selected').removeClass('altered')
32
32
  })
33
33
  return false;
34
34
  });
35
35
 
36
36
  $('#data-selector').bind('click',function(e){
37
- alert('argh')
38
37
  if (e.target.nodeName.toLowerCase() === 'option')
39
38
  $('#data-select').trigger('submit');
40
39
  });
@@ -0,0 +1,13 @@
1
+ <html>
2
+ <head>
3
+ <title>Thimblr - A Tumblr Theme Editor</title>
4
+ </head>
5
+ <frameset rows="32px,*" frameborder="no" framespacing="0" border="0">
6
+
7
+ <frame src="menu" />
8
+ <frame src="help" name="tumblr" id="theme-preview" />
9
+
10
+ </frameset>
11
+
12
+ </html>
13
+
@@ -72,42 +72,6 @@ Hope you guys don't mind… -->
72
72
  </div>
73
73
  </td>
74
74
  </tr>
75
-
76
- <a name="ThemesLocation"></a>
77
- <tr id="opt-ThemesLocation">
78
- <td><label for="ThemesLocation">Location of Themes</label></td>
79
- <td><input id="ThemesLocation" type="text" name="ThemesLocation" value="<%=get_relative(settings.themes)%>/" /></td>
80
- <td class="explain">
81
- <div class="contain">
82
- <img src="/images/preset.png" class="presetIcon"/>
83
- <div class="presetbox">
84
- <div class="presets" rel="ThemesLocation">
85
- <% Thimblr::Application::Locations.each do |loc| %>
86
- <a href="#" rel="<%=loc['dir']%>themes/" title="<%=loc['name']%>" class="preset <%=loc['platform']%>"><img src="/images/os/<%=loc['platform']%>.png"/></a>
87
- <% end %>
88
- </div>
89
- </div>
90
- </div>
91
- </td>
92
- </tr>
93
-
94
- <a name="DataLocation"></a>
95
- <tr id="opt-DataLocation">
96
- <td><label for="DataLocation">Location of Data</label></td>
97
- <td><input id="DataLocation" type="text" name="DataLocation" value="<%=get_relative(settings.data)%>/" /></td>
98
- <td class="explain">
99
- <div class="contain">
100
- <img src="/images/preset.png" class="presetIcon"/>
101
- <div class="presetbox">
102
- <div class="presets" rel="DataLocation">
103
- <% Thimblr::Application::Locations.each do |loc| %>
104
- <a href="#" rel="<%=loc['dir']%>data/" title="<%=loc['name']%>" class="preset <%=loc['platform']%>"><img src="/images/os/<%=loc['platform']%>.png"/></a>
105
- <% end %>
106
- </div>
107
- </div>
108
- </div>
109
- </td>
110
- </tr>
111
75
 
112
76
  <a name="Port"></a>
113
77
  <tr id="opt-Port">
@@ -119,7 +83,7 @@ Hope you guys don't mind… -->
119
83
  </tr>
120
84
  </table>
121
85
  </form>
122
- <p><em>Changes are saved automatically</em></p>
86
+ <p id="saved"><em>Changes are saved automatically</em></p>
123
87
  </div>
124
88
  </div>
125
89
  <div style="background-color: rgb(192, 205, 129);" id="right_column">
@@ -130,10 +94,10 @@ Hope you guys don't mind… -->
130
94
  </div>
131
95
  <div class="goody_container">
132
96
  <h2>Import Data</h2>
133
- <p>Once I've gone outside for a while I'll write a script here that will download a tumblr user's posts here. Not ready yet tho, sorry :(</p>
134
- <form action="/import" id="import">
97
+ <p>Imports posts data from a specific tumblelog, so you can test themes with real-world data!</p>
98
+ <form action="/import/" id="import">
135
99
  <label for="TumblrUser">Tumblr Username</label>
136
- <div style="margin-bottom: 5px;" class="goody_data">
100
+ <div style="margin-bottom: 5px;" class="goody_data" id="importinput">
137
101
  <input type="text" value="" name="TumblrUser" id="TumblrUser" />
138
102
  </div>
139
103
  </form>
@@ -42,8 +42,5 @@
42
42
  </form>
43
43
  <a id="docs" href="/help#Instructions" target="tumblr">Help</a>
44
44
  </div>
45
- <div id="theme-container">
46
- <iframe id="theme-preview" src="/help" border="0" frameborder="0" name="tumblr"></iframe>
47
- </div>
48
45
  </body>
49
46
  </html>
metadata CHANGED
@@ -1,7 +1,12 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: Thimblr
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.12
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 8
8
+ - 0
9
+ version: 0.8.0
5
10
  platform: ruby
6
11
  authors:
7
12
  - JP Hastings-Spital
@@ -9,29 +14,34 @@ autorequire:
9
14
  bindir: bin
10
15
  cert_chain: []
11
16
 
12
- date: 2010-04-07 00:00:00 +01:00
17
+ date: 2010-04-12 00:00:00 +01:00
13
18
  default_executable: thimblr
14
19
  dependencies:
15
20
  - !ruby/object:Gem::Dependency
16
21
  name: sinatra
17
- type: :runtime
18
- version_requirement:
19
- version_requirements: !ruby/object:Gem::Requirement
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
20
24
  requirements:
21
25
  - - ">="
22
26
  - !ruby/object:Gem::Version
27
+ segments:
28
+ - 1
29
+ - 0
23
30
  version: "1.0"
24
- version:
31
+ type: :runtime
32
+ version_requirements: *id001
25
33
  - !ruby/object:Gem::Dependency
26
34
  name: launchy
27
- type: :runtime
28
- version_requirement:
29
- version_requirements: !ruby/object:Gem::Requirement
35
+ prerelease: false
36
+ requirement: &id002 !ruby/object:Gem::Requirement
30
37
  requirements:
31
38
  - - ">="
32
39
  - !ruby/object:Gem::Version
40
+ segments:
41
+ - 0
33
42
  version: "0"
34
- version:
43
+ type: :runtime
44
+ version_requirements: *id002
35
45
  description: A webserver built to help you test tumblr themes as you edit them
36
46
  email: jphastings@gmail.com
37
47
  executables:
@@ -43,8 +53,8 @@ extra_rdoc_files: []
43
53
  files:
44
54
  - config/demo.yml
45
55
  - config/settings.default.yaml
46
- - config/settings.yaml
47
56
  - lib/thimblr.rb
57
+ - lib/thimblr/importer.rb
48
58
  - lib/thimblr/parser.rb
49
59
  - public/assets/help.js
50
60
  - public/assets/iphone-style-checkboxes.js
@@ -82,13 +92,14 @@ files:
82
92
  - public/images/slider_center.png
83
93
  - public/images/slider_left.png
84
94
  - public/images/slider_right.png
95
+ - public/index.html
85
96
  - public/stylesheets/help.css
86
97
  - public/stylesheets/tumblr.css
87
98
  - themes/101.html
88
99
  - themes/Redux.html
89
100
  - themes/Stationary.html
90
101
  - views/help.erb
91
- - views/index.erb
102
+ - views/menu.erb
92
103
  has_rdoc: true
93
104
  homepage: http://github.com/jphastings/Thimblr
94
105
  licenses: []
@@ -102,18 +113,20 @@ required_ruby_version: !ruby/object:Gem::Requirement
102
113
  requirements:
103
114
  - - ">="
104
115
  - !ruby/object:Gem::Version
116
+ segments:
117
+ - 0
105
118
  version: "0"
106
- version:
107
119
  required_rubygems_version: !ruby/object:Gem::Requirement
108
120
  requirements:
109
121
  - - ">="
110
122
  - !ruby/object:Gem::Version
123
+ segments:
124
+ - 0
111
125
  version: "0"
112
- version:
113
126
  requirements: []
114
127
 
115
128
  rubyforge_project:
116
- rubygems_version: 1.3.5
129
+ rubygems_version: 1.3.6
117
130
  signing_key:
118
131
  specification_version: 3
119
132
  summary: Helper for Tumblr theme editors
@@ -1,18 +0,0 @@
1
- ## Settings affecting Tumblr
2
- Tumblr:
3
- PostsPerPage: 10
4
-
5
- ## Settings affecting Thimblr
6
- # The location of your theme files
7
- ThemesLocation: themes
8
- # The location of your data files
9
- DataLocation: data
10
-
11
- # This allows the thimblr server to open your chosen editor to edit
12
- # the theme you're currently viewing. There is a (very small!) risk
13
- # that this could do malicious things, so you can disable it if you
14
- # like
15
- AllowEditing: true
16
- # The editor you want to use for editing your themes. Below should
17
- # be what you'd use if you were launching it from the command line
18
- Editor: mate