hobix 0.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +7 -0
  2. data/bin/hobix +90 -0
  3. data/lib/hobix/api.rb +91 -0
  4. data/lib/hobix/article.rb +22 -0
  5. data/lib/hobix/base.rb +477 -0
  6. data/lib/hobix/bixwik.rb +200 -0
  7. data/lib/hobix/commandline.rb +661 -0
  8. data/lib/hobix/comments.rb +99 -0
  9. data/lib/hobix/config.rb +39 -0
  10. data/lib/hobix/datamarsh.rb +110 -0
  11. data/lib/hobix/entry.rb +83 -0
  12. data/lib/hobix/facets/comments.rb +74 -0
  13. data/lib/hobix/facets/publisher.rb +314 -0
  14. data/lib/hobix/facets/trackbacks.rb +80 -0
  15. data/lib/hobix/linklist.rb +76 -0
  16. data/lib/hobix/out/atom.rb +92 -0
  17. data/lib/hobix/out/erb.rb +64 -0
  18. data/lib/hobix/out/okaynews.rb +55 -0
  19. data/lib/hobix/out/quick.rb +312 -0
  20. data/lib/hobix/out/rdf.rb +97 -0
  21. data/lib/hobix/out/redrum.rb +26 -0
  22. data/lib/hobix/out/rss.rb +115 -0
  23. data/lib/hobix/plugin/bloglines.rb +73 -0
  24. data/lib/hobix/plugin/calendar.rb +220 -0
  25. data/lib/hobix/plugin/flickr.rb +110 -0
  26. data/lib/hobix/plugin/recent_comments.rb +82 -0
  27. data/lib/hobix/plugin/sections.rb +91 -0
  28. data/lib/hobix/plugin/tags.rb +60 -0
  29. data/lib/hobix/publish/ping.rb +53 -0
  30. data/lib/hobix/publish/replicate.rb +283 -0
  31. data/lib/hobix/publisher.rb +18 -0
  32. data/lib/hobix/search/dictionary.rb +141 -0
  33. data/lib/hobix/search/porter_stemmer.rb +203 -0
  34. data/lib/hobix/search/simple.rb +209 -0
  35. data/lib/hobix/search/vector.rb +100 -0
  36. data/lib/hobix/storage/filesys.rb +398 -0
  37. data/lib/hobix/trackbacks.rb +94 -0
  38. data/lib/hobix/util/objedit.rb +193 -0
  39. data/lib/hobix/util/patcher.rb +155 -0
  40. data/lib/hobix/webapp/cli.rb +195 -0
  41. data/lib/hobix/webapp/htmlform.rb +107 -0
  42. data/lib/hobix/webapp/message.rb +177 -0
  43. data/lib/hobix/webapp/urigen.rb +141 -0
  44. data/lib/hobix/webapp/webrick-servlet.rb +90 -0
  45. data/lib/hobix/webapp.rb +723 -0
  46. data/lib/hobix/weblog.rb +860 -0
  47. data/lib/hobix.rb +223 -0
  48. metadata +87 -0
@@ -0,0 +1,314 @@
1
+ #
2
+ # = hobix/facets/publisher.rb
3
+ #
4
+ # Hobix command-line weblog system, web-based publishing interface.
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
+
17
+ require 'erb'
18
+ require 'yaml'
19
+
20
+ module Hobix
21
+ module Facets
22
+
23
+ # The Publisher plugin adds a web interface for managing Hobix blogs.
24
+ # Basically, to add the publisher to your site, ensure the plugin
25
+ # is loaded within your hobix.yaml `requires' list:
26
+ #
27
+ # requires:
28
+ # - hobix/facets/publisher
29
+ #
30
+ class Publisher < BaseFacet
31
+ class MissingRequired < Exception; end
32
+
33
+ def initialize( weblog, defaults = {} )
34
+ @weblog = weblog
35
+ end
36
+ def get app
37
+ if app.respond_to? :action_uri
38
+ ns, method_id = app.action_uri.split( '/', 2 )
39
+ return false unless ( ns.nil? or ns == "publisher" )
40
+
41
+ case method_id
42
+ when /\.js$/
43
+ app.content_type = "text/javascript"
44
+ app.puts File.read( File.join( Hobix::SHARE_PATH, "publisher", method_id ) )
45
+ return true
46
+ when /\.css$/
47
+ app.content_type = "text/css"
48
+ app.puts File.read( File.join( Hobix::SHARE_PATH, "publisher", method_id ) )
49
+ return true
50
+ when /\.png$/
51
+ app.content_type = "image/png"
52
+ app.puts File.read( File.join( Hobix::SHARE_PATH, "publisher", method_id ) )
53
+ return true
54
+ end
55
+
56
+ # dispatch the url action
57
+ method_args = (method_id || "config").split( /\// )
58
+ method_id = "config"
59
+ method_args.length.downto(1) do |i|
60
+ if respond_to? "get_#{ method_args[0,i].join( '_' ) }"
61
+ method_id = "get_#{ method_args.slice!(0,i).join( '_' ) }"
62
+ break
63
+ end
64
+ end
65
+ method_args.unshift app
66
+ return false unless respond_to? method_id
67
+ @screen = method( method_id ).call( *method_args )
68
+ return true unless @screen
69
+
70
+ # Display publisher page
71
+ erb_src = File.read( File.join( Hobix::SHARE_PATH, "publisher/index.erb" ) )
72
+ app.content_type = 'text/html'
73
+ app.puts ::ERB.new( erb_src, nil, nil, "_hobixpublish" ).result( binding )
74
+ return true
75
+ end
76
+ end
77
+
78
+ def make_form( form )
79
+ form_erb = %q{
80
+ % current = nil
81
+ <style type="text/css">
82
+ <!--
83
+ ul.edit_as_map, ol.edit_as_omap {
84
+ list-style-image:none;
85
+ list-style-type:none;
86
+ margin-top:5px;
87
+ margin:0px;
88
+ padding:0px;
89
+ margin-left:140px;
90
+ }
91
+ ul.edit_as_map li, ol.edit_as_omap li {
92
+ padding: 3px 0px;
93
+ margin: 0px;
94
+ }
95
+ ol.edit_as_omap li .handle {
96
+ cursor: move;
97
+ }
98
+ -->
99
+ </style>
100
+ <script type="text/javascript" language="javascript">
101
+ // <![CDATA[
102
+ function sortable_to_csv(element) {
103
+ var element = $(element);
104
+ var options = {
105
+ tag: element.sortable.tag,
106
+ only: element.sortable.only,
107
+ name: element.id
108
+ }.extend(arguments[1] || {});
109
+
110
+ var items = $(element).childNodes;
111
+ var queryComponents = new Array();
112
+
113
+ for(var i=0; i<items.length; i++)
114
+ if(items[i].tagName && items[i].tagName==options.tag.toUpperCase() &&
115
+ (!options.only || (Element.Class.has(items[i], options.only))))
116
+ queryComponents.push(items[i].id.replace(element.id+'_',''));
117
+
118
+ return queryComponents;
119
+ }
120
+ // ]]>
121
+ </script>
122
+ <h2><%= form[:full_title] %></h2>
123
+ <form id="publisher_form" method="post" enctype="multipart/form-data">
124
+ <p><%= RedCloth.new( form[:intro] ).to_html %></p>
125
+ % form[:object].class.properties.each do |name, opts|
126
+ % next unless opts and opts[:edit_as]
127
+ % if sect = form[:object].class.prop_sections.detect { |k,v| v[:__sect] == current }
128
+ <fieldset>
129
+ <legend><%= sect[0] %></legend>
130
+ % end
131
+ % title = name.to_s.gsub( '_', ' ' ).capitalize
132
+ % val = form[:object].instance_variable_get( "@" + name.to_s )
133
+ % if name == :notes
134
+ <div class="notes">
135
+ <h4><%= title %></h4>
136
+ </div>
137
+ % else
138
+ <div class="<%= opts[:req] ? 'required' : 'optional' %>">
139
+ <label for="<%= name %>"><%= title %>:</label>
140
+ % case opts[:edit_as]
141
+ % when :password
142
+ <input type="password" name="<%= name %>" id="<%= name %>"
143
+ class="inputPassword" size="10" tabindex=""
144
+ maxlength="25" value="<%= val %>" />
145
+ % when :checkbox
146
+ <input type="checkbox" name="<%= name %>" id="<%= name %>"
147
+ class="inputCheckbox" tabindex="" value="1" />
148
+ % when :textarea
149
+ <textarea name="<%= name %>" id="<%= name %>" rows="<%= opts[:edit_rows] || 4 %>" cols="<%= opts[:edit_cols] || 36 %>" tabindex=""><%= val %></textarea>
150
+ % when :omap
151
+ <ol id="<%= name %>sort" class="edit_as_omap">
152
+ % val.each do |vkey, vval|
153
+ % vkey = vkey.keys.first if vkey.is_a? Hash
154
+ <li id="<%= name %>sort_<%= vkey %>" class="sorty" name="<%= vkey %>">
155
+ <span class="handle">&raquo;</span>
156
+ <%= vkey %>
157
+ <a href="<%= form[:app].absuri( :path_info => "/publisher/#{ @title }/edit/#{ name }/#{ vkey }" ) %>">edit</a>
158
+ <a href="<%= form[:app].absuri( :path_info => "/publisher/#{ @title }/del/#{ name }/#{ vkey }" ) %>">remove</a>
159
+ </li>
160
+ % end if val
161
+ <li class="new_item">
162
+ <span>&raquo;</span>
163
+ <input type="text" name="<%= name %>_new" id="<%= name %>_new" style="width:150px"
164
+ class="inputText" tabindex="" maxlength="255" value="" />
165
+ <a href="<%= form[:app].absuri( :path_info => "/publisher/#{ @title }/add/#{ name }" ) %>">add</a>
166
+ </li>
167
+ </ul>
168
+ <input type="hidden" name="<%= name %>" id="<%= name %>" value="" />
169
+ <script type="text/javascript" language="javascript">
170
+ // <![CDATA[
171
+ Sortable.create("<%= name %>sort", {handle:'handle', only: 'sorty', onUpdate:function () {
172
+ $("<%= name %>").value = sortable_to_csv(this.element).join(' ');
173
+ }});
174
+ // ]]>
175
+ </script>
176
+ % when :map
177
+ <ul id="<%= name %>_order" class="edit_as_map">
178
+ % val.each do |vkey, vval|
179
+ <li id="<%= name %>_<%= vkey %>">
180
+ <%= vkey %>
181
+ <a href="<%= form[:app].absuri( :path_info => "/publisher/#{ @title }/edit/#{ name }/#{ vkey }" ) %>">edit</a>
182
+ <a href="<%= form[:app].absuri( :path_info => "/publisher/#{ @title }/del/#{ name }/#{ vkey }" ) %>">remove</a>
183
+ </li>
184
+ % end if val
185
+ <li id="new_item">
186
+ <input type="text" name="<%= name %>_new" id="<%= name %>_new" style="width:150px"
187
+ class="inputText" tabindex="" maxlength="255" value="" />
188
+ <a href="<%= form[:app].absuri( :path_info => "/publisher/#{ @title }/add/#{ name }" ) %>">add</a>
189
+ </li>
190
+ </ul>
191
+ % else
192
+ <input type="text" name="<%= name %>" id="<%= name %>"
193
+ class="inputText" size="<%= opts[:edit_size] || 24 %>" tabindex=""
194
+ maxlength="255" value="<%= val %>" />
195
+ % end
196
+
197
+ % if opts.include? :notes
198
+ <small><%= fopts['notes'] %></small>
199
+ % end
200
+ % if form[:object].respond_to? "default_#{ name }"
201
+ <small><em>Defaults to <strong><%= form[:object].method( "default_#{ name }" ).call %></strong>.</em></small>
202
+ % end
203
+ </div>
204
+ % end
205
+ % current = name
206
+ % if form[:object].class.prop_sections.detect { |k,v| v[:__sect] == current }
207
+ </fieldset>
208
+ % end
209
+ % end
210
+ </fieldset>
211
+ <fieldset>
212
+ <div class="submit">
213
+ <div>
214
+
215
+ <input type="submit" class="inputSubmit" tabindex="" value="Submit &raquo;" />
216
+ <input type="submit" class="inputSubmit" tabindex="" value="Cancel" />
217
+ </div>
218
+ </div>
219
+ </fieldset>
220
+ </form>
221
+ }
222
+ return ::ERB.new( form_erb, 0, "%<>", "_hobixpublishFORM" ).result( binding )
223
+ end
224
+
225
+ def save_form( obj, app )
226
+ obj = obj.dup
227
+ missing = []
228
+ obj.class.properties.each do |name, opts|
229
+ next unless opts
230
+ next unless app._POST.has_key? name.to_s
231
+ val = app._POST[name.to_s]
232
+ val = nil if val and val.empty?
233
+ missing << name if val.nil? and opts[:req]
234
+
235
+ case opts[:edit_as]
236
+ when :omap
237
+ omap = obj.instance_variable_get( "@#{name}" )
238
+ sorted = val.to_s.split(/\s+/)
239
+ sorted.each { |item| omap << [item] unless omap.assoc(item) }
240
+ omap.sort_by { |item, val| sorted.index(item) || sorted.length }
241
+ when :map
242
+ map = obj.instance_variable_get( "@#{name}" )
243
+ val.to_s.split(/\s+/).each do |item|
244
+ map[item] ||= nil
245
+ end
246
+ else
247
+ obj.instance_variable_set( "@#{name}", val )
248
+ end
249
+ end
250
+ [obj, missing]
251
+ end
252
+
253
+ def red( str ); RedCloth.new( str ).to_html; end
254
+
255
+ def show_weblog_form( weblog, app )
256
+ make_form :app => app,
257
+ :full_title => 'Configure Your Weblahhg',
258
+ :intro => %q{
259
+ Generally speaking, you shouldn't have to alter many of your weblog settings.
260
+ Most of the below are available for those who really want to customize.
261
+
262
+ **Bold** fields are required.
263
+ }.gsub( /^ +/, '' ),
264
+ :object => weblog
265
+ end
266
+
267
+ def get_config( app )
268
+ @title = 'config'
269
+ case app.request_method
270
+ when "GET"
271
+ show_weblog_form( @weblog, app )
272
+ when "POST"
273
+ weblog, missing = save_form( @weblog, app )
274
+ # if missing.empty?
275
+ # weblog.save( weblog.hobix_yaml + ".edit" )
276
+ # red %{
277
+ # *Your configuraton has been saved.*
278
+ #
279
+ # Please note that this development version of Hobix isn't
280
+ # yet equipped to deal with re-sorting of the requires. I'm not that great with Prototype
281
+ # yet and I also want to write some code to sandbox the configuration, to check that the
282
+ # requires will load right before saving it.
283
+ # }
284
+ # else
285
+ # show_weblog_form( weblog, app )
286
+ # end
287
+ [weblog, missing].to_yaml
288
+ end
289
+ end
290
+
291
+ def get_entries( app, *entry_id )
292
+ @title = 'entries'
293
+ unless entry_id.empty?
294
+ e = @weblog.storage.load_entry( entry_id.join( '/' ) )
295
+ else
296
+ e = Hobix::Entry.new
297
+ end
298
+ case app.request_method
299
+ when "GET"
300
+ make_form :app => app,
301
+ :full_title => 'Post an Entry',
302
+ :intro => %q{
303
+ **Bold** fields are required.
304
+ }.gsub( /^ +/, '' ),
305
+ :object => e
306
+ when "POST"
307
+ e, missing = save_form( e, app )
308
+ e.inspect
309
+ end
310
+ end
311
+ end
312
+
313
+ end
314
+ end
@@ -0,0 +1,80 @@
1
+ #
2
+ # = hobix/facets/trackbacks.rb
3
+ #
4
+ # Hobix command-line weblog system, support for trackbacks.
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
+
17
+ require 'hobix/entry'
18
+
19
+ module Hobix
20
+ module Facets
21
+
22
+ # The Trackbacks plugin adds support for the TrackBack specification
23
+ # (http://www.sixapart.com/pronet/docs/trackback_spec).
24
+ #
25
+ # Add this require to your hobix.yaml:
26
+ #
27
+ # requires:
28
+ # - hobix/trackbacks
29
+ #
30
+ class Trackbacks < BaseFacet
31
+ def self.trackback_fields; ['url','title', 'excerpt', 'blog_name']; end
32
+ def self.trackback_class; Hobix::Trackback; end
33
+
34
+ def initialize( weblog, defaults = {} )
35
+ @weblog = weblog
36
+ end
37
+ def get app
38
+ if app.respond_to? :action_uri
39
+ action, entry_id = app.action_uri.split( '/', 2 )
40
+ case action
41
+ when "trackback"
42
+ # Validate
43
+ on_entry = @weblog.storage.load_entry( entry_id ) rescue nil
44
+ return send_trackback_response( app, false, 'No such entry' ) if on_entry.nil?
45
+
46
+ # Create a trackback comment
47
+ trackback = Trackbacks.trackback_class.new do |t|
48
+ Trackbacks.trackback_fields.each do |tf|
49
+ t.method( "#{tf}=" ).call( app._POST[tf].to_s )
50
+ end
51
+ return send_trackback_response( app, false, 'Missing URL field' ) if (t.url || '').empty?
52
+ t.created = Time.now
53
+ t.ipaddress = app.remote_addr
54
+ end
55
+
56
+ # Save the trackback, upgen
57
+ @weblog.storage.append_to_attachment( entry_id, 'trackbacks', trackback )
58
+ @weblog.regenerate :update
59
+
60
+ # Send response
61
+ send_trackback_response( app, true )
62
+ return true
63
+ end
64
+ end
65
+ end
66
+
67
+ def send_trackback_response(app, ok = true, message = nil)
68
+ app.content_type = 'text/xml'
69
+ app.puts %{<?xml version="1.0" encoding="UTF-8"?>
70
+ <response>
71
+ <error>%d</error>
72
+ %s
73
+ </response>
74
+ } % [ok ? 0 : 1, message ? %{<message>#{message}</message>} : '']
75
+ true
76
+ end
77
+ end
78
+
79
+ end
80
+ end
@@ -0,0 +1,76 @@
1
+ #
2
+ # = hobix/linklist.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/entry'
17
+ require 'redcloth'
18
+ require 'yaml'
19
+
20
+ # The LinkList class is an entry type for storing links. It's
21
+ # also a good example of how to subclass the Entry class so you
22
+ # can store your own kinds of entries.
23
+ #
24
+ # == Properties
25
+ #
26
+ # The LinkList responds to many of the same properties listed
27
+ # in the +Hobix::Entry+ class. The primary difference is that,
28
+ # instead of having a +content+ property, there is a +links+
29
+ # property.
30
+ #
31
+ # links:: Internally, this class stores a +YAML::Omap+, an
32
+ # Array of pairs. The links are kept in the order
33
+ # shown in the YAML file. They consist of a link
34
+ # title, paired with a URL.
35
+ #
36
+ # == Sample LinkList
37
+ #
38
+ # --- %YAML:1.0 !hobix.com,2004/linklist
39
+ # title: Hobix Links
40
+ # author: why
41
+ # created: 2004-05-30 18:53:00 -06:00
42
+ # links:
43
+ # - Hobix: http://hobix.com/
44
+ # - Learn Hobix: http://hobix.com/learn/
45
+ # - Textile Reference: http://hobix.com/textile/
46
+ #
47
+ module Hobix
48
+ class LinkList < BaseEntry
49
+
50
+ _ :links, [:req, :textarea]
51
+
52
+ # Converts the link list into a RedCloth string for display
53
+ # in templates.
54
+ def content
55
+ RedCloth.new(
56
+ @links.collect do |title, url|
57
+ "* \"#{ title }\":#{ url }"
58
+ end.join( "\n" )
59
+ )
60
+ end
61
+
62
+ # LinkLists currently output as YAML type family
63
+ # !hobix.com,2004/linklist.
64
+ yaml_type "tag:hobix.com,2004:linklist"
65
+ end
66
+ end
67
+
68
+ YAML::add_domain_type( 'hobix.com,2004', 'linklist' ) do |type, val|
69
+ ['tagline', 'summary'].each do |f|
70
+ val[f] = RedCloth.new( val[f].to_s ) if val[f]
71
+ end
72
+ if val['links'].class == ::Array
73
+ val['links'] = YAML::transfer( 'omap', val['links'] )
74
+ end
75
+ YAML::object_maker( Hobix::LinkList, val )
76
+ end
@@ -0,0 +1,92 @@
1
+ #
2
+ # = hobix/out/atom.rb
3
+ #
4
+ # Atom output for Hobix.
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/base'
17
+ require 'rexml/document'
18
+ require 'uri'
19
+ require 'cgi'
20
+
21
+ module Hobix
22
+ module Out
23
+ module XmlQuick
24
+ def x( title, txt, attrs = nil )
25
+ e = REXML::Element.new title
26
+ e.text = txt if txt
27
+ attrs.each { |a,b| e.attributes[a] = b } if attrs
28
+ self << e
29
+ end
30
+ end
31
+ class Atom < Hobix::BaseOutput
32
+ def initialize( weblog )
33
+ @path = weblog.skel_path
34
+ end
35
+ def extension
36
+ "atom"
37
+ end
38
+ def load( file_name, vars )
39
+ rssdoc = REXML::Document.new( <<EOXML )
40
+ <feed version="0.3"
41
+ xmlns="http://purl.org/atom/ns#"
42
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
43
+ xml:lang="en">
44
+ <title></title>
45
+ <link rel="alternate" type="text/html" href="" />
46
+ <modified></modified>
47
+ <tagline></tagline>
48
+ <id></id>
49
+ <generator url="http://hobix.com/" version="#{ Hobix::VERSION }">Hobix</generator>
50
+ <copyright></copyright>
51
+ </feed>
52
+ EOXML
53
+ uri = vars[:weblog].link
54
+ rssdoc << REXML::XMLDecl.new
55
+ rssdoc.elements['/feed/title'].text = vars[:weblog].title
56
+ rssdoc.elements['/feed/link'].attributes['href'] = vars[:weblog].link.to_s
57
+ rssdoc.elements['/feed/tagline'].text = vars[:weblog].tagline
58
+ rssdoc.elements['/feed/modified'].text = vars[:page].updated.strftime( "%Y-%m-%dT%H:%M:%SZ" )
59
+ rssdoc.elements['/feed/id'].text = "tag:#{ uri.host },#{ Time.now.year }:blog#{ uri.path }"
60
+ rssdoc.elements['/feed/copyright'].text = vars[:weblog].copyright || "None"
61
+ ( vars[:entries] || [vars[:entry]] ).each do |e|
62
+ ele = REXML::Element.new 'entry'
63
+ ele.extend XmlQuick
64
+ ele.x( 'title', e.title )
65
+ ele.x( 'link', nil, {'rel' => 'alternate', 'type' => 'text/html', 'href' => e.link } )
66
+ ele.x( 'id', "tag:#{ uri.host },#{ Time.now.year }:blog#{ CGI.escape(uri.path) }entry#{ CGI.escape( "/#{ e.id }" ) }" )
67
+ ele.x( 'issued', e.created.strftime( "%Y-%m-%dT%H:%M:%SZ" ) )
68
+ ele.x( 'modified', e.modified.strftime( "%Y-%m-%dT%H:%M:%SZ" ) )
69
+ ele.x( 'dc:subject', e.section_id )
70
+ e.tags.each do |t|
71
+ ele.x( 'dc:subject', t )
72
+ end
73
+ ele.x( 'summary',
74
+ e.summary.to_html.gsub( /img src="\//, "img src=\"#{ vars[:weblog].link }/" ),
75
+ {'type' => 'text/html', 'mode' => 'escaped'} ) if e.respond_to? :summary and e.summary
76
+ author = vars[:weblog].authors[e.author]
77
+ ele_auth = REXML::Element.new 'author'
78
+ ele_auth.extend XmlQuick
79
+ ele_auth.x( 'name', author['name'] )
80
+ ele_auth.x( 'url', author['url'] ) if author['url']
81
+ ele_auth.x( 'email', author['email'] ) if author['email']
82
+ ele << ele_auth
83
+ ele.x( 'content',
84
+ e.content.to_html.gsub( /img src="\//, "img src=\"#{ vars[:weblog].link }/" ),
85
+ {'type' => 'text/html', 'mode' => 'escaped'} )
86
+ rssdoc.elements['/feed'].add ele
87
+ end
88
+ rssdoc.to_s
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,64 @@
1
+ #
2
+ # = hobix/out/erb.rb
3
+ #
4
+ # Hobix processing of ERB templates.
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/base'
17
+ require 'erb'
18
+
19
+ module Hobix
20
+ module Out
21
+ class ERBError < StandardError; end
22
+ class ERB < Hobix::BaseOutput
23
+ def initialize( weblog )
24
+ @path = weblog.skel_path
25
+ end
26
+ def extension
27
+ "erb"
28
+ end
29
+ def load( file_name, vars )
30
+ @bind = binding
31
+ vars.each do |k, v|
32
+ k.untaint
33
+ k_inspect = k.inspect.untaint
34
+ eval( "#{ k } = vars[#{ k_inspect }]", @bind )
35
+ end
36
+ @relpath = File.dirname( file_name )
37
+ @load_erb = import_erb( file_name )
38
+ begin
39
+ @load_erb.result( @bind )
40
+ rescue Exception => e
41
+ raise ERBError, "Error `#{ e.message }' in erb #{ file_name }."
42
+ end
43
+ end
44
+ def expand( fname )
45
+ if fname =~ /^\//
46
+ File.join( @path, fname )
47
+ else
48
+ File.join( @relpath, fname )
49
+ end
50
+ end
51
+ def import( fname, bindto = @bind )
52
+ import_erb( expand( fname ) ).result( bindto )
53
+ end
54
+ def import_erb(fname)
55
+ File.open(fname) do |fp|
56
+ src = fp.read.gsub( /<\+\s*([\w\.\/\\\-]+)\s*\+>/ ) do
57
+ File.read( expand( $1 ) )
58
+ end
59
+ ::ERB.new( src, nil, nil, "_hobixout#{ rand( 9999999 ) }" )
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,55 @@
1
+ #
2
+ # = hobix/out/okaynews.rb
3
+ #
4
+ # YAML !okay/news output for Hobix.
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/base'
17
+
18
+ module Hobix
19
+ class Weblog
20
+ def to_okaynews( entries )
21
+ YAML::quick_emit( self.object_id ) do |out|
22
+ out.map( "!okay/news/^feed" ) do |map|
23
+ ['@title', '@tagline', '@link', '@period',
24
+ '@created', '@issued', '@modified',
25
+ '@authors', '@contributors'
26
+ ].each do |m|
27
+ map.add( m[1..-1], instance_variable_get( m ) )
28
+ end
29
+ entries = entries.collect do |e|
30
+ e = e.dup
31
+ e.author = @authors[e.author]
32
+ def e.to_yaml_type
33
+ "!^entry"
34
+ end
35
+ e
36
+ end
37
+ map.add( 'entries', entries )
38
+ end
39
+ end
40
+ end
41
+ end
42
+ module Out
43
+ class OkayNews < Hobix::BaseOutput
44
+ def initialize( weblog )
45
+ @path = weblog.skel_path
46
+ end
47
+ def extension
48
+ "okaynews"
49
+ end
50
+ def load( file_name, vars )
51
+ vars[:weblog].to_okaynews( vars[:entries] || [vars[:entry]] )
52
+ end
53
+ end
54
+ end
55
+ end