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,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,81 @@
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 => true, :edit_as => :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
+ # Adds support for enumeration.
63
+ def each
64
+ @links.each { |title, url| yield title, url }
65
+ end
66
+
67
+ # LinkLists currently output as YAML type family
68
+ # !hobix.com,2004/linklist.
69
+ yaml_type "tag:hobix.com,2004:linklist"
70
+ end
71
+ end
72
+
73
+ YAML::add_domain_type( 'hobix.com,2004', 'linklist' ) do |type, val|
74
+ ['tagline', 'summary'].each do |f|
75
+ val[f] = RedCloth.new( val[f].to_s ) if val[f]
76
+ end
77
+ if val['links'].class == ::Array
78
+ val['links'] = YAML::transfer( 'omap', val['links'] )
79
+ end
80
+ YAML::object_maker( Hobix::LinkList, val )
81
+ end
@@ -0,0 +1,101 @@
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 'erb'
19
+ require 'uri'
20
+ require 'cgi'
21
+
22
+ module Hobix
23
+ module Out
24
+ module XmlQuick
25
+ def x( title, txt, attrs = nil )
26
+ e = REXML::Element.new title
27
+ e.attributes['xml:space'] = "preserve"
28
+ # self-quote to work around REXML quoting issues with HTML entities
29
+ REXML::Text.new ::ERB::Util.h( txt ), true, e, true if txt
30
+ attrs.each { |a,b| e.attributes[a] = b } if attrs
31
+ self << e
32
+ end
33
+ end
34
+ class Atom < Hobix::BaseOutput
35
+ def initialize( weblog )
36
+ @path = weblog.skel_path
37
+ end
38
+ def extension
39
+ "atom"
40
+ end
41
+ def load( file_name, vars )
42
+ rssdoc = REXML::Document.new( <<EOXML )
43
+ <feed
44
+ xmlns="http://www.w3.org/2005/Atom"
45
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
46
+ xml:lang="en">
47
+ <title></title>
48
+ <link rel="alternate" type="text/html" href="" />
49
+ <link rel="self" type="application/atom+xml" href="" />
50
+ <updated></updated>
51
+ <subtitle></subtitle>
52
+ <id></id>
53
+ <generator uri="http://hobix.com/" version="#{ Hobix::VERSION }">Hobix</generator>
54
+ <rights></rights>
55
+ </feed>
56
+ EOXML
57
+ uri = vars[:weblog].link
58
+ rssdoc << REXML::XMLDecl.new
59
+ rssdoc.elements['/feed/title'].text = vars[:weblog].title
60
+ alt_uri = vars[:weblog].link.to_s
61
+ REXML::XPath.first(rssdoc, '/atom:feed/atom:link[@rel="alternate"]',
62
+ { 'atom' => 'http://www.w3.org/2005/Atom' }).attributes['href'] = alt_uri
63
+ self_uri = "#{vars[:weblog].link}#{vars[:page].link}"
64
+ REXML::XPath.first(rssdoc, '/atom:feed/atom:link[@rel="self"]',
65
+ { 'atom' => 'http://www.w3.org/2005/Atom' }).attributes['href'] = self_uri
66
+ rssdoc.elements['/feed'].attributes['xml:base'] = self_uri
67
+ rssdoc.elements['/feed/subtitle'].text = vars[:weblog].tagline
68
+ rssdoc.elements['/feed/updated'].text = vars[:page].updated.strftime( "%Y-%m-%dT%H:%M:%SZ" )
69
+ rssdoc.elements['/feed/id'].text = "tag:#{ uri.host },#{ Time.now.year }:blog#{ uri.path }"
70
+ rssdoc.elements['/feed/rights'].text = vars[:weblog].copyright || "None"
71
+ ( vars[:entries] || [vars[:entry]] ).each do |e|
72
+ ele = REXML::Element.new 'entry'
73
+ ele.extend XmlQuick
74
+ ele.attributes['xml:base'] = e.link
75
+ ele.x( 'title', e.title )
76
+ ele.x( 'link', nil, {'rel' => 'alternate', 'type' => 'text/html', 'href' => e.link } )
77
+ ele.x( 'id', "tag:#{ uri.host },#{ Time.now.year }:blog#{ CGI.escape(uri.path) }entry#{ CGI.escape( "/#{ e.id }" ) }" )
78
+ ele.x( 'published', e.created.strftime( "%Y-%m-%dT%H:%M:%SZ" ) )
79
+ ele.x( 'updated', (e.modified || e.created).strftime( "%Y-%m-%dT%H:%M:%SZ" ) )
80
+ ele.x( 'dc:subject', e.section_id )
81
+ e.tags.find_all {|t| not (t.nil? or t == '') }.each do |t|
82
+ ele.x( 'category', '', { 'term' => t, 'scheme' => "http://hobix.com/tags" } )
83
+ end
84
+ ele.x( 'summary',
85
+ e.summary.to_html.gsub( /img src="\//, "img src=\"#{ vars[:weblog].link }/" ),
86
+ {'type' => 'html', 'mode' => 'escaped'} ) if e.respond_to? :summary and e.summary and !e.summary.empty?
87
+ author = vars[:weblog].authors[e.author]
88
+ ele_auth = REXML::Element.new 'author'
89
+ ele_auth.extend XmlQuick
90
+ ele_auth.x( 'name', author['name'] )
91
+ ele_auth.x( 'uri', author['url'] ) if author['url']
92
+ ele_auth.x( 'email', author['email'] ) if author['email']
93
+ ele << ele_auth
94
+ ele.x( 'content', e.content.to_html, {'type' => 'html'} )
95
+ rssdoc.elements['/feed'].add ele
96
+ end
97
+ rssdoc.to_s
98
+ end
99
+ end
100
+ end
101
+ 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