cortex-reaver 0.0.1

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 (128) hide show
  1. data/LICENSE +25 -0
  2. data/README +48 -0
  3. data/bin/console +11 -0
  4. data/bin/cortex_reaver +110 -0
  5. data/bin/import.rb +78 -0
  6. data/lib/cortex_reaver.rb +114 -0
  7. data/lib/cortex_reaver/config.rb +21 -0
  8. data/lib/cortex_reaver/controller/comment.rb +113 -0
  9. data/lib/cortex_reaver/controller/config.rb +14 -0
  10. data/lib/cortex_reaver/controller/journal.rb +40 -0
  11. data/lib/cortex_reaver/controller/main.rb +56 -0
  12. data/lib/cortex_reaver/controller/page.rb +34 -0
  13. data/lib/cortex_reaver/controller/photograph.rb +45 -0
  14. data/lib/cortex_reaver/controller/project.rb +40 -0
  15. data/lib/cortex_reaver/controller/tag.rb +67 -0
  16. data/lib/cortex_reaver/controller/user.rb +71 -0
  17. data/lib/cortex_reaver/helper/activity.rb +9 -0
  18. data/lib/cortex_reaver/helper/attachments.rb +139 -0
  19. data/lib/cortex_reaver/helper/auth.rb +45 -0
  20. data/lib/cortex_reaver/helper/canonical.rb +55 -0
  21. data/lib/cortex_reaver/helper/crud.rb +317 -0
  22. data/lib/cortex_reaver/helper/date.rb +29 -0
  23. data/lib/cortex_reaver/helper/error.rb +14 -0
  24. data/lib/cortex_reaver/helper/feeds.rb +88 -0
  25. data/lib/cortex_reaver/helper/form.rb +114 -0
  26. data/lib/cortex_reaver/helper/navigation.rb +142 -0
  27. data/lib/cortex_reaver/helper/photographs.rb +25 -0
  28. data/lib/cortex_reaver/helper/tags.rb +47 -0
  29. data/lib/cortex_reaver/helper/workflow.rb +27 -0
  30. data/lib/cortex_reaver/migrations/001_users.rb +24 -0
  31. data/lib/cortex_reaver/migrations/002_pages.rb +29 -0
  32. data/lib/cortex_reaver/migrations/003_journals.rb +26 -0
  33. data/lib/cortex_reaver/migrations/004_photographs.rb +24 -0
  34. data/lib/cortex_reaver/migrations/005_projects.rb +27 -0
  35. data/lib/cortex_reaver/migrations/006_tags.rb +64 -0
  36. data/lib/cortex_reaver/migrations/007_comments.rb +40 -0
  37. data/lib/cortex_reaver/migrations/008_config.rb +23 -0
  38. data/lib/cortex_reaver/model/comment.rb +109 -0
  39. data/lib/cortex_reaver/model/journal.rb +53 -0
  40. data/lib/cortex_reaver/model/page.rb +87 -0
  41. data/lib/cortex_reaver/model/photograph.rb +133 -0
  42. data/lib/cortex_reaver/model/project.rb +49 -0
  43. data/lib/cortex_reaver/model/tag.rb +72 -0
  44. data/lib/cortex_reaver/model/user.rb +147 -0
  45. data/lib/cortex_reaver/public/css/admin.css +45 -0
  46. data/lib/cortex_reaver/public/css/custom.css +0 -0
  47. data/lib/cortex_reaver/public/css/form.css +51 -0
  48. data/lib/cortex_reaver/public/css/main.css +325 -0
  49. data/lib/cortex_reaver/public/css/photo.css +113 -0
  50. data/lib/cortex_reaver/public/css/ramaze_error.css +90 -0
  51. data/lib/cortex_reaver/public/css/text.css +25 -0
  52. data/lib/cortex_reaver/public/dispatch.fcgi +11 -0
  53. data/lib/cortex_reaver/public/images/CortexReaver.gif +0 -0
  54. data/lib/cortex_reaver/public/images/atom-xml-icon.png +0 -0
  55. data/lib/cortex_reaver/public/images/body.png +0 -0
  56. data/lib/cortex_reaver/public/images/border_bottom.png +0 -0
  57. data/lib/cortex_reaver/public/images/border_bottom_left.png +0 -0
  58. data/lib/cortex_reaver/public/images/border_bottom_right.png +0 -0
  59. data/lib/cortex_reaver/public/images/border_left.png +0 -0
  60. data/lib/cortex_reaver/public/images/border_right.png +0 -0
  61. data/lib/cortex_reaver/public/images/border_top.png +0 -0
  62. data/lib/cortex_reaver/public/images/border_top_left.png +0 -0
  63. data/lib/cortex_reaver/public/images/border_top_right.png +0 -0
  64. data/lib/cortex_reaver/public/images/comment.gif +0 -0
  65. data/lib/cortex_reaver/public/images/dark_trans.png +0 -0
  66. data/lib/cortex_reaver/public/images/delete.gif +0 -0
  67. data/lib/cortex_reaver/public/images/edit.gif +0 -0
  68. data/lib/cortex_reaver/public/images/header.png +0 -0
  69. data/lib/cortex_reaver/public/images/header.xcf +0 -0
  70. data/lib/cortex_reaver/public/images/header_background.png +0 -0
  71. data/lib/cortex_reaver/public/images/parent.gif +0 -0
  72. data/lib/cortex_reaver/public/images/rss-xml-icon.png +0 -0
  73. data/lib/cortex_reaver/public/images/sections.png +0 -0
  74. data/lib/cortex_reaver/public/images/sections_highlight.png +0 -0
  75. data/lib/cortex_reaver/public/js/admin.js +36 -0
  76. data/lib/cortex_reaver/public/js/cookie.js +27 -0
  77. data/lib/cortex_reaver/public/js/jquery.js +32 -0
  78. data/lib/cortex_reaver/public/js/photo.js +33 -0
  79. data/lib/cortex_reaver/snippets/array.rb +7 -0
  80. data/lib/cortex_reaver/snippets/ramaze/dispatcher/file.rb +37 -0
  81. data/lib/cortex_reaver/support/attachments.rb +235 -0
  82. data/lib/cortex_reaver/support/cached_rendering.rb +79 -0
  83. data/lib/cortex_reaver/support/canonical.rb +107 -0
  84. data/lib/cortex_reaver/support/comments.rb +69 -0
  85. data/lib/cortex_reaver/support/pagination.rb +38 -0
  86. data/lib/cortex_reaver/support/renderer.rb +196 -0
  87. data/lib/cortex_reaver/support/sequenceable.rb +248 -0
  88. data/lib/cortex_reaver/support/tags.rb +108 -0
  89. data/lib/cortex_reaver/support/timestamps.rb +33 -0
  90. data/lib/cortex_reaver/version.rb +8 -0
  91. data/lib/cortex_reaver/view/adminbox.rhtml +56 -0
  92. data/lib/cortex_reaver/view/blank_layout.rhtml +46 -0
  93. data/lib/cortex_reaver/view/comments/comment.rhtml +34 -0
  94. data/lib/cortex_reaver/view/comments/form.rhtml +25 -0
  95. data/lib/cortex_reaver/view/comments/list.rhtml +5 -0
  96. data/lib/cortex_reaver/view/comments/post_form.rhtml +36 -0
  97. data/lib/cortex_reaver/view/config/form.rhtml +10 -0
  98. data/lib/cortex_reaver/view/error.rhtml +72 -0
  99. data/lib/cortex_reaver/view/journals/form.rhtml +12 -0
  100. data/lib/cortex_reaver/view/journals/journal.rhtml +39 -0
  101. data/lib/cortex_reaver/view/journals/list.rhtml +33 -0
  102. data/lib/cortex_reaver/view/journals/short.rhtml +3 -0
  103. data/lib/cortex_reaver/view/journals/show.rhtml +5 -0
  104. data/lib/cortex_reaver/view/pages/form.rhtml +12 -0
  105. data/lib/cortex_reaver/view/pages/list.rhtml +26 -0
  106. data/lib/cortex_reaver/view/pages/show.rhtml +12 -0
  107. data/lib/cortex_reaver/view/photographs/atom_fragment.rhtml +82 -0
  108. data/lib/cortex_reaver/view/photographs/form.rhtml +19 -0
  109. data/lib/cortex_reaver/view/photographs/grid.rhtml +36 -0
  110. data/lib/cortex_reaver/view/photographs/list.rhtml +9 -0
  111. data/lib/cortex_reaver/view/photographs/short.rhtml +3 -0
  112. data/lib/cortex_reaver/view/photographs/show.rhtml +114 -0
  113. data/lib/cortex_reaver/view/photographs/sidebar.rhtml +7 -0
  114. data/lib/cortex_reaver/view/projects/form.rhtml +13 -0
  115. data/lib/cortex_reaver/view/projects/list.rhtml +27 -0
  116. data/lib/cortex_reaver/view/projects/show.rhtml +38 -0
  117. data/lib/cortex_reaver/view/tags/form.rhtml +9 -0
  118. data/lib/cortex_reaver/view/tags/list.rhtml +28 -0
  119. data/lib/cortex_reaver/view/tags/show.rhtml +51 -0
  120. data/lib/cortex_reaver/view/text_layout.rhtml +78 -0
  121. data/lib/cortex_reaver/view/users/form.rhtml +16 -0
  122. data/lib/cortex_reaver/view/users/list.rhtml +38 -0
  123. data/lib/cortex_reaver/view/users/login.rhtml +13 -0
  124. data/lib/cortex_reaver/view/users/register.rhtml +13 -0
  125. data/lib/cortex_reaver/view/users/show.rhtml +37 -0
  126. data/lib/cortex_reaver/view/users/user.rhtml +35 -0
  127. data/lib/proto/cortex_reaver.yaml +28 -0
  128. metadata +285 -0
