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.
- data/app/assets/javascripts/application.js.erb +2 -0
- data/app/assets/stylesheets/admin.css.scss +16 -0
- data/app/controllers/admin_controller.rb +93 -0
- data/app/controllers/application_controller.rb +38 -0
- data/app/controllers/cache_controller.rb +34 -0
- data/app/controllers/settings_controller.rb +35 -0
- data/app/controllers/site_controller.rb +49 -0
- data/app/controllers/static_cache_controller.rb +19 -0
- data/app/helpers/application_helper.rb +74 -0
- data/app/helpers/cache_helper.rb +17 -0
- data/app/models/backup.rb +11 -0
- data/app/models/database_file.rb +2 -0
- data/app/models/markdown.rb +17 -0
- data/app/models/page_cache.rb +102 -0
- data/app/models/setting.rb +258 -0
- data/app/models/simple_template.rb +20 -0
- data/app/models/sitemap_entry.rb +5 -0
- data/app/models/static_cache.rb +47 -0
- data/app/models/tweetable.rb +17 -0
- data/app/views/admin/main.html.erb +7 -0
- data/app/views/admin/sign_in.html.erb +19 -0
- data/app/views/cache/index.html.erb +61 -0
- data/app/views/layouts/admin.html.erb +43 -0
- data/app/views/layouts/application.html.erb +134 -0
- data/app/views/layouts/application.rss.erb +16 -0
- data/app/views/layouts/application.xml.erb +2 -0
- data/app/views/layouts/general_admin.html.erb +10 -0
- data/app/views/layouts/sign_in.html.erb +12 -0
- data/app/views/settings/initial_setup.html.erb +25 -0
- data/app/views/settings/show.html.erb +39 -0
- data/app/views/site/robots.text.erb +1 -0
- data/app/views/site/sitemap.xml.erb +12 -0
- data/app/views/static_cache/index.html.erb +29 -0
- data/cartoonist.gemspec +3 -2
- data/config/locales/en.yml +99 -0
- data/db/migrate/20120320043253_create_database_files.rb +9 -0
- data/db/migrate/20120401014029_create_settings.rb +12 -0
- data/lib/cartoonist/engine.rb +129 -0
- data/lib/cartoonist.rb +236 -0
- data/public/errors/404.html +13 -0
- data/public/errors/422.html +11 -0
- data/public/errors/500.html +13 -0
- metadata +41 -2
@@ -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
|
+
"© #{h copyright_years} #{h Setting[:copyright_owners]}".html_safe
|
73
|
+
end
|
74
|
+
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
|