hobix 0.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (156) hide show
  1. data/COPYING +18 -0
  2. data/README +18 -0
  3. data/Rakefile +96 -0
  4. data/bin/hobix +94 -0
  5. data/contrib/blosxom-to-hobix.rb +253 -0
  6. data/contrib/txp-to-hobix.rb +56 -0
  7. data/contrib/webrick-all-mine.rb +20 -0
  8. data/doc/CHANGELOG +285 -0
  9. data/doc/rdoc/classes/Hobix/API.html +382 -0
  10. data/doc/rdoc/classes/Hobix/Article.html +111 -0
  11. data/doc/rdoc/classes/Hobix/BaseContent.html +692 -0
  12. data/doc/rdoc/classes/Hobix/BaseEntry.html +218 -0
  13. data/doc/rdoc/classes/Hobix/BaseFacet.html +205 -0
  14. data/doc/rdoc/classes/Hobix/BaseOutput.html +122 -0
  15. data/doc/rdoc/classes/Hobix/BasePlugin.html +201 -0
  16. data/doc/rdoc/classes/Hobix/BaseProperties/ClassMethods.html +243 -0
  17. data/doc/rdoc/classes/Hobix/BaseProperties.html +218 -0
  18. data/doc/rdoc/classes/Hobix/BasePublish.html +157 -0
  19. data/doc/rdoc/classes/Hobix/BaseStorage.html +417 -0
  20. data/doc/rdoc/classes/Hobix/BixWik/Entry.html +196 -0
  21. data/doc/rdoc/classes/Hobix/BixWik/IndexEntry.html +170 -0
  22. data/doc/rdoc/classes/Hobix/BixWik/WikiRedCloth.html +111 -0
  23. data/doc/rdoc/classes/Hobix/BixWik.html +418 -0
  24. data/doc/rdoc/classes/Hobix/BixWikPlugin.html +158 -0
  25. data/doc/rdoc/classes/Hobix/CommandLine.html +1970 -0
  26. data/doc/rdoc/classes/Hobix/Comment.html +113 -0
  27. data/doc/rdoc/classes/Hobix/Config.html +212 -0
  28. data/doc/rdoc/classes/Hobix/DataMarsh.html +667 -0
  29. data/doc/rdoc/classes/Hobix/Entry.html +178 -0
  30. data/doc/rdoc/classes/Hobix/EntryEnum.html +162 -0
  31. data/doc/rdoc/classes/Hobix/Enumerable.html +170 -0
  32. data/doc/rdoc/classes/Hobix/Facets/WikiEdit.html +180 -0
  33. data/doc/rdoc/classes/Hobix/Facets.html +111 -0
  34. data/doc/rdoc/classes/Hobix/LinkList.html +182 -0
  35. data/doc/rdoc/classes/Hobix/Out/Quick.html +412 -0
  36. data/doc/rdoc/classes/Hobix/Out.html +119 -0
  37. data/doc/rdoc/classes/Hobix/Page.html +381 -0
  38. data/doc/rdoc/classes/Hobix/Trackback.html +113 -0
  39. data/doc/rdoc/classes/Hobix/UriStr.html +198 -0
  40. data/doc/rdoc/classes/Hobix/WebApp/QueryString.html +207 -0
  41. data/doc/rdoc/classes/Hobix/WebApp/QueryValidationFailure.html +111 -0
  42. data/doc/rdoc/classes/Hobix/WebApp.html +1383 -0
  43. data/doc/rdoc/classes/Hobix/Weblog/AuthorNotFound.html +111 -0
  44. data/doc/rdoc/classes/Hobix/Weblog.html +2082 -0
  45. data/doc/rdoc/classes/Hobix.html +399 -0
  46. data/doc/rdoc/classes/Kernel.html +139 -0
  47. data/doc/rdoc/classes/Regexp.html +154 -0
  48. data/doc/rdoc/classes/YAML/Omap.html +144 -0
  49. data/doc/rdoc/classes/YAML.html +111 -0
  50. data/doc/rdoc/created.rid +1 -0
  51. data/doc/rdoc/files/COPYING.html +129 -0
  52. data/doc/rdoc/files/README.html +131 -0
  53. data/doc/rdoc/files/doc/CHANGELOG.html +101 -0
  54. data/doc/rdoc/files/lib/hobix/api_rb.html +119 -0
  55. data/doc/rdoc/files/lib/hobix/article_rb.html +126 -0
  56. data/doc/rdoc/files/lib/hobix/base_rb.html +128 -0
  57. data/doc/rdoc/files/lib/hobix/bixwik_rb.html +126 -0
  58. data/doc/rdoc/files/lib/hobix/commandline_rb.html +140 -0
  59. data/doc/rdoc/files/lib/hobix/comments_rb.html +126 -0
  60. data/doc/rdoc/files/lib/hobix/config_rb.html +125 -0
  61. data/doc/rdoc/files/lib/hobix/datamarsh_rb.html +108 -0
  62. data/doc/rdoc/files/lib/hobix/entry_rb.html +118 -0
  63. data/doc/rdoc/files/lib/hobix/linklist_rb.html +127 -0
  64. data/doc/rdoc/files/lib/hobix/publisher_rb.html +126 -0
  65. data/doc/rdoc/files/lib/hobix/trackbacks_rb.html +128 -0
  66. data/doc/rdoc/files/lib/hobix/webapp_rb.html +127 -0
  67. data/doc/rdoc/files/lib/hobix/weblog_rb.html +135 -0
  68. data/doc/rdoc/files/lib/hobix_rb.html +127 -0
  69. data/doc/rdoc/fr_class_index.html +67 -0
  70. data/doc/rdoc/fr_file_index.html +44 -0
  71. data/doc/rdoc/fr_method_index.html +307 -0
  72. data/doc/rdoc/index.html +24 -0
  73. data/doc/rdoc/rdoc-style.css +208 -0
  74. data/git_hobix_update.php +13 -0
  75. data/lib/hobix/api.rb +91 -0
  76. data/lib/hobix/article.rb +22 -0
  77. data/lib/hobix/base.rb +480 -0
  78. data/lib/hobix/bixwik.rb +200 -0
  79. data/lib/hobix/commandline.rb +677 -0
  80. data/lib/hobix/comments.rb +98 -0
  81. data/lib/hobix/config.rb +39 -0
  82. data/lib/hobix/datamarsh.rb +110 -0
  83. data/lib/hobix/entry.rb +84 -0
  84. data/lib/hobix/facets/comments.rb +99 -0
  85. data/lib/hobix/facets/publisher.rb +314 -0
  86. data/lib/hobix/facets/trackbacks.rb +80 -0
  87. data/lib/hobix/linklist.rb +81 -0
  88. data/lib/hobix/out/atom.rb +101 -0
  89. data/lib/hobix/out/erb.rb +64 -0
  90. data/lib/hobix/out/okaynews.rb +55 -0
  91. data/lib/hobix/out/quick.rb +314 -0
  92. data/lib/hobix/out/rdf.rb +97 -0
  93. data/lib/hobix/out/redrum.rb +26 -0
  94. data/lib/hobix/out/rss.rb +128 -0
  95. data/lib/hobix/plugin/akismet.rb +196 -0
  96. data/lib/hobix/plugin/bloglines.rb +73 -0
  97. data/lib/hobix/plugin/calendar.rb +212 -0
  98. data/lib/hobix/plugin/flickr.rb +110 -0
  99. data/lib/hobix/plugin/recent_comments.rb +84 -0
  100. data/lib/hobix/plugin/sections.rb +91 -0
  101. data/lib/hobix/plugin/tags.rb +60 -0
  102. data/lib/hobix/publish/ping.rb +53 -0
  103. data/lib/hobix/publish/replicate.rb +283 -0
  104. data/lib/hobix/publisher.rb +18 -0
  105. data/lib/hobix/search/dictionary.rb +141 -0
  106. data/lib/hobix/search/porter_stemmer.rb +203 -0
  107. data/lib/hobix/search/simple.rb +209 -0
  108. data/lib/hobix/search/vector.rb +100 -0
  109. data/lib/hobix/storage/filesys.rb +408 -0
  110. data/lib/hobix/trackbacks.rb +93 -0
  111. data/lib/hobix/util/objedit.rb +193 -0
  112. data/lib/hobix/util/patcher.rb +155 -0
  113. data/lib/hobix/webapp/cli.rb +195 -0
  114. data/lib/hobix/webapp/htmlform.rb +107 -0
  115. data/lib/hobix/webapp/message.rb +177 -0
  116. data/lib/hobix/webapp/urigen.rb +141 -0
  117. data/lib/hobix/webapp/webrick-servlet.rb +90 -0
  118. data/lib/hobix/webapp.rb +723 -0
  119. data/lib/hobix/weblog.rb +893 -0
  120. data/lib/hobix.rb +230 -0
  121. data/share/default-blog/hobix.yaml +16 -0
  122. data/share/default-blog/htdocs/site.css +174 -0
  123. data/share/default-blog/skel/entry.html.quick +0 -0
  124. data/share/default-blog/skel/index.atom.atom +0 -0
  125. data/share/default-blog/skel/index.html.quick-summary +0 -0
  126. data/share/default-blog/skel/index.xml.rss +0 -0
  127. data/share/default-blog/skel/index.yaml.okaynews +0 -0
  128. data/share/default-blog/skel/monthly.html.quick-archive +0 -0
  129. data/share/default-blog/skel/section.html.quick-archive +0 -0
  130. data/share/default-blog/skel/yearly.html.quick-archive +0 -0
  131. data/share/default-blog-modes.yaml +7 -0
  132. data/share/default-blog.apache-cgi.patch +8 -0
  133. data/share/default-blog.apache-ssi.patch +38 -0
  134. data/share/default-blog.apache2-ssi.patch +3 -0
  135. data/share/default-blog.cgi.patch +8 -0
  136. data/share/default-blog.comments.patch +5 -0
  137. data/share/default-blog.prototype.patch +766 -0
  138. data/share/default-blog.publisher.patch +5 -0
  139. data/share/default-blog.wiki.patch +29 -0
  140. data/share/publisher/css/control.css +90 -0
  141. data/share/publisher/css/form.css +238 -0
  142. data/share/publisher/css/form.import.css +72 -0
  143. data/share/publisher/css/main-menu.css +134 -0
  144. data/share/publisher/i/hobix-emblazen-1.png +0 -0
  145. data/share/publisher/i/hobix-emblazen-2.png +0 -0
  146. data/share/publisher/i/hobix-emblazen-3.png +0 -0
  147. data/share/publisher/i/hobix-emblazen-4.png +0 -0
  148. data/share/publisher/i/hobix-emblazen-5.png +0 -0
  149. data/share/publisher/i/hobix-emblazen-6.png +0 -0
  150. data/share/publisher/i/hobix-emblazen-7.png +0 -0
  151. data/share/publisher/index.erb +66 -0
  152. data/share/publisher/js/controls.js +261 -0
  153. data/share/publisher/js/dragdrop.js +476 -0
  154. data/share/publisher/js/effects.js +570 -0
  155. data/share/publisher/js/prototype.js +1011 -0
  156. metadata +230 -0
@@ -0,0 +1,677 @@
1
+ #
2
+ # = hobix/commandline.rb
3
+ #
4
+ # Hobix command-line weblog system.
5
+ #
6
+ # Copyright (c) 2003-2004 why the lucky stiff
7
+ # Copyright (c) 2005 MenTaLguY
8
+ #
9
+ # Written & maintained by why the lucky stiff <why@ruby-lang.org>
10
+ # Additional bits by MenTaLguY <mental@rydia.net>
11
+ #
12
+ # This program is free software, released under a BSD license.
13
+ # See COPYING for details.
14
+ #
15
+ #--
16
+ # $Id$
17
+ #++
18
+ require 'hobix'
19
+ require 'tempfile'
20
+
21
+ module Hobix
22
+ module CommandLine
23
+ ##
24
+ ## Locate RC
25
+ ##
26
+ [
27
+ [ENV['HOME'], ENV['HOME']],
28
+ [ENV['APPDATA'], File.join( ENV['APPDATA'] || "", 'Hobix' )]
29
+ ].each do |home_top, home_dir|
30
+ next unless home_top
31
+ if File.exists? home_top
32
+ File.makedirs( home_dir )
33
+ HOME_DIR = home_dir
34
+ break
35
+ end
36
+ end
37
+ RC = File.join( HOME_DIR, '.hobixrc' )
38
+
39
+ def CommandLine.extended( o )
40
+ #
41
+ # When extended we should get all required plugin for the
42
+ # whole Hobix stuff
43
+ #
44
+ return unless File.exists? RC
45
+
46
+ config = YAML::load( File.open( RC ) )
47
+
48
+ #
49
+ # Add a new instance variable to o
50
+ #
51
+ o.instance_variable_set( :@config, config )
52
+
53
+ #
54
+ # Eventually add user specified path
55
+ #
56
+ if config['libs']
57
+ config['libs'].each do |p|
58
+ if File.exists?( p ) && File.directory?( p )
59
+ $LOAD_PATH << p
60
+ else
61
+ warn "#{p} not loaded. Either inexistant or not a directory"
62
+ end
63
+ end
64
+ end
65
+
66
+ #
67
+ # And system wide path too
68
+ #
69
+ if File.exists?( Hobix::SHARE_PATH )
70
+ $LOAD_PATH << File.join(Hobix::SHARE_PATH,"lib")
71
+ end
72
+
73
+ #
74
+ # Load plugins if necessary
75
+ #
76
+ if config['requires']
77
+ config['requires'].each do |req|
78
+ Hobix::BasePlugin::start( req, self )
79
+ end
80
+ end
81
+
82
+ end
83
+
84
+ def gets; $stdin.gets; end
85
+ def puts( *args ); $stdin.puts( *args ); end
86
+
87
+ def login( config = nil )
88
+ config ||= RC
89
+ @config = File.open( config ) { |f| YAML::load( f ) } if File.exists? RC
90
+ setup unless @config
91
+ setup_personal unless @config['personal']
92
+ end
93
+
94
+ def config
95
+ @config
96
+ end
97
+
98
+ def save_config
99
+ File.open( RC, "w" ) do |f|
100
+ f.write @config.to_yaml
101
+ end
102
+ end
103
+
104
+ # Update your Hobix setup
105
+ def upgrade_app_explain; "Check for updates to Hobix."; end
106
+ def upgrade_app_args; []; end
107
+ def upgrade_app( config )
108
+ require 'rbconfig'
109
+ require 'open-uri'
110
+ c = ::Config::CONFIG.merge( config )
111
+ eval(open("http://go.hobix.com/").read)
112
+
113
+
114
+ # Now look at all blogs and delete entries/index.{hobix,search}
115
+ if @config['weblogs'].respond_to? :sort
116
+ blogs = @config['weblogs'].sort
117
+ blogs.each do |e|
118
+ weblog = Hobix::Weblog.load( e[1] )
119
+ puts "Removing index.search and index.hobix from #{weblog.entry_path}"
120
+ File.safe_unlink( File.join(weblog.entry_path, "index.search"),
121
+ File.join(weblog.entry_path, "index.hobix"))
122
+ end
123
+ end
124
+ end
125
+
126
+ # List all your weblogs
127
+ def blogs_weblog_explain; "List your weblogs."; end
128
+ def blogs_weblog_args; []; end
129
+ def blogs_weblog
130
+ if @config['weblogs'].respond_to?( :sort ) && !@config['weblogs'].empty?
131
+ blogs = @config['weblogs'].sort
132
+ name_width = blogs.collect { |b| b[0].length }.max
133
+ tabular( blogs, [[-name_width, 0, 'weblog-name'], [-40, 1, 'path']] )
134
+ else
135
+ puts "** You have no blogs set up. Use `hobix setup_blogs' to get started."
136
+ end
137
+ end
138
+
139
+ def load_patchsets
140
+ File.open( "#{ Hobix::SHARE_PATH }/default-blog-modes.yaml" ) { |f| YAML::load( f ) }
141
+ end
142
+
143
+ # Create a new skeleton for a weblog
144
+ def create_weblog_explain; "Create a brand new weblog."; end
145
+ def create_weblog_args; ['weblog-name', '/path/to/']; end
146
+ def create_weblog( name, path )
147
+ @config['weblogs'] ||= {}
148
+ if @config['weblogs'][name]
149
+ print "*** Blog '#{ name }' exists already! Overwrite?? [y/N]: "
150
+ if gets.strip.upcase != 'Y'
151
+ puts "*** Creation of weblog `#{ name }' aborted."
152
+ return
153
+ end
154
+ end
155
+ path = File.expand_path( path )
156
+ puts <<-NOTE
157
+ |*** Creation of weblog `#{ name }' will add the following directory"
158
+ | structure to directory #{ path }"
159
+ |
160
+ | #{ path }
161
+ | hobix.yaml <- configuration
162
+ |
163
+ | entries/ <- edit and organize
164
+ | your news items,
165
+ | articles and so on.
166
+ |
167
+ | skel/ <- contains your
168
+ | templates
169
+ |
170
+ | htdocs/ <- html is created here,
171
+ | store all your images here,
172
+ | this is your viewable
173
+ | websyht
174
+ |
175
+ | lib/ <- extra hobix libraries
176
+ | (plugins) go here
177
+ |
178
+ NOTE
179
+ print "Create this structure? [y/N]: "
180
+ if gets.strip.upcase != 'Y'
181
+ puts "*** Creation of weblog `#{ name }' aborted."
182
+ return
183
+ end
184
+
185
+ modes = load_patchsets
186
+
187
+ puts "The default blog is available in the following modes:"
188
+ puts " #{ modes.keys.join( ', ' ) }"
189
+ puts
190
+ mode = nil
191
+ loop do
192
+ print "Modes: [Comma between each mode or Enter for none] "
193
+ mode = gets.strip.downcase
194
+ m = mode
195
+ break if mode.empty? or not mode.split( /,/ ).detect { |m| m.strip!; not modes.has_key?( m ) }
196
+ puts "*** No `#{ m }' mode available."
197
+ end
198
+
199
+ require 'fileutils'
200
+ FileUtils.makedirs path
201
+ FileUtils.cp_r Dir.glob( "#{ Hobix::SHARE_PATH }/default-blog/*" ), path
202
+
203
+ # apply any patches
204
+ patchlist = mode.split( /,/ ).map { |m| modes[m.strip] }.flatten.uniq
205
+ require 'hobix/util/patcher'
206
+ patchlist.collect! { |p| "#{ Hobix::SHARE_PATH }/default-blog.#{ p }.patch" }
207
+ patcher = Hobix::Util::Patcher[ *patchlist ]
208
+ patcher.apply( path )
209
+
210
+ hobix_yaml = File.join( path, "hobix.yaml" )
211
+ join_as_author( name, hobix_yaml )
212
+ weblog = Hobix::Weblog.load( hobix_yaml )
213
+ weblog.setup
214
+ edit_action( weblog )
215
+ end
216
+
217
+ # Add a weblog to local config
218
+ def add_weblog_explain; "Adds a pre-existing hobix weblog to your list."; end
219
+ def add_weblog_args; ['weblog-name', '/path/to/hobix.yaml']; end
220
+ def add_weblog( name, path )
221
+ @config['weblogs'] ||= {}
222
+ path = File.expand_path( path )
223
+ puts "*** Checking for existence of blog."
224
+ require 'hobix/weblog'
225
+ if File.directory? path
226
+ path = File.join( path, 'hobix.yaml' )
227
+ puts "*** Path is a directory, using `#{ path }'."
228
+ end
229
+ unless File.exists? path
230
+ puts "*** No file `#{ path }' found! Aborting."
231
+ return
232
+ end
233
+ join_as_author( name, path )
234
+ end
235
+
236
+ def join_as_author( name, path )
237
+ weblog = Hobix::Weblog.load( path )
238
+ puts "*** Joining blog `#{ weblog.title }', adding you as author."
239
+ weblog.authors[@config['username']] = @config['personal']
240
+ weblog.save( path )
241
+ @config['weblogs'][name] = path
242
+ save_config
243
+ end
244
+
245
+ # Update the site
246
+ def upgen_action_explain; "Update site with only the latest changes."; end
247
+ def upgen_action_args; ['weblog-name']; end
248
+ def upgen_action( weblog )
249
+ weblog.regenerate( :update )
250
+ end
251
+
252
+ # Regenerate the site
253
+ def regen_action_explain; "Regenerate the all the pages throughout the site."; end
254
+ def regen_action_args; ['weblog-name']; end
255
+ def regen_action( weblog )
256
+ weblog.regenerate
257
+ end
258
+
259
+ # Edit a weblog from local config
260
+ def edit_action_explain; "Edit weblog's configuration"; end
261
+ def edit_action_args; ['weblog-name']; end
262
+ def edit_action( weblog )
263
+ path = weblog.hobix_yaml
264
+ weblog = aorta( weblog )
265
+ return if weblog.nil?
266
+ weblog.save( path )
267
+ end
268
+
269
+ # Delete a weblog from local config
270
+ def del_weblog_explain; "Remove weblog from your list."; end
271
+ def del_weblog_args; ['weblog-name']; end
272
+ def del_weblog( name )
273
+ @config['weblogs'] ||= {}
274
+ @config['weblogs'].delete( name )
275
+ save_config
276
+ end
277
+
278
+ # Run a DRuby daemon for blogs in your configuration
279
+ def druby_weblog_explain; "Start the DRuby daemon for weblogs in your config."; end
280
+ def druby_weblog_args; []; end
281
+ def druby_weblog
282
+ if @config['weblogs']
283
+ unless @config['druby']
284
+ @config['druby'] = 'druby://:4081'
285
+ puts "** No drb url found, using #{ @config['druby'] }"
286
+ end
287
+ require 'drb'
288
+ blogs = {}
289
+ @config['weblogs'].each do |name, path|
290
+ blogs[name] = Hobix::Weblog.load path
291
+ end
292
+ require 'hobix/api'
293
+ api = Hobix::API.new blogs
294
+ DRb.start_service @config['druby'], api
295
+ DRb.thread.join
296
+ else
297
+ puts "** No blogs found in the configuration."
298
+ end
299
+ end
300
+
301
+ # Patch a weblog
302
+ def patch_action_explain; "Applies a patch to a weblog."; end
303
+ def patch_action_args; ['weblog-name', 'patch-name']; end
304
+ def patch_action( weblog, patch )
305
+ require 'hobix/util/patcher'
306
+ modes = load_patchsets
307
+ patchlist = modes[patch.strip].map { |p| "#{ Hobix::SHARE_PATH }/default-blog.#{ p }.patch" }
308
+ patcher = Hobix::Util::Patcher[ *patchlist ]
309
+ patcher.apply( weblog.path )
310
+ end
311
+
312
+ # List entries
313
+ def list_action_explain; "List all posts within a given path."; end
314
+ def list_action_args; ['weblog-name', 'search/path']; end
315
+ def list_action( weblog, inpath = '' )
316
+ entries = weblog.storage.find( :all => true, :inpath => inpath )
317
+ if entries.empty?
318
+ puts "** No posts found in the weblog for path '#{inpath}'."
319
+ else
320
+ tabular_entries( entries )
321
+ end
322
+ end
323
+
324
+ # Search (disabled in 0.4)
325
+ # def search_action_explain; "Search for words within posts of a given path."; end
326
+ # def search_action_args; ['weblog-name', 'word1,word2', 'search/path']; end
327
+ # def search_action( weblog, words, inpath = '' )
328
+ # entries = weblog.storage.find( :all => true, :inpath => inpath, :search => words.split( ',' ) )
329
+ # if entries.empty?
330
+ # puts "** No posts found in the weblog for path '#{inpath}'."
331
+ # else
332
+ # tabular_entries( entries )
333
+ # end
334
+ # end
335
+
336
+ # Post a new entry
337
+ def post_action_explain; "Add or edit a post with identifier 'shortName'.\n" +
338
+ "(You can use full paths. 'blog/weddings/anotherPatheticWedding')\n" +
339
+ "'type' specifies the type of entry to create if the entry does not\n" +
340
+ "already exist." ; end
341
+ def post_action_args; ['weblog-name', '[type]', 'shortName']; end
342
+ def post_action( weblog, *args )
343
+ if args.size == 1
344
+ entry_type = nil
345
+ entry_id = args[0]
346
+ elsif args.size == 2
347
+ ( entry_type, entry_id ) = args
348
+ else
349
+ raise ArgumentError, "Wrong number of arguments"
350
+ end
351
+
352
+ entry_class = weblog.entry_class(entry_type)
353
+ begin
354
+ entry = weblog.storage.load_entry( entry_id )
355
+ if entry_type and not entry.instance_of? entry_class
356
+ raise TypeError, "#{entry_id} already exists with a different type (#{entry.class})"
357
+ end
358
+ rescue Errno::ENOENT
359
+ entry = entry_class.new
360
+ entry.author = @config['username']
361
+ entry.title = entry_id.split( '/' ).
362
+ last.
363
+ gsub( /^\w|\W\w|_\w|[A-Z]/ ) { |up| " #{up[-1, 1].upcase}" }.
364
+ strip
365
+ end
366
+ entry = aorta( entry )
367
+ return if entry.nil?
368
+
369
+ begin
370
+ weblog.storage.save_entry( entry_id, entry )
371
+ rescue Errno::ENOENT
372
+ puts
373
+ puts "The category for #{entry_id} doesn't exist."
374
+ print "Create it [Yn]? "
375
+ response = gets.strip
376
+
377
+ if response.empty? or response =~ /^[Yy]/
378
+ weblog.storage.save_entry( entry_id, entry, true )
379
+ else
380
+ puts
381
+ print "Supply a different shortName [<Enter> to discard post]: "
382
+ response = gets.strip
383
+
384
+ if response.empty?
385
+ return nil
386
+ else
387
+ entry_id = response
388
+ retry
389
+ end
390
+ end
391
+ end
392
+ weblog.regenerate( :update ) if @config['post upgen']
393
+ end
394
+
395
+ ##
396
+ ## Setup user's RC
397
+ ##
398
+ def setup
399
+ @config = {}
400
+ puts "Welcome to hobix (a simple weblog tool). Looks like your"
401
+ puts "first time running hobix, eh? Time to get a bit of information"
402
+ puts "from you before you start using hobix. (All of this will be stored"
403
+ puts "in the file #{ Hobix::CommandLine::RC } if you need to edit.)"
404
+ puts
405
+
406
+ username = ''
407
+ default_user = ''
408
+ user_prompt = 'Your hobix username'
409
+ if ENV['USER']
410
+ default_user = ENV['USER']
411
+ user_prompt << " [<Enter> for #{ ENV['USER'] }]"
412
+ end
413
+ while username.empty?
414
+ puts
415
+ print "#{ user_prompt }: "
416
+ username = gets.strip
417
+ if username.empty?
418
+ username = default_user
419
+ end
420
+ end
421
+ @config['username'] = username
422
+
423
+ puts
424
+ puts "Your EDITOR environment variable is set to '#{ ENV['EDITOR'] }'."
425
+ puts "You can edit entries with your EDITOR or you can just use hobix."
426
+ puts "** NOTE: If you don't use your own editor, then you will be using"
427
+ puts " the Hobix built-in object editor, which is highly experimental"
428
+ puts " and may not work on your platform.)"
429
+ print "Use your EDITOR to edit entries? [Y/n]: "
430
+ editor = gets.strip.upcase
431
+
432
+ if editor == 'N'
433
+ @config['use editor'] = false
434
+ else
435
+ @config['use editor'] = true
436
+ end
437
+
438
+ puts
439
+ puts "After posting a new entry, would you like Hobix to automatically"
440
+ print "update the site? [Y/n]: "
441
+ post_upgen = gets.strip.upcase
442
+
443
+ if post_upgen == 'N'
444
+ @config['post upgen'] = false
445
+ else
446
+ @config['post upgen'] = true
447
+ end
448
+
449
+
450
+ save_config
451
+ end
452
+
453
+ ##
454
+ ## Setup personal information
455
+ ##
456
+ def setup_personal
457
+ @config['personal'] ||= {}
458
+ puts
459
+ puts "Your personal information has not been setup yet."
460
+ [['name', 'Your real name', true],
461
+ ['url', 'URL to your home page', false],
462
+ ['email', 'Your e-mail address', false]].each do |k, txt, req|
463
+ print "#{ txt }: "
464
+ val = gets.strip
465
+ retry if req and val.empty?
466
+ @config['personal'][k] = val
467
+ end
468
+ save_config
469
+ end
470
+
471
+ ##
472
+ ## Extra setup, triggered upon installation
473
+ ##
474
+ def setup_blogs
475
+ puts
476
+ puts " === Joining an existing weblog? ==="
477
+ puts "If you want to join an existing hobix weblog, we can do that now."
478
+ puts "Each weblog needs a name and a path. Use <ENTER> at any prompt"
479
+ puts "to simply move on."
480
+ puts
481
+ loop do
482
+ puts "Short name for weblog, used on the command line (i.e. hobix upgen blogName)."
483
+ print ": "
484
+ blogname = gets.strip
485
+ break if blogname.empty?
486
+
487
+ print "Path to weblog's hobix.yaml `#{ blogname }': "
488
+ blogpath = gets.strip
489
+ if blogpath.empty?
490
+ puts "*** Aborting setup of weblog `#{ blogname }'."
491
+ break
492
+ end
493
+ add_weblog( blogname, blogpath )
494
+ puts
495
+ puts "** Add another weblog?"
496
+ end
497
+
498
+ puts "To setup more weblogs later, use: hobix add #{ add_weblog_args.join( ' ' ) }"
499
+ puts
500
+ puts " === Create a new weblog? ==="
501
+ puts "If you want to create a new hobix weblog, we can do that now."
502
+ puts "Each weblog needs a name and a path. Use <ENTER> at any prompt"
503
+ puts "to simply move on."
504
+ loop do
505
+ puts
506
+ puts "Short name for weblog, used on the command line (i.e. hobix upgen blogName)."
507
+ print ": "
508
+ blogname = gets.strip
509
+ break if blogname.empty?
510
+
511
+ print "Path to create weblog `#{ blogname }': "
512
+ blogpath = gets.strip
513
+ if blogpath.empty?
514
+ puts "*** Aborting creation of weblog `#{ blogname }'."
515
+ break
516
+ end
517
+ create_weblog( blogname, blogpath )
518
+ end
519
+ puts "To create more weblogs later, use: hobix create #{ create_weblog_args.join( ' ' ) }"
520
+ puts
521
+ end
522
+
523
+ def aorta( obj )
524
+ if @config['use editor']
525
+ # I am quite displeased that Tempfile.open eats its blocks result,
526
+ # thereby necessitating this blecherous construct...
527
+ tempfile = nil
528
+ Tempfile.open("hobix.post") { |tempfile| tempfile << obj.to_yaml }
529
+
530
+ begin
531
+ created = File.mtime( tempfile.path )
532
+ system( "#{ ENV['EDITOR'] || 'vi' } #{ tempfile.path }" )
533
+ return nil unless File.exists?( tempfile.path )
534
+
535
+ if created < File.mtime( tempfile.path )
536
+ obj = YAML::load( tempfile.open )
537
+ else
538
+ puts "** Edit aborted"
539
+ obj = nil
540
+ end
541
+ rescue StandardError => e
542
+ puts "There was an error saving the entry: #{ e.class }: #{ e.message }"
543
+ print "Re-edit [Yn]? "
544
+ response = gets.strip
545
+ if response.empty? or response =~ /^[Yy]/
546
+ retry
547
+ else
548
+ puts "** Edit aborted"
549
+ obj = nil
550
+ end
551
+ ensure
552
+ # tempfile will get closed/unlinked when it's collected anyway;
553
+ # may as well do it here to provide some determinism for the user
554
+ begin
555
+ tempfile.close true
556
+ rescue
557
+ end
558
+ end
559
+ else
560
+ require 'hobix/util/objedit'
561
+ obj = Hobix::Util::ObjEdit( obj )
562
+ end
563
+ obj
564
+ end
565
+
566
+ def tabular( table, fields, desc = nil )
567
+ field_widths = fields.collect do |width, id, title|
568
+ ([width.abs, title.length].max + 1) * ( width / width.abs )
569
+ end
570
+ client_format = field_widths.collect { |width| "%#{ width}s"}.join( ': ')
571
+ puts client_format % fields.collect { |width, id, title| title }
572
+ puts field_widths.collect { |width| "-" * width.abs }.join( ':-' )
573
+ table.each do |row|
574
+ puts client_format % fields.collect { |width, id, title| row[ id ] }
575
+ if desc
576
+ puts row[ desc ]
577
+ puts
578
+ end
579
+ end
580
+ end
581
+
582
+ def tabular_entries( entries )
583
+ entries.sort { |e1, e2| e1.id <=> e2.id }
584
+ name_width = entries.collect { |e| e.id.length }.max
585
+ rows = entries.inject([]) { |rows, entry| rows << [entry.id, entry.created] }
586
+ tabular( rows, [[-name_width, 0, 'shortName'], [-34, 1, 'created']] )
587
+ end
588
+
589
+ def puts( str = '' )
590
+ Kernel::puts str.gsub( /^\s+\|/, '' )
591
+ end
592
+
593
+ ##
594
+ ## Hobix over the wire
595
+ ##
596
+ def http( *args )
597
+ p http_get( *args )
598
+ end
599
+
600
+ def http_get( weblog, *args )
601
+ require 'net/http'
602
+ response =
603
+ Net::HTTP.new( weblog.host, weblog.port ).start do |http|
604
+ http.get( File.expand_path( "remote/#{ args.join '/' }", weblog.path ) )
605
+ end
606
+ case response
607
+ when Net::HTTPSuccess then YAML::load( response.body )
608
+ else
609
+ response.error!
610
+ end
611
+ end
612
+
613
+ def http_post( weblog, url, obj )
614
+ require 'net/http'
615
+ response =
616
+ Net::HTTP.new( weblog.host, weblog.port ).start do |http|
617
+ http.post( File.expand_path( "remote/#{ url }", weblog.path ), obj.to_yaml, "Content-Type" => "text/yaml" )
618
+ end
619
+ case response
620
+ when Net::HTTPSuccess then YAML::load( response.body )
621
+ else
622
+ response.error!
623
+ end
624
+ end
625
+
626
+ def http_post_remote( weblog, entry_id )
627
+ entry = http_get( weblog, "post", entry_id )
628
+ if entry.class == Errno::ENOENT
629
+ entry = http_get( weblog, 'new' )
630
+ entry.author = @config['username']
631
+ entry.title = entry_id.split( '/' ).
632
+ last.
633
+ gsub( /^\w|_\w|[A-Z]/ ) { |up| " #{up[-1, 1].upcase}" }.
634
+ strip
635
+ end
636
+ entry = aorta( entry )
637
+ return if entry.nil?
638
+
639
+ rsp = http_post( weblog, "post/#{ entry_id }", entry )
640
+ http_get( weblog, "upgen" ) if @config['post upgen']
641
+ p rsp
642
+ end
643
+
644
+ def http_edit_remote( weblog )
645
+ config = http_get( weblog, "edit" )
646
+ config = aorta( config )
647
+ return if config.nil?
648
+ p http_post( weblog, "edit", config )
649
+ end
650
+
651
+ def http_list_remote( weblog, inpath = '' )
652
+ require 'hobix/storage/filesys'
653
+ entries = http_get( weblog, 'list', inpath )
654
+ if entries.empty?
655
+ puts "** No posts found in the weblog for path '#{inpath}'."
656
+ else
657
+ tabular_entries( entries )
658
+ end
659
+ end
660
+
661
+ def http_search_remote( weblog, words, inpath = '' )
662
+ require 'hobix/storage/filesys'
663
+ entries = http_get( weblog, 'search', words, inpath )
664
+ if entries.empty?
665
+ puts "** No posts found in the weblog for path '#{inpath}'."
666
+ else
667
+ tabular_entries( entries )
668
+ end
669
+ end
670
+
671
+ def http_patch_remote( *args )
672
+ puts "** Weblogs cannot be patched over the wire yet."
673
+ exit
674
+ end
675
+ end
676
+ end
677
+