rethoth 0.4.1

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 (109) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +26 -0
  3. data/bin/thoth +233 -0
  4. data/lib/proto/config.ru +45 -0
  5. data/lib/proto/thoth.conf.sample +206 -0
  6. data/lib/thoth/cache.rb +53 -0
  7. data/lib/thoth/config.rb +158 -0
  8. data/lib/thoth/controller/admin.rb +75 -0
  9. data/lib/thoth/controller/api/comment.rb +73 -0
  10. data/lib/thoth/controller/api/page.rb +134 -0
  11. data/lib/thoth/controller/api/post.rb +122 -0
  12. data/lib/thoth/controller/api/tag.rb +59 -0
  13. data/lib/thoth/controller/archive.rb +50 -0
  14. data/lib/thoth/controller/comment.rb +173 -0
  15. data/lib/thoth/controller/main.rb +193 -0
  16. data/lib/thoth/controller/media.rb +172 -0
  17. data/lib/thoth/controller/page.rb +167 -0
  18. data/lib/thoth/controller/post.rb +310 -0
  19. data/lib/thoth/controller/search.rb +86 -0
  20. data/lib/thoth/controller/tag.rb +107 -0
  21. data/lib/thoth/controller.rb +48 -0
  22. data/lib/thoth/errors.rb +35 -0
  23. data/lib/thoth/helper/admin.rb +86 -0
  24. data/lib/thoth/helper/cookie.rb +45 -0
  25. data/lib/thoth/helper/error.rb +122 -0
  26. data/lib/thoth/helper/pagination.rb +131 -0
  27. data/lib/thoth/helper/wiki.rb +77 -0
  28. data/lib/thoth/helper/ysearch.rb +89 -0
  29. data/lib/thoth/importer/pants.rb +81 -0
  30. data/lib/thoth/importer/poseidon.rb +54 -0
  31. data/lib/thoth/importer/thoth.rb +103 -0
  32. data/lib/thoth/importer.rb +131 -0
  33. data/lib/thoth/layout/default.rhtml +47 -0
  34. data/lib/thoth/middleware/minify.rb +82 -0
  35. data/lib/thoth/migrate/001_create_schema.rb +108 -0
  36. data/lib/thoth/migrate/002_add_media_size.rb +37 -0
  37. data/lib/thoth/migrate/003_add_post_draft.rb +38 -0
  38. data/lib/thoth/migrate/004_add_comment_email.rb +37 -0
  39. data/lib/thoth/migrate/005_add_page_position.rb +37 -0
  40. data/lib/thoth/migrate/006_add_comment_close_delete.rb +43 -0
  41. data/lib/thoth/migrate/007_add_comment_summary.rb +37 -0
  42. data/lib/thoth/model/comment.rb +216 -0
  43. data/lib/thoth/model/media.rb +87 -0
  44. data/lib/thoth/model/page.rb +204 -0
  45. data/lib/thoth/model/post.rb +262 -0
  46. data/lib/thoth/model/tag.rb +80 -0
  47. data/lib/thoth/model/tags_posts_map.rb +34 -0
  48. data/lib/thoth/monkeypatch/sequel/model/errors.rb +37 -0
  49. data/lib/thoth/plugin/thoth_delicious.rb +105 -0
  50. data/lib/thoth/plugin/thoth_flickr.rb +86 -0
  51. data/lib/thoth/plugin/thoth_pinboard.rb +98 -0
  52. data/lib/thoth/plugin/thoth_tags.rb +68 -0
  53. data/lib/thoth/plugin/thoth_twitter.rb +175 -0
  54. data/lib/thoth/plugin.rb +59 -0
  55. data/lib/thoth/public/css/admin.css +223 -0
  56. data/lib/thoth/public/css/thoth.css +592 -0
  57. data/lib/thoth/public/images/admin-sprite.png +0 -0
  58. data/lib/thoth/public/images/thoth-sprite.png +0 -0
  59. data/lib/thoth/public/js/admin/comments.js +116 -0
  60. data/lib/thoth/public/js/admin/name.js +244 -0
  61. data/lib/thoth/public/js/admin/tagcomplete.js +332 -0
  62. data/lib/thoth/public/js/lazyload-min.js +4 -0
  63. data/lib/thoth/public/js/thoth.js +203 -0
  64. data/lib/thoth/public/robots.txt +5 -0
  65. data/lib/thoth/version.rb +37 -0
  66. data/lib/thoth/view/admin/index.rhtml +1 -0
  67. data/lib/thoth/view/admin/login.rhtml +23 -0
  68. data/lib/thoth/view/admin/toolbar.rhtml +117 -0
  69. data/lib/thoth/view/admin/welcome.rhtml +58 -0
  70. data/lib/thoth/view/archive/index.rhtml +24 -0
  71. data/lib/thoth/view/comment/comment.rhtml +47 -0
  72. data/lib/thoth/view/comment/delete.rhtml +15 -0
  73. data/lib/thoth/view/comment/form.rhtml +81 -0
  74. data/lib/thoth/view/comment/index.rhtml +68 -0
  75. data/lib/thoth/view/comment/list.rhtml +48 -0
  76. data/lib/thoth/view/media/delete.rhtml +15 -0
  77. data/lib/thoth/view/media/edit.rhtml +12 -0
  78. data/lib/thoth/view/media/form.rhtml +7 -0
  79. data/lib/thoth/view/media/list.rhtml +35 -0
  80. data/lib/thoth/view/media/media.rhtml +44 -0
  81. data/lib/thoth/view/media/new.rhtml +7 -0
  82. data/lib/thoth/view/page/delete.rhtml +15 -0
  83. data/lib/thoth/view/page/edit.rhtml +15 -0
  84. data/lib/thoth/view/page/form.rhtml +57 -0
  85. data/lib/thoth/view/page/index.rhtml +9 -0
  86. data/lib/thoth/view/page/list.rhtml +49 -0
  87. data/lib/thoth/view/page/new.rhtml +15 -0
  88. data/lib/thoth/view/post/comments.rhtml +12 -0
  89. data/lib/thoth/view/post/compact.rhtml +48 -0
  90. data/lib/thoth/view/post/delete.rhtml +15 -0
  91. data/lib/thoth/view/post/edit.rhtml +15 -0
  92. data/lib/thoth/view/post/form.rhtml +83 -0
  93. data/lib/thoth/view/post/index.rhtml +48 -0
  94. data/lib/thoth/view/post/list.rhtml +61 -0
  95. data/lib/thoth/view/post/new.rhtml +15 -0
  96. data/lib/thoth/view/post/tiny.rhtml +4 -0
  97. data/lib/thoth/view/search/index.rhtml +45 -0
  98. data/lib/thoth/view/tag/index.rhtml +34 -0
  99. data/lib/thoth/view/thoth/css.rhtml +9 -0
  100. data/lib/thoth/view/thoth/footer.rhtml +15 -0
  101. data/lib/thoth/view/thoth/header.rhtml +23 -0
  102. data/lib/thoth/view/thoth/index.rhtml +11 -0
  103. data/lib/thoth/view/thoth/js.rhtml +6 -0
  104. data/lib/thoth/view/thoth/sidebar.rhtml +38 -0
  105. data/lib/thoth/view/thoth/util/pager.rhtml +23 -0
  106. data/lib/thoth/view/thoth/util/simple_pager.rhtml +15 -0
  107. data/lib/thoth/view/thoth/util/table_sortheader.rhtml +20 -0
  108. data/lib/thoth.rb +394 -0
  109. metadata +409 -0
