cartoonist 0.0.3.5 → 0.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 (43) hide show
  1. data/app/assets/javascripts/application.js.erb +2 -0
  2. data/app/assets/stylesheets/admin.css.scss +16 -0
  3. data/app/controllers/admin_controller.rb +93 -0
  4. data/app/controllers/application_controller.rb +38 -0
  5. data/app/controllers/cache_controller.rb +34 -0
  6. data/app/controllers/settings_controller.rb +35 -0
  7. data/app/controllers/site_controller.rb +49 -0
  8. data/app/controllers/static_cache_controller.rb +19 -0
  9. data/app/helpers/application_helper.rb +74 -0
  10. data/app/helpers/cache_helper.rb +17 -0
  11. data/app/models/backup.rb +11 -0
  12. data/app/models/database_file.rb +2 -0
  13. data/app/models/markdown.rb +17 -0
  14. data/app/models/page_cache.rb +102 -0
  15. data/app/models/setting.rb +258 -0
  16. data/app/models/simple_template.rb +20 -0
  17. data/app/models/sitemap_entry.rb +5 -0
  18. data/app/models/static_cache.rb +47 -0
  19. data/app/models/tweetable.rb +17 -0
  20. data/app/views/admin/main.html.erb +7 -0
  21. data/app/views/admin/sign_in.html.erb +19 -0
  22. data/app/views/cache/index.html.erb +61 -0
  23. data/app/views/layouts/admin.html.erb +43 -0
  24. data/app/views/layouts/application.html.erb +134 -0
  25. data/app/views/layouts/application.rss.erb +16 -0
  26. data/app/views/layouts/application.xml.erb +2 -0
  27. data/app/views/layouts/general_admin.html.erb +10 -0
  28. data/app/views/layouts/sign_in.html.erb +12 -0
  29. data/app/views/settings/initial_setup.html.erb +25 -0
  30. data/app/views/settings/show.html.erb +39 -0
  31. data/app/views/site/robots.text.erb +1 -0
  32. data/app/views/site/sitemap.xml.erb +12 -0
  33. data/app/views/static_cache/index.html.erb +29 -0
  34. data/cartoonist.gemspec +3 -2
  35. data/config/locales/en.yml +99 -0
  36. data/db/migrate/20120320043253_create_database_files.rb +9 -0
  37. data/db/migrate/20120401014029_create_settings.rb +12 -0
  38. data/lib/cartoonist/engine.rb +129 -0
  39. data/lib/cartoonist.rb +236 -0
  40. data/public/errors/404.html +13 -0
  41. data/public/errors/422.html +11 -0
  42. data/public/errors/500.html +13 -0
  43. metadata +41 -2
