cortex-reaver 0.0.1

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