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,14 @@
1
+ module CortexReaver
2
+ class ConfigController < Ramaze::Controller
3
+ map '/config'
4
+ layout '/text_layout'
5
+ template :edit, :form
6
+ engine :Erubis
7
+
8
+ helper :error,
9
+ :auth,
10
+ :form,
11
+ :workflow
12
+
13
+ end
14
+ end
@@ -0,0 +1,40 @@
1
+ module CortexReaver
2
+ class JournalController < Ramaze::Controller
3
+ MODEL = Journal
4
+
5
+ map '/journals'
6
+ layout '/text_layout'
7
+ deny_layout :atom
8
+ template :edit, :form
9
+ template :new, :form
10
+ engine :Erubis
11
+
12
+ helper :error,
13
+ :auth,
14
+ :form,
15
+ :workflow,
16
+ :navigation,
17
+ :date,
18
+ :tags,
19
+ :canonical,
20
+ :crud,
21
+ :attachments,
22
+ :feeds
23
+
24
+ on_second_save do |journal, request|
25
+ journal.tags = request[:tags]
26
+ add_attachments(journal, request[:attachments])
27
+ journal.body = request[:body]
28
+ end
29
+
30
+ on_save do |journal, request|
31
+ journal.title = request[:title]
32
+ journal.name = Journal.canonicalize request[:name], journal.id
33
+ journal.user = session[:user]
34
+ end
35
+
36
+ for_feed do |journal, x|
37
+ x.content journal.body_cache, :type => 'html'
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,56 @@
1
+ require 'builder'
2
+
3
+ module CortexReaver
4
+ class MainController < Ramaze::Controller
5
+ map '/'
6
+ layout '/text_layout'
7
+ helper :workflow, :auth, :error, :navigation, :date, :tags, :form, :feeds
8
+ engine :Erubis
9
+
10
+ # the index action is called automatically when no other action is specified
11
+ def index(id = nil)
12
+ if id and @page = Page.get(id)
13
+ # Render that page.
14
+ @title = @page.title
15
+
16
+ workflow "Edit this page", R(PageController, :edit, @page.name)
17
+ workflow "Delete this page", R(PageController, :delete, @page.name)
18
+
19
+ render_template 'pages/show'
20
+ elsif id
21
+ # Didn't have that page
22
+ error_404
23
+ else
24
+ # Default welcome page
25
+ @photographs = Photograph.recent
26
+ @journals = Journal.recent
27
+
28
+ if @photographs.size > 0
29
+ @sidebar ||= []
30
+ @sidebar.unshift render_template('photographs/sidebar.rhtml')
31
+ end
32
+
33
+ workflow "New Page", R(JournalController, :new)
34
+ workflow "New Project", R(JournalController, :new)
35
+ workflow "New Journal", R(JournalController, :new)
36
+ workflow "New Photograph", R(JournalController, :new)
37
+
38
+ feed 'Photographs', Rs(PhotographController, :atom)
39
+ feed 'Journals', Rs(JournalController, :atom)
40
+ feed 'Projects', Rs(ProjectController, :atom)
41
+ feed 'Comments', Rs(CommentController, :atom)
42
+
43
+ render_template 'journals/list.rhtml'
44
+ end
45
+ end
46
+
47
+ private
48
+
49
+ # the string returned at the end of the function is used as the html body
50
+ # if there is no template for the action. if there is a template, the string
51
+ # is silently ignored
52
+ def notemplate
53
+ "there is no 'notemplate.xhtml' associated with this action"
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,34 @@
1
+ module CortexReaver
2
+ class PageController < Ramaze::Controller
3
+ MODEL = Page
4
+
5
+ map '/pages'
6
+ layout '/text_layout'
7
+ template :edit, :form
8
+ template :new, :form
9
+ engine :Erubis
10
+
11
+ helper :error,
12
+ :auth,
13
+ :form,
14
+ :workflow,
15
+ :navigation,
16
+ :date,
17
+ :tags,
18
+ :canonical,
19
+ :crud,
20
+ :attachments
21
+
22
+ on_second_save do |page, request|
23
+ page.tags = request[:tags]
24
+ add_attachments(page, request[:attachments])
25
+ end
26
+
27
+ on_save do |page, request|
28
+ page.title = request[:title]
29
+ page.name = Page.canonicalize request[:name], page.id
30
+ page.body = request[:body]
31
+ page.user = session[:user]
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,45 @@
1
+ require 'exifr'
2
+ require 'RMagick'
3
+ module CortexReaver
4
+ class PhotographController < Ramaze::Controller
5
+ MODEL = Photograph
6
+
7
+ map '/photographs'
8
+ layout '/blank_layout'
9
+ layout '/text_layout' => [:index, :edit, :new, :page]
10
+ template_paths << 'view'
11
+ template :edit, :form
12
+ template :new, :form
13
+ engine :Erubis
14
+
15
+ helper :error,
16
+ :auth,
17
+ :form,
18
+ :workflow,
19
+ :navigation,
20
+ :date,
21
+ :tags,
22
+ :canonical,
23
+ :crud,
24
+ :attachments,
25
+ :photographs,
26
+ :feeds
27
+
28
+
29
+ on_save do |photograph, request|
30
+ photograph.title = request[:title]
31
+ photograph.name = Photograph.canonicalize request[:name], photograph.id
32
+ photograph.user = session[:user]
33
+ end
34
+
35
+ on_second_save do |photograph, request|
36
+ photograph.tags = request[:tags]
37
+ photograph.image = request[:image][:tempfile] if request[:image]
38
+ photograph.infer_date_from_exif! if request[:infer_date]
39
+ end
40
+
41
+ for_feed do |photograph, x|
42
+ x.content render_template('atom_fragment.rhtml', :photograph => photograph)
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,40 @@
1
+ module CortexReaver
2
+ class ProjectController < Ramaze::Controller
3
+ MODEL = Project
4
+
5
+ map '/projects'
6
+ layout '/text_layout'
7
+ template :edit, :form
8
+ template :new, :form
9
+ engine :Erubis
10
+
11
+ helper :error,
12
+ :auth,
13
+ :form,
14
+ :workflow,
15
+ :navigation,
16
+ :date,
17
+ :tags,
18
+ :canonical,
19
+ :crud,
20
+ :attachments,
21
+ :feeds
22
+
23
+ on_second_save do |project, request|
24
+ project.tags = request[:tags]
25
+ add_attachments(project, request[:attachments])
26
+ end
27
+
28
+ on_save do |project, request|
29
+ project.title = request[:title]
30
+ project.description = request[:description]
31
+ project.name = Journal.canonicalize request[:name], project.id
32
+ project.body = request[:body]
33
+ project.user = session[:user]
34
+ end
35
+
36
+ for_feed do |project, x|
37
+ x.content project.body_cache, :type => 'html'
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,67 @@
1
+ module CortexReaver
2
+ class TagController < Ramaze::Controller
3
+ MODEL = Tag
4
+
5
+ map '/tags'
6
+ layout '/text_layout'
7
+ template :edit, :form
8
+ template :new, :form
9
+ engine :Erubis
10
+
11
+ helper :error,
12
+ :auth,
13
+ :form,
14
+ :workflow,
15
+ :navigation,
16
+ :canonical,
17
+ :crud
18
+
19
+ on_save do |tag, request|
20
+ tag.title = request[:title]
21
+ tag.name = Tag.canonicalize request[:name], tag.id
22
+ end
23
+
24
+ def index(*ids)
25
+ if ids.size > 0
26
+ raw_redirect Rs([:show] + ids), :status => 301
27
+ else
28
+ # Index
29
+ @title = "All Tags"
30
+ @tags = Tag.order(:count).reverse
31
+ render_template :list
32
+ end
33
+ end
34
+
35
+ def show(*ids)
36
+ # Find tags
37
+ tags = []
38
+ bad_ids = []
39
+ ids.each do |id|
40
+ if tag = Tag.get(id)
41
+ tags << tag
42
+ else
43
+ bad_ids << id
44
+ end
45
+ end
46
+
47
+ # Error message
48
+ unless bad_ids.empty?
49
+ flash[:error] = "No tags called #{h bad_ids.join(', ')}."
50
+ end
51
+
52
+ if tags.empty?
53
+ # Index
54
+ redirect :index
55
+ else
56
+ # Specific tags
57
+ @tags = tags
58
+ @title = "Tags: #{h tags.join(', ')}"
59
+ end
60
+ end
61
+
62
+ # Tags are only created through tagging.
63
+ def new
64
+ error_404
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,71 @@
1
+ module CortexReaver
2
+ class UserController < Ramaze::Controller
3
+ MODEL = User
4
+
5
+ map '/users'
6
+ layout '/text_layout'
7
+ template :edit, :form
8
+ template :new, :form
9
+ engine :Erubis
10
+
11
+ helper :error,
12
+ :auth,
13
+ :form,
14
+ :workflow,
15
+ :navigation,
16
+ :date,
17
+ :tags,
18
+ :canonical,
19
+ :crud,
20
+ :aspect
21
+
22
+ on_save do |user, request|
23
+ user.login = request[:login]
24
+ user.name = request[:name]
25
+ user.http = request[:http]
26
+ user.email = request[:email]
27
+ user.admin = request[:admin] || false
28
+
29
+ unless request[:password].blank? and request[:password_confirmation].blank?
30
+ # Set password
31
+ user.password = request[:password]
32
+ user.password_confirmation = request[:password_confirmation]
33
+ end
34
+ end
35
+
36
+ # Listing users outright is a little dodgy.
37
+ before :index do
38
+ require_admin
39
+ end
40
+
41
+ def login
42
+ @title = "Login"
43
+
44
+ if request.post?
45
+ if user = do_login(request[:login], request[:password])
46
+ # Successful login
47
+ flash[:notice] = "Welcome, #{user.name}."
48
+
49
+ if uri = session.delete(:target_uri) || request[:target_uri]
50
+ # Send the user to their original destination.
51
+ redirect uri
52
+ else
53
+ # Try the main page.
54
+ redirect R(:/)
55
+ end
56
+ else
57
+ # Nope, no login.
58
+ flash[:error] = "Wrong username or password."
59
+ redirect Rs(:login)
60
+ end
61
+ end
62
+ end
63
+
64
+ def logout
65
+ if user = do_logout
66
+ flash[:notice] = "Goodbye, #{user.name}"
67
+ end
68
+ redirect '/'
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,9 @@
1
+ module Ramaze
2
+ module Helper
3
+ module Activity
4
+ # TODO: think about this
5
+ def activity_list(records)
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,139 @@
1
+ module Ramaze
2
+ module Helper
3
+ # Helps with attachments. Requires CRUD.
4
+ module Attachments
5
+ Helper::LOOKUP << self
6
+
7
+ # Deletes an attachment on a model identified by id.
8
+ def delete_attachment(id, name)
9
+ require_admin
10
+
11
+ unless @model = model_class.get(id)
12
+ flash[:error] = "No such #{model_class.to_s.downcase} (#{h id}) exists."
13
+ redirect Rs()
14
+ end
15
+
16
+ unless attachment = @model.attachment(name)
17
+ flash[:error] = "No such attachment (#{h name}) exists."
18
+ redirect @model.url
19
+ end
20
+
21
+ begin
22
+ attachment.delete
23
+ flash[:notice] = "Deleted attachment #{h name}."
24
+ rescue => e
25
+ flash[:error] = "Couldn't delete attachment #{h name}: #{h e.message}."
26
+ end
27
+
28
+ redirect @model.url
29
+ end
30
+
31
+ private
32
+
33
+ # Saves all attachments from a request which match form to model. Returns true if
34
+ # all were successful. Returns an array of attachments which were added.
35
+ def add_attachments(model, attachments)
36
+ attachments.each do |key, file|
37
+ puts "adding #{key}: #{file.inspect}"
38
+ if tempfile = file[:tempfile] and filename = file[:filename] and not filename.blank?
39
+ model.attachment(filename).file = tempfile
40
+ end
41
+ end
42
+ attachments
43
+ end
44
+
45
+ # Renders an attachments form for a model. Shows current attachments,
46
+ # provides links to edit/delete, and a multiple-upload box. Doesn't
47
+ # include form tags.
48
+ def attachment_form(model)
49
+ s = "<div id=\"files\" class=\"files\">\n <ul>\n "
50
+ model.attachments.each do |attachment|
51
+ s << "<li><a href=\"#{attachment.public_path}\">#{attachment.name}</a> (#{A('delete', :href => Rs(:delete_attachment, model.name, attachment.name))})</li>\n"
52
+ end
53
+ s << "</ul>\n</div>\n\n"
54
+
55
+ s << <<EOF
56
+ <script type="text/javascript">
57
+ // The number of created attachments.
58
+ var created_attachments = 0;
59
+ // The number of existing attachments.
60
+ var existing_attachments = 0;
61
+
62
+ function add_attachment_field_to(element) {
63
+ // The attachment id we'll be creating;
64
+ var id = created_attachments++;
65
+ existing_attachments++;
66
+
67
+ // The paragraph which contains our file entry line.
68
+ var p = document.createElement('p');
69
+
70
+ // Label.
71
+ var label = document.createElement('label');
72
+ label.setAttribute('for', 'attachments_' + id);
73
+ label.appendChild(document.createTextNode('Attach:'));
74
+ p.appendChild(label);
75
+
76
+ // File field.
77
+ field = document.createElement('input');
78
+ field.setAttribute('id', 'attachments_' + id);
79
+ field.setAttribute('name', 'attachments[' + id + ']');
80
+ field.setAttribute('size', '30');
81
+ field.setAttribute('type', 'file');
82
+ field.setAttribute('onchange', 'attachment_field_changed(this);');
83
+ p.appendChild(field);
84
+
85
+ // Remove button
86
+ remove = document.createElement('input');
87
+ remove.setAttribute('type', 'button');
88
+ remove.setAttribute('value', 'Clear');
89
+ remove.setAttribute('onclick', 'clear_attachment_field(this.previousSibling);');
90
+ p.appendChild(remove);
91
+
92
+ // Add paragraph to element.
93
+ element.appendChild(p);
94
+ }
95
+
96
+ function attachment_field_changed(field) {
97
+ if (field.value.length == 0) {
98
+ remove_attachment_field(field);
99
+ }
100
+ if (field.value.length > 0) {
101
+ add_attachment_field_to(field.parentNode.parentNode);
102
+ }
103
+ }
104
+
105
+ function clear_attachment_field(field) {
106
+ field.value = '';
107
+ attachment_field_changed(field);
108
+ }
109
+
110
+ function remove_attachment_field(field) {
111
+ if (existing_attachments > 1) {
112
+ field.parentNode.parentNode.removeChild(field.parentNode);
113
+ existing_attachments--;
114
+ }
115
+ }
116
+
117
+ // Add the first attachment field.
118
+ add_attachment_field_to(document.getElementById('files'));
119
+ </script>
120
+ EOF
121
+ end
122
+
123
+ # An HTML table of all attachments on a model
124
+ def attachment_list(model)
125
+ s = '<div class="attachments">'
126
+ s << '<h2>Files</h2>'
127
+ s << '<table>'
128
+ model.attachments.each do |attachment|
129
+ s << "
130
+ <tr>
131
+ <td><a href=\"#{attr_h attachment.public_path}\">#{h attachment.name}</a></td>
132
+ <td class=\"date\">#{File.mtime(attachment.local_path).strftime('%A, %d %B %Y, %H:%M')}</td>
133
+ </tr>"
134
+ end
135
+ s << "</table>\n</div>"
136
+ end
137
+ end
138
+ end
139
+ end