cartoonist 0.0.3.5 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
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