hobix 0.4 → 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 (129) hide show
  1. data/COPYING +18 -0
  2. data/README +18 -0
  3. data/Rakefile +96 -0
  4. data/bin/hobix +7 -3
  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/base.rb +6 -3
  76. data/lib/hobix/bixwik.rb +12 -12
  77. data/lib/hobix/commandline.rb +18 -2
  78. data/lib/hobix/comments.rb +4 -5
  79. data/lib/hobix/entry.rb +2 -1
  80. data/lib/hobix/facets/comments.rb +31 -6
  81. data/lib/hobix/linklist.rb +6 -1
  82. data/lib/hobix/out/atom.rb +29 -20
  83. data/lib/hobix/out/quick.rb +8 -6
  84. data/lib/hobix/out/rss.rb +16 -3
  85. data/lib/hobix/plugin/akismet.rb +196 -0
  86. data/lib/hobix/plugin/calendar.rb +6 -14
  87. data/lib/hobix/plugin/recent_comments.rb +4 -2
  88. data/lib/hobix/plugin/tags.rb +1 -1
  89. data/lib/hobix/storage/filesys.rb +41 -31
  90. data/lib/hobix/trackbacks.rb +1 -2
  91. data/lib/hobix/weblog.rb +73 -40
  92. data/lib/hobix.rb +9 -2
  93. data/share/default-blog/hobix.yaml +16 -0
  94. data/share/default-blog/htdocs/site.css +174 -0
  95. data/share/default-blog/skel/entry.html.quick +0 -0
  96. data/share/default-blog/skel/index.atom.atom +0 -0
  97. data/share/default-blog/skel/index.html.quick-summary +0 -0
  98. data/share/default-blog/skel/index.xml.rss +0 -0
  99. data/share/default-blog/skel/index.yaml.okaynews +0 -0
  100. data/share/default-blog/skel/monthly.html.quick-archive +0 -0
  101. data/share/default-blog/skel/section.html.quick-archive +0 -0
  102. data/share/default-blog/skel/yearly.html.quick-archive +0 -0
  103. data/share/default-blog-modes.yaml +7 -0
  104. data/share/default-blog.apache-cgi.patch +8 -0
  105. data/share/default-blog.apache-ssi.patch +38 -0
  106. data/share/default-blog.apache2-ssi.patch +3 -0
  107. data/share/default-blog.cgi.patch +8 -0
  108. data/share/default-blog.comments.patch +5 -0
  109. data/share/default-blog.prototype.patch +766 -0
  110. data/share/default-blog.publisher.patch +5 -0
  111. data/share/default-blog.wiki.patch +29 -0
  112. data/share/publisher/css/control.css +90 -0
  113. data/share/publisher/css/form.css +238 -0
  114. data/share/publisher/css/form.import.css +72 -0
  115. data/share/publisher/css/main-menu.css +134 -0
  116. data/share/publisher/i/hobix-emblazen-1.png +0 -0
  117. data/share/publisher/i/hobix-emblazen-2.png +0 -0
  118. data/share/publisher/i/hobix-emblazen-3.png +0 -0
  119. data/share/publisher/i/hobix-emblazen-4.png +0 -0
  120. data/share/publisher/i/hobix-emblazen-5.png +0 -0
  121. data/share/publisher/i/hobix-emblazen-6.png +0 -0
  122. data/share/publisher/i/hobix-emblazen-7.png +0 -0
  123. data/share/publisher/index.erb +66 -0
  124. data/share/publisher/js/controls.js +261 -0
  125. data/share/publisher/js/dragdrop.js +476 -0
  126. data/share/publisher/js/effects.js +570 -0
  127. data/share/publisher/js/prototype.js +1011 -0
  128. metadata +196 -53
  129. checksums.yaml +0 -7