@@ -0,0 +1,133 @@
1
+ module CortexReaver
2
+ class Photograph < Sequel::Model(:photographs)
3
+ def self.url
4
+ '/photographs'
5
+ end
6
+
7
+ include CortexReaver::Model::Timestamps
8
+ include CortexReaver::Model::Canonical
9
+ include CortexReaver::Model::Attachments
10
+ include CortexReaver::Model::Comments
11
+ include CortexReaver::Model::Tags
12
+ include CortexReaver::Model::Sequenceable
13
+
14
+ # Target image sizes
15
+ SIZES = {
16
+ :thumbnail => '150x',
17
+ :grid => '150x150',
18
+ :small => 'x512',
19
+ :medium => 'x768',
20
+ :large => 'x1024',
21
+ }
22
+
23
+ many_to_many :tags, :class => 'CortexReaver::Tag'
24
+ belongs_to :user, :class => 'CortexReaver::User'
25
+ has_many :comments, :class => 'CortexReaver::Comment'
26
+
27
+ validates do
28
+ uniqueness_of :name
29
+ presence_of :name
30
+ length_of :name, :maximum => 255
31
+ presence_of :title
32
+ end
33
+
34
+ def self.get(id)
35
+ self[:name => id] || self[id]
36
+ end
37
+
38
+ def self.recent
39
+ reverse_order(:created_on).limit(16)
40
+ end
41
+
42
+ def atom_url
43
+ '/photographs/atom/' + name
44
+ end
45
+
46
+ # Returns the exif data for this photograph
47
+ def exif
48
+ EXIFR::JPEG.new(self.full_local_path).exif
49
+ end
50
+
51
+ # Gives the best guess for this photograph's date.
52
+ def date
53
+ if exif = self.exif
54
+ fields = [:date_time_original, :date_time_digitized, :date_time]
55
+ fields.each do |field|
56
+ begin
57
+ if date = exif.send(field)
58
+ return date
59
+ end
60
+ rescue
61
+ end
62
+ end
63
+ end
64
+
65
+ # Fall back on creation date
66
+ self.created_on
67
+ end
68
+
69
+ # Store an photograph on disk in the appropriate sizes.
70
+ def image=(file)
71
+ if file.blank? or file.size == 0
72
+ return nil
73
+ end
74
+
75
+ # Write file to disk
76
+ attachment = Attachment.new(self, 'original.jpg')
77
+ attachment.file = file
78
+
79
+ # Read image through ImageMagick
80
+ image = Magick::Image.read(attachment.local_path).first
81
+
82
+ # Write appropriate sizes to disk
83
+ SIZES.each do |size, geometry|
84
+ image.change_geometry(geometry) do |width, height|
85
+ attachment = Attachment.new(self, size.to_s + '.jpg')
86
+ image.scale(width, height).write(attachment.local_path)
87
+ end
88
+ end
89
+
90
+ true
91
+ end
92
+
93
+ # Sets the date from the EXIF date tag.
94
+ def infer_date_from_exif!
95
+ if exif = self.exif
96
+ self.created_on = self.date
97
+ end
98
+ end
99
+
100
+ # Returns the system path to the full photograph
101
+ def full_local_path
102
+ path :local, 'medium'
103
+ end
104
+
105
+ # Returns the path to the full photograph
106
+ def full_public_path
107
+ path :public, 'medium'
108
+ end
109
+
110
+ # The path to a photograph
111
+ def path(type = :local, size = :full)
112
+ attachment(size.to_s + '.jpg').path(type)
113
+ end
114
+
115
+ # Returns the system path to the thumbnail photograph
116
+ def thumbnail_local_path
117
+ path :local, 'thumbnail'
118
+ end
119
+
120
+ # Returns the path to the thumbnail photograph
121
+ def thumbnail_public_path
122
+ path :public, 'thumbnail'
123
+ end
124
+
125
+ def to_s
126
+ title || name
127
+ end
128
+
129
+ def url
130
+ '/photographs/show/' + name
131
+ end
132
+ end
133
+ end
@@ -0,0 +1,49 @@
1
+ module CortexReaver
2
+ class Project < Sequel::Model(:projects)
3
+ def self.url
4
+ '/projects'
5
+ end
6
+
7
+ include CortexReaver::Model::Timestamps
8
+ include CortexReaver::Model::CachedRendering
9
+ include CortexReaver::Model::Renderer
10
+ include CortexReaver::Model::Canonical
11
+ include CortexReaver::Model::Attachments
12
+ include CortexReaver::Model::Comments
13
+ include CortexReaver::Model::Tags
14
+ include CortexReaver::Model::Sequenceable
15
+
16
+ many_to_many :tags, :class => 'CortexReaver::Tag'
17
+ belongs_to :user, :class => 'CortexReaver::User'
18
+ has_many :comments, :class => 'CortexReaver::Comment'
19
+
20
+ validates do
21
+ uniqueness_of :name
22
+ presence_of :name
23
+ length_of :name, :maximum => 255
24
+ presence_of :title
25
+ end
26
+
27
+ render :body
28
+
29
+ def self.get(id)
30
+ self[:name => id] || self[id]
31
+ end
32
+
33
+ def self.recent
34
+ reverse_order(:updated_on).limit(16)
35
+ end
36
+
37
+ def atom_url
38
+ '/projects/atom/' + name
39
+ end
40
+
41
+ def url
42
+ '/projects/show/' + name
43
+ end
44
+
45
+ def to_s
46
+ title || name
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,72 @@
1
+ module CortexReaver
2
+ class Tag < Sequel::Model(:tags)
3
+ include CortexReaver::Model::Canonical
4
+
5
+ many_to_many :photographs, :class => 'CortexReaver::Photograph'
6
+ many_to_many :journals, :class => 'CortexReaver::Journal'
7
+ many_to_many :projects, :class => 'CortexReaver::Project'
8
+ many_to_many :pages, :class => 'CortexReaver::Page'
9
+
10
+ validates do
11
+ uniqueness_of :name
12
+ presence_of :name
13
+ length_of :name, :maximum => 255
14
+ presence_of :title
15
+ length_of :title, :maximum => 255
16
+ end
17
+
18
+ # When we delete a tag, ensure nothing else is linked to it.
19
+ before_destroy(:drop_associations) do
20
+ remove_all_photographs
21
+ remove_all_journals
22
+ remove_all_projects
23
+ remove_all_pages
24
+ end
25
+
26
+ # Autocompletes a tag. Returns an array of matching candidates
27
+ def self.autocomplete(string)
28
+ filter(:title.like(/^#{string}/i)).limit(6).map(:title)
29
+ end
30
+
31
+ # Recalculates the number of children on each tag, and saves the updated values.
32
+ def self.refresh_counts
33
+ all.each do |tag|
34
+ tag.refresh_count
35
+ end
36
+ end
37
+
38
+ def self.get(id)
39
+ self[:name => id] || self[id]
40
+ end
41
+
42
+ def self.url
43
+ '/tags'
44
+ end
45
+
46
+ def atom_url
47
+ '/tags/atom/' + name
48
+ end
49
+
50
+ # Recalculates the number of children on this tag, and saves the update value.
51
+ def refresh_count
52
+ # Find counts
53
+ self[:count] = photographs_dataset.count +
54
+ journals_dataset.count +
55
+ pages_dataset.count +
56
+ projects_dataset.count
57
+
58
+ # Save and return
59
+ self.skip_timestamp_update = true
60
+ self.save
61
+ self[:count]
62
+ end
63
+
64
+ def url
65
+ '/tags/show/' + name
66
+ end
67
+
68
+ def to_s
69
+ title || name
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,147 @@
1
+ require 'digest/sha2'
2
+
3
+ module CortexReaver
4
+ class User < Sequel::Model(:users)
5
+ include CortexReaver::Model::Timestamps
6
+ include CortexReaver::Model::Sequenceable
7
+
8
+ has_many :comments, :class => 'CortexReaver::Comment'
9
+ has_many :journals, :class => 'CortexReaver::Journal'
10
+ has_many :pages, :class => 'CortexReaver::Page'
11
+ has_many :photographs, :class => 'CortexReaver::Photograph'
12
+ has_many :projects, :class => 'CortexReaver::Project'
13
+
14
+ validates do
15
+ uniqueness_of :login
16
+ length_of :login, :with => 5..255, :allow_blank => true
17
+ format_of :login, :with => /^[A-Za-z0-9\-_]+$/
18
+ length_of :name, :maximum => 255
19
+ length_of :http, :allow_blank => true, :maximum => 255
20
+ uniqueness_of :email
21
+ length_of :email, :allow_blank => true, :maximum => 255
22
+ format_of :email,
23
+ :with => /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/, :allow_blank => true
24
+ confirmation_of :password, :allow_nil => true#, :allow_false => true
25
+ each(:password_length, :tag => :password_length) do |object, attribute, value|
26
+ unless value.nil? or (8..255) === value
27
+ object.errors['password'] << 'must be between 8 and 255 characters.'
28
+ end
29
+ end
30
+ end
31
+
32
+ # Ensure an administrator is always available. TODO: This is probably
33
+ # subject to a race condition, especially between servers. Backup plan?
34
+ validates_each :admin do |object, attribute, value|
35
+ if User.filter(:admin => true).count == 1 and not value
36
+ object.errors[attribute] << "can't be unset; only one administrator left!"
37
+ end
38
+ end
39
+
40
+ self.window_size = 64
41
+
42
+ # Returns an authenticated user by login and password, or nil.
43
+ def self.authenticate(login, password)
44
+ user = self[:login => login]
45
+ if user and user.authenticate(password)
46
+ user
47
+ else
48
+ nil
49
+ end
50
+ end
51
+
52
+ # CRUD uses this to construct URLs. Even though we don't need the full
53
+ # power of Canonical, CRUD is pretty useful. :)
54
+ def self.canonical_name_attr
55
+ :login
56
+ end
57
+
58
+ # Get a user
59
+ def self.get(id)
60
+ self[:login => id] || self[id]
61
+ end
62
+
63
+ # Returns true if the user is an administrator.
64
+ def admin?
65
+ self.admin
66
+ end
67
+
68
+ # Authenticate with password
69
+ def authenticate(test_password)
70
+ if self[:password] == self.class.crypt(test_password, self.salt)
71
+ true
72
+ else
73
+ false
74
+ end
75
+ end
76
+
77
+ # Set user password
78
+ def password=(password)
79
+ self.salt ||= self.class.new_salt
80
+ self[:password] = self.class.crypt(password, self.salt)
81
+ @password_length = password.length
82
+ end
83
+
84
+ # Password confirmation
85
+ def password_confirmation=(password)
86
+ self.salt ||= self.class.new_salt
87
+ @password_confirmation = self.class.crypt(password, self.salt)
88
+ end
89
+
90
+ def password_confirmation
91
+ # If password_confirmation was set, use that. Otherwise, fall back
92
+ # to the normal password, so we don't need set the confirmation every
93
+ # time the password is updated programmatically.
94
+ @password_confirmation || self.password
95
+ end
96
+
97
+ # A cache for password length, so we can validate without keeping the
98
+ # password as plaintext.
99
+ def password_length
100
+ @password_length
101
+ end
102
+
103
+ def to_s
104
+ if name.blank?
105
+ login
106
+ else
107
+ name
108
+ end
109
+ end
110
+
111
+ # A URL to view this user
112
+ def url
113
+ '/users/show/' + login
114
+ end
115
+
116
+ private
117
+ # Valid characters for salt
118
+ SALT_CHARS = ('0'..'9').to_a + ('a'..'z').to_a + ('A'..'Z').to_a
119
+
120
+ # Returns hash of password with salt.
121
+ def self.crypt(password, salt)
122
+ Digest::SHA512.hexdigest(password.to_s + salt.to_s)
123
+ end
124
+
125
+ # Returns random salt
126
+ def self.new_salt
127
+ salt = ''
128
+ 40.times do
129
+ salt << SALT_CHARS[rand(SALT_CHARS.size - 1).to_i]
130
+ end
131
+ salt
132
+ end
133
+
134
+ public
135
+
136
+ # Create default user if none exist
137
+ if table_exists? and count == 0
138
+ u = User.new(
139
+ :login => 'shodan',
140
+ :name => 'Shodan',
141
+ :admin => true
142
+ )
143
+ u.password = 'shodan'
144
+ u.save!
145
+ end
146
+ end
147
+ end
@@ -0,0 +1,45 @@
1
+ #adminbox {
2
+ display: none;
3
+ padding: 0.5em;
4
+ margin: 0 0 1em 0;
5
+ background-color: #eaeaea;
6
+ border: 1px solid #aaa;
7
+ border-top: none;
8
+ font-size: 80%;
9
+ }
10
+
11
+ #main #adminbox {
12
+ margin: -0.8em 0em 1em 0em;
13
+ }
14
+
15
+ #adminbox .actions-table {
16
+ width: 100%;
17
+ }
18
+
19
+ #adminbox p {
20
+ margin: 0;
21
+ padding: 0;
22
+ }
23
+
24
+ #adminbox td.title {
25
+ font-weight: bold;
26
+ }
27
+
28
+ #adminbox input[type=text] {
29
+ width: 160px;
30
+ }
31
+
32
+ #admin_section_actions {
33
+ text-align: center;
34
+ }
35
+
36
+ #admin_page_actions {
37
+ }
38
+
39
+ #admin_new_actions {
40
+ float: left;
41
+ }
42
+
43
+ #admin_user_actions {
44
+ float: right;
45
+ }