@@ -0,0 +1,2 @@
1
+ <% require_asset "jquery" %>
2
+ <% Cartoonist::Asset.included_js.each { |js| require_asset js } %>
@@ -0,0 +1,16 @@
1
+ form.inline { display: inline-block; }
2
+
3
+ table.cache {
4
+ tbody {
5
+ tr:nth-of-type(odd) {
6
+ background-color: #ddd;
7
+ .cached { background-color: #0c0; }
8
+ .not-cached { background-color: #c00; }
9
+ }
10
+
11
+ tr:nth-of-type(even) {
12
+ .cached { background-color: #5f5; }
13
+ .not-cached { background-color: #f55; }
14
+ }
15
+ }
16
+ }
@@ -0,0 +1,93 @@
1
+ class AdminController < ApplicationController
2
+ before_filter :ensure_ssl!, :except => [:cache_cron, :tweet_cron]
3
+ before_filter :check_admin!, :except => [:cache_cron, :index, :sign_in, :tweet_cron]
4
+
5
+ def index
6
+ redirect_to "/admin/sign_in"
7
+ end
8
+
9
+ def backup
10
+ respond_to do |format|
11
+ format.html { redirect_to "/admin/main" }
12
+ format.json do
13
+ prefix = "dev-" unless Rails.env.production?
14
+ filename = "#{prefix}comics-backup-#{Time.now.strftime("%Y-%m-%d_%H%M%S")}.json"
15
+ headers["Content-Disposition"] = "attachment; filename=\"#{filename}\""
16
+ render :text => Backup.all.to_json, :content_type => "application/json"
17
+ end
18
+ end
19
+ end
20
+
21
+ def reload
22
+ if Rails.env.production?
23
+ %x[git pull]
24
+ %x[touch #{File.join Rails.root, "tmp/restart.txt"}]
25
+ flash[:message] = "Updated and restarted."
26
+ end
27
+
28
+ redirect_to "/admin/main"
29
+ end
30
+
31
+ def main
32
+ render :layout => "general_admin"
33
+ end
34
+
35
+ def cache_cron
36
+ Dir.glob(File.join(Rails.root, "public/cache/**/*.tmp.html*"), File::FNM_DOTMATCH).each do |file|
37
+ if 2.hours.ago > File.mtime(file)
38
+ File.delete file
39
+ end
40
+ end
41
+
42
+ render :text => "Success.", :layout => false
43
+ rescue
44
+ render :text => "Failure.", :layout => false
45
+ end
46
+
47
+ def tweet_cron
48
+ messages = []
49
+
50
+ Comic.untweeted.each do |comic|
51
+ comic.tweet!
52
+ messages << "Comic: #{comic.tweet}"
53
+ end
54
+
55
+ BlogPost.untweeted.each do |post|
56
+ post.tweet!
57
+ messages << "Blog Post: #{post.tweet}"
58
+ end
59
+
60
+ unless Rails.env.production?
61
+ content = "#{messages.join "\n"}\n"
62
+ end
63
+
64
+ render :text => "#{content}Success.", :layout => false
65
+ rescue
66
+ render :text => "Failure.", :layout => false
67
+ end
68
+
69
+ def sign_in
70
+ return redirect_to "/admin/main" if session[:admin]
71
+ return redirect_to "/settings/initial_setup" if initial_setup_required?
72
+
73
+ if request.post?
74
+ user = Setting[:admin_users][params[:username]]
75
+
76
+ if user && params[:password] == user[:password]
77
+ session[:admin] = true
78
+ session[:user] = user[:name]
79
+ return redirect_to "/admin/main"
80
+ else
81
+ flash[:error] = "Wrong username or password!"
82
+ return redirect_to "/admin/sign_in"
83
+ end
84
+ end
85
+
86
+ render :layout => "sign_in"
87
+ end
88
+
89
+ def logout
90
+ reset_session
91
+ redirect_to "/admin/sign_in"
92
+ end
93
+ end
@@ -0,0 +1,38 @@
1
+ class ApplicationController < ActionController::Base
2
+ protect_from_forgery
3
+ before_filter :check_mobile
4
+
5
+ private
6
+ def initial_setup_required?
7
+ Setting[:admin_users].empty?
8
+ end
9
+
10
+ def ensure_ssl!
11
+ return unless Rails.env.production?
12
+ redirect_to "https://#{request.host_with_port}#{request.fullpath}" unless request.ssl?
13
+ end
14
+
15
+ def check_admin!
16
+ raise ActionController::RoutingError.new("Not Found") unless session[:admin]
17
+ end
18
+
19
+ def check_mobile
20
+ @mobile = (request.subdomain == "m") || params[:mobile]
21
+ end
22
+
23
+ def preview!
24
+ @for_preview = true
25
+ end
26
+
27
+ def cache_type
28
+ if @mobile
29
+ "m"
30
+ else
31
+ "www"
32
+ end
33
+ end
34
+
35
+ def cache_page_as(path)
36
+ cache_page @response, "/cache/#{path}"
37
+ end
38
+ end
@@ -0,0 +1,34 @@
1
+ class CacheController < ApplicationController
2
+ before_filter :ensure_ssl!
3
+ before_filter :check_admin!
4
+ layout "general_admin"
5
+
6
+ def index
7
+ @caches = PageCache.all
8
+ end
9
+
10
+ def destroy
11
+ PageCache.find(params[:id]).expire!
12
+ redirect_to "/cache"
13
+ end
14
+
15
+ def expire_www
16
+ PageCache.expire_www!
17
+ redirect_to "/cache"
18
+ end
19
+
20
+ def expire_m
21
+ PageCache.expire_m!
22
+ redirect_to "/cache"
23
+ end
24
+
25
+ def expire_tmp
26
+ PageCache.expire_tmp!
27
+ redirect_to "/cache"
28
+ end
29
+
30
+ def expire_all
31
+ PageCache.expire_all!
32
+ redirect_to "/cache"
33
+ end
34
+ end
@@ -0,0 +1,35 @@
1
+ class SettingsController < ApplicationController
2
+ before_filter :ensure_ssl!
3
+ before_filter :check_admin!, :except => [:initial_setup, :save_initial_setup]
4
+
5
+ def index
6
+ redirect_to "/settings/general"
7
+ end
8
+
9
+ def show
10
+ @tab = Setting::Tab[params[:id]]
11
+ render :layout => "general_admin"
12
+ end
13
+
14
+ def update
15
+ params[:included_settings].each do |setting|
16
+ Setting[setting] = params[setting]
17
+ end
18
+
19
+ redirect_to "/settings/#{params[:id]}"
20
+ end
21
+
22
+ def initial_setup
23
+ return redirect_to "/admin/sign_in" unless initial_setup_required?
24
+ render :layout => "sign_in"
25
+ end
26
+
27
+ def save_initial_setup
28
+ return redirect_to "/admin/sign_in" unless initial_setup_required?
29
+ Setting[:admin_users] = { params[:admin_username] => { :name => params[:admin_name], :password => params[:admin_password] } }
30
+ Setting[:domain] = params[:domain]
31
+ Setting[:site_name] = params[:site_name]
32
+ Setting[:secret_token] = SecureRandom.hex 30
33
+ redirect_to "/admin/sign_in"
34
+ end
35
+ end
@@ -0,0 +1,49 @@
1
+ class SiteController < ApplicationController
2
+ def favicon
3
+ respond_to do |format|
4
+ format.html { redirect_to "/" }
5
+
6
+ format.ico do
7
+ path = ActionController::Base.helpers.asset_path Cartoonist::Theme.favicon
8
+ path = File.join Rails.root, "public", path
9
+ send_data File.read(path), :filename => "favicon.ico", :type => "image/x-icon", :disposition => "inline"
10
+ cache_page_as "static/favicon.ico"
11
+ end
12
+ end
13
+ end
14
+
15
+ def robots
16
+ respond_to do |format|
17
+ format.html { redirect_to "/" }
18
+
19
+ format.text do
20
+ render :layout => false
21
+ cache_page_as "static/robots.txt"
22
+ end
23
+ end
24
+ end
25
+
26
+ def sitemap
27
+ respond_to do |format|
28
+ format.html { redirect_to "/" }
29
+
30
+ format.xml do
31
+ @entries = load_sitemap_content
32
+ render :content_type => "application/xml"
33
+ end
34
+ end
35
+ end
36
+
37
+ private
38
+ def load_sitemap_content
39
+ result = site_cache.read "sitemap-entries"
40
+ return result if result
41
+ result = Cartoonist::Sitemap.all
42
+ site_cache.write "sitemap-entries", result
43
+ result
44
+ end
45
+
46
+ def site_cache
47
+ @@site_cache ||= ActiveSupport::Cache::MemoryStore.new(:expires_in => 2.hours)
48
+ end
49
+ end
@@ -0,0 +1,19 @@
1
+ class StaticCacheController < ApplicationController
2
+ before_filter :ensure_ssl!
3
+ before_filter :check_admin!
4
+ layout "general_admin"
5
+
6
+ def index
7
+ @caches = StaticCache.all
8
+ end
9
+
10
+ def destroy
11
+ StaticCache.find(params[:id]).expire!
12
+ redirect_to "/static_cache"
13
+ end
14
+
15
+ def expire_all
16
+ StaticCache.expire_all!
17
+ redirect_to "/static_cache"
18
+ end
19
+ end
@@ -0,0 +1,74 @@
1
+ module ApplicationHelper
2
+ def selected(a, b = true)
3
+ if a == b
4
+ 'selected="selected"'.html_safe
5
+ end
6
+ end
7
+
8
+ def checked(a, b = true)
9
+ if a == b
10
+ 'checked="checked"'.html_safe
11
+ end
12
+ end
13
+
14
+ def markdown(text)
15
+ Markdown.render text
16
+ end
17
+
18
+ def licensed!
19
+ @licensed = true
20
+ end
21
+
22
+ def licensed?
23
+ @licensed
24
+ end
25
+
26
+ def rss!(path, title)
27
+ @rss_path = path
28
+ @rss_title = title
29
+ end
30
+
31
+ def rss?
32
+ @rss_path
33
+ end
34
+
35
+ def rss_path
36
+ @rss_path
37
+ end
38
+
39
+ def rss_title
40
+ @rss_title
41
+ end
42
+
43
+ def mobile?
44
+ @mobile
45
+ end
46
+
47
+ def html_class
48
+ if mobile?
49
+ "m"
50
+ else
51
+ "www"
52
+ end
53
+ end
54
+
55
+ def enable_disqus!(options)
56
+ @disqus_enabled = true
57
+ @disqus_options = options
58
+ end
59
+
60
+ def preview?
61
+ @for_preview
62
+ end
63
+
64
+ def content_license_url
65
+ "http://creativecommons.org/licenses/by-nc/3.0/"
66
+ end
67
+
68
+ def copyright_message
69
+ year = Date.today.strftime "%Y"
70
+ copyright_years = Setting[:copyright_starting_year].to_s
71
+ copyright_years = "#{copyright_years}-#{year}" if year != copyright_years
72
+ "&copy; #{h copyright_years} #{h Setting[:copyright_owners]}".html_safe
73
+ end
74
+ end
@@ -0,0 +1,17 @@
1
+ module CacheHelper
2
+ def td_cache_class(test)
3
+ if test
4
+ "cached"
5
+ else
6
+ "not-cached"
7
+ end
8
+ end
9
+
10
+ def td_cache_text(test)
11
+ if test
12
+ t "cach.index.cached"
13
+ else
14
+ t "cach.index.not_cached"
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,11 @@
1
+ class Backup
2
+ class << self
3
+ def all
4
+ result = Cartoonist::Backup.all.map do |key, value|
5
+ [key, value.call]
6
+ end
7
+
8
+ Hash[result]
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,2 @@
1
+ class DatabaseFile < ActiveRecord::Base
2
+ end
@@ -0,0 +1,17 @@
1
+ class Markdown
2
+ class << self
3
+ def config
4
+ @markdown ||= Redcarpet::Markdown.new Redcarpet::Render::HTML
5
+ end
6
+
7
+ def render(text, safe = true)
8
+ result = config.render text
9
+
10
+ if safe
11
+ result.html_safe
12
+ else
13
+ result
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,102 @@
1
+ class PageCache
2
+ attr_reader :name
3
+
4
+ CACHE_PATH = File.join Rails.root, "public/cache"
5
+
6
+ def initialize(name)
7
+ @name = name
8
+ end
9
+
10
+ def display_name
11
+ if name == ""
12
+ "INDEX"
13
+ else
14
+ name
15
+ end
16
+ end
17
+
18
+ def delete_name
19
+ if name == ""
20
+ "INDEX"
21
+ else
22
+ name
23
+ end
24
+ end
25
+
26
+ def www?
27
+ return @www_exists unless @www_exists.nil?
28
+ @www_exists = File.exists? File.join(CACHE_PATH, "#{name}.www.html")
29
+ end
30
+
31
+ def www_tmp?
32
+ return @www_tmp_exists unless @www_tmp_exists.nil?
33
+ @www_tmp_exists = File.exists? File.join(CACHE_PATH, "#{name}.www.tmp.html")
34
+ end
35
+
36
+ def m?
37
+ return @m_exists unless @m_exists.nil?
38
+ @m_exists = File.exists? File.join(CACHE_PATH, "#{name}.m.html")
39
+ end
40
+
41
+ def m_tmp?
42
+ return @m_tmp_exists unless @m_tmp_exists.nil?
43
+ @m_tmp_exists = File.exists? File.join(CACHE_PATH, "#{name}.m.tmp.html")
44
+ end
45
+
46
+ def expire!
47
+ PageCache.cache_files(:with_gz => true).select do |file|
48
+ extracted_name = file.sub /\.(?:www|m)(?:\.tmp)?\.html(?:.gz)?$/, ""
49
+ extracted_name == name
50
+ end.each do |file|
51
+ File.delete File.join(CACHE_PATH, file)
52
+ end
53
+ end
54
+
55
+ class << self
56
+ def find(name)
57
+ name = "" if name == "INDEX"
58
+ actual = cache_names.select { |x| x == name }.first
59
+ raise ActiveRecord::RecordNotFound.new("No records found!") unless actual
60
+ PageCache.new actual
61
+ end
62
+
63
+ def all
64
+ cache_names.map do |name|
65
+ PageCache.new name
66
+ end
67
+ end
68
+
69
+ def cache_files(options = {})
70
+ globber = "**/*.html"
71
+ globber += "*" if options[:with_gz]
72
+
73
+ Dir.glob(File.join(CACHE_PATH, globber), File::FNM_DOTMATCH).map do |file|
74
+ file.sub "#{CACHE_PATH}/", ""
75
+ end
76
+ end
77
+
78
+ def cache_names
79
+ cache_files.map do |file|
80
+ file.sub /\.(?:www|m)(?:\.tmp)?\.html$/, ""
81
+ end.sort.uniq
82
+ end
83
+
84
+ def expire_www!
85
+ File.delete *Dir.glob(File.join(CACHE_PATH, "**/*.www.html*"), File::FNM_DOTMATCH)
86
+ File.delete *Dir.glob(File.join(CACHE_PATH, "**/*.www.tmp.html*"), File::FNM_DOTMATCH)
87
+ end
88
+
89
+ def expire_m!
90
+ File.delete *Dir.glob(File.join(CACHE_PATH, "**/*.m.html*"), File::FNM_DOTMATCH)
91
+ File.delete *Dir.glob(File.join(CACHE_PATH, "**/*.m.tmp.html*"), File::FNM_DOTMATCH)
92
+ end
93
+
94
+ def expire_tmp!
95
+ File.delete *Dir.glob(File.join(CACHE_PATH, "**/*.tmp.html*"), File::FNM_DOTMATCH)
96
+ end
97
+
98
+ def expire_all!
99
+ File.delete *Dir.glob(File.join(CACHE_PATH, "**/*.html*"), File::FNM_DOTMATCH)
100
+ end
101
+ end
102
+ end