@@ -0,0 +1,208 @@
1
+
2
+ body {
3
+ font-family: Verdana,Arial,Helvetica,sans-serif;
4
+ font-size: 90%;
5
+ margin: 0;
6
+ margin-left: 40px;
7
+ padding: 0;
8
+ background: white;
9
+ }
10
+
11
+ h1,h2,h3,h4 { margin: 0; color: #efefef; background: transparent; }
12
+ h1 { font-size: 150%; }
13
+ h2,h3,h4 { margin-top: 1em; }
14
+
15
+ a { background: #eef; color: #039; text-decoration: none; }
16
+ a:hover { background: #039; color: #eef; }
17
+
18
+ /* Override the base stylesheet's Anchor inside a table cell */
19
+ td > a {
20
+ background: transparent;
21
+ color: #039;
22
+ text-decoration: none;
23
+ }
24
+
25
+ /* and inside a section title */
26
+ .section-title > a {
27
+ background: transparent;
28
+ color: #eee;
29
+ text-decoration: none;
30
+ }
31
+
32
+ /* === Structural elements =================================== */
33
+
34
+ div#index {
35
+ margin: 0;
36
+ margin-left: -40px;
37
+ padding: 0;
38
+ font-size: 90%;
39
+ }
40
+
41
+
42
+ div#index a {
43
+ margin-left: 0.7em;
44
+ }
45
+
46
+ div#index .section-bar {
47
+ margin-left: 0px;
48
+ padding-left: 0.7em;
49
+ background: #ccc;
50
+ font-size: small;
51
+ }
52
+
53
+
54
+ div#classHeader, div#fileHeader {
55
+ width: auto;
56
+ color: white;
57
+ padding: 0.5em 1.5em 0.5em 1.5em;
58
+ margin: 0;
59
+ margin-left: -40px;
60
+ border-bottom: 3px solid #006;
61
+ }
62
+
63
+ div#classHeader a, div#fileHeader a {
64
+ background: inherit;
65
+ color: white;
66
+ }
67
+
68
+ div#classHeader td, div#fileHeader td {
69
+ background: inherit;
70
+ color: white;
71
+ }
72
+
73
+
74
+ div#fileHeader {
75
+ background: #057;
76
+ }
77
+
78
+ div#classHeader {
79
+ background: #048;
80
+ }
81
+
82
+
83
+ .class-name-in-header {
84
+ font-size: 180%;
85
+ font-weight: bold;
86
+ }
87
+
88
+
89
+ div#bodyContent {
90
+ padding: 0 1.5em 0 1.5em;
91
+ }
92
+
93
+ div#description {
94
+ padding: 0.5em 1.5em;
95
+ background: #efefef;
96
+ border: 1px dotted #999;
97
+ }
98
+
99
+ div#description h1,h2,h3,h4,h5,h6 {
100
+ color: #125;;
101
+ background: transparent;
102
+ }
103
+
104
+ div#validator-badges {
105
+ text-align: center;
106
+ }
107
+ div#validator-badges img { border: 0; }
108
+
109
+ div#copyright {
110
+ color: #333;
111
+ background: #efefef;
112
+ font: 0.75em sans-serif;
113
+ margin-top: 5em;
114
+ margin-bottom: 0;
115
+ padding: 0.5em 2em;
116
+ }
117
+
118
+
119
+ /* === Classes =================================== */
120
+
121
+ table.header-table {
122
+ color: white;
123
+ font-size: small;
124
+ }
125
+
126
+ .type-note {
127
+ font-size: small;
128
+ color: #DEDEDE;
129
+ }
130
+
131
+ .xxsection-bar {
132
+ background: #eee;
133
+ color: #333;
134
+ padding: 3px;
135
+ }
136
+
137
+ .section-bar {
138
+ color: #333;
139
+ border-bottom: 1px solid #999;
140
+ margin-left: -20px;
141
+ }
142
+
143
+
144
+ .section-title {
145
+ background: #79a;
146
+ color: #eee;
147
+ padding: 3px;
148
+ margin-top: 2em;
149
+ margin-left: -30px;
150
+ border: 1px solid #999;
151
+ }
152
+
153
+ .top-aligned-row { vertical-align: top }
154
+ .bottom-aligned-row { vertical-align: bottom }
155
+
156
+ /* --- Context section classes ----------------------- */
157
+
158
+ .context-row { }
159
+ .context-item-name { font-family: monospace; font-weight: bold; color: black; }
160
+ .context-item-value { font-size: small; color: #448; }
161
+ .context-item-desc { color: #333; padding-left: 2em; }
162
+
163
+ /* --- Method classes -------------------------- */
164
+ .method-detail {
165
+ background: #efefef;
166
+ padding: 0;
167
+ margin-top: 0.5em;
168
+ margin-bottom: 1em;
169
+ border: 1px dotted #ccc;
170
+ }
171
+ .method-heading {
172
+ color: black;
173
+ background: #ccc;
174
+ border-bottom: 1px solid #666;
175
+ padding: 0.2em 0.5em 0 0.5em;
176
+ }
177
+ .method-signature { color: black; background: inherit; }
178
+ .method-name { font-weight: bold; }
179
+ .method-args { font-style: italic; }
180
+ .method-description { padding: 0 0.5em 0 0.5em; }
181
+
182
+ /* --- Source code sections -------------------- */
183
+
184
+ a.source-toggle { font-size: 90%; }
185
+ div.method-source-code {
186
+ background: #262626;
187
+ color: #ffdead;
188
+ margin: 1em;
189
+ padding: 0.5em;
190
+ border: 1px dashed #999;
191
+ overflow: hidden;
192
+ }
193
+
194
+ div.method-source-code pre { color: #ffdead; overflow: hidden; }
195
+
196
+ /* --- Ruby keyword styles --------------------- */
197
+
198
+ .standalone-code { background: #221111; color: #ffdead; overflow: hidden; }
199
+
200
+ .ruby-constant { color: #7fffd4; background: transparent; }
201
+ .ruby-keyword { color: #00ffff; background: transparent; }
202
+ .ruby-ivar { color: #eedd82; background: transparent; }
203
+ .ruby-operator { color: #00ffee; background: transparent; }
204
+ .ruby-identifier { color: #ffdead; background: transparent; }
205
+ .ruby-node { color: #ffa07a; background: transparent; }
206
+ .ruby-comment { color: #b22222; font-weight: bold; background: transparent; }
207
+ .ruby-regexp { color: #ffa07a; background: transparent; }
208
+ .ruby-value { color: #7fffd4; background: transparent; }
@@ -0,0 +1,13 @@
1
+ <?php
2
+ /**
3
+ * This file should be placed in the htdocs of your actual blog repo
4
+ */
5
+ if($_POST["payload"]) {
6
+ #run git pull
7
+ system("git pull origin master:master");
8
+ echo "Thank you, come again!";
9
+ } else {
10
+ echo "Sorry, you don't come bearing gifts.\n\n";
11
+ }
12
+ ?>
13
+
data/lib/hobix/base.rb CHANGED
@@ -94,6 +94,7 @@ class BaseStorage < BasePlugin
94
94
  e.link = e.class.url_link e, @link, "html"
95
95
  e.created = Time.now
96
96
  e.modified = Time.now
97
+ e.updated = Time.now
97
98
  e.title = "This Ghostly Message From the Slime Will Soon Vanish!"
98
99
  e.tagline = "A temporary message, a tingling sensation, Hobix is up!!"
99
100
  e.author = author
@@ -304,7 +305,8 @@ class BaseContent
304
305
  _ :link
305
306
  _ :title, :edit_as => :text, :search => :fulltext
306
307
  _ :created, :edit_as => :datetime, :search => :prefix
307
- _ :modified
308
+ _ :modified, :show_as => :datetime
309
+ _ :updated, :show_as => :datetime
308
310
  _ :tags, :edit_as => :text, :search => :prefix
309
311
 
310
312
  def initialize; yield self if block_given?; end
@@ -395,7 +397,7 @@ class BaseContent
395
397
  instance_eval %{
396
398
  def @#{ f }.to_yaml( opts = {} )
397
399
  s = self.to_str
398
- def s.to_yaml_style; :fold; end
400
+ def s.to_yaml_style; :literal; end
399
401
  s.to_yaml( opts )
400
402
  end
401
403
  }
@@ -446,7 +448,8 @@ class BaseEntry < BaseContent
446
448
  _ :author, :req => true, :edit_as => :text, :search => :prefix
447
449
  _ :contributors, :edit_as => :array, :search => :prefix
448
450
  _ :created, :edit_as => :datetime, :search => :prefix
449
- _ :modified
451
+ _ :modified, :show_as => :datetime
452
+ _ :updated, :show_as => :datetime
450
453
  _ :tags, :edit_as => :text, :search => :prefix
451
454
  _ :content, :edit_as => :textarea, :search => :fulltext, :text_processor => true
452
455
  _ :content_ratings, :edit_as => :array
data/lib/hobix/bixwik.rb CHANGED
@@ -42,9 +42,9 @@ module BixWik
42
42
 
43
43
  # Handler for templates with `index' prefix. These pages simply
44
44
  # mirror the `HomePage' entry.
45
- def skel_index( path_storage )
45
+ def skel_index( path_storage, section_path )
46
46
  homePage = path_storage.match( /^HomePage$/ ).first
47
- page = Page.new( '/index' )
47
+ page = Page.new( 'index' )
48
48
  unless homePage
49
49
  homePage = Hobix::Storage::IndexEntry.new( path_storage.default_entry( authors.keys.first ) )
50
50
  end
@@ -56,32 +56,32 @@ module BixWik
56
56
  # Handler for templates with `list/index' prefix. These templates will
57
57
  # receive IndexEntry objects for every entry in the system. Only one
58
58
  # index page is requested by this handler.
59
- def skel_recent_index( path_storage )
59
+ def skel_recent_index( path_storage, section_path )
60
60
  index_entries = storage.find( :all => true )
61
- page = Page.new( '/list/index' )
61
+ page = Page.new( 'list/index' )
62
62
  page.timestamp = index_entries.first.created
63
- page.updated = storage.last_modified( index_entries )
63
+ page.updated = storage.last_updated( index_entries )
64
64
  yield :page => page, :entries => index_entries
65
65
  end
66
66
 
67
67
  # Handler for templates with `recent/index' prefix. These templates will
68
68
  # receive entries loaded by +Hobix::BaseStorage#lastn+. Only one
69
69
  # index page is requested by this handler.
70
- def skel_recent_index( path_storage )
70
+ def skel_recent_index( path_storage, section_path )
71
71
  index_entries = storage.lastn( @lastn || 120 )
72
- page = Page.new( '/recent/index' )
72
+ page = Page.new( 'recent/index' )
73
73
  page.timestamp = index_entries.first.created
74
- page.updated = storage.last_modified( index_entries )
74
+ page.updated = storage.last_updated( index_entries )
75
75
  yield :page => page, :entries => index_entries
76
76
  end
77
77
 
78
78
  # Handler for templates with `list/index' prefix. These templates will
79
79
  # receive a list of all pages in the Wiki.
80
- def skel_list_index( path_storage )
80
+ def skel_list_index( path_storage, section_path )
81
81
  all_pages = storage.all
82
- page = Page.new( '/list/index' )
82
+ page = Page.new( 'list/index' )
83
83
  page.timestamp = all_pages.first.created
84
- page.updated = storage.last_modified( all_pages )
84
+ page.updated = storage.last_updated( all_pages )
85
85
  yield :page => page, :entries => all_pages, :no_load => true
86
86
  end
87
87
 
@@ -193,7 +193,7 @@ def entry_content_erb
193
193
  end
194
194
  def sidebar_erb; nil; end
195
195
  def entry_footer_erb; %{
196
- Revision from <%= ( entry.modified || entry.created ).strftime( "%B %d, %Y %H:%M" ) %> by <%= weblog.wiki_link( "authors/" + entry.author ) %> }
196
+ Revision from <%= ( entry.modified || entry.created ).strftime( "%d %B %Y at %H:%M" ) %> by <%= weblog.wiki_link( "authors/" + entry.author ) %> }
197
197
  end
198
198
  end
199
199
  end
@@ -109,13 +109,25 @@ module CommandLine
109
109
  require 'open-uri'
110
110
  c = ::Config::CONFIG.merge( config )
111
111
  eval(open("http://go.hobix.com/").read)
112
+
113
+
114
+ # Now look at all blogs and delete entries/index.{hobix,search}
115
+ if @config['weblogs'].respond_to? :sort
116
+ blogs = @config['weblogs'].sort
117
+ blogs.each do |e|
118
+ weblog = Hobix::Weblog.load( e[1] )
119
+ puts "Removing index.search and index.hobix from #{weblog.entry_path}"
120
+ File.safe_unlink( File.join(weblog.entry_path, "index.search"),
121
+ File.join(weblog.entry_path, "index.hobix"))
122
+ end
123
+ end
112
124
  end
113
125
 
114
126
  # List all your weblogs
115
127
  def blogs_weblog_explain; "List your weblogs."; end
116
128
  def blogs_weblog_args; []; end
117
129
  def blogs_weblog
118
- if @config['weblogs'].respond_to? :sort
130
+ if @config['weblogs'].respond_to?( :sort ) && !@config['weblogs'].empty?
119
131
  blogs = @config['weblogs'].sort
120
132
  name_width = blogs.collect { |b| b[0].length }.max
121
133
  tabular( blogs, [[-name_width, 0, 'weblog-name'], [-40, 1, 'path']] )
@@ -433,6 +445,8 @@ module CommandLine
433
445
  else
434
446
  @config['post upgen'] = true
435
447
  end
448
+
449
+
436
450
  save_config
437
451
  end
438
452
 
@@ -463,8 +477,8 @@ module CommandLine
463
477
  puts "If you want to join an existing hobix weblog, we can do that now."
464
478
  puts "Each weblog needs a name and a path. Use <ENTER> at any prompt"
465
479
  puts "to simply move on."
480
+ puts
466
481
  loop do
467
- puts
468
482
  puts "Short name for weblog, used on the command line (i.e. hobix upgen blogName)."
469
483
  print ": "
470
484
  blogname = gets.strip
@@ -477,6 +491,8 @@ module CommandLine
477
491
  break
478
492
  end
479
493
  add_weblog( blogname, blogpath )
494
+ puts
495
+ puts "** Add another weblog?"
480
496
  end
481
497
 
482
498
  puts "To setup more weblogs later, use: hobix add #{ add_weblog_args.join( ' ' ) }"
@@ -45,14 +45,13 @@ append_def :entry_erb, %{
45
45
 
46
46
  def entry_comment_erb; %{
47
47
  <% entry_id = entry.id %>
48
- <a name="comments"></a>
49
48
  <div id="comments">
50
49
  <% comments = weblog.storage.load_attached( entry_id, "comments" ) rescue [] %>
51
50
  <% comments.each do |comment| %>
52
51
  <div class="entry">
53
52
  <div class="entryAttrib">
54
53
  <div class="entryAuthor"><h3><%= comment.author %></h3></div>
55
- <div class="entryTime">said on <%= comment.created.strftime( "<nobr>%d %b %Y</nobr> at <nobr>%I:%M %p</nobr>" ) %></div>
54
+ <div class="entryTime">said on <%= comment.created.strftime( "<nobr>%d %b %Y</nobr> at <nobr>%H:%M</nobr>" ) %></div>
56
55
  </div>
57
56
  <div class="entryContentOuter"><div class="entryContent"><%= comment.content.to_html %></div></div>
58
57
  </div>
@@ -61,15 +60,15 @@ def entry_comment_erb; %{
61
60
 
62
61
  def entry_comment_form_erb; %{
63
62
  <div class="entry">
64
- <form id="userComment" method="post" action="/control/comment/<%= entry_id %>">
63
+ <form id="userComment" method="post" action="<%= weblog.expand_path( '/control/comment/' + entry_id )%>">
65
64
  <div class="entryAttrib">
66
65
  <div class="entryAuthor"><input name="<%= Hobix::Facets::Comments.form_field 'author' %>" type="textbox" size="15" maxlength="50" /></div>
67
- <div id="liveTime" class="entryTime">said on <nobr>DD Mon YYYY</nobr> <nobr>at HH:MM AM</nobr></div>
66
+ <div id="liveTime" class="entryTime">said on <%= Time.now.strftime( "<nobr>%d %b %Y</nobr> at <nobr>%H:%M</nobr>")%></div>
68
67
  </div>
69
68
  <div class="entryContentOuter"><div class="entryContent">
70
69
  <textarea name="<%= Hobix::Facets::Comments.form_field 'content' %>" rows="6" cols="50"></textarea>
71
70
  <p><input type="button" name="pleasePreview" value="preview"
72
- onClick="new Ajax.Request('/control/preview', {parameters: Form.serialize('userComment'), onComplete: function(req) { $('textilePreview').innerHTML = req.responseText }})" />
71
+ onClick="new Ajax.Request( '<%= weblog.expand_path '/control/preview' %>', {parameters: Form.serialize('userComment'), onComplete: function(req) { $('textilePreview').innerHTML = req.responseText }})" />
73
72
  <input type="submit" name="<%= Hobix::Facets::Comments.form_field 'submit' %>" value="&gt;&gt;" />
74
73
  <small>* do <a href="javascript:quickRedReference();">fancy stuff</a> in your comment.</small>
75
74
  </p>
data/lib/hobix/entry.rb CHANGED
@@ -31,8 +31,9 @@ module Hobix
31
31
  # tags:: A list of free-tagged categories.
32
32
  # author:: The author's username.
33
33
  # contributors:: An Array of contributors' usernames.
34
- # modified:: A modification time.
35
34
  # created:: The time the Entry was initially created.
35
+ # modified:: A modification time.
36
+ # updated:: Used internally to ensure an entry has been generated.
36
37
  # summary:: A brief description of this entry. Can be used
37
38
  # for an abbreviated text of a long article.
38
39
  # content:: The full text of the entry.
@@ -54,13 +54,38 @@ class Comments < BaseFacet
54
54
  c.ipaddress = app.remote_addr
55
55
  end
56
56
 
57
- # Save the comment, upgen
58
- @weblog.storage.append_to_attachment( entry_id, 'comments', comment )
59
- @weblog.regenerate :update
60
-
61
- # Redirect
57
+ # A quick hack to try akismet content spam checking
58
+ if @weblog.requires.detect{ |i| i['hobix/plugin/akismet'] }
59
+ @akismet = Akismet.new(@weblog.link, AkismetKey.key)
60
+ if @akismet.verifyAPIKey
61
+ if @akismet.commentCheck(
62
+ app.remote_addr, # remote IP
63
+ app.get_request_header('User-Agent'), # user agent
64
+ app.get_request_header('Referer'), # http referer
65
+ '', # permalink
66
+ 'comment', # comment type
67
+ app._POST['hobix_comment:author'].to_s, # author name
68
+ '', # author email
69
+ '', # author url
70
+ app._POST['hobix_comment:comment'].to_s, # comment text
71
+ {}) # other
72
+ app.puts( "Sorry, that smelled like spam. If wasn't meant to, go back and try again" )
73
+ return true
74
+ end
75
+ else
76
+ # If the key does not verify, post the comment
77
+ # but note the failure in the apache error logs.
78
+ $stderr.puts( "Hobix: Akismet API key did not verify." )
79
+ end
80
+ end
81
+
82
+ # Save the comment, upgen
83
+ @weblog.storage.append_to_attachment( entry_id, 'comments', comment )
84
+ @weblog.regenerate :update
85
+
86
+ # Redirect
62
87
  link = @weblog.output_entry_map[entry_id]
63
- app.setup_redirection( 302, link[:page].link )
88
+ app.setup_redirection( 302, @weblog.expand_path( link[:page].link ) )
64
89
  return true
65
90
  when "preview"
66
91
  app.puts RedCloth.new( app._POST[Comments.form_field('content')] ).to_html
@@ -47,7 +47,7 @@ require 'yaml'
47
47
  module Hobix
48
48
  class LinkList < BaseEntry
49
49
 
50
- _ :links, [:req, :textarea]
50
+ _ :links, :req => true, :edit_as => :textarea
51
51
 
52
52
  # Converts the link list into a RedCloth string for display
53
53
  # in templates.
@@ -59,6 +59,11 @@ class LinkList < BaseEntry
59
59
  )
60
60
  end
61
61
 
62
+ # Adds support for enumeration.
63
+ def each
64
+ @links.each { |title, url| yield title, url }
65
+ end
66
+
62
67
  # LinkLists currently output as YAML type family
63
68
  # !hobix.com,2004/linklist.
64
69
  yaml_type "tag:hobix.com,2004:linklist"
@@ -15,6 +15,7 @@
15
15
  #++
16
16
  require 'hobix/base'
17
17
  require 'rexml/document'
18
+ require 'erb'
18
19
  require 'uri'
19
20
  require 'cgi'
20
21
 
@@ -23,7 +24,9 @@ module Out
23
24
  module XmlQuick
24
25
  def x( title, txt, attrs = nil )
25
26
  e = REXML::Element.new title
26
- e.text = txt if txt
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
27
30
  attrs.each { |a,b| e.attributes[a] = b } if attrs
28
31
  self << e
29
32
  end
@@ -37,52 +40,58 @@ class Atom < Hobix::BaseOutput
37
40
  end
38
41
  def load( file_name, vars )
39
42
  rssdoc = REXML::Document.new( <<EOXML )
40
- <feed version="0.3"
41
- xmlns="http://purl.org/atom/ns#"
43
+ <feed
44
+ xmlns="http://www.w3.org/2005/Atom"
42
45
  xmlns:dc="http://purl.org/dc/elements/1.1/"
43
46
  xml:lang="en">
44
47
  <title></title>
45
48
  <link rel="alternate" type="text/html" href="" />
46
- <modified></modified>
47
- <tagline></tagline>
49
+ <link rel="self" type="application/atom+xml" href="" />
50
+ <updated></updated>
51
+ <subtitle></subtitle>
48
52
  <id></id>
49
- <generator url="http://hobix.com/" version="#{ Hobix::VERSION }">Hobix</generator>
50
- <copyright></copyright>
53
+ <generator uri="http://hobix.com/" version="#{ Hobix::VERSION }">Hobix</generator>
54
+ <rights></rights>
51
55
  </feed>
52
56
  EOXML
53
57
  uri = vars[:weblog].link
54
58
  rssdoc << REXML::XMLDecl.new
55
59
  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" )
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" )
59
69
  rssdoc.elements['/feed/id'].text = "tag:#{ uri.host },#{ Time.now.year }:blog#{ uri.path }"
60
- rssdoc.elements['/feed/copyright'].text = vars[:weblog].copyright || "None"
70
+ rssdoc.elements['/feed/rights'].text = vars[:weblog].copyright || "None"
61
71
  ( vars[:entries] || [vars[:entry]] ).each do |e|
62
72
  ele = REXML::Element.new 'entry'
63
73
  ele.extend XmlQuick
74
+ ele.attributes['xml:base'] = e.link
64
75
  ele.x( 'title', e.title )
65
76
  ele.x( 'link', nil, {'rel' => 'alternate', 'type' => 'text/html', 'href' => e.link } )
66
77
  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" ) )
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" ) )
69
80
  ele.x( 'dc:subject', e.section_id )
70
- e.tags.each do |t|
71
- ele.x( 'dc:subject', t )
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" } )
72
83
  end
73
84
  ele.x( 'summary',
74
85
  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
86
+ {'type' => 'html', 'mode' => 'escaped'} ) if e.respond_to? :summary and e.summary and !e.summary.empty?
76
87
  author = vars[:weblog].authors[e.author]
77
88
  ele_auth = REXML::Element.new 'author'
78
89
  ele_auth.extend XmlQuick
79
90
  ele_auth.x( 'name', author['name'] )
80
- ele_auth.x( 'url', author['url'] ) if author['url']
91
+ ele_auth.x( 'uri', author['url'] ) if author['url']
81
92
  ele_auth.x( 'email', author['email'] ) if author['email']
82
93
  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'} )
94
+ ele.x( 'content', e.content.to_html, {'type' => 'html'} )
86
95
  rssdoc.elements['/feed'].add ele
87
96
  end
88
97
  rssdoc.to_s
@@ -189,7 +189,7 @@ class Quick < Hobix::BaseOutput
189
189
  %{ <div class="sidebarBox">
190
190
  <h2 class="sidebarTitle">Syndicate</h2>
191
191
  <ul>
192
- <li><a href="<%= weblog.link %>/index.xml">RSS 2.0</a></li>
192
+ <li><a href="<%= weblog.link %>/index.atom">Atom 1.0</a></li>
193
193
  </ul>
194
194
  </div> }
195
195
  end
@@ -205,14 +205,15 @@ class Quick < Hobix::BaseOutput
205
205
  end
206
206
  def entries_erb
207
207
  %{ <% entries.each_day do |day, day_entries| %>
208
- <a name="<%= day.strftime( "%Y%m%d" ) %>"></a>
209
208
  <+ day_header +>
210
209
  <% day_entries.each do |entry| %>
211
210
  <+ entry +>
212
211
  <% end %>
213
212
  <% end %> }
214
213
  end
215
- def day_header_erb; %{ <h2 class="dayHeader"><%= day.strftime( "%A, %B %d, %Y" ) %></h2> }; end
214
+ def day_header_erb;
215
+ %{ <h2 class="dayHeader" id="<%= day.strftime( "day%Y%m%d" ) %>"><%= day.strftime( "%A, %B %d, %Y" ) %></h2> };
216
+ end
216
217
  def entry_erb
217
218
  %{ <div class="entry">
218
219
  <+ entry_title +>
@@ -228,7 +229,8 @@ class Quick < Hobix::BaseOutput
228
229
  %{ <div class="entryContent"><%= entry.content.to_html %></div> }
229
230
  end
230
231
  def entry_footer_erb
231
- %{ posted by <%= weblog.authors[entry.author]['name'] %> | <a href="<%= entry.link %>"><%= entry.created.strftime( "%I:%M %p" ) %></a> }
232
+ %{ posted by <%= weblog.authors[entry.author]['name'] %> |
233
+ <a href="<%= weblog.expand_path entry.id %>.html"><%= entry.created.strftime( "%I:%M %p" ) %></a> }
232
234
  end
233
235
  def head_tags_erb; end
234
236
  def css_erb; %{ @import "<%= weblog.expand_path "site.css" %>"; }; end
@@ -276,7 +278,7 @@ class QuickSummary < Quick
276
278
  %{ <div class="entryContent">
277
279
  <% if entry.respond_to? :summary and entry.summary %>
278
280
  <%= entry.summary.to_html %>
279
- <p><a href="<%= entry.link %>">Continue to full post.</a></p>
281
+ <p><a href="<%= weblog.expand_path entry.id %>">Continue to full post.</a></p>
280
282
  <% else %>
281
283
  <%= entry.content.to_html %>
282
284
  <% end %>
@@ -289,7 +291,7 @@ class QuickArchive < Quick
289
291
  "quick-archive"
290
292
  end
291
293
  def entry_erb
292
- %{ <h3 class="entryTitle"><a href="<%= entry.link %>"><%= entry.title %></a></h3> }
294
+ %{ <h3 class="entryTitle"><a href="<%= weblog.expand_path entry.id %>"><%= entry.title %></a></h3> }
293
295
  end
294
296
  def entries_erb
295
297
  %{ <div id="archives">