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
data/lib/hobix/base.rb ADDED
@@ -0,0 +1,480 @@
1
+ #
2
+ # = hobix/weblog.rb
3
+ #
4
+ # Hobix command-line weblog system.
5
+ #
6
+ # Copyright (c) 2003-2004 why the lucky stiff
7
+ #
8
+ # Written & maintained by why the lucky stiff <why@ruby-lang.org>
9
+ #
10
+ # This program is free software, released under a BSD license.
11
+ # See COPYING for details.
12
+ #
13
+ #--
14
+ # $Id$
15
+ #++
16
+ require 'redcloth'
17
+ require 'yaml'
18
+
19
+ module YAML
20
+ class Omap
21
+ def keys; map { |k, v| k }; end
22
+ end
23
+ end
24
+
25
+ module Hobix
26
+ # The BasePlugin class is *bingo* the underlying class for
27
+ # all Hobix plugins. The +Class::inherited+ hook is used
28
+ # by this class to keep track of all classes that inherit
29
+ # from it.
30
+ class BasePlugin
31
+ @@plugins = {}
32
+ @@required_from = nil
33
+ # Initializes all the plugins, returning
34
+ # an Array of plugin objects. (Used by the
35
+ # +Hobix::Weblog+ class.)
36
+ def BasePlugin.start( req, opts, weblog )
37
+ @@required_from = req = req.dup
38
+ if req.tainted?
39
+ req.untaint if req =~ /^[\w\/\\]+$/
40
+ end
41
+ require( req )
42
+ @@required_from = nil
43
+
44
+ if @@plugins[req]
45
+ @@plugins[req].collect do |p|
46
+ if opts
47
+ p.new( weblog, opts )
48
+ else
49
+ p.new( weblog )
50
+ end
51
+ end
52
+ else
53
+ []
54
+ end
55
+ end
56
+ def BasePlugin.inherited( sub )
57
+ @@plugins[@@required_from] ||= []
58
+ @@plugins[@@required_from] << sub
59
+ end
60
+ end
61
+
62
+ # The BaseStorage class outlines the fundamental API for
63
+ # all storage plugins. Storage plugins are responsible
64
+ # for abstracting away entry queries and managing the loading
65
+ # of Entry objects. The goal being: cache as much as you can,
66
+ # be efficient and tidy.
67
+ #
68
+ # == Query Methods
69
+ #
70
+ # find:: Each of the query methods below uses the +find+ method
71
+ # to perform its search. This method accepts a Hash of
72
+ # parameters. Please note that calling +find+ without
73
+ # parameters will return all entries which qualify for
74
+ # placement on the front page.
75
+ #
76
+ # all:: Returns all entries. Searches find( :all => true )
77
+ # lastn:: Returns the last _n_ entries which qualify for the
78
+ # front page.
79
+ # inpath:: Returns entries within a path which qualify for the
80
+ # front page.
81
+ # after:: Returns entries created after a given date.
82
+ # before:: Returns entries created before a given date.
83
+ # within:: Returns entries created between a start and
84
+ # end date.
85
+ #
86
+ class BaseStorage < BasePlugin
87
+ def initialize( weblog )
88
+ @link = weblog.link
89
+ end
90
+ def default_entry_id; "hobix-default-entry"; end
91
+ def default_entry( author )
92
+ Hobix::Entry.new do |e|
93
+ e.id = default_entry_id
94
+ e.link = e.class.url_link e, @link, "html"
95
+ e.created = Time.now
96
+ e.modified = Time.now
97
+ e.updated = Time.now
98
+ e.title = "This Ghostly Message From the Slime Will Soon Vanish!"
99
+ e.tagline = "A temporary message, a tingling sensation, Hobix is up!!"
100
+ e.author = author
101
+ e.content = Hobix::Entry.text_processor.new( "Welcome to Hobix! Once you make your first blog post, this entry will disappear. However, in the meantime, you can tweak the CSS of your blog until it suits your satisfaction and you have this bit of words to act as a place holder." )
102
+ end
103
+ end
104
+ def all
105
+ find( :all => true )
106
+ end
107
+ def lastn( n )
108
+ find( :lastn => ( n || 10 ) )
109
+ end
110
+ def inpath( path, n = nil )
111
+ find( :inpath => path, :lastn => n )
112
+ end
113
+ def after( after, n = nil )
114
+ find( :after => after, :lastn => n )
115
+ end
116
+ def before( before, n = nil )
117
+ find( :before => before, :lastn => n )
118
+ end
119
+ def match( expr )
120
+ find( :match => expr )
121
+ end
122
+ def within( after, before )
123
+ find( :after => after, :before => before )
124
+ end
125
+ end
126
+
127
+ # The BaseOutput plugin is the underlying class for all output
128
+ # plugins. These plugins are associated to templates. Based on
129
+ # a template's suffix, the proper output plugin is loaded and
130
+ # used to generate page output.
131
+ class BaseOutput < BasePlugin
132
+ end
133
+
134
+ # The BasePublish plugin is the underlying class for all publishing
135
+ # plugins, which are notified of updates to pages.
136
+ #
137
+ # Publish plugins are executed after generation of the site. The plugin
138
+ # may choose to watch updates to certain types of pages. The plugin also
139
+ # receives a list of all the pages which have been updated.
140
+ #
141
+ # Generally, publish plugins fall into two categories:
142
+ #
143
+ # * Plugins which contact a service when certain updates happen.
144
+ # (Hobix includes an XML-RPC ping, which is triggered whenever
145
+ # the front page is updated.)
146
+ # * Plugins which transform Hobix output. (Hobix includes a
147
+ # replication plugin, which copies updated pages to a remote
148
+ # system via FTP or SFTP.)
149
+ #
150
+ # == Publish methods
151
+ #
152
+ # initialize( weblog, settings ):: Like all other plugins, the initialize method takes two parameters,
153
+ # a Hobix::Weblog object for the weblog being published and the
154
+ # settings data from the plugin's entry in hobix.yaml.
155
+ # watch:: (Optional) Returns an array of page types which, when published, activate the plugin.
156
+ # publish( pages ):: If pages are published and the watch criteria qualifies this plugin,
157
+ # this method is called with a hash of pages published. The key is the page type
158
+ # and the value is an array of Page objects.
159
+ class BasePublish < BasePlugin
160
+ end
161
+
162
+ # The BaseFacet plugin is the superclass for all plugins which have
163
+ # an interface (CGI, UI, etc.) These interfaces expose some functionality
164
+ # to the user through an entry form or series of views.
165
+ class BaseFacet < BasePlugin
166
+ def self.not_found app
167
+ app.send_not_found "Action `#{ app.action_uri }' not found. If this address should work, check your plugins."
168
+ end
169
+ def protect app, weblog
170
+ auth = ENV['HTTP_AUTHORIZATION'] || ENV['X-HTTP_AUTHORIZATION']
171
+ if auth
172
+ realm = 'Hobix login'
173
+ auth_type, auth = auth.split ' ', 2
174
+ authorized = false
175
+ case auth_type.downcase
176
+ when 'basic'
177
+ require 'base64'
178
+ name, pass = Base64::decode64( auth.strip ).split ':', 2
179
+ authorized = weblog.authorize name, pass
180
+ when 'digest'
181
+ require 'md5'
182
+ opts = {}
183
+ auth.gsub( /(\w+)="(.*?)"/ ) { opts[$1] = $2 }
184
+ app.puts opts.inspect
185
+ end
186
+ return true if authorized
187
+ end
188
+
189
+ app.send_unauthorized
190
+ # nonce = ["#{ Time.now.to_f }:#{ app.action_uri }"].pack("m").gsub /\s/, ''
191
+ # app.set_header 'WWW-Authenticate', %{Digest qop="auth", realm="#{ realm }", nonce="#{ nonce }", algorithm="MD5"}
192
+ app.set_header 'WWW-Authenticate', %{Basic realm="#{ realm }"}
193
+ false
194
+ end
195
+ end
196
+
197
+ # Enumerable::each_with_neighbors from Joel Vanderwerf's
198
+ # enum extenstions.
199
+ module Enumerable
200
+ def each_with_neighbors n = 1, empty = nil
201
+ nbrs = [empty] * (2 * n + 1)
202
+ offset = n
203
+
204
+ each { |x|
205
+ nbrs.shift
206
+ nbrs.push x
207
+ if offset == 0 # offset is now the offset of the first element, x0,
208
+ yield nbrs # of the sequence from the center of nbrs, or 0,
209
+ else # if x0 has already passed the center.
210
+ offset -= 1
211
+ end
212
+ }
213
+
214
+ n.times {
215
+ nbrs.shift
216
+ nbrs.push empty
217
+ if offset == 0
218
+ yield nbrs
219
+ else
220
+ offset -= 1
221
+ end
222
+ }
223
+
224
+ self
225
+ end
226
+ end
227
+
228
+ module BaseProperties
229
+ # Returns the complete list of properties for the immediate class.
230
+ # If called on an inheriting class, inherited properties are included.
231
+ module ClassMethods
232
+ def properties
233
+ if superclass.respond_to? :properties
234
+ s = superclass.properties.dup
235
+ (@__props || {}).each { |k, v| s[k] = v }
236
+ s
237
+ else
238
+ (@__props || {})
239
+ end
240
+ end
241
+ def prop_sections
242
+ if superclass.respond_to? :prop_sections
243
+ s = superclass.prop_sections.dup
244
+ (@__sects || {}).each { |k, v| s[k] = v }
245
+ s
246
+ else
247
+ (@__sects || {})
248
+ end
249
+ end
250
+ # Quick property definitions in class definitions.
251
+ def _ name, opts = nil
252
+ @__props ||= YAML::Omap[]
253
+ @__props[name] = opts
254
+ attr_accessor name unless method_defined? "#{ name }="
255
+ end
256
+ # Property sections
257
+ def _! name, opts = {}
258
+ @__sects ||= YAML::Omap[]
259
+ opts[:__sect] = @__props.last[0] rescue nil
260
+ @__sects[name] = opts
261
+ end
262
+ end
263
+ # Build a simple map of properties
264
+ def property_map
265
+ self.class.properties.map do |name, opts|
266
+ if opts
267
+ yreq = opts[:req] ? :req : :opt
268
+ ["@#{ name }", yreq] if yreq
269
+ end
270
+ end.compact
271
+ end
272
+ # Build a property map for the YAML module
273
+ def to_yaml_properties
274
+ property_map.find_all do |prop, req|
275
+ case req
276
+ when :opt
277
+ not instance_variable_get( prop ).nil?
278
+ when :req
279
+ true
280
+ end
281
+ end.
282
+ collect do |prop, req|
283
+ prop
284
+ end
285
+ end
286
+ def self.append_features klass
287
+ super
288
+ klass.extend ClassMethods
289
+ end
290
+ end
291
+
292
+ # placed here to avoid dependency cycle between base.rb and weblog.rb
293
+ class Weblog
294
+ @@entry_classes = []
295
+ def self.add_entry_class( c )
296
+ @@entry_classes << c
297
+ end
298
+ end
299
+
300
+ class BaseContent
301
+ include BaseProperties
302
+
303
+ _! 'Entry Information'
304
+ _ :id
305
+ _ :link
306
+ _ :title, :edit_as => :text, :search => :fulltext
307
+ _ :created, :edit_as => :datetime, :search => :prefix
308
+ _ :modified, :show_as => :datetime
309
+ _ :updated, :show_as => :datetime
310
+ _ :tags, :edit_as => :text, :search => :prefix
311
+
312
+ def initialize; yield self if block_given?; end
313
+ def day_id; created.strftime( "%Y/%m/%d" ) if created; end
314
+ def month_id; created.strftime( "%Y/%m" ) if created; end
315
+ def year_id; created.strftime( "%Y" ) if created; end
316
+ def section_id; File.dirname( id ) if id; end
317
+ def base_id; File.basename( id ) if id; end
318
+ def self.url_link( e, url = nil, ext = nil ); "#{ url }/#{ link_format e }#{ '.' + ext if ext }"; end
319
+ def self.link_format( e ); e.id; end
320
+ def force_tags; []; end
321
+
322
+ #
323
+ # If set to true, tags won't be deduced from the entry id
324
+ #
325
+ @@no_implicit_tags = false
326
+
327
+ def self.no_implicit_tags
328
+ @@no_implicit_tags = true
329
+ end
330
+
331
+ #
332
+ # When using implicit tag, the blog root (i.e) is not considered
333
+ # unless you set the value of +@@root_tag+ to what you need.
334
+ #
335
+ @@root_tag = nil
336
+ def self.root_tag=( tag )
337
+ @@root_tag = tag
338
+ end
339
+
340
+ #
341
+ # When computing so-called implicit 'implicit-tag', whether
342
+ # or not we should split the path into several tags
343
+ # (default: false)
344
+ #
345
+ @@split_implicit_tags = false
346
+
347
+ def self.split_implicit_tags
348
+ @@split_implicit_tags = true
349
+ end
350
+
351
+ #
352
+ # return an array of tags deduced from the path
353
+ # i.e. a path like ruby/hobix/foo.yml will lead
354
+ # to [ ruby, hobix ] tags
355
+ # Occurence of . (alone) will be either removed or replaced
356
+ # by the value of +root_tag+
357
+ #
358
+ def path_to_tags( path )
359
+ return [] if @@no_implicit_tags
360
+ return [] if path.nil?
361
+ if @@split_implicit_tags
362
+ tags_array = path.split("/").find_all { |e| e.size > 0 }
363
+ tags_array.pop # Last item is the entry title
364
+ else
365
+ tags_array = [ File.dirname( path )]
366
+ end
367
+ tags_array.map { |e| e == '.' ? @@root_tag : e }.compact
368
+ end
369
+
370
+ #
371
+ # return canonical tags, i.e. tags that are forced and that are deduced
372
+ # from the entry path
373
+ #
374
+ def canonical_tags( path=nil )
375
+ ( force_tags + path_to_tags( path || self.id ) ).uniq
376
+ end
377
+
378
+ def tags;( canonical_tags + Array( @tags ) ).uniq; end
379
+
380
+ def self.yaml_type( tag )
381
+ # if self.respond_to? :yaml_as
382
+ # yaml_as tag
383
+ # else
384
+ if tag =~ /^tag:([^:]+):(.+)$/
385
+ define_method( :to_yaml_type ) { "!#$1/#$2" }
386
+ YAML::add_domain_type( $1, $2 ) { |t, v| self.maker( v ) }
387
+ end
388
+ # end
389
+ end
390
+
391
+ alias to_yaml_orig to_yaml
392
+ def to_yaml( opts = {} )
393
+ opts[:UseFold] = true if opts.respond_to? :[]
394
+ self.class.text_processor_fields.each do |f|
395
+ v = instance_variable_get( '@' + f )
396
+ if v.is_a? self.class.text_processor
397
+ instance_eval %{
398
+ def @#{ f }.to_yaml( opts = {} )
399
+ s = self.to_str
400
+ def s.to_yaml_style; :literal; end
401
+ s.to_yaml( opts )
402
+ end
403
+ }
404
+ end
405
+ end
406
+ to_yaml_orig( opts )
407
+ end
408
+
409
+ # Load the weblog entry from a file.
410
+ def self.load( file )
411
+ File.open( file ) { |f| YAML::load( f ) }
412
+ end
413
+
414
+ # Accessor which returns the text processor used for untyped
415
+ # strings in Entry fields. (defaults to +RedCloth+.)
416
+ def self.text_processor; RedCloth; end
417
+ # Returns an Array of fields to which the text processor applies.
418
+ def self.text_processor_fields
419
+ self.properties.map do |name, opts|
420
+ name.to_s if opts and opts[:text_processor]
421
+ end.compact
422
+ end
423
+ # Factory method for generating Entry classes from a hash. Used
424
+ # by the YAML loader.
425
+ def self.maker( val )
426
+ self::text_processor_fields.each do |f|
427
+ if val[f].respond_to? :value
428
+ str = val[f].value
429
+ def str.to_html
430
+ self
431
+ end
432
+ val[f] = str
433
+ elsif val[f].respond_to? :to_str
434
+ val[f] = self::text_processor.new( val[f].to_str )
435
+ end
436
+ end
437
+ YAML::object_maker( self, val )
438
+ end
439
+ end
440
+
441
+ # The BaseEntry class is the underlying class for all Hobix
442
+ # entries (i.e. the content for your website/blahhg.)
443
+ class BaseEntry < BaseContent
444
+
445
+ _ :id
446
+ _ :link
447
+ _ :title, :edit_as => :text, :search => :fulltext
448
+ _ :author, :req => true, :edit_as => :text, :search => :prefix
449
+ _ :contributors, :edit_as => :array, :search => :prefix
450
+ _ :created, :edit_as => :datetime, :search => :prefix
451
+ _ :modified, :show_as => :datetime
452
+ _ :updated, :show_as => :datetime
453
+ _ :tags, :edit_as => :text, :search => :prefix
454
+ _ :content, :edit_as => :textarea, :search => :fulltext, :text_processor => true
455
+ _ :content_ratings, :edit_as => :array
456
+
457
+ def content_ratings; @content_ratings || [:ham]; end
458
+
459
+ def self.inherited( sub )
460
+ Weblog::add_entry_class( sub )
461
+ end
462
+
463
+ # Build the searchable text
464
+ def to_search
465
+ self.class.properties.map do |name, opts|
466
+ next unless opts
467
+ val = instance_variable_get( "@#{ name }" )
468
+ next unless val
469
+ val = val.strftime "%Y-%m-%dT%H:%M:%S" if val.respond_to? :strftime
470
+ case opts[:search]
471
+ when :prefix
472
+ "#{ name }:" + val.to_s
473
+ when :fulltext
474
+ val.to_s
475
+ end
476
+ end.compact.join "\n"
477
+ end
478
+
479
+ end
480
+ end
@@ -0,0 +1,200 @@
1
+ #
2
+ # = hobix/bixwik.rb
3
+ #
4
+ # Hobix command-line weblog system.
5
+ #
6
+ # Copyright (c) 2003-2004 why the lucky stiff
7
+ #
8
+ # Written & maintained by why the lucky stiff <why@ruby-lang.org>
9
+ #
10
+ # This program is free software, released under a BSD license.
11
+ # See COPYING for details.
12
+ #
13
+ #--
14
+ # $Id$
15
+ #++
16
+ require 'hobix/weblog'
17
+
18
+ module Hobix
19
+ # The BixWik class is an extended Weblog, which acts like a Wiki.
20
+ # (See http://instiki.org/ for inspiration.)
21
+ class BixWikPlugin < Hobix::BasePlugin
22
+ def initialize( weblog )
23
+ class << weblog
24
+ include Hobix::BixWik
25
+ end
26
+ end
27
+ end
28
+
29
+ module BixWik
30
+
31
+ QUICK_MENU = YAML::load <<-END
32
+ --- %YAML:1.0 !omap
33
+ - HomePage: [Home Page, H, Start Over]
34
+ - list/index: [All Pages, A, Alphabetically sorted list of pages]
35
+ - recent/index: [Recently Revised, U, Pages sorted by when they were last changed]
36
+ - authors/index: [Authors, ~, Who wrote what]
37
+ - FeedList: [Feed List, ~, Subscribe to changes by RSS]
38
+ END
39
+
40
+ def default_entry_class; "Hobix::BixWik::Entry"; end
41
+ def default_index_class; "Hobix::BixWik::IndexEntry"; end
42
+
43
+ # Handler for templates with `index' prefix. These pages simply
44
+ # mirror the `HomePage' entry.
45
+ def skel_index( path_storage, section_path )
46
+ homePage = path_storage.match( /^HomePage$/ ).first
47
+ page = Page.new( 'index' )
48
+ unless homePage
49
+ homePage = Hobix::Storage::IndexEntry.new( path_storage.default_entry( authors.keys.first ) )
50
+ end
51
+ page.timestamp = homePage.created
52
+ page.updated = homePage.created
53
+ yield :page => page, :entry => homePage
54
+ end
55
+
56
+ # Handler for templates with `list/index' prefix. These templates will
57
+ # receive IndexEntry objects for every entry in the system. Only one
58
+ # index page is requested by this handler.
59
+ def skel_recent_index( path_storage, section_path )
60
+ index_entries = storage.find( :all => true )
61
+ page = Page.new( 'list/index' )
62
+ page.timestamp = index_entries.first.created
63
+ page.updated = storage.last_updated( index_entries )
64
+ yield :page => page, :entries => index_entries
65
+ end
66
+
67
+ # Handler for templates with `recent/index' prefix. These templates will
68
+ # receive entries loaded by +Hobix::BaseStorage#lastn+. Only one
69
+ # index page is requested by this handler.
70
+ def skel_recent_index( path_storage, section_path )
71
+ index_entries = storage.lastn( @lastn || 120 )
72
+ page = Page.new( 'recent/index' )
73
+ page.timestamp = index_entries.first.created
74
+ page.updated = storage.last_updated( index_entries )
75
+ yield :page => page, :entries => index_entries
76
+ end
77
+
78
+ # Handler for templates with `list/index' prefix. These templates will
79
+ # receive a list of all pages in the Wiki.
80
+ def skel_list_index( path_storage, section_path )
81
+ all_pages = storage.all
82
+ page = Page.new( 'list/index' )
83
+ page.timestamp = all_pages.first.created
84
+ page.updated = storage.last_updated( all_pages )
85
+ yield :page => page, :entries => all_pages, :no_load => true
86
+ end
87
+
88
+ def abs_link( word )
89
+ output_entry_map[word] && output_entry_map[word][:page].link
90
+ end
91
+
92
+ def wiki_page( src )
93
+ src.gsub( /\b([A-Z][a-z]+[A-Z][\w\/]+)\b/ ) { wiki_link( $1 ) }
94
+ end
95
+
96
+ def wiki_link( word )
97
+ abs_link = output_entry_map[word]
98
+ if abs_link
99
+ "<a class=\"existingWikiWord\" href=\"#{ expand_path( abs_link[:page].link ) }\">#{ Hobix::BixWik::wiki_word word }</a>"
100
+ else
101
+ "<span class=\"newWikiWord\">#{ Hobix::BixWik::wiki_word word }<a href=\"#{ expand_path( "control/edit/#{ word }" ) }\">?</a></span>"
102
+ end
103
+ end
104
+
105
+ def self.wiki_word( id )
106
+ Hobix::BixWik::QUICK_MENU[ id ].to_a.first || id.gsub( /^\w|_\w|[A-Z]/ ) { |up| " #{up[-1, 1].upcase}" }.strip
107
+ end
108
+
109
+ require 'redcloth'
110
+ class WikiRedCloth < RedCloth
111
+ end
112
+
113
+ class IndexEntry < Hobix::IndexEntry
114
+ _ :author
115
+ def title
116
+ Hobix::BixWik::wiki_word( self.id )
117
+ end
118
+ def to_yaml_type
119
+ "!hobix.com,2004/bixwik/indexEntry"
120
+ end
121
+ end
122
+
123
+ class Entry < Hobix::Entry
124
+ def title
125
+ Hobix::BixWik::wiki_word( self.id )
126
+ end
127
+ def to_yaml_type
128
+ "!hobix.com,2004/bixwik/entry"
129
+ end
130
+ def self.text_processor; WikiRedCloth; end
131
+ end
132
+ end
133
+ end
134
+
135
+ YAML::add_domain_type( 'hobix.com,2004', 'bixwik/entry' ) do |type, val|
136
+ Hobix::BixWik::Entry::maker( val )
137
+ end
138
+
139
+ YAML::add_domain_type( 'hobix.com,2004', 'bixwik/indexEntry' ) do |type, val|
140
+ YAML::object_maker( Hobix::BixWik::IndexEntry, val )
141
+ end
142
+
143
+ module Hobix
144
+ module Facets
145
+ class WikiEdit < BaseFacet
146
+ def initialize( weblog, defaults = {} )
147
+ @weblog = weblog
148
+ end
149
+ def get app
150
+ if app.respond_to? :action_uri
151
+ ns, method_id = app.action_uri.split( '/', 2 )
152
+ return false unless ns == "edit"
153
+
154
+ # Display publisher page
155
+ app.content_type = 'text/html'
156
+ app.puts ::ERB.new( erb_src, nil, nil, "_bixwik" ).result( binding )
157
+ return true
158
+ end
159
+ end
160
+ end
161
+ end
162
+
163
+ module Out
164
+ class Quick
165
+ def banner_erb; %{
166
+ <% page_id = page.id %>
167
+ <% page_id = 'HomePage' if page.id == 'index' %>
168
+ <% page_name = Hobix::BixWik::wiki_word( page_id ) %>
169
+ <div id="banner">
170
+ <% if page_id == "HomePage" %>
171
+ <h1 id="title"><%= weblog.title %></h1>
172
+ <% if weblog.tagline %><div id="tagline"><%= weblog.tagline %></div><% end %>
173
+ <% else %>
174
+ <div id="title"><%= weblog.title %></div>
175
+ <h1 id="pageName"><%= page_name %></h1>
176
+ <% end %>
177
+ <form id="navigationForm" class="navigation" action="<%= weblog.expand_path( 'search' ) %>" action="get" style="font-size: 10px">
178
+ <% Hobix::BixWik::QUICK_MENU.each do |menu_link, attr| %>
179
+ <% if page_id == menu_link %>
180
+ <%= attr[0] %>
181
+ <% else %>
182
+ <a href="<%= weblog.abs_link( menu_link ) %>" title="<% if attr[1] %>[<%= attr[1] %>] <% end %><%= attr[2] %>"
183
+ accesskey="<%= attr[1] %>"><%= attr[0] %></a>
184
+ <% end %> |
185
+ <% end %>
186
+ <input type="text" id="searchField" name="query" style="font-size: 10px" value="Search" onClick="this.value == 'Search' ? this.value = '' : true">
187
+ </form>
188
+ </div> }
189
+ end
190
+ def entry_title_erb; end
191
+ def entry_content_erb
192
+ %{ <div class="entryContent"><%= weblog.wiki_page( entry.content.to_html ) %></div> }
193
+ end
194
+ def sidebar_erb; nil; end
195
+ def entry_footer_erb; %{
196
+ Revision from <%= ( entry.modified || entry.created ).strftime( "%d %B %Y at %H:%M" ) %> by <%= weblog.wiki_link( "authors/" + entry.author ) %> }
197
+ end
198
+ end
199
+ end
200
+ end