hobix 0.4

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 (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