data/lib/thoth.rb ADDED
@@ -0,0 +1,394 @@
1
+ #--
2
+ # Copyright (c) 2017 John Pagonis <john@pagonis.org>
3
+ # Copyright (c) 2009 Ryan Grove <ryan@wonko.com>
4
+ # All rights reserved.
5
+ #
6
+ # Redistribution and use in source and binary forms, with or without
7
+ # modification, are permitted provided that the following conditions are met:
8
+ #
9
+ # * Redistributions of source code must retain the above copyright notice,
10
+ # this list of conditions and the following disclaimer.
11
+ # * Redistributions in binary form must reproduce the above copyright notice,
12
+ # this list of conditions and the following disclaimer in the documentation
13
+ # and/or other materials provided with the distribution.
14
+ # * Neither the name of this project nor the names of its contributors may be
15
+ # used to endorse or promote products derived from this software without
16
+ # specific prior written permission.
17
+ #
18
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21
+ # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
22
+ # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23
+ # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24
+ # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25
+ # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26
+ # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27
+ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
+ #++
29
+
30
+ # Prepend this file's directory to the include path if it's not there already.
31
+ $:.unshift(File.dirname(File.expand_path(__FILE__)))
32
+ $:.uniq!
33
+
34
+ require 'fileutils'
35
+
36
+ require 'builder'
37
+ require 'cssmin'
38
+ require 'erubis'
39
+ require 'jsmin'
40
+ require 'json'
41
+ require 'ramaze'
42
+ require 'redcloth'
43
+ require 'sanitize'
44
+ require 'sequel'
45
+ require 'sequel/extensions/migration'
46
+ require 'sequel/extensions/pagination'
47
+ require 'time'
48
+ require 'yaml'
49
+
50
+ # The main Thoth namespace.
51
+ module Thoth
52
+ HOME_DIR = ENV['THOTH_HOME'] || File.expand_path('.') unless const_defined?(:HOME_DIR)
53
+ LIB_DIR = File.join(File.dirname(File.expand_path(__FILE__)), 'thoth')
54
+ PUBLIC_DIR = File.join(LIB_DIR, 'public') unless const_defined?(:PUBLIC_DIR)
55
+ VIEW_DIR = File.join(LIB_DIR, 'view') unless const_defined?(:VIEW_DIR)
56
+
57
+ # Thoth Helper namespace.
58
+ module Helper; end
59
+ end
60
+
61
+ module Thoth
62
+ # LIFTED from old Ramaze.acquire
63
+ # Require all .rb and .so files on the given globs, utilizes Dir::[].
64
+ #
65
+ # Examples:
66
+ # # Given following directory structure:
67
+ # # src/foo.rb
68
+ # # src/bar.so
69
+ # # src/foo.yaml
70
+ # # src/foobar/baz.rb
71
+ # # src/foobar/README
72
+ #
73
+ # # requires all files in 'src':
74
+ # Ramaze.acquire 'src/*'
75
+ #
76
+ # # requires all files in 'src' recursive:
77
+ # Ramaze.acquire 'src/**/*'
78
+ #
79
+ # # require 'src/foo.rb' and 'src/bar.so' and 'src/foobar/baz.rb'
80
+ # Ramaze.acquire 'src/*', 'src/foobar/*'
81
+ def self.acquire(*globs)
82
+ globs.flatten.each do |glob|
83
+ Dir[glob].each do |file|
84
+ require file if file =~ /\.(rb|so)$/
85
+ puts "#{file} loaded"
86
+ end
87
+ end
88
+ end
89
+
90
+
91
+ # Lifted from old Ramaze, which was also lifter by Rack::Directory
92
+ # Stolen from Ramaze
93
+ FILESIZE_FORMAT = [
94
+ ['%.1fT', 1 << 40],
95
+ ['%.1fG', 1 << 30],
96
+ ['%.1fM', 1 << 20],
97
+ ['%.1fK', 1 << 10],
98
+ ]
99
+
100
+ def self.filesize_format(int)
101
+ FILESIZE_FORMAT.each do |format, size|
102
+ return format % (int.to_f / size) if int >= size
103
+ end
104
+
105
+ "#{int}B"
106
+ end
107
+
108
+ end
109
+
110
+ require 'thoth/errors'
111
+ require 'thoth/config'
112
+ require 'thoth/version'
113
+ require 'thoth/plugin'
114
+ require 'thoth/middleware/minify'
115
+
116
+ # Monkeypatchery!
117
+ require 'thoth/monkeypatch/sequel/model/errors'
118
+
119
+ module Thoth
120
+ include Innate::Traited
121
+
122
+ # This is only here because assignments like trait[:foo] ||= :bar will fail if
123
+ # the trait hash hasn't been created yet.
124
+ trait(:traits_broken => true)
125
+
126
+ # Ramaze adapter to use.
127
+ trait[:adapter] ||= nil
128
+
129
+ # Path to the config file.
130
+ trait[:config_file] ||= ENV['THOTH_CONF'] || File.join(HOME_DIR, 'thoth.conf')
131
+
132
+ # Daemon command to execute (:start, :stop, :restart) or nil.
133
+ trait[:daemon] ||= nil
134
+
135
+ # IP address this Thoth instance should attach to.
136
+ trait[:ip] ||= nil
137
+
138
+ # Whether or not to start Thoth within an IRB session.
139
+ trait[:irb] ||= false
140
+
141
+ # What mode we're running in (either :devel or :production).
142
+ trait[:mode] ||= :production
143
+
144
+ # Port number this Thoth instance should attach to.
145
+ trait[:port] ||= nil
146
+
147
+ # Path to the daemon process id file.
148
+ trait[:pidfile] ||= File.join(HOME_DIR, "thoth_#{trait[:ip]}_#{trait[:port]}.pid")
149
+
150
+ # Filename to which all SQL commands should be logged, or nil to disable
151
+ # SQL logging.
152
+ trait[:sql_log] ||= nil
153
+
154
+ class << self
155
+ attr_reader :db
156
+
157
+ # Creates a new Thoth home directory with a sample config file at the
158
+ # specified path.
159
+ def create(path)
160
+ path = File.expand_path(path)
161
+
162
+ if File.exist?(path)
163
+ raise "specified path already exists: #{path}"
164
+ end
165
+
166
+ FileUtils.mkdir_p(File.join(path, 'log'))
167
+ FileUtils.mkdir(File.join(path, 'media'))
168
+ FileUtils.mkdir(File.join(path, 'plugin'))
169
+ FileUtils.mkdir(File.join(path, 'public'))
170
+ FileUtils.mkdir(File.join(path, 'view'))
171
+ FileUtils.mkdir(File.join(path, 'tmp'))
172
+
173
+ FileUtils.cp(File.join(LIB_DIR, '..', 'proto', 'config.ru'), File.join(path, 'config.ru'))
174
+ FileUtils.cp(File.join(LIB_DIR, '..', 'proto', 'thoth.conf.sample'), File.join(path, 'thoth.conf'))
175
+
176
+ File.chmod(0750, File.join(path, 'log'))
177
+ File.chmod(0750, File.join(path, 'media'))
178
+ File.chmod(0750, File.join(path, 'plugin'))
179
+ File.chmod(0755, File.join(path, 'public'))
180
+ File.chmod(0750, File.join(path, 'view'))
181
+ File.chmod(0750, File.join(path, 'tmp'))
182
+
183
+ File.chmod(0640, File.join(path, 'config.ru'))
184
+ File.chmod(0640, File.join(path, 'thoth.conf'))
185
+ end
186
+
187
+ # Opens a connection to the Thoth database and loads helpers, controllers,
188
+ # models and plugins.
189
+ def init_thoth
190
+ trait[:ip] ||= Config.server['address']
191
+ trait[:port] ||= Config.server['port']
192
+
193
+ open_db
194
+
195
+ # Ensure that the database schema is up to date.
196
+ #
197
+ # TODO: need to rethink how migration works since newer versions of Sequel
198
+ # don't expose the migration versions.
199
+ #
200
+ # unless Sequel::Migrator.get_current_migration_version(@db) ==
201
+ # Sequel::Migrator.latest_migration_version(File.join(LIB_DIR, 'migrate'))
202
+ #
203
+ # if trait[:mode] == :production
204
+ # raise SchemaError, "Database schema is missing or out of date. " <<
205
+ # "Please run `thoth --migrate`."
206
+ # else
207
+ # raise SchemaError, "Database schema is missing or out of date. " <<
208
+ # "Please run `thoth --devel --migrate`."
209
+ # end
210
+ # end
211
+
212
+ # If caching is disabled, replace the default cache store with a no-op
213
+ # API.
214
+ if Config.server['enable_cache']
215
+ # Cache templates once read to prevent unnecessary disk thrashing.
216
+ Innate::View.options.read_cache = true
217
+
218
+ if Config.server['memcache']['enabled']
219
+ Ramaze::Cache::MemCache::OPTIONS[:servers] = Config.server['memcache']['servers']
220
+ Ramaze::Cache.options.default = Ramaze::Cache::MemCache
221
+ end
222
+ else
223
+ require 'thoth/cache'
224
+ Ramaze::Cache.options.default = Thoth::Cache::Noop
225
+ end
226
+
227
+ # Create a cache for plugins to use.
228
+ Ramaze::Cache.add(:plugin)
229
+
230
+ # Tell Innate where to find Thoth's helpers.
231
+ Innate::HelpersHelper.options.paths << LIB_DIR
232
+ Innate::HelpersHelper.options.namespaces << Thoth::Helper
233
+
234
+ # Load Thoth controllers.
235
+ require File.join(LIB_DIR, 'controller')
236
+
237
+ # Load Thoth models.
238
+ require File.join(LIB_DIR, 'helper/wiki')
239
+ Thoth.acquire(File.join(LIB_DIR, 'model', '*'))
240
+
241
+ # Load startup plugins.
242
+ Config.plugins.each {|plugin| Plugin.load(plugin)}
243
+
244
+ Ramaze::Log.info "Thoth home: #{HOME_DIR}"
245
+ Ramaze::Log.info "Thoth lib : #{LIB_DIR}"
246
+ Ramaze::Log.info "Running in #{trait[:mode] == :production ? 'live' : 'dev'} mode"
247
+
248
+ Ramaze.options.setup << self
249
+ end
250
+
251
+ # Opens a Sequel database connection to the Thoth database.
252
+ def open_db
253
+ if Config.db =~ /^sqlite:\/{3}(.+)$/
254
+ dir = File.dirname($1)
255
+ FileUtils.mkdir_p(dir) unless File.directory?(dir)
256
+ end
257
+
258
+ Sequel.datetime_class = Time
259
+
260
+ @db = Sequel.connect(Config.db, :encoding => 'utf8')
261
+ @db.test_connection
262
+
263
+ if trait[:sql_log]
264
+ require 'logger'
265
+ @db.logger = Logger.new(trait[:sql_log])
266
+ end
267
+
268
+ @db.extension(:pagination)
269
+
270
+ rescue => e
271
+ Ramaze::Log.error("Unable to connect to database: #{e}")
272
+ exit(1)
273
+ end
274
+
275
+ # Restarts the running Thoth daemon (if any).
276
+ def restart
277
+ stop
278
+ sleep(1)
279
+ start
280
+ end
281
+
282
+ # Runs Thoth.
283
+ def run
284
+ init_thoth
285
+
286
+ begin
287
+ Ramaze.start(
288
+ :adapter => trait[:adapter],
289
+ :host => trait[:ip],
290
+ :port => trait[:port],
291
+ :root => LIB_DIR
292
+ )
293
+ rescue LoadError => ex
294
+ Ramaze::Log.error("Unable to start Ramaze due to LoadError: #{ex}")
295
+ exit(1)
296
+ end
297
+ end
298
+
299
+ # Initializes Ramaze.
300
+ def setup
301
+ Ramaze.options.merge!(
302
+ :mode => trait[:mode] == :production ? :live : :dev,
303
+ :roots => [HOME_DIR, LIB_DIR]
304
+ )
305
+
306
+ case trait[:mode]
307
+ when :devel
308
+ Ramaze.middleware :dev do
309
+ use Rack::Lint
310
+ use Rack::CommonLogger, Ramaze::Log
311
+ use Rack::ShowExceptions
312
+ use Rack::ShowStatus
313
+ use Rack::RouteExceptions
314
+ use Rack::ConditionalGet
315
+ use Rack::ETag
316
+ use Rack::Head
317
+ use Ramaze::Reloader
318
+ use Thoth::Minify if Config.server['enable_minify']
319
+ run Ramaze::AppMap
320
+ end
321
+
322
+ when :production
323
+ Ramaze.middleware :live do
324
+ use Rack::CommonLogger, Ramaze::Log
325
+ use Rack::RouteExceptions
326
+ use Rack::ShowStatus
327
+ use Rack::ConditionalGet
328
+ use Rack::ETag
329
+ use Rack::Head
330
+ use Thoth::Minify if Config.server['enable_minify']
331
+ run Ramaze::AppMap
332
+ end
333
+
334
+ # Ensure that exceptions result in an HTTP 500 response.
335
+ Rack::RouteExceptions.route(Exception, '/error_500')
336
+
337
+ # Log all errors to the error log file if one is configured.
338
+ if Config.server['error_log'].empty?
339
+ Ramaze::Log.loggers = []
340
+ else
341
+ log_dir = File.dirname(Config.server['error_log'])
342
+
343
+ unless File.directory?(log_dir)
344
+ FileUtils.mkdir_p(log_dir)
345
+ File.chmod(0750, log_dir)
346
+ end
347
+
348
+ Ramaze::Log.loggers = [Logger.new(Config.server['error_log'])]
349
+ Ramaze::Log.level = Logger::Severity::ERROR
350
+ end
351
+ end
352
+ end
353
+
354
+ # Starts Thoth as a daemon.
355
+ def start
356
+ if File.file?(trait[:pidfile])
357
+ pid = File.read(trait[:pidfile], 20).strip
358
+ abort("thoth already running? (pid=#{pid})")
359
+ end
360
+
361
+ puts "Starting thoth."
362
+
363
+ fork do
364
+ Process.setsid
365
+ exit if fork
366
+
367
+ File.open(trait[:pidfile], 'w') {|file| file << Process.pid}
368
+ at_exit {FileUtils.rm(trait[:pidfile]) if File.exist?(trait[:pidfile])}
369
+
370
+ Dir.chdir(HOME_DIR)
371
+ File.umask(0000)
372
+
373
+ STDIN.reopen('/dev/null')
374
+ STDOUT.reopen('/dev/null', 'a')
375
+ STDERR.reopen(STDOUT)
376
+
377
+ run
378
+ end
379
+ end
380
+
381
+ # Stops the running Thoth daemon (if any).
382
+ def stop
383
+ unless File.file?(trait[:pidfile])
384
+ abort("thoth not running? (check #{trait[:pidfile]}).")
385
+ end
386
+
387
+ puts "Stopping thoth."
388
+
389
+ pid = File.read(trait[:pidfile], 20).strip
390
+ FileUtils.rm(trait[:pidfile]) if File.exist?(trait[:pidfile])
391
+ pid && Process.kill('TERM', pid.to_i)
392
+ end
393
+ end
394
+ end