hobix 0.6

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.
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
+