radiant-page_attachments-extension 1.0.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.
Files changed (88) hide show
  1. data/.gitignore +5 -0
  2. data/LICENSE +20 -0
  3. data/README.md +129 -0
  4. data/Rakefile +57 -0
  5. data/VERSION +1 -0
  6. data/app/models/observe_page_attachments.rb +5 -0
  7. data/app/models/page_attachment.rb +28 -0
  8. data/app/models/page_attachment_associations.rb +20 -0
  9. data/app/models/page_attachment_tags.rb +261 -0
  10. data/app/models/page_attachments_interface.rb +19 -0
  11. data/app/views/admin/pages/_attachment.html.haml +12 -0
  12. data/app/views/admin/pages/_attachments_box.html.haml +12 -0
  13. data/db/migrate/001_create_page_attachments_extension_schema.rb +22 -0
  14. data/db/migrate/002_add_page_attachments_fields.rb +23 -0
  15. data/features/attachments.feature +42 -0
  16. data/features/step_definitions/attachment_steps.rb +61 -0
  17. data/features/support/env.rb +57 -0
  18. data/features/support/paths.rb +14 -0
  19. data/lib/difference_test_helper.rb +20 -0
  20. data/lib/radiant-page_attachments-extension.rb +0 -0
  21. data/lib/tasks/page_attachments_extension_tasks.rake +28 -0
  22. data/page_attachments_extension.rb +27 -0
  23. data/public/images/admin/drag_order.png +0 -0
  24. data/public/javascripts/admin/page_attachments.js +30 -0
  25. data/public/stylesheets/admin/page_attachments.css +133 -0
  26. data/radiant-page_attachments-extension.gemspec +139 -0
  27. data/spec/controllers/pages_controller_spec.rb +10 -0
  28. data/spec/datasets/page_attachments_dataset.rb +31 -0
  29. data/spec/fixtures/foo.txt +1 -0
  30. data/spec/fixtures/rails.png +0 -0
  31. data/spec/models/observe_page_attachments_spec.rb +14 -0
  32. data/spec/models/page_attachment_spec.rb +18 -0
  33. data/spec/models/page_attachment_tags_spec.rb +172 -0
  34. data/spec/models/page_spec.rb +32 -0
  35. data/spec/spec.opts +6 -0
  36. data/spec/spec_helper.rb +39 -0
  37. data/vendor/plugins/acts_as_list/README +23 -0
  38. data/vendor/plugins/acts_as_list/init.rb +3 -0
  39. data/vendor/plugins/acts_as_list/lib/active_record/acts/list.rb +256 -0
  40. data/vendor/plugins/acts_as_list/test/list_test.rb +332 -0
  41. data/vendor/plugins/attachment_fu/CHANGELOG +35 -0
  42. data/vendor/plugins/attachment_fu/LICENSE +20 -0
  43. data/vendor/plugins/attachment_fu/README +193 -0
  44. data/vendor/plugins/attachment_fu/Rakefile +22 -0
  45. data/vendor/plugins/attachment_fu/amazon_s3.yml.tpl +17 -0
  46. data/vendor/plugins/attachment_fu/init.rb +16 -0
  47. data/vendor/plugins/attachment_fu/install.rb +7 -0
  48. data/vendor/plugins/attachment_fu/lib/geometry.rb +93 -0
  49. data/vendor/plugins/attachment_fu/lib/technoweenie/attachment_fu/backends/cloud_file_backend.rb +211 -0
  50. data/vendor/plugins/attachment_fu/lib/technoweenie/attachment_fu/backends/db_file_backend.rb +39 -0
  51. data/vendor/plugins/attachment_fu/lib/technoweenie/attachment_fu/backends/file_system_backend.rb +126 -0
  52. data/vendor/plugins/attachment_fu/lib/technoweenie/attachment_fu/backends/s3_backend.rb +394 -0
  53. data/vendor/plugins/attachment_fu/lib/technoweenie/attachment_fu/processors/core_image_processor.rb +59 -0
  54. data/vendor/plugins/attachment_fu/lib/technoweenie/attachment_fu/processors/gd2_processor.rb +54 -0
  55. data/vendor/plugins/attachment_fu/lib/technoweenie/attachment_fu/processors/image_science_processor.rb +61 -0
  56. data/vendor/plugins/attachment_fu/lib/technoweenie/attachment_fu/processors/mini_magick_processor.rb +132 -0
  57. data/vendor/plugins/attachment_fu/lib/technoweenie/attachment_fu/processors/rmagick_processor.rb +57 -0
  58. data/vendor/plugins/attachment_fu/lib/technoweenie/attachment_fu.rb +514 -0
  59. data/vendor/plugins/attachment_fu/rackspace_cloudfiles.yml.tpl +14 -0
  60. data/vendor/plugins/attachment_fu/test/backends/db_file_test.rb +16 -0
  61. data/vendor/plugins/attachment_fu/test/backends/file_system_test.rb +143 -0
  62. data/vendor/plugins/attachment_fu/test/backends/remote/cloudfiles_test.rb +102 -0
  63. data/vendor/plugins/attachment_fu/test/backends/remote/s3_test.rb +119 -0
  64. data/vendor/plugins/attachment_fu/test/base_attachment_tests.rb +77 -0
  65. data/vendor/plugins/attachment_fu/test/basic_test.rb +70 -0
  66. data/vendor/plugins/attachment_fu/test/database.yml +18 -0
  67. data/vendor/plugins/attachment_fu/test/extra_attachment_test.rb +67 -0
  68. data/vendor/plugins/attachment_fu/test/fixtures/attachment.rb +226 -0
  69. data/vendor/plugins/attachment_fu/test/fixtures/files/fake/rails.png +0 -0
  70. data/vendor/plugins/attachment_fu/test/fixtures/files/foo.txt +1 -0
  71. data/vendor/plugins/attachment_fu/test/fixtures/files/rails.png +0 -0
  72. data/vendor/plugins/attachment_fu/test/geometry_test.rb +108 -0
  73. data/vendor/plugins/attachment_fu/test/processors/core_image_test.rb +37 -0
  74. data/vendor/plugins/attachment_fu/test/processors/gd2_test.rb +31 -0
  75. data/vendor/plugins/attachment_fu/test/processors/image_science_test.rb +31 -0
  76. data/vendor/plugins/attachment_fu/test/processors/mini_magick_test.rb +103 -0
  77. data/vendor/plugins/attachment_fu/test/processors/rmagick_test.rb +255 -0
  78. data/vendor/plugins/attachment_fu/test/schema.rb +134 -0
  79. data/vendor/plugins/attachment_fu/test/test_helper.rb +150 -0
  80. data/vendor/plugins/attachment_fu/test/validation_test.rb +55 -0
  81. data/vendor/plugins/attachment_fu/vendor/red_artisan/core_image/filters/color.rb +27 -0
  82. data/vendor/plugins/attachment_fu/vendor/red_artisan/core_image/filters/effects.rb +31 -0
  83. data/vendor/plugins/attachment_fu/vendor/red_artisan/core_image/filters/perspective.rb +25 -0
  84. data/vendor/plugins/attachment_fu/vendor/red_artisan/core_image/filters/quality.rb +25 -0
  85. data/vendor/plugins/attachment_fu/vendor/red_artisan/core_image/filters/scale.rb +47 -0
  86. data/vendor/plugins/attachment_fu/vendor/red_artisan/core_image/filters/watermark.rb +32 -0
  87. data/vendor/plugins/attachment_fu/vendor/red_artisan/core_image/processor.rb +123 -0
  88. metadata +178 -0
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ .svn
2
+ .DS_Store
3
+ coverage
4
+ rdoc
5
+ pkg
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010 Sean Cribbs
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,129 @@
1
+ Page Attachments
2
+ ===
3
+
4
+ About
5
+ ---
6
+
7
+ A [Radiant][rd] Extension by [Sean Cribbs][sc] that adds page-attachment-style asset management. Page Attachments adds support for file uploads realized as attachments to individual pages. Attachments can have an order via acts_as_list, a title, a description and various metadata fields as provided by AttachmentFu.
8
+
9
+ Installation
10
+ ---
11
+
12
+ If you want `page_attachments` to generate and display thumbnails of your uploaded images you'll first need to install one of, [`image_science`][is], [`mini-magick`][mm] or [`rmagick`][rm] on your server. This is completely optional, `page_attachments` will still function in every other way without any of these packages installed.
13
+
14
+ Now you're ready to install `page_attachments`.
15
+
16
+ cd /path/to/radiant
17
+ git clone git://github.com/radiant/radiant-page-attachments-extension.git vendor/extensions/page_attachments
18
+ rake radiant:extensions:page_attachments:migrate
19
+ rake radiant:extensions:page_attachments:update
20
+
21
+ If you don't have `git` you can [download a tarball][pa].
22
+
23
+ Attachment thumbnails for images default to `:icon => '50x50>'`. You can customize that by setting
24
+ `PAGE_ATTACHMENT_SIZES` to whatever you need in your `config/environment.rb` file
25
+
26
+ PAGE_ATTACHMENT_SIZES = {:thumb => '120x120>', :normal => '640x480>'}
27
+
28
+ Restart your server and refresh the admin interface.
29
+
30
+ Running features and specs
31
+ ---
32
+ The Cucumber features use Webrat and Selenium. You'll need the webrat 0.5.1 and selenium-client 1.2.16 gems installed and you'll need to change the version of webrat specified in environments/test.rb.
33
+
34
+ Managing Attachments
35
+ ---
36
+
37
+ Now when you login and edit a page, you'll find the "Attachments" interface below the text editing area. To add a new attachment to the page click the **+** icon. If you need to you can upload multiple attachments at once by clicking the **+** icon once for each attachment you'll be adding. Attachments are **not** added or deleted until the page is saved. Therefore, if you accidentally deleted something you meant to keep, simply cancel the page edit.
38
+
39
+ Usage
40
+ ---
41
+
42
+ * See the "available tags" documentation built into the Radiant page admin for more details.
43
+ * Reference an attachment by name `<r:attachment name="file.txt">...</r:attachment>`
44
+ * Display an attachment's URL `<r:attachment:url name="file.jpg"/>`
45
+ * Display an attachment's `#{key}` attribute `<r:attachment:#{key} name="file.jpg"/>`
46
+ * Display the date an attachment was added `<r:attachment:date name="file.txt"/>`
47
+ * Display an attached image `<r:attachment:image name="file.jpg"/>`
48
+ * Display a link to an attachment `<r:attachment:link name="file.jpg"/>` or `<r:attachment:link name="file.jpg">Click Here</r:attachment:link>`
49
+ * Display name of the user who added the attachment `<r:attachment:author name="file.jpg"/>`
50
+ * Iterate through all the attachments on a page `<r:attachment:each><r:link/></r:attachment:each>`
51
+ * Display the extension of an attachement inside iterations with <r:attachment:extension/>
52
+
53
+ Troubleshooting
54
+ ---
55
+
56
+ If you have a problem running the migrate task, and it fails with an error something like this:
57
+
58
+ ld: library not found for -lfreeimage
59
+ collect2: ld returned 1 exit status
60
+
61
+ It means you have installed the ImageScience gem but you don't have FreeImage installed. So, either install FreeImage or uninstall the gem with
62
+
63
+ gem uninstall image_science
64
+
65
+ ---
66
+
67
+ If you're using ImageScience as a user without a home directory, you may see this error:
68
+
69
+ Define INLINEDIR or HOME in your environment and try again
70
+
71
+ This is caused by RubyInline not having a place to store its generated files and can be easily fixed by specifying a path in your environment file like so:
72
+
73
+ ENV['INLINEDIR'] = File.join(RAILS_ROOT,'tmp','ruby_inline')
74
+
75
+ ---
76
+
77
+ If you have trouble attaching files to Page Types other than the normal type, try editing the following line in your `config/environment.rb` file
78
+
79
+ config.extensions = [ :all ]
80
+
81
+ to look like
82
+
83
+ config.extensions = [ :page_attachments, :all ]
84
+
85
+ Amazon S3 for Attachment storage
86
+ ---
87
+
88
+ Since `page_attachments` uses `attachment_fu` for the handling of attachments it's just as easy to use [S3][s3] as it is to use your hard drive. Before you get started with this there are a few things to keep in mind:
89
+
90
+ * If you've already started storing attachments on your hard drive **this will break** any `<r:attachment...>` tags pointing to those files. You'll need to remove all existing attachments and re-add them to Amazon S3.
91
+ * You have to install the `AWS::S3` gem. In some shared hosting environments this might not be possible.
92
+ * The `AWS::S3` gem does not (currently) support EU buckets, so if that's all you have you'll need to create a bucket in the US.
93
+
94
+ Before you start make sure you have `page_attachments` working using your hard drive. Once you've tested an upload or two to the hard drive and feel confident the basic setup is working, dive right in.
95
+
96
+ 1. `gem install aws-s3`
97
+ 2. `cd /path/to/radiant`
98
+ 3. `cp vendor/plugins/attachment_fu/amazon_s3.yml.tpl config/amazon_s3.yml`
99
+ 4. edit `config/amazon_s3.yml` with your S3 credentials
100
+ 5. `cp vendor/extensions/page_attachments/app/models/page_attachment.rb vendor/extensions/page_attachments/app/models/page_attachment.rb.bak`
101
+ 6. edit line 2 of `vendor/extensions/page_attachments/app/models/page_attachment.rb` changing `:file_system` to `:s3`
102
+ 7. restart your server
103
+
104
+ Add an attachment and make sure the link it gives back is on S3. You should see all your attachments start showing up at `http://s3.amazonaws.com/bucket-name/page_attachments/`. While it is possible to customize the URL to Amazon (i.e. http://attachments.your-domain.com/) but it's beyond the scope of this document and a task best left for those that really need custom URLs.
105
+
106
+ Contributors
107
+ ---
108
+
109
+ These people have contributed patches that have been added to the extension:
110
+
111
+ * [John Muhl][jm]
112
+ * [Daniel Collis-Puro][djcp]
113
+ * [James Burka][jb]
114
+ * [Istvan Hoka][ihoka]
115
+ * [Oleg Ivanov][oleg]
116
+
117
+ [rd]: http://radiantcms.org/
118
+ [sc]: http://seancribbs.com/
119
+ [is]: http://seattlerb.rubyforge.org/ImageScience.html
120
+ [mm]: http://rubyforge.org/projects/mini-magick/
121
+ [rm]: http://rmagick.rubyforge.org/
122
+ [af]: http://github.com/technoweenie/attachment_fu/tarball/master
123
+ [pa]: http://github.com/radiant/radiant-page-attachments-extension/tarball/master
124
+ [s3]: http://www.amazon.com/gp/browse.html?node=16427261
125
+ [jm]: http://github.com/johnmuhl
126
+ [djcp]: http://www.kookdujour.com/
127
+ [jb]: http://github.com/jjburka
128
+ [ihoka]: http://github.com/ihoka
129
+ [oleg]: http://github.com/morhekil
data/Rakefile ADDED
@@ -0,0 +1,57 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "radiant-page_attachments-extension"
8
+ gem.summary = %Q{Adds page-attachment-style asset management.}
9
+ gem.description = %Q{Page Attachments adds support for file uploads realized as attachments to individual pages. Attachments can have an order via acts_as_list, a title, a description and various metadata fields as provided by AttachmentFu.}
10
+ gem.email = "radiant@radiantcms.org"
11
+ gem.homepage = "http://github.com/radiant/radiant-page-attachments-extension"
12
+ gem.authors = ["Sean Cribbs"]
13
+ gem.add_development_dependency "rspec"
14
+ gem.add_development_dependency "cucumber"
15
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
16
+ end
17
+ Jeweler::GemcutterTasks.new
18
+ rescue LoadError
19
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
20
+ end
21
+
22
+ require 'spec/rake/spectask'
23
+ Spec::Rake::SpecTask.new(:spec) do |spec|
24
+ spec.libs << 'lib' << 'spec'
25
+ spec.spec_files = FileList['spec/**/*_spec.rb']
26
+ end
27
+
28
+ Spec::Rake::SpecTask.new(:rcov) do |spec|
29
+ spec.libs << 'lib' << 'spec'
30
+ spec.pattern = 'spec/**/*_spec.rb'
31
+ spec.rcov = true
32
+ end
33
+
34
+ task :spec => :check_dependencies
35
+
36
+ begin
37
+ require 'cucumber/rake/task'
38
+ Cucumber::Rake::Task.new(:features)
39
+
40
+ task :features => :check_dependencies
41
+ rescue LoadError
42
+ task :features do
43
+ abort "Cucumber is not available. In order to run features, you must: sudo gem install cucumber"
44
+ end
45
+ end
46
+
47
+ task :default => :spec
48
+
49
+ require 'rake/rdoctask'
50
+ Rake::RDocTask.new do |rdoc|
51
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
52
+
53
+ rdoc.rdoc_dir = 'rdoc'
54
+ rdoc.title = "radiant-page_attachments-extension #{version}"
55
+ rdoc.rdoc_files.include('README*')
56
+ rdoc.rdoc_files.include('lib/**/*.rb')
57
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.0.0
@@ -0,0 +1,5 @@
1
+ module ObservePageAttachments
2
+ def self.included(base)
3
+ base.send :observe, User, Page, Layout, Snippet, PageAttachment
4
+ end
5
+ end
@@ -0,0 +1,28 @@
1
+ class PageAttachment < ActiveRecord::Base
2
+ acts_as_list :scope => :page_id
3
+ has_attachment :storage => :file_system,
4
+ :thumbnails => defined?(PAGE_ATTACHMENT_SIZES) && PAGE_ATTACHMENT_SIZES || {:icon => '50x50>'},
5
+ :max_size => 10.megabytes
6
+ validates_as_attachment
7
+
8
+ belongs_to :created_by,
9
+ :class_name => 'User',
10
+ :foreign_key => 'created_by'
11
+ belongs_to :updated_by,
12
+ :class_name => 'User',
13
+ :foreign_key => 'updated_by'
14
+ belongs_to :page
15
+
16
+ def short_filename(wanted_length = 15, suffix = ' ...')
17
+ (self.filename.length > wanted_length) ? (self.filename[0,(wanted_length - suffix.length)] + suffix) : self.filename
18
+ end
19
+
20
+ def short_title(wanted_length = 15, suffix = ' ...')
21
+ (self.title.length > wanted_length) ? (self.title[0,(wanted_length - suffix.length)] + suffix) : self.title
22
+ end
23
+
24
+ def short_description(wanted_length = 15, suffix = ' ...')
25
+ (self.description.length > wanted_length) ? (self.description[0,(wanted_length - suffix.length)] + suffix) : self.description
26
+ end
27
+
28
+ end
@@ -0,0 +1,20 @@
1
+ module PageAttachmentAssociations
2
+ def self.included(base)
3
+ base.class_eval {
4
+ has_many :attachments,
5
+ :class_name => "PageAttachment",
6
+ :dependent => :destroy,
7
+ :order => 'position'
8
+ include InstanceMethods
9
+ accepts_nested_attributes_for :attachments, :allow_destroy => true
10
+ }
11
+ end
12
+
13
+ module InstanceMethods
14
+ # Currently recursive, but could be simplified with some SQL
15
+ def attachment(name)
16
+ att = attachments.find(:first, :conditions => ["filename LIKE ?", name.to_s])
17
+ att.blank? ? ((parent.attachment(name) if parent) or nil) : att
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,261 @@
1
+ module PageAttachmentTags
2
+ include Radiant::Taggable
3
+
4
+ class TagError < StandardError; end
5
+
6
+ desc %{
7
+ The namespace for referencing page attachments/files. You may specify the 'name'
8
+ attribute (for the filename) on this tag for all contained tags to refer to that attachment.
9
+ Attachments can be inherited from parent pages.
10
+
11
+ *Usage*:
12
+
13
+ <pre><code><r:attachment name="file.txt">...</r:attachment></code></pre>
14
+ }
15
+ tag "attachment" do |tag|
16
+ page = tag.locals.page
17
+ tag.locals.attachment = page.attachment(tag.attr['name']) rescue nil if tag.attr['name']
18
+ tag.expand
19
+ end
20
+
21
+ desc %{
22
+ Renders the url or public filename of the attachment for use in links, stylesheets, etc.
23
+ The 'name' attribute is required on this tag or the parent tag. The optional 'size' attribute
24
+ applies only to images.
25
+
26
+ *Usage*:
27
+
28
+ <pre><code><r:attachment:url name="file.jpg" [size="icon"]/></code></pre>
29
+ }
30
+ tag "attachment:url" do |tag|
31
+ raise TagError, "'name' attribute required" unless name = tag.attr['name'] or tag.locals.attachment
32
+ page = tag.locals.page
33
+ size = tag.attr['size'] || nil
34
+ attachment = tag.locals.attachment || page.attachment(name)
35
+ attachment.public_filename(size)
36
+ end
37
+
38
+ [:short_title,:short_description,:short_filename].each do |key|
39
+ desc %{
40
+ Renders the '#{key}' attribute of the attachment.
41
+ The 'name' attribute is required on this tag or the parent tag.
42
+ The optional 'length' attribute defines how many characters of the attribute to display. It defaults to 15 total characters, including the 'suffix'.
43
+ If the attribute exceeds 'length', 'suffix' says what to tag onto the back to show truncation. It defaults to ' ...'
44
+
45
+ *Usage*:
46
+
47
+ <pre><code><r:attachment:#{key} name="file.jpg" [length="number of characters"] [suffix="More . . ."]/></code></pre>
48
+ }
49
+ tag "attachment:#{key}" do |tag|
50
+ raise TagError, "'name' attribute required" unless name = tag.attr['name'] or tag.locals.attachment
51
+ page = tag.locals.page
52
+ attachment = tag.locals.attachment || page.attachment(name)
53
+ tlength = (tag.attr['length']) ? tag.attr['length'].to_i : 15
54
+ suffix = (tag.attr['suffix']) ? tag.attr['suffix'].to_s : ' ...'
55
+ attachment.send("#{key}",tlength,suffix)
56
+ end
57
+ end
58
+
59
+ desc %{
60
+ Renders the 'size' attribute of the attachment.
61
+ The 'name' attribute is required on this tag or the parent tag. Returns bytes by default. Use the optional 'units' parameter to change the units this tag returns.
62
+
63
+ *Usage*:
64
+
65
+ <pre><code><r:attachment:size name="file.jpg" [units="bytes|kilobytes|megabytes|gigabytes"] /></code></pre>
66
+ }
67
+ tag "attachment:size" do |tag|
68
+ raise TagError, "'name' attribute required" unless name = tag.attr['name'] or tag.locals.attachment
69
+ page = tag.locals.page
70
+ attachment = tag.locals.attachment || page.attachment(name)
71
+ units = tag.attr['units'] || 'bytes'
72
+ valid_units = ['bytes','byte','kilobytes','kilobyte','megabytes','megabyte','gigabytes','gigabyte']
73
+ units = (valid_units.include?(units)) ? units : 'bytes'
74
+ return attachment.size if units == 'bytes'
75
+ sprintf('%.2f',(attachment.size.to_f / 1.send(units)))
76
+ end
77
+
78
+
79
+ [:content_type, :width, :height, :title, :description, :position, :filename].each do |key|
80
+ desc %{
81
+ Renders the '#{key}' attribute of the attachment.
82
+ The 'name' attribute is required on this tag or the parent tag. The optional 'size'
83
+ attributes applies only to images.
84
+
85
+ *Usage*:
86
+
87
+ <pre><code><r:attachment:#{key} name="file.jpg" [size="icon"]/></code></pre>
88
+ }
89
+ tag "attachment:#{key}" do |tag|
90
+ raise TagError, "'name' attribute required" unless name = tag.attr['name'] or tag.locals.attachment
91
+ page = tag.locals.page
92
+ attachment = tag.locals.attachment || page.attachment(name)
93
+ attachment.attributes["#{key}"]
94
+ end
95
+ end
96
+
97
+ desc %{
98
+ Renders the date the attachment was uploaded using the specified 'format' (Ruby's strftime syntax).
99
+ The 'name' attribute is required on this tag or the parent tag.
100
+ }
101
+ tag "attachment:date" do |tag|
102
+ raise TagError, "'name' attribute required" unless name = tag.attr['name'] or tag.locals.attachment
103
+ page = tag.locals.page
104
+ attachment = tag.locals.attachment || page.attachment(name)
105
+ format = tag.attr['format'] || "%F"
106
+ attachment.created_at.strftime(format)
107
+ end
108
+
109
+ desc %{
110
+ Renders an image tag for the attachment (assuming it's an image).
111
+ The 'name' attribute is required on this tag or the parent tag.
112
+ Any other attributes will be added as HTML attributes to the rendered tag.
113
+ The optional 'size' attribute allows you to show the icon size of the image.
114
+
115
+ *Usage*:
116
+
117
+ <pre><code><r:attachment:image name="file.jpg" [size="icon"]/></code></pre>
118
+
119
+ }
120
+ tag "attachment:image" do |tag|
121
+ raise TagError, "'name' attribute required" unless name = tag.attr.delete('name') or tag.locals.attachment
122
+ page = tag.locals.page
123
+ attachment = tag.locals.attachment || page.attachment(name)
124
+ size = tag.attr['size'] || nil
125
+ raise TagError, "attachment is not an image." unless attachment.content_type.strip =~ /^image\//
126
+ filename = attachment.public_filename(size) rescue ""
127
+ attributes = tag.attr.inject([]){ |a,(k,v)| a << %{#{k}="#{v}"} }.join(" ").strip
128
+ %{<img src="#{filename}" #{attributes + " " unless attributes.empty?}/>}
129
+ end
130
+
131
+ desc %{
132
+ Renders a hyperlink to the attachment. The 'name' attribute is required on this tag or the parent tag.
133
+ You can use the 'label' attribute to specify the textual contents of the tag. Any other attributes
134
+ will be added as HTML attributes to the rendered tag. This tag works as both a singleton and a container.
135
+ Any contained content will be rendered inside the resulting link. The optional 'size' attribute applies only to images.
136
+
137
+ *Usage*:
138
+
139
+ <pre><code><r:attachment:link name="file.jpg" [size="thumbnail"]/></code></pre>
140
+ <pre><code><r:attachment:link name="file.jpg" [size="thumbnail"]> Some text in the link </r:attachment:link></code></pre>
141
+ }
142
+ tag "attachment:link" do |tag|
143
+ raise TagError, "'name' attribute required" unless name = tag.attr.delete('name') or tag.locals.attachment
144
+ page = tag.locals.page
145
+ attachment = tag.locals.attachment || page.attachment(name)
146
+ label = tag.attr.delete('label') || attachment.filename
147
+ size = tag.attr.delete('size') || nil
148
+ filename = attachment.public_filename(size) rescue ""
149
+ attributes = tag.attr.inject([]){ |a,(k,v)| a << %{#{k}="#{v}"} }.join(" ").strip
150
+ output = %{<a href="#{filename}"#{" " + attributes unless attributes.empty?}>}
151
+ output << (tag.double? ? tag.expand : label)
152
+ output << "</a>"
153
+ end
154
+
155
+ desc %{
156
+ Renders the name of who uploaded the attachment. The 'name' attribute is required on this tag or the parent tag.
157
+
158
+ *Usage*:
159
+
160
+ <pre><code><r:attachment:author name="file.jpg" /></code></pre>
161
+ }
162
+ tag "attachment:author" do |tag|
163
+ raise TagError, "'name' attribute required" unless name = tag.attr.delete('name') or tag.locals.attachment
164
+ page = tag.locals.page
165
+ attachment = tag.locals.attachment || page.attachment(name)
166
+ if attachment and author = attachment.created_by
167
+ author.name
168
+ end
169
+ end
170
+
171
+ desc %{
172
+ Iterates through all the attachments in the current page. The 'name' attribute is not required
173
+ on any nested attachment tags.
174
+
175
+ *Usage*:
176
+
177
+ <pre><code><r:attachment:each [order="asc|desc"] [by="filename|size|created_at|..."] [limit=0] [offset=0] [extensions="png|pdf|doc"]>
178
+ <r:link /> - <r:date>
179
+ </r:attachment:each></code></pre>
180
+ }
181
+ tag "attachment:each" do |tag|
182
+ page = tag.locals.page
183
+
184
+ returning String.new do |output|
185
+ page.attachments.find(:all, attachments_find_options(tag)).each do |att|
186
+ tag.locals.attachment = att
187
+ output << tag.expand
188
+ end
189
+ end
190
+ end
191
+
192
+ desc %{
193
+ Renders the contained elements only if the current contextual page has one or
194
+ more attachments. The @min_count@ attribute specifies the minimum number of required
195
+ attachments. You can also filter by extensions with the @extensions@ attribute.
196
+
197
+ *Usage:*
198
+ <pre><code><r:if_attachments [min_count="n"] [extensions="doc|pdf"]>...</r:if_attachments></code></pre>
199
+ }
200
+ tag "if_attachments" do |tag|
201
+ count = tag.attr['min_count'] && tag.attr['min_count'].to_i || 0
202
+ attachments = tag.locals.page.attachments.count(:conditions => attachments_find_options(tag)[:conditions])
203
+ tag.expand if attachments >= count
204
+ end
205
+
206
+ desc %{
207
+ Renders the contained elements only if the current contextual page has no attachments.
208
+
209
+ *Usage:*
210
+ <pre><code><r:unless_attachments>...</r:unless_attachments></code></pre>
211
+ }
212
+ tag "unless_attachments" do |tag|
213
+ count = tag.attr[''].to_i
214
+ attachments = tag.locals.page.attachments.count(:conditions => attachments_find_options(tag)[:conditions])
215
+ tag.expand if attachments == 0
216
+ end
217
+
218
+ desc %{
219
+ Renders the 'extension' virtual attribute of the attachment, extracted from filename.
220
+
221
+ *Usage*:
222
+
223
+ <pre><code>
224
+ <ul>
225
+ <r:attachment:each extensions="doc|pdf">
226
+ <li class="<r:extension/>">
227
+ <r:link/>
228
+ </li>
229
+ </r:attachment:each>
230
+ </ul>
231
+ </code></pre>
232
+ }
233
+ tag "attachment:extension" do |tag|
234
+ raise TagError, "must be nested inside an attachment or attachment:each tag" unless tag.locals.attachment
235
+ attachment = tag.locals.attachment
236
+ attachment.filename[/\.(\w+)$/, 1]
237
+ end
238
+
239
+ private
240
+ def attachments_find_options(tag)
241
+ attr = tag.attr.symbolize_keys
242
+
243
+ extensions = attr[:extensions] && attr[:extensions].split('|') || []
244
+ conditions = unless extensions.blank?
245
+ [ extensions.map { |ext| "page_attachments.filename LIKE ?"}.join(' OR '),
246
+ *extensions.map { |ext| "%.#{ext}" } ]
247
+ else
248
+ nil
249
+ end
250
+
251
+ by = attr[:by] || "position"
252
+ order = attr[:order] || "asc"
253
+
254
+ options = {
255
+ :order => "#{by} #{order}",
256
+ :offset => attr[:offset] || nil,
257
+ :limit => attr[:limit] || (attr[:offset] ? 9999 : nil),
258
+ :conditions => conditions
259
+ }
260
+ end
261
+ end