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,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