cortex-reaver 0.1.0 → 0.2.0
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/README +1 -1
- data/bin/cortex_reaver +3 -4
- data/lib/cortex_reaver.rb +270 -110
- data/lib/cortex_reaver/cache.rb +23 -0
- data/lib/cortex_reaver/config.rb +178 -74
- data/lib/cortex_reaver/controller/admin.rb +64 -4
- data/lib/cortex_reaver/controller/comment.rb +4 -4
- data/lib/cortex_reaver/controller/controller.rb +3 -3
- data/lib/cortex_reaver/controller/journal.rb +4 -2
- data/lib/cortex_reaver/controller/main.rb +117 -26
- data/lib/cortex_reaver/controller/page.rb +7 -0
- data/lib/cortex_reaver/controller/photograph.rb +12 -10
- data/lib/cortex_reaver/controller/tag.rb +1 -1
- data/lib/cortex_reaver/controller/user.rb +7 -2
- data/lib/cortex_reaver/helper/attachments.rb +2 -2
- data/lib/cortex_reaver/helper/crud.rb +7 -7
- data/lib/cortex_reaver/helper/feeds.rb +56 -38
- data/lib/cortex_reaver/helper/form.rb +12 -16
- data/lib/cortex_reaver/helper/navigation.rb +35 -16
- data/lib/cortex_reaver/helper/photographs.rb +2 -2
- data/lib/cortex_reaver/helper/sidebar.rb +44 -0
- data/lib/cortex_reaver/helper/tags.rb +32 -9
- data/lib/cortex_reaver/helper/workflow.rb +2 -14
- data/lib/cortex_reaver/layout/blank.rhtml +27 -31
- data/lib/cortex_reaver/layout/text.rhtml +54 -67
- data/lib/cortex_reaver/migrations/014_convert_projects_to_pages.rb +79 -0
- data/lib/cortex_reaver/model/comment.rb +7 -8
- data/lib/cortex_reaver/model/page.rb +5 -3
- data/lib/cortex_reaver/model/photograph.rb +57 -13
- data/lib/cortex_reaver/model/tag.rb +1 -4
- data/lib/cortex_reaver/model/user.rb +6 -3
- data/lib/cortex_reaver/plugin.rb +1 -1
- data/lib/cortex_reaver/plugins/twitter.rb +185 -0
- data/lib/cortex_reaver/public/css/actions.css +31 -0
- data/lib/cortex_reaver/public/css/admin.css +50 -27
- data/lib/cortex_reaver/public/css/attachments.css +11 -0
- data/lib/cortex_reaver/public/css/autotags.css +38 -0
- data/lib/cortex_reaver/public/css/code.css +10 -2
- data/lib/cortex_reaver/public/css/colophon.css +10 -0
- data/lib/cortex_reaver/public/css/commments.css +7 -0
- data/lib/cortex_reaver/public/css/custom.css +1 -0
- data/lib/cortex_reaver/public/css/flash.css +17 -0
- data/lib/cortex_reaver/public/css/fonts.css +22 -0
- data/lib/cortex_reaver/public/css/form.css +15 -3
- data/lib/cortex_reaver/public/css/generics.css +39 -0
- data/lib/cortex_reaver/public/css/icons.css +4 -0
- data/lib/cortex_reaver/public/css/journals.css +3 -0
- data/lib/cortex_reaver/public/css/main.css +30 -312
- data/lib/cortex_reaver/public/css/pagination.css +50 -0
- data/lib/cortex_reaver/public/css/photo-show.css +87 -0
- data/lib/cortex_reaver/public/css/photo.css +28 -97
- data/lib/cortex_reaver/public/css/progress.css +35 -0
- data/lib/cortex_reaver/public/css/sidebar.css +83 -0
- data/lib/cortex_reaver/public/css/table-of-contents.css +26 -0
- data/lib/cortex_reaver/public/css/table.css +3 -0
- data/lib/cortex_reaver/public/css/tags.css +29 -0
- data/lib/cortex_reaver/public/css/text.css +19 -1
- data/lib/cortex_reaver/public/css/top_actions.css +50 -0
- data/lib/cortex_reaver/public/css/users.css +3 -0
- data/lib/cortex_reaver/public/images/admin/icons.png +0 -0
- data/lib/cortex_reaver/public/images/admin/icons.xcf +0 -0
- data/lib/cortex_reaver/public/images/background_tile.png +0 -0
- data/lib/cortex_reaver/public/images/edit_34.png +0 -0
- data/lib/cortex_reaver/public/images/edit_34_prelight.png +0 -0
- data/lib/cortex_reaver/public/images/elided.png +0 -0
- data/lib/cortex_reaver/public/images/grid_34.png +0 -0
- data/lib/cortex_reaver/public/images/grid_34_prelight.png +0 -0
- data/lib/cortex_reaver/public/images/next_11.png +0 -0
- data/lib/cortex_reaver/public/images/next_34.png +0 -0
- data/lib/cortex_reaver/public/images/next_34_prelight.png +0 -0
- data/lib/cortex_reaver/public/images/prev_11.png +0 -0
- data/lib/cortex_reaver/public/images/prev_34.png +0 -0
- data/lib/cortex_reaver/public/images/prev_34_prelight.png +0 -0
- data/lib/cortex_reaver/public/js/admin.js +15 -22
- data/lib/cortex_reaver/public/js/autotags.js +120 -0
- data/lib/cortex_reaver/public/js/jquery.autocomplete.js +135 -176
- data/lib/cortex_reaver/public/js/jquery.color.js +124 -0
- data/lib/cortex_reaver/public/js/jquery.corners.min.js +7 -0
- data/lib/cortex_reaver/public/js/jquery.hotkeys-0.7.9.js +244 -0
- data/lib/cortex_reaver/public/js/jquery.js +4361 -4
- data/lib/cortex_reaver/public/js/jquery.periodicalupdater.js +98 -0
- data/lib/cortex_reaver/public/js/photo.js +3 -32
- data/lib/cortex_reaver/public/robots.txt +3 -0
- data/lib/cortex_reaver/snippets/ramaze/cache/memcached.rb +13 -10
- data/lib/cortex_reaver/snippets/range.rb +9 -0
- data/lib/cortex_reaver/support/attachments.rb +12 -0
- data/lib/cortex_reaver/support/comments.rb +1 -3
- data/lib/cortex_reaver/support/renderer.rb +20 -17
- data/lib/cortex_reaver/support/sequenceable.rb +6 -6
- data/lib/cortex_reaver/support/tags.rb +15 -7
- data/lib/cortex_reaver/version.rb +1 -1
- data/lib/cortex_reaver/view/admin/configuration.rhtml +7 -0
- data/lib/cortex_reaver/view/admin/index.rhtml +3 -0
- data/lib/cortex_reaver/view/admin/regenerate_photo_sizes.rhtml +16 -0
- data/lib/cortex_reaver/view/adminbox.rhtml +34 -44
- data/lib/cortex_reaver/view/comments/comment.rhtml +1 -1
- data/lib/cortex_reaver/view/comments/form.rhtml +1 -2
- data/lib/cortex_reaver/view/comments/post_form.rhtml +2 -10
- data/lib/cortex_reaver/view/head.rhtml +11 -0
- data/lib/cortex_reaver/view/journals/journal.rhtml +3 -3
- data/lib/cortex_reaver/view/journals/show.rhtml +0 -4
- data/lib/cortex_reaver/view/js.rhtml +1 -0
- data/lib/cortex_reaver/view/pages/list.rhtml +3 -23
- data/lib/cortex_reaver/view/pages/row.rhtml +13 -0
- data/lib/cortex_reaver/view/photographs/grid.rhtml +30 -36
- data/lib/cortex_reaver/view/photographs/show.rhtml +42 -108
- data/lib/cortex_reaver/view/sidebar/explore_photos.rhtml +7 -0
- data/lib/cortex_reaver/view/sidebar/photographs.rhtml +15 -0
- data/lib/cortex_reaver/view/sidebar/sections.rhtml +4 -0
- data/lib/cortex_reaver/view/sidebar/twitter.rhtml +12 -0
- data/lib/cortex_reaver/view/tags/show.rhtml +0 -10
- data/lib/cortex_reaver/view/tracker.rhtml +0 -0
- data/lib/cortex_reaver/view/users/list.rhtml +1 -7
- data/lib/cortex_reaver/view/users/login.rhtml +1 -1
- metadata +103 -43
- data/lib/cortex_reaver/controller/project.rb +0 -53
- data/lib/cortex_reaver/helper/template.rb +0 -37
- data/lib/cortex_reaver/model/project.rb +0 -57
- data/lib/cortex_reaver/public/css/ramaze_error.css +0 -90
- data/lib/cortex_reaver/public/images/atom-xml-icon.png +0 -0
- data/lib/cortex_reaver/public/images/body.png +0 -0
- data/lib/cortex_reaver/public/images/border_bottom.png +0 -0
- data/lib/cortex_reaver/public/images/border_bottom_left.png +0 -0
- data/lib/cortex_reaver/public/images/border_bottom_right.png +0 -0
- data/lib/cortex_reaver/public/images/border_left.png +0 -0
- data/lib/cortex_reaver/public/images/border_right.png +0 -0
- data/lib/cortex_reaver/public/images/border_top.png +0 -0
- data/lib/cortex_reaver/public/images/border_top_left.png +0 -0
- data/lib/cortex_reaver/public/images/border_top_right.png +0 -0
- data/lib/cortex_reaver/public/images/header.png +0 -0
- data/lib/cortex_reaver/public/images/header.xcf +0 -0
- data/lib/cortex_reaver/public/images/indicator.gif +0 -0
- data/lib/cortex_reaver/public/images/rss-xml-icon.png +0 -0
- data/lib/cortex_reaver/public/images/sections.png +0 -0
- data/lib/cortex_reaver/public/images/sections_highlight.png +0 -0
- data/lib/cortex_reaver/public/js/jquery.autocompletefb.js +0 -125
- data/lib/cortex_reaver/view/photographs/sidebar.rhtml +0 -7
- data/lib/cortex_reaver/view/projects/form.rhtml +0 -14
- data/lib/cortex_reaver/view/projects/list.rhtml +0 -31
- data/lib/cortex_reaver/view/projects/show.rhtml +0 -42
- data/lib/proto/cortex_reaver.yaml +0 -47
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
module CortexReaver
|
|
2
|
+
class ProjectsToPagesSchema < Sequel::Migration
|
|
3
|
+
|
|
4
|
+
def down
|
|
5
|
+
# Nope. Not even going to try.
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def up
|
|
9
|
+
pages = self[:pages]
|
|
10
|
+
projects = self[:projects]
|
|
11
|
+
|
|
12
|
+
# Create projects page
|
|
13
|
+
unless root = pages.filter(:name => 'projects').first
|
|
14
|
+
pages.insert(
|
|
15
|
+
:name => 'projects',
|
|
16
|
+
:title => 'Projects',
|
|
17
|
+
:created_on => Time.now,
|
|
18
|
+
:updated_on => Time.now
|
|
19
|
+
)
|
|
20
|
+
end
|
|
21
|
+
root = pages.filter(:name => 'projects').first
|
|
22
|
+
|
|
23
|
+
# Migrate each project
|
|
24
|
+
projects.all.each do |project|
|
|
25
|
+
project_id = project[:id]
|
|
26
|
+
|
|
27
|
+
# Create page
|
|
28
|
+
project.delete :description
|
|
29
|
+
project.delete :id
|
|
30
|
+
project[:page_id] = root[:id]
|
|
31
|
+
pages << project
|
|
32
|
+
|
|
33
|
+
# Get page id
|
|
34
|
+
page_id = pages.filter(:page_id => root[:id], :name => project[:name]).first[:id]
|
|
35
|
+
|
|
36
|
+
# Reparent comments
|
|
37
|
+
self[:comments].filter(:project_id => project_id).update(
|
|
38
|
+
:project_id => nil,
|
|
39
|
+
:page_id => page_id
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
# Copy tags
|
|
43
|
+
self[:projects_tags].filter(:project_id => project_id).each do |t|
|
|
44
|
+
self[:pages_tags] << {:page_id => page_id, :tag_id => t[:tag_id]}
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Move attachments
|
|
48
|
+
# This is a little tricky; I'd like to deprecate the entire model at
|
|
49
|
+
# some point. We assume it lives in config.public_dir/data/projects/id
|
|
50
|
+
project_dir = File.join(
|
|
51
|
+
CortexReaver.config.public_root, 'data', 'projects', project_id.to_s
|
|
52
|
+
)
|
|
53
|
+
page_dir = File.join(
|
|
54
|
+
CortexReaver.config.public_root, 'data', 'pages', page_id.to_s
|
|
55
|
+
)
|
|
56
|
+
if File.directory? project_dir
|
|
57
|
+
if File.directory? page_dir
|
|
58
|
+
puts "WARNING: #{page_dir} already exists. Not moving contents of #{project_dir} into it. You should take a look."
|
|
59
|
+
else
|
|
60
|
+
FileUtils.move project_dir, page_dir
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
projects.filter(:id => project_id).delete
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Drop associated columns
|
|
68
|
+
# Can't do this yet--foreign key constraints...
|
|
69
|
+
# alter_table :comments do
|
|
70
|
+
# drop_column :project_id
|
|
71
|
+
# end
|
|
72
|
+
|
|
73
|
+
# Drop projects table
|
|
74
|
+
unless table_exists? :projects
|
|
75
|
+
drop_table :projects
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
@@ -10,7 +10,6 @@ module CortexReaver
|
|
|
10
10
|
many_to_one :creator, :class => 'CortexReaver::User', :key => 'created_by'
|
|
11
11
|
many_to_one :updater, :class => 'CortexReaver::User', :key => 'updated_by'
|
|
12
12
|
many_to_one :journal, :class => 'CortexReaver::Journal'
|
|
13
|
-
many_to_one :project, :class => 'CortexReaver::Project'
|
|
14
13
|
many_to_one :photograph, :class => 'CortexReaver::Photograph'
|
|
15
14
|
many_to_one :page, :class => 'CortexReaver::Page'
|
|
16
15
|
many_to_one :comment, :class => 'CortexReaver::Comment'
|
|
@@ -90,21 +89,21 @@ module CortexReaver
|
|
|
90
89
|
|
|
91
90
|
def validate
|
|
92
91
|
validates_presence :body
|
|
93
|
-
validates_max_length 255, :title
|
|
94
|
-
validates_max_length 255, :name
|
|
95
|
-
validates_max_length 255, :http
|
|
96
|
-
validates_max_length 255, :email
|
|
97
|
-
validates_format(/^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/, :email)
|
|
92
|
+
validates_max_length 255, :title, :allow_blank => true
|
|
93
|
+
validates_max_length 255, :name, :allow_blank => true
|
|
94
|
+
validates_max_length 255, :http, :allow_blank => true
|
|
95
|
+
validates_max_length 255, :email, :allow_blank => true
|
|
96
|
+
validates_format(/^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/, :email, :allow_blank => true)
|
|
98
97
|
|
|
99
98
|
# Ensure comments with an email specified do *not* conflict with another
|
|
100
99
|
# user.
|
|
101
|
-
if (not email.blank?) and User.filter(:email =>
|
|
100
|
+
if (not email.blank?) and User.filter(:email => email).count > 0
|
|
102
101
|
self.errors[:email] << 'conflicts with a registered user'
|
|
103
102
|
end
|
|
104
103
|
|
|
105
104
|
# Ensures comments belong to exactly one parent.
|
|
106
105
|
count = 0
|
|
107
|
-
[:page_id, :
|
|
106
|
+
[:page_id, :journal_id, :comment_id, :photograph_id].each do |field|
|
|
108
107
|
unless self[field].blank?
|
|
109
108
|
count += 1
|
|
110
109
|
if count > 1
|
|
@@ -10,8 +10,10 @@ module CortexReaver
|
|
|
10
10
|
plugin :viewable
|
|
11
11
|
include CortexReaver::Model::Renderer
|
|
12
12
|
|
|
13
|
+
self.sequence_order = :name
|
|
14
|
+
|
|
13
15
|
many_to_one :page, :class => 'CortexReaver::Page'
|
|
14
|
-
one_to_many :pages, :class => 'CortexReaver::Page'
|
|
16
|
+
one_to_many :pages, :class => 'CortexReaver::Page', :order => :name
|
|
15
17
|
many_to_one :creator, :class => 'CortexReaver::User', :key => 'created_by'
|
|
16
18
|
many_to_one :updater, :class => 'CortexReaver::User', :key => 'updated_by'
|
|
17
19
|
one_to_many :comments, :class => 'CortexReaver::Comment'
|
|
@@ -111,12 +113,12 @@ module CortexReaver
|
|
|
111
113
|
end
|
|
112
114
|
|
|
113
115
|
# Create a default page if none exists.
|
|
114
|
-
if
|
|
116
|
+
if db.table_exists?(table_name) and Page.count == 0
|
|
115
117
|
Page.new(
|
|
116
118
|
:name => 'about',
|
|
117
119
|
:title => 'About Cortex Reaver',
|
|
118
120
|
:body => <<EOF
|
|
119
|
-
<p>Cortex Reaver is a blog engine designed for managing photographs,
|
|
121
|
+
<p>Cortex Reaver is a blog engine designed for managing photographs, pages,
|
|
120
122
|
journal entries, and more, with support for tags and comments. Cortex
|
|
121
123
|
Reaver is written in <a href="http://ruby-lang.org">Ruby</a> using <a
|
|
122
124
|
href="http://ramaze.net">Ramaze</a>, uses the <a
|
|
@@ -10,7 +10,7 @@ module CortexReaver
|
|
|
10
10
|
|
|
11
11
|
# Target image sizes
|
|
12
12
|
SIZES = {
|
|
13
|
-
:thumbnail => '
|
|
13
|
+
:thumbnail => '166x',
|
|
14
14
|
:grid => '150x150',
|
|
15
15
|
:small => 'x512',
|
|
16
16
|
:medium => 'x768',
|
|
@@ -34,6 +34,12 @@ module CortexReaver
|
|
|
34
34
|
reverse_order(:created_on).limit(16)
|
|
35
35
|
end
|
|
36
36
|
|
|
37
|
+
def self.regenerate_sizes
|
|
38
|
+
all.each do |p|
|
|
39
|
+
p.regenerate_sizes
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
37
43
|
def self.url
|
|
38
44
|
'/photographs'
|
|
39
45
|
end
|
|
@@ -72,19 +78,10 @@ module CortexReaver
|
|
|
72
78
|
end
|
|
73
79
|
|
|
74
80
|
# Write file to disk
|
|
75
|
-
attachment = Attachment.new(self, 'original.jpg')
|
|
76
|
-
attachment.file = file
|
|
77
|
-
|
|
78
|
-
# Read image through ImageMagick
|
|
79
|
-
image = Magick::Image.read(attachment.local_path).first
|
|
81
|
+
attachment = Attachment.new(self, 'original.jpg').file = file
|
|
80
82
|
|
|
81
|
-
#
|
|
82
|
-
|
|
83
|
-
image.change_geometry(geometry) do |width, height|
|
|
84
|
-
attachment = Attachment.new(self, size.to_s + '.jpg')
|
|
85
|
-
image.scale(width, height).write(attachment.local_path)
|
|
86
|
-
end
|
|
87
|
-
end
|
|
83
|
+
# Compute thumbnails
|
|
84
|
+
regenerate_sizes
|
|
88
85
|
|
|
89
86
|
true
|
|
90
87
|
end
|
|
@@ -111,6 +108,53 @@ module CortexReaver
|
|
|
111
108
|
attachment(size.to_s + '.jpg').path(type)
|
|
112
109
|
end
|
|
113
110
|
|
|
111
|
+
# Regenerates various photo sizes.
|
|
112
|
+
def regenerate_sizes
|
|
113
|
+
Ramaze::Log.info "Regenerating photo sizes for #{self}"
|
|
114
|
+
|
|
115
|
+
# Find existing attachments, in order of decreasing (roughly) size
|
|
116
|
+
known_files = attachments.map{|a| a.name.sub(/\.jpg$/,'')} & (SIZES.keys.map(&:to_s) + ['original'])
|
|
117
|
+
largest = attachment(
|
|
118
|
+
known_files.sort_by { |f|
|
|
119
|
+
File.stat(attachment("#{f}.jpg").path).size
|
|
120
|
+
}.last + '.jpg'
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
# Replace original.jpg with the largest available, if necessary.
|
|
124
|
+
orig = attachment 'original.jpg'
|
|
125
|
+
unless orig.exists? and orig == largest
|
|
126
|
+
orig.file = largest
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# Delete everything but original.jpg
|
|
130
|
+
attachments.reject {|a| a.name == 'original.jpg'}.each{|a| a.delete}
|
|
131
|
+
|
|
132
|
+
# Read image through ImageMagick
|
|
133
|
+
begin
|
|
134
|
+
image = Magick::Image.read(orig.local_path).first
|
|
135
|
+
rescue => e
|
|
136
|
+
Ramaze::Log.error "Invalid image #{orig.local_path}; not processing."
|
|
137
|
+
return false
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# Write appropriate sizes to disk
|
|
141
|
+
SIZES.each do |size, geometry|
|
|
142
|
+
image.change_geometry(geometry) do |width, height|
|
|
143
|
+
attachment = attachment(size.to_s + '.jpg')
|
|
144
|
+
image.scale(width, height).write(attachment.local_path)
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# Yep, GC time. Gotta clear out those imagemagick stubs.
|
|
150
|
+
GC.start
|
|
151
|
+
|
|
152
|
+
# Free IM stubs
|
|
153
|
+
GC.start
|
|
154
|
+
|
|
155
|
+
true
|
|
156
|
+
end
|
|
157
|
+
|
|
114
158
|
# Returns the system path to the thumbnail photograph
|
|
115
159
|
def thumbnail_local_path
|
|
116
160
|
path :local, 'thumbnail'
|
|
@@ -4,7 +4,6 @@ module CortexReaver
|
|
|
4
4
|
|
|
5
5
|
many_to_many :photographs, :class => 'CortexReaver::Photograph'
|
|
6
6
|
many_to_many :journals, :class => 'CortexReaver::Journal'
|
|
7
|
-
many_to_many :projects, :class => 'CortexReaver::Project'
|
|
8
7
|
many_to_many :pages, :class => 'CortexReaver::Page'
|
|
9
8
|
|
|
10
9
|
subset :unused, :count => 0
|
|
@@ -23,7 +22,6 @@ module CortexReaver
|
|
|
23
22
|
|
|
24
23
|
remove_all_photographs
|
|
25
24
|
remove_all_journals
|
|
26
|
-
remove_all_projects
|
|
27
25
|
remove_all_pages
|
|
28
26
|
|
|
29
27
|
true
|
|
@@ -62,8 +60,7 @@ module CortexReaver
|
|
|
62
60
|
old_count = self[:count]
|
|
63
61
|
self[:count] = photographs_dataset.count +
|
|
64
62
|
journals_dataset.count +
|
|
65
|
-
pages_dataset.count
|
|
66
|
-
projects_dataset.count
|
|
63
|
+
pages_dataset.count
|
|
67
64
|
|
|
68
65
|
# Save and return
|
|
69
66
|
changed = changed_columns.include? :count
|
|
@@ -9,12 +9,10 @@ module CortexReaver
|
|
|
9
9
|
one_to_many :created_journals, :key => 'created_by', :class => 'CortexReaver::Journal'
|
|
10
10
|
one_to_many :created_pages, :key => 'created_by', :class => 'CortexReaver::Page'
|
|
11
11
|
one_to_many :created_photographs, :key => 'created_by', :class => 'CortexReaver::Photograph'
|
|
12
|
-
one_to_many :created_projects, :key => 'created_by', :class => 'CortexReaver::Project'
|
|
13
12
|
one_to_many :updated_comments, :key => 'updated_by', :class => 'CortexReaver::Comment'
|
|
14
13
|
one_to_many :updated_journals, :key => 'updated_by', :class => 'CortexReaver::Journal'
|
|
15
14
|
one_to_many :updated_pages, :key => 'updated_by', :class => 'CortexReaver::Page'
|
|
16
15
|
one_to_many :updated_photographs, :key => 'updated_by', :class => 'CortexReaver::Photograph'
|
|
17
|
-
one_to_many :updated_projects, :key => 'updated_by', :class => 'CortexReaver::Project'
|
|
18
16
|
|
|
19
17
|
|
|
20
18
|
self.window_size = 64
|
|
@@ -70,6 +68,11 @@ module CortexReaver
|
|
|
70
68
|
self[:login => id] || self[id]
|
|
71
69
|
end
|
|
72
70
|
|
|
71
|
+
# Class URL
|
|
72
|
+
def self.url
|
|
73
|
+
'/users'
|
|
74
|
+
end
|
|
75
|
+
|
|
73
76
|
# Returns true if the user is an administrator.
|
|
74
77
|
def admin?
|
|
75
78
|
self.admin
|
|
@@ -269,7 +272,7 @@ module CortexReaver
|
|
|
269
272
|
public
|
|
270
273
|
|
|
271
274
|
# Create default user if none exist
|
|
272
|
-
if
|
|
275
|
+
if db.table_exists?(table_name) and count == 0
|
|
273
276
|
u = User.new(
|
|
274
277
|
:login => 'shodan',
|
|
275
278
|
:name => 'Shodan',
|
data/lib/cortex_reaver/plugin.rb
CHANGED
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
#--
|
|
2
|
+
#
|
|
3
|
+
# Kyle Kingsbury: This plugin is adapted from Ryan's Thoth. His license follows:
|
|
4
|
+
#
|
|
5
|
+
# Copyright (c) 2009 Ryan Grove <ryan@wonko.com>
|
|
6
|
+
# All rights reserved.
|
|
7
|
+
#
|
|
8
|
+
# Redistribution and use in source and binary forms, with or without
|
|
9
|
+
# modification, are permitted provided that the following conditions are met:
|
|
10
|
+
#
|
|
11
|
+
# * Redistributions of source code must retain the above copyright notice,
|
|
12
|
+
# this list of conditions and the following disclaimer.
|
|
13
|
+
# * Redistributions in binary form must reproduce the above copyright notice,
|
|
14
|
+
# this list of conditions and the following disclaimer in the documentation
|
|
15
|
+
# and/or other materials provided with the distribution.
|
|
16
|
+
# * Neither the name of this project nor the names of its contributors may be
|
|
17
|
+
# used to endorse or promote products derived from this software without
|
|
18
|
+
# specific prior written permission.
|
|
19
|
+
#
|
|
20
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
21
|
+
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
22
|
+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
23
|
+
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
|
24
|
+
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
25
|
+
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
26
|
+
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
27
|
+
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
28
|
+
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
29
|
+
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
30
|
+
#++
|
|
31
|
+
|
|
32
|
+
require 'json'
|
|
33
|
+
require 'open-uri'
|
|
34
|
+
require 'timeout'
|
|
35
|
+
require 'uri'
|
|
36
|
+
|
|
37
|
+
module CortexReaver; module Plugins
|
|
38
|
+
|
|
39
|
+
# Twitter plugin for Cortex Reaver.
|
|
40
|
+
module Twitter
|
|
41
|
+
Config = CortexReaver.config.plugins.twitter ||= Construct.new
|
|
42
|
+
|
|
43
|
+
# The twitter username to look for
|
|
44
|
+
Config.define :username, :default => 'aphyr_'
|
|
45
|
+
|
|
46
|
+
# Whether or not to include replies. If this is false, the most recent
|
|
47
|
+
# non-reply tweets will be displayed.
|
|
48
|
+
Config.define :include_replies, :default => false
|
|
49
|
+
|
|
50
|
+
# Time in seconds to cache results. It's a good idea to keep this nice
|
|
51
|
+
# and high both to improve the performance of your site and to avoid
|
|
52
|
+
# pounding on Twitter's servers. Default is 600 seconds (10 minutes).
|
|
53
|
+
Config.define :cache_ttl, :default => 600
|
|
54
|
+
|
|
55
|
+
# Request timeout in seconds.
|
|
56
|
+
Config.define :request_timeout, :default => 3
|
|
57
|
+
|
|
58
|
+
# If Twitter fails to respond at least this many times in a row, no new
|
|
59
|
+
# requests will be sent until the failure_timeout expires in order to
|
|
60
|
+
# avoid hindering your blog's performance.
|
|
61
|
+
Config.define :failure_threshold, :default => 3
|
|
62
|
+
|
|
63
|
+
# After the failure_threshold is reached, the plugin will wait this many
|
|
64
|
+
# seconds before trying again. Default is 600 seconds (10 minutes).
|
|
65
|
+
Config.define :failure_timeout, :default => 600
|
|
66
|
+
|
|
67
|
+
class << self
|
|
68
|
+
|
|
69
|
+
# Parses tweet text and converts it into HTML. Explicit URLs and @username
|
|
70
|
+
# or #hashtag references will be turned into links.
|
|
71
|
+
def parse_tweet(tweet)
|
|
72
|
+
index = 0
|
|
73
|
+
html = tweet.dup
|
|
74
|
+
protocols = ['ftp', 'ftps', 'git', 'http', 'https', 'mailto', 'scp',
|
|
75
|
+
'sftp', 'ssh', 'telnet']
|
|
76
|
+
urls = []
|
|
77
|
+
|
|
78
|
+
# Extract URLs and replace them with placeholders for later.
|
|
79
|
+
URI.extract(html.dup, protocols) do |url|
|
|
80
|
+
html.sub!(url, "__URL#{index}__")
|
|
81
|
+
urls << url
|
|
82
|
+
index += 1
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# Replace URL placeholders with links.
|
|
86
|
+
urls.each_with_index do |url, index|
|
|
87
|
+
html.sub!("__URL#{index}__", "<a href=\"#{url}\">" <<
|
|
88
|
+
"#{url.length > 26 ? url[0..26] + '...' : url}</a>")
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# Turn @username into a link to the specified user's Twitter profile.
|
|
92
|
+
html.gsub!(/@([a-zA-Z0-9_]{1,16})([^a-zA-Z0-9_])?/,
|
|
93
|
+
'@<a href="http://twitter.com/\1">\1</a>\2')
|
|
94
|
+
|
|
95
|
+
# Turn #hashtags into links.
|
|
96
|
+
html.gsub!(/#([a-zA-Z0-9_]{1,32})([^a-zA-Z0-9_])?/,
|
|
97
|
+
'<a href="http://search.twitter.com/search?q=%23\1">#\1</a>\2')
|
|
98
|
+
|
|
99
|
+
return html
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# Gets a Hash containing recent tweets for the specified _user_. The only
|
|
103
|
+
# valid option currently is <code>:count</code>, which specifies the
|
|
104
|
+
# maximum number of tweets that should be returned.
|
|
105
|
+
def recent_tweets(user = Config.username, options = {:count => 1})
|
|
106
|
+
if @skip_until
|
|
107
|
+
return [] if @skip_until > Time.now
|
|
108
|
+
@skip_until = nil
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
cache = Ramaze::Cache.plugin
|
|
112
|
+
options = {:count => 5}.merge(options)
|
|
113
|
+
count = options[:count].to_i
|
|
114
|
+
|
|
115
|
+
count += 10 unless Config.include_replies
|
|
116
|
+
count = 200 if count > 200
|
|
117
|
+
|
|
118
|
+
url = "http://twitter.com/statuses/user_timeline/#{user}.json?count=" <<
|
|
119
|
+
count.to_s
|
|
120
|
+
|
|
121
|
+
if value = cache[url]
|
|
122
|
+
return value
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
tweets = []
|
|
126
|
+
|
|
127
|
+
Timeout.timeout(Config.request_timeout, StandardError) do
|
|
128
|
+
failed = 0
|
|
129
|
+
begin
|
|
130
|
+
tweets = JSON.parse(open(url).read)
|
|
131
|
+
rescue JSON::ParserError => e
|
|
132
|
+
# Twitter likes to hand out weird HTML responses sometimes. :/
|
|
133
|
+
failed += 1
|
|
134
|
+
retry unless failed > 3
|
|
135
|
+
|
|
136
|
+
# Admit defeat
|
|
137
|
+
raise RuntimeError.new("Failed to parse Twitter response 4 times: #{e}")
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
# Weed out replies if necessary.
|
|
142
|
+
unless Config.include_replies
|
|
143
|
+
tweets.delete_if do |tweet|
|
|
144
|
+
!tweet['in_reply_to_status_id'].nil? ||
|
|
145
|
+
!tweet['in_reply_to_user_id'].nil?
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
tweets = tweets.slice(0, options[:count].to_i)
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# Parse the tweets into an easier-to-use format.
|
|
152
|
+
tweets.map! do |tweet|
|
|
153
|
+
{
|
|
154
|
+
:created_at => Time.parse(tweet['created_at']),
|
|
155
|
+
:html => parse_tweet(tweet['text']),
|
|
156
|
+
:id => tweet['id'],
|
|
157
|
+
:source => tweet['source'],
|
|
158
|
+
:text => tweet['text'],
|
|
159
|
+
:truncated => tweet['truncated'],
|
|
160
|
+
:url => "http://twitter.com/#{user}/statuses/#{tweet['id']}"
|
|
161
|
+
}
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
@failures = 0
|
|
165
|
+
|
|
166
|
+
return cache.store(url, tweets, :ttl => Config.cache_ttl)
|
|
167
|
+
|
|
168
|
+
rescue => e
|
|
169
|
+
Ramaze::Log.error "CortexReaver::Plugins::Twitter: #{e.message}"
|
|
170
|
+
|
|
171
|
+
@failures ||= 0
|
|
172
|
+
@failures += 1
|
|
173
|
+
|
|
174
|
+
if @failures >= Config.failure_threshold
|
|
175
|
+
@skip_until = Time.now + Config.failure_timeout
|
|
176
|
+
Ramaze::Log.error "CortexReaver::Plugins::Twitter: Twitter failed to respond #{@failures} times. Will retry after #{@skip_until}."
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
return []
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
end
|
|
185
|
+
end; end
|