cortex-reaver 0.0.9 → 0.1.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/bin/cortex_reaver +7 -2
- data/lib/cortex_reaver.rb +51 -71
- data/lib/cortex_reaver/config.rb +23 -7
- data/lib/cortex_reaver/controller/admin.rb +6 -8
- data/lib/cortex_reaver/controller/comment.rb +17 -17
- data/lib/cortex_reaver/controller/config.rb +3 -2
- data/lib/cortex_reaver/controller/controller.rb +22 -0
- data/lib/cortex_reaver/controller/documentation.rb +1 -3
- data/lib/cortex_reaver/controller/journal.rb +13 -12
- data/lib/cortex_reaver/controller/main.rb +36 -29
- data/lib/cortex_reaver/controller/page.rb +15 -11
- data/lib/cortex_reaver/controller/photograph.rb +21 -15
- data/lib/cortex_reaver/controller/project.rb +16 -13
- data/lib/cortex_reaver/controller/tag.rb +16 -14
- data/lib/cortex_reaver/controller/user.rb +11 -13
- data/lib/cortex_reaver/helper/attachments.rb +18 -12
- data/lib/cortex_reaver/helper/auth.rb +2 -2
- data/lib/cortex_reaver/helper/canonical.rb +2 -2
- data/lib/cortex_reaver/helper/crud.rb +78 -38
- data/lib/cortex_reaver/helper/feeds.rb +2 -5
- data/lib/cortex_reaver/helper/form.rb +1 -1
- data/lib/cortex_reaver/helper/navigation.rb +1 -1
- data/lib/cortex_reaver/helper/photographs.rb +12 -3
- data/lib/cortex_reaver/helper/template.rb +37 -0
- data/lib/cortex_reaver/{view/blank_layout.rhtml → layout/blank.rhtml} +1 -1
- data/lib/cortex_reaver/{view/text_layout.rhtml → layout/text.rhtml} +1 -1
- data/lib/cortex_reaver/migrations/013_draft.rb +17 -0
- data/lib/cortex_reaver/model/comment.rb +64 -53
- data/lib/cortex_reaver/model/journal.rb +23 -21
- data/lib/cortex_reaver/model/model.rb +9 -0
- data/lib/cortex_reaver/model/page.rb +24 -42
- data/lib/cortex_reaver/model/photograph.rb +17 -17
- data/lib/cortex_reaver/model/project.rb +21 -18
- data/lib/cortex_reaver/model/tag.rb +12 -8
- data/lib/cortex_reaver/model/user.rb +79 -41
- data/lib/cortex_reaver/public/css/main.css +4 -0
- data/lib/cortex_reaver/snippets/numeric.rb +15 -0
- data/lib/cortex_reaver/snippets/ramaze/cache/memcached.rb +14 -0
- data/lib/cortex_reaver/support/attachments.rb +113 -105
- data/lib/cortex_reaver/support/cached_rendering.rb +65 -62
- data/lib/cortex_reaver/support/canonical.rb +82 -85
- data/lib/cortex_reaver/support/comments.rb +57 -51
- data/lib/cortex_reaver/support/cortex_reaver_validation_helpers.rb +13 -0
- data/lib/cortex_reaver/support/sequenceable.rb +202 -203
- data/lib/cortex_reaver/support/tags.rb +103 -94
- data/lib/cortex_reaver/support/timestamps.rb +27 -21
- data/lib/cortex_reaver/support/viewable.rb +17 -0
- data/lib/cortex_reaver/version.rb +3 -3
- data/lib/cortex_reaver/view/admin/index.rhtml +2 -2
- data/lib/cortex_reaver/view/comments/comment.rhtml +4 -1
- data/lib/cortex_reaver/view/comments/list.rhtml +1 -1
- data/lib/cortex_reaver/view/comments/post_form.rhtml +1 -1
- data/lib/cortex_reaver/view/journals/form.rhtml +3 -1
- data/lib/cortex_reaver/view/journals/journal.rhtml +6 -4
- data/lib/cortex_reaver/view/journals/list.rhtml +2 -2
- data/lib/cortex_reaver/view/journals/show.rhtml +1 -1
- data/lib/cortex_reaver/view/pages/form.rhtml +2 -1
- data/lib/cortex_reaver/view/pages/list.rhtml +2 -2
- data/lib/cortex_reaver/view/pages/show.rhtml +1 -1
- data/lib/cortex_reaver/view/photographs/form.rhtml +7 -3
- data/lib/cortex_reaver/view/photographs/list.rhtml +1 -1
- data/lib/cortex_reaver/view/photographs/show.rhtml +7 -7
- data/lib/cortex_reaver/view/projects/form.rhtml +1 -0
- data/lib/cortex_reaver/view/projects/list.rhtml +3 -3
- data/lib/cortex_reaver/view/projects/show.rhtml +5 -2
- data/lib/cortex_reaver/view/tags/list.rhtml +6 -2
- data/lib/cortex_reaver/view/tags/show.rhtml +10 -5
- data/lib/cortex_reaver/view/users/form.rhtml +1 -1
- data/lib/cortex_reaver/view/users/list.rhtml +5 -2
- data/lib/cortex_reaver/view/users/login.rhtml +1 -1
- data/lib/cortex_reaver/view/users/show.rhtml +5 -1
- metadata +159 -149
- data/lib/cortex_reaver/public/dispatch.fcgi +0 -11
- data/lib/cortex_reaver/snippets/ramaze/dispatcher/file.rb +0 -37
- data/lib/cortex_reaver/support/pagination.rb +0 -38
- data/lib/cortex_reaver/view/error.rhtml +0 -72
- data/lib/cortex_reaver/view/photographs/short.rhtml +0 -3
@@ -1,6 +1,6 @@
|
|
1
1
|
module CortexReaver
|
2
2
|
class Tag < Sequel::Model(:tags)
|
3
|
-
|
3
|
+
plugin :canonical
|
4
4
|
|
5
5
|
many_to_many :photographs, :class => 'CortexReaver::Photograph'
|
6
6
|
many_to_many :journals, :class => 'CortexReaver::Journal'
|
@@ -9,20 +9,24 @@ module CortexReaver
|
|
9
9
|
|
10
10
|
subset :unused, :count => 0
|
11
11
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
12
|
+
def validate
|
13
|
+
validates_unique :name
|
14
|
+
validates_presence :name
|
15
|
+
validates_max_length 255, :name
|
16
|
+
validates_presence :title
|
17
|
+
validates_max_length 255, :title
|
18
18
|
end
|
19
19
|
|
20
20
|
# When we delete a tag, ensure nothing else is linked to it.
|
21
|
-
before_destroy
|
21
|
+
def before_destroy
|
22
|
+
return false if super == false
|
23
|
+
|
22
24
|
remove_all_photographs
|
23
25
|
remove_all_journals
|
24
26
|
remove_all_projects
|
25
27
|
remove_all_pages
|
28
|
+
|
29
|
+
true
|
26
30
|
end
|
27
31
|
|
28
32
|
# Autocompletes a tag. Returns an array of matching candidates
|
@@ -2,47 +2,28 @@ require 'digest/sha2'
|
|
2
2
|
|
3
3
|
module CortexReaver
|
4
4
|
class User < Sequel::Model(:users)
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
validates do
|
20
|
-
uniqueness_of :login
|
21
|
-
length_of :login, :with => 5..255, :allow_blank => true
|
22
|
-
format_of :login, :with => /^[A-Za-z0-9\-_]+$/
|
23
|
-
length_of :name, :maximum => 255
|
24
|
-
length_of :http, :allow_blank => true, :maximum => 255
|
25
|
-
uniqueness_of :email
|
26
|
-
length_of :email, :allow_blank => true, :maximum => 255
|
27
|
-
format_of :email,
|
28
|
-
:with => /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/, :allow_blank => true
|
29
|
-
confirmation_of :password, :allow_nil => true#, :allow_false => true
|
30
|
-
each(:password_length, :tag => :password_length) do |object, attribute, value|
|
31
|
-
unless value.nil? or (8..255) === value
|
32
|
-
object.errors['password'] << 'must be between 8 and 255 characters.'
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
5
|
+
plugin :timestamps
|
6
|
+
plugin :sequenceable
|
7
|
+
|
8
|
+
one_to_many :created_comments, :key => 'created_by', :class => 'CortexReaver::Comment'
|
9
|
+
one_to_many :created_journals, :key => 'created_by', :class => 'CortexReaver::Journal'
|
10
|
+
one_to_many :created_pages, :key => 'created_by', :class => 'CortexReaver::Page'
|
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
|
+
one_to_many :updated_comments, :key => 'updated_by', :class => 'CortexReaver::Comment'
|
14
|
+
one_to_many :updated_journals, :key => 'updated_by', :class => 'CortexReaver::Journal'
|
15
|
+
one_to_many :updated_pages, :key => 'updated_by', :class => 'CortexReaver::Page'
|
16
|
+
one_to_many :updated_photographs, :key => 'updated_by', :class => 'CortexReaver::Photograph'
|
17
|
+
one_to_many :updated_projects, :key => 'updated_by', :class => 'CortexReaver::Project'
|
36
18
|
|
37
|
-
# Ensure an administrator is always available.
|
38
|
-
validates_each :admin do |object, attribute, value|
|
39
|
-
if admins = User.filter(:admin => true) and admins.size == 1 and admins.first.id == self.id and not value
|
40
|
-
object.errors[attribute] << "can't be unset; only one administrator left!"
|
41
|
-
end
|
42
|
-
end
|
43
19
|
|
44
20
|
self.window_size = 64
|
45
21
|
|
22
|
+
# Is this the special anonymous user?
|
23
|
+
def anonymous?
|
24
|
+
false
|
25
|
+
end
|
26
|
+
|
46
27
|
# Returns an authenticated user by login and password, or nil.
|
47
28
|
def self.authenticate(login, password)
|
48
29
|
user = self[:login => login]
|
@@ -60,6 +41,8 @@ module CortexReaver
|
|
60
41
|
|
61
42
|
# Create anonymous user
|
62
43
|
@anonymous_user = self.new(:name => "Anonymous")
|
44
|
+
|
45
|
+
# These functions are embedded for speed. Much faster public browsing!
|
63
46
|
def @anonymous_user.can_create? other
|
64
47
|
false
|
65
48
|
end
|
@@ -69,6 +52,9 @@ module CortexReaver
|
|
69
52
|
def @anonymous_user.can_delete? other
|
70
53
|
false
|
71
54
|
end
|
55
|
+
def @anonymous_user.anonymous?
|
56
|
+
true
|
57
|
+
end
|
72
58
|
|
73
59
|
@anonymous_user
|
74
60
|
end
|
@@ -98,6 +84,16 @@ module CortexReaver
|
|
98
84
|
end
|
99
85
|
end
|
100
86
|
|
87
|
+
# Ensure that we don't destroy the only admin.
|
88
|
+
def before_destroy
|
89
|
+
return false if super == false
|
90
|
+
|
91
|
+
if admins = User.filter(:admin => true) and admins.count == 1 and admins.first.id == self.id
|
92
|
+
self.errors.add nil, "Can't destroy the only administrator."
|
93
|
+
return false
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
101
97
|
def can_create?(other)
|
102
98
|
if admin?
|
103
99
|
# Administrators may create anything
|
@@ -157,6 +153,22 @@ module CortexReaver
|
|
157
153
|
end
|
158
154
|
end
|
159
155
|
|
156
|
+
def can_view?(other)
|
157
|
+
if other.respond_to? :draft and other.draft
|
158
|
+
# Draft
|
159
|
+
if admin? or can_edit? other
|
160
|
+
# User can edit this draft
|
161
|
+
true
|
162
|
+
else
|
163
|
+
# Nope, not yet!
|
164
|
+
false
|
165
|
+
end
|
166
|
+
else
|
167
|
+
# Not a draft
|
168
|
+
true
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
160
172
|
# Returns true if user is a contributor
|
161
173
|
def contributor?
|
162
174
|
self.contributor
|
@@ -172,11 +184,17 @@ module CortexReaver
|
|
172
184
|
self.moderator
|
173
185
|
end
|
174
186
|
|
187
|
+
# Name falls back to login if blank
|
188
|
+
def name
|
189
|
+
name = self[:name]
|
190
|
+
name.blank? ? login : name
|
191
|
+
end
|
192
|
+
|
175
193
|
# Set user password
|
176
194
|
def password=(password)
|
177
195
|
self.salt ||= self.class.new_salt
|
178
196
|
self[:password] = self.class.crypt(password, self.salt)
|
179
|
-
@password_length = password.length
|
197
|
+
@password_length = '*' * password.length
|
180
198
|
end
|
181
199
|
|
182
200
|
# Password confirmation
|
@@ -211,6 +229,25 @@ module CortexReaver
|
|
211
229
|
'/users/show/' + login
|
212
230
|
end
|
213
231
|
|
232
|
+
def validate
|
233
|
+
validates_unique(:login, :message => "Already taken.")
|
234
|
+
validates_max_length(255, :login, :message => "Please enter a username shorter than 255 characters.")
|
235
|
+
validates_format(/^[A-Za-z0-9\-_]+$/, :login, :message => "Logins can only contain alphanumeric characters, dashes, and underscores.")
|
236
|
+
validates_max_length(255, :name, :allow_blank => true, :message => "Please enter a name shorter than 255 characters.")
|
237
|
+
validates_max_length(255, :http, :allow_blank => true, :message => "Please enter an HTTP address shorter than 255 characters.")
|
238
|
+
validates_unique(:email, :message => "Already taken.")
|
239
|
+
validates_max_length(255, :email, :message => "Please enter an email address shorter than 255 characters.")
|
240
|
+
validates_format(/^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/, :email, :message => "Please enter a valid email address.")
|
241
|
+
validates_confirmation(:password, :message => "Make sure your passwords match.")
|
242
|
+
validates_min_length(8, :password_length, :message => "Passwords must be at least 8 characters.", :allow_nil => true)
|
243
|
+
validates_max_length(255, :password_length, :message => "Passwords must be at most 255 characters.", :allow_nil => true)
|
244
|
+
|
245
|
+
# Ensure an administrator is always available.
|
246
|
+
if admins = User.filter(:admin => true) and admins.count == 1 and admins.first.id == self.id and not admin?
|
247
|
+
errors[:admin] << "can't be unset; only one administrator left!"
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
214
251
|
private
|
215
252
|
# Valid characters for salt
|
216
253
|
SALT_CHARS = ('0'..'9').to_a + ('a'..'z').to_a + ('A'..'Z').to_a
|
@@ -232,14 +269,15 @@ module CortexReaver
|
|
232
269
|
public
|
233
270
|
|
234
271
|
# Create default user if none exist
|
235
|
-
if table_exists? and count == 0
|
272
|
+
if dataset.table_exists? and count == 0
|
236
273
|
u = User.new(
|
237
274
|
:login => 'shodan',
|
238
275
|
:name => 'Shodan',
|
276
|
+
:email => 'shodan@localhost.localdomain',
|
239
277
|
:admin => true
|
240
278
|
)
|
241
|
-
u.password = '
|
242
|
-
u.save
|
279
|
+
u.password = 'citadelstation'
|
280
|
+
u.save
|
243
281
|
end
|
244
282
|
end
|
245
283
|
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
class Numeric
|
2
|
+
# Returns a -th, -nd, ... string
|
3
|
+
def ordinal
|
4
|
+
if (11..13).include?(self.to_i % 100)
|
5
|
+
"#{self}th"
|
6
|
+
else
|
7
|
+
case self.to_i % 10
|
8
|
+
when 1; "#{self}st"
|
9
|
+
when 2; "#{self}nd"
|
10
|
+
when 3; "#{self}rd"
|
11
|
+
else "#{self}th"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Ramaze
|
2
|
+
class MemcachedCache
|
3
|
+
# I can't figure out a good way to specify the parameters Ramaze::Cache
|
4
|
+
# uses when instantiating new caches, so I'm just going to override the
|
5
|
+
# setup here. Innate hardcodes the app name as "pristine". :(
|
6
|
+
def cache_setup(host, user, app, name)
|
7
|
+
app = Ramaze.options.app.name
|
8
|
+
@namespace = [host, user, app, name].compact.join('-')
|
9
|
+
options = {:namespace => @namespace}.merge(OPTIONS)
|
10
|
+
servers = options.delete(:servers)
|
11
|
+
@store = ::MemCache.new(servers, options)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -1,25 +1,12 @@
|
|
1
1
|
require 'fileutils'
|
2
2
|
|
3
|
-
module
|
4
|
-
module
|
3
|
+
module Sequel
|
4
|
+
module Plugins
|
5
5
|
module Attachments
|
6
|
-
|
7
6
|
# Attachments allows records to manipulate files associated with them.
|
8
7
|
#
|
9
8
|
# For our purposes, an attachment represents an object that is associated
|
10
|
-
# with a single parent object. A file represents a file on disk.
|
11
|
-
# closely wrap files, but shouldn't be confused with them.
|
12
|
-
|
13
|
-
def self.included(base)
|
14
|
-
base.class_eval do
|
15
|
-
# When we delete a record with attachments, delete the attachments first.
|
16
|
-
before_delete(:delete_attachments) do
|
17
|
-
attachments.each do |attachment|
|
18
|
-
attachment.delete
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
9
|
+
# with a single parent object. A file represents a file on disk.
|
23
10
|
|
24
11
|
# The separator used for public (e.g. HTTP URL) paths.
|
25
12
|
PUBLIC_PATH_SEPARATOR = '/'
|
@@ -27,8 +14,100 @@ module CortexReaver
|
|
27
14
|
# How many bytes to read at a time when saving files.
|
28
15
|
DEFAULT_ATTACHMENT_DIRECTORY_MODE = 0755
|
29
16
|
|
17
|
+
module InstanceMethods
|
18
|
+
# When we delete a record with attachments, delete the attachments
|
19
|
+
# first.
|
20
|
+
def before_delete
|
21
|
+
return false if super == false
|
22
|
+
attachments.each do |attachment|
|
23
|
+
attachment.delete
|
24
|
+
end
|
25
|
+
|
26
|
+
true
|
27
|
+
end
|
28
|
+
|
29
|
+
# Returns a named attachment
|
30
|
+
def attachment(name)
|
31
|
+
Attachment.new(self, name)
|
32
|
+
end
|
33
|
+
|
34
|
+
# Returns the directory which contains attachments for this record.
|
35
|
+
# Forces a save of the record if an ID does not exist.
|
36
|
+
def attachment_path(type = :local, force_save = true)
|
37
|
+
sep = ''
|
38
|
+
case type
|
39
|
+
when :local
|
40
|
+
# We're interested in a local path on disk.
|
41
|
+
sep = File::SEPARATOR
|
42
|
+
path = CortexReaver.config.public_root.dup
|
43
|
+
when :public
|
44
|
+
# We're interested in a public (e.g. HTTP URL) path.
|
45
|
+
sep = PUBLIC_PATH_SEPARATOR
|
46
|
+
path = ''
|
47
|
+
else
|
48
|
+
raise ArgumentError.new('type must be either :local or :public')
|
49
|
+
end
|
50
|
+
|
51
|
+
# If we don't have an ID, save the record to obtain one.
|
52
|
+
if force_save and id.nil?
|
53
|
+
unless save
|
54
|
+
# Save failed!
|
55
|
+
return nil
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# Complete the path.
|
60
|
+
path <<
|
61
|
+
sep + 'data' +
|
62
|
+
sep + self.class.to_s.demodulize.underscore.pluralize +
|
63
|
+
sep + self.id.to_s
|
64
|
+
end
|
65
|
+
|
66
|
+
# Returns an array of attachments.
|
67
|
+
def attachments
|
68
|
+
# Unsaved new records, naturally, have no attachments.
|
69
|
+
return [] if new?
|
70
|
+
|
71
|
+
attachments = Array.new
|
72
|
+
if path = local_attachment_path and File.directory? path
|
73
|
+
begin
|
74
|
+
attachments = Dir.open(path).reject do |name|
|
75
|
+
# Don't include dotfiles
|
76
|
+
name =~ /^\./
|
77
|
+
end
|
78
|
+
attachments.collect! do |name|
|
79
|
+
Attachment.new self, name
|
80
|
+
end
|
81
|
+
rescue
|
82
|
+
# Couldn't read the directory
|
83
|
+
end
|
84
|
+
end
|
85
|
+
attachments
|
86
|
+
end
|
87
|
+
|
88
|
+
# Ensures the attachment directory exists.
|
89
|
+
def create_attachment_directory
|
90
|
+
path = local_attachment_path
|
91
|
+
unless File.directory? path
|
92
|
+
FileUtils.mkdir_p path, :mode => DEFAULT_ATTACHMENT_DIRECTORY_MODE
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
# Returns the local directory which contains attachments for this
|
97
|
+
# record.
|
98
|
+
def local_attachment_path
|
99
|
+
attachment_path :local
|
100
|
+
end
|
101
|
+
|
102
|
+
# Returns the public directory which contains attachments for this
|
103
|
+
# record.
|
104
|
+
def public_attachment_path
|
105
|
+
attachment_path :public
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
30
109
|
class Attachment
|
31
|
-
#
|
110
|
+
# Wraps a file associated with a parent object.
|
32
111
|
|
33
112
|
BUFFER_SIZE = 65536
|
34
113
|
DEFAULT_MODE = 0644
|
@@ -62,8 +141,8 @@ module CortexReaver
|
|
62
141
|
end
|
63
142
|
|
64
143
|
# Returns a File object for this attachment. Optionally specify a mode
|
65
|
-
# string, which is passed to File.new. Creates the attachment directory
|
66
|
-
# necessary.
|
144
|
+
# string, which is passed to File.new. Creates the attachment directory
|
145
|
+
# if necessary.
|
67
146
|
def file(how = 'r', mode = nil)
|
68
147
|
@parent.create_attachment_directory
|
69
148
|
if mode
|
@@ -84,9 +163,10 @@ module CortexReaver
|
|
84
163
|
# :copy copies the file contents using FileUtils.cp
|
85
164
|
# :move moves the file.
|
86
165
|
#
|
87
|
-
# Ramaze offers us temporary File objects from form uploads. Creating a
|
88
|
-
# link is extremely quick, saves disk space, but also doesn't
|
89
|
-
# the temporary file in case someone else wants access
|
166
|
+
# Ramaze offers us temporary File objects from form uploads. Creating a
|
167
|
+
# hard link is extremely quick, saves disk space, but also doesn't
|
168
|
+
# interfere with the temporary file in case someone else wants access
|
169
|
+
# to it.
|
90
170
|
def file=(readable, mode = :hard_link)
|
91
171
|
# Create attachment directory if necessary.
|
92
172
|
@parent.create_attachment_directory
|
@@ -94,11 +174,14 @@ module CortexReaver
|
|
94
174
|
if readable.respond_to? :path
|
95
175
|
ret = case mode
|
96
176
|
when :hard_link
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
177
|
+
begin
|
178
|
+
FileUtils.rm local_path if File.exist? local_path
|
179
|
+
FileUtils.ln readable.path, local_path
|
180
|
+
rescue
|
181
|
+
# Hmm, try copy. Could be a cross-device link, or the FS
|
182
|
+
# doesn't support it.
|
183
|
+
FileUtils.copy readable.path, local_path
|
184
|
+
end
|
102
185
|
when :copy
|
103
186
|
FileUtils.rm local_path if File.exist? local_path
|
104
187
|
FileUtils.copy readable.path, local_path
|
@@ -106,7 +189,7 @@ module CortexReaver
|
|
106
189
|
FileUtils.rm local_path if File.exist? local_path
|
107
190
|
FileUtils.move readable.path, local_path
|
108
191
|
else
|
109
|
-
raise RuntimeError.new("mode must be :hard_link
|
192
|
+
raise RuntimeError.new("mode must be :hard_link :copy, or :move--got #{mode.inspect}")
|
110
193
|
end
|
111
194
|
reset_permissions
|
112
195
|
ret
|
@@ -153,83 +236,8 @@ module CortexReaver
|
|
153
236
|
FileUtils.chmod(DEFAULT_MODE, path(:local))
|
154
237
|
end
|
155
238
|
end
|
156
|
-
|
157
|
-
# Returns a named attachment
|
158
|
-
def attachment(name)
|
159
|
-
Attachment.new(self, name)
|
160
|
-
end
|
161
|
-
|
162
|
-
# Returns the directory which contains attachments for this record. Forces
|
163
|
-
# a save of the record if an ID does not exist.
|
164
|
-
def attachment_path(type = :local, force_save = true)
|
165
|
-
sep = ''
|
166
|
-
case type
|
167
|
-
when :local
|
168
|
-
# We're interested in a local path on disk.
|
169
|
-
sep = File::SEPARATOR
|
170
|
-
path = CortexReaver.config[:public_root].dup
|
171
|
-
when :public
|
172
|
-
# We're interested in a public (e.g. HTTP URL) path.
|
173
|
-
sep = PUBLIC_PATH_SEPARATOR
|
174
|
-
path = ''
|
175
|
-
else
|
176
|
-
raise ArgumentError.new('type must be either :local or :public')
|
177
|
-
end
|
178
|
-
|
179
|
-
# If we don't have an ID, save the record to obtain one.
|
180
|
-
if force_save and id.nil?
|
181
|
-
unless save
|
182
|
-
# Save failed!
|
183
|
-
return nil
|
184
|
-
end
|
185
|
-
end
|
186
|
-
|
187
|
-
# Complete the path.
|
188
|
-
path <<
|
189
|
-
sep + 'data' +
|
190
|
-
sep + self.class.to_s.demodulize.underscore.pluralize +
|
191
|
-
sep + self.id.to_s
|
192
|
-
end
|
193
|
-
|
194
|
-
# Returns an array of attachments.
|
195
|
-
def attachments
|
196
|
-
# Unsaved new records, naturally, have no attachments.
|
197
|
-
return [] if new?
|
198
|
-
|
199
|
-
attachments = Array.new
|
200
|
-
if path = local_attachment_path and File.directory? path
|
201
|
-
begin
|
202
|
-
attachments = Dir.open(path).reject do |name|
|
203
|
-
# Don't include dotfiles
|
204
|
-
name =~ /^\./
|
205
|
-
end
|
206
|
-
attachments.collect! do |name|
|
207
|
-
Attachment.new self, name
|
208
|
-
end
|
209
|
-
rescue
|
210
|
-
# Couldn't read the directory
|
211
|
-
end
|
212
|
-
end
|
213
|
-
attachments
|
214
|
-
end
|
215
|
-
|
216
|
-
# Ensures the attachment directory exists.
|
217
|
-
def create_attachment_directory
|
218
|
-
path = local_attachment_path
|
219
|
-
unless File.directory? path
|
220
|
-
FileUtils.mkdir_p path, :mode => DEFAULT_ATTACHMENT_DIRECTORY_MODE
|
221
|
-
end
|
222
|
-
end
|
223
|
-
|
224
|
-
# Returns the local directory which contains attachments for this record.
|
225
|
-
def local_attachment_path
|
226
|
-
attachment_path :local
|
227
|
-
end
|
228
|
-
|
229
|
-
# Returns the public directory which contains attachments for this record.
|
230
|
-
def public_attachment_path
|
231
|
-
attachment_path :public
|
232
|
-
end
|
233
239
|
end
|
234
240
|
end
|
235
241
|
end
|
242
|
+
|
243
|
+
CortexReaver::Attachment = Sequel::Plugins::Attachments::Attachment
|