radiant-images-extension 0.1.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 (41) hide show
  1. data/.gitignore +1 -0
  2. data/LICENSE +19 -0
  3. data/README.md +41 -0
  4. data/Rakefile +141 -0
  5. data/VERSION +1 -0
  6. data/app/controllers/admin/images_controller.rb +50 -0
  7. data/app/models/image.rb +91 -0
  8. data/app/views/admin/images/_bottom.haml +13 -0
  9. data/app/views/admin/images/_fields.haml +22 -0
  10. data/app/views/admin/images/_fields_bottom.haml +10 -0
  11. data/app/views/admin/images/_fields_top.haml +7 -0
  12. data/app/views/admin/images/_image.haml +12 -0
  13. data/app/views/admin/images/edit.haml +14 -0
  14. data/app/views/admin/images/index.haml +16 -0
  15. data/app/views/admin/images/new.haml +12 -0
  16. data/app/views/admin/images/remove.haml +12 -0
  17. data/config/locales/en.yml +8 -0
  18. data/config/routes.rb +7 -0
  19. data/cucumber.yml +1 -0
  20. data/db/migrate/20100601042237_create_images.rb +22 -0
  21. data/db/migrate/20100602044124_add_position_to_images.rb +13 -0
  22. data/features/support/env.rb +16 -0
  23. data/features/support/paths.rb +14 -0
  24. data/images_extension.rb +53 -0
  25. data/lib/images/interface/admin/images.rb +37 -0
  26. data/lib/images/tags/image.rb +171 -0
  27. data/lib/tasks/images_extension_tasks.rake +77 -0
  28. data/public/images/extensions/images/missing_icon.png +0 -0
  29. data/public/images/extensions/images/missing_normal.png +0 -0
  30. data/public/images/extensions/images/missing_preview.png +0 -0
  31. data/public/javascripts/admin/extensions/images/edit.js +0 -0
  32. data/public/stylesheets/sass/admin/extensions/images/edit.sass +8 -0
  33. data/public/stylesheets/sass/admin/extensions/images/index.sass +103 -0
  34. data/radiant-images-extension.gemspec +99 -0
  35. data/spec/controllers/admin/images_controller_spec.rb +135 -0
  36. data/spec/datasets/images.rb +15 -0
  37. data/spec/lib/images/tags/image_spec.rb +240 -0
  38. data/spec/models/image_spec.rb +65 -0
  39. data/spec/spec.opts +4 -0
  40. data/spec/spec_helper.rb +22 -0
  41. metadata +191 -0
@@ -0,0 +1,12 @@
1
+ - @page_title = 'New Image - ' + default_page_title
2
+
3
+ - render_region :top do |top|
4
+ - top.title do
5
+ %h1 New Image
6
+
7
+ - form_for :image, @image, :url => admin_images_path, :html => {:multipart => true, :id => 'new_image', 'data-onsubmit_status'=>"Creating Image…"} do |fields|
8
+ #fields
9
+ = render :partial => 'fields', :locals => { :fields => fields }
10
+
11
+ #fields_bottom
12
+ = render :partial => 'fields_bottom'
@@ -0,0 +1,12 @@
1
+ %h1
2
+ = t('remove_image')
3
+
4
+ %p
5
+ = t('text.remove_image.warning')
6
+
7
+ - form_for [:admin, @image.becomes(Image)], :html => {:method => :delete, 'data-onsubmit_status'=>"Removing Image…"} do
8
+
9
+ %p.buttons
10
+ %input.button{:type=>"submit", :value => t('delete_image') }/
11
+ = t('or')
12
+ = link_to t('cancel'), admin_images_path
@@ -0,0 +1,8 @@
1
+ ---
2
+ en:
3
+ images: Images
4
+ new_image: 'New Image'
5
+ remove_image: 'Remove Image'
6
+ delete_image: 'Delete Image'
7
+ text.remove_image.warning: 'Are you sure you want to permanently remove the following Images?'
8
+
data/config/routes.rb ADDED
@@ -0,0 +1,7 @@
1
+ ActionController::Routing::Routes.draw do |map|
2
+
3
+ map.namespace :admin, :member => { :remove => :get } do |admin|
4
+ admin.resources :images, :collection => { :search => :get }
5
+ end
6
+
7
+ end
data/cucumber.yml ADDED
@@ -0,0 +1 @@
1
+ default: --format progress features --tags ~@proposed,~@in_progress
@@ -0,0 +1,22 @@
1
+ class CreateImages < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :images do |t|
4
+ t.string :title
5
+ t.string :caption
6
+
7
+ t.string :asset_file_name
8
+ t.string :asset_content_type
9
+ t.string :asset_file_size
10
+
11
+ t.integer :created_by
12
+ t.integer :updated_by
13
+ t.datetime :created_at
14
+ t.datetime :updated_at
15
+ end
16
+
17
+ end
18
+
19
+ def self.down
20
+ drop_table :images
21
+ end
22
+ end
@@ -0,0 +1,13 @@
1
+ class AddPositionToImages < ActiveRecord::Migration
2
+ def self.up
3
+ add_column :images, :position, :integer
4
+
5
+ Image.all.each_with_index do |image, i|
6
+ image.update_attribute('position', i+1)
7
+ end
8
+ end
9
+
10
+ def self.down
11
+ remove_column :images, :position
12
+ end
13
+ end
@@ -0,0 +1,16 @@
1
+ # Sets up the Rails environment for Cucumber
2
+ ENV["RAILS_ENV"] = "test"
3
+ # Extension root
4
+ extension_env = File.expand_path(File.dirname(__FILE__) + '/../../../../../config/environment')
5
+ require extension_env+'.rb'
6
+
7
+ Dir.glob(File.join(RADIANT_ROOT, "features", "**", "*.rb")).each {|step| require step}
8
+
9
+ Cucumber::Rails::World.class_eval do
10
+ include Dataset
11
+ datasets_directory "#{RADIANT_ROOT}/spec/datasets"
12
+ Dataset::Resolver.default = Dataset::DirectoryResolver.new("#{RADIANT_ROOT}/spec/datasets", File.dirname(__FILE__) + '/../../spec/datasets', File.dirname(__FILE__) + '/../datasets')
13
+ self.datasets_database_dump_path = "#{Rails.root}/tmp/dataset"
14
+
15
+ # dataset :images
16
+ end
@@ -0,0 +1,14 @@
1
+ def path_to(page_name)
2
+ case page_name
3
+
4
+ when /the homepage/i
5
+ root_path
6
+
7
+ when /login/i
8
+ login_path
9
+ # Add more page name => path mappings here
10
+
11
+ else
12
+ raise "Can't find mapping from \"#{page_name}\" to a path."
13
+ end
14
+ end
@@ -0,0 +1,53 @@
1
+ # Uncomment this if you reference any of your controllers in activate
2
+ # require_dependency 'application_controller'
3
+
4
+ class ImagesExtension < Radiant::Extension
5
+ version "0.1"
6
+ description "Images stores images on s3"
7
+ url "http://github.com/squaretalent/radiant-images-extension"
8
+
9
+ extension_config do |config|
10
+ config.gem 'paperclip', :version => '~> 2.3.1.1'
11
+ config.gem 'aws-s3', :version => '>= 0.6.2', :lib => 'aws/s3'
12
+ config.gem 'acts_as_list', :version => '>= 0.1.2'
13
+
14
+ if RAILS_ENV == :test
15
+ config.gem 'rr', :version => '>= 1.0.0'
16
+ end
17
+
18
+ end
19
+
20
+ def activate
21
+
22
+ # require the settings extension to be loaded
23
+ unless Radiant::Extension.descendants.any? { |extension| extension.extension_name == 'Settings' }
24
+ warn 'Error: The Images extension requires the Settings extension to be installed.'
25
+ warn 'Either install the Settings extension or remove Images.'
26
+ exit(1)
27
+ end
28
+
29
+ unless defined? admin.image
30
+ Radiant::AdminUI.send :include, Images::Interface::Admin::Images
31
+ admin.image = Radiant::AdminUI.load_default_image_regions
32
+ end
33
+
34
+
35
+ Page.send :include, Images::Tags::Image
36
+
37
+ UserActionObserver.instance.send :add_observer!, Image
38
+
39
+ tab 'Content' do
40
+ add_item 'Images', '/admin/images', :after => 'Pages'
41
+ end
42
+
43
+ Radiant::Config['images.styles'] ||= "icon=45x45#,preview=200x200#,normal=640x640#"
44
+ Radiant::Config['images.default'] ||= "original"
45
+
46
+ Radiant::Config['s3.host_alias'] ||= ""
47
+ Radiant::Config['s3.key'] ||= "set"
48
+ Radiant::Config['s3.secret'] ||= "set"
49
+ Radiant::Config['s3.bucket'] ||= "fs.domain.com"
50
+ Radiant::Config['s3.path'] ||= ":class/:basename-:style.:extension"
51
+
52
+ end
53
+ end
@@ -0,0 +1,37 @@
1
+ module Images
2
+ module Interface
3
+ module Admin
4
+ module Images
5
+
6
+ def self.included(base)
7
+ base.class_eval do
8
+
9
+ attr_accessor :image
10
+ alias_method :images, :image
11
+
12
+ protected
13
+
14
+ def load_default_image_regions
15
+ returning OpenStruct.new do |image|
16
+ image.edit = Radiant::AdminUI::RegionSet.new do |edit|
17
+ edit.top.concat %w{ title }
18
+ edit.form_top.concat %w{ asset_image }
19
+ edit.form.concat %w{ title drawer upload popups }
20
+ edit.form_bottom.concat %w{ buttons timestamps }
21
+ end
22
+ image.new = image.edit
23
+ image.index = Radiant::AdminUI::RegionSet.new do |index|
24
+ index.attributes.concat %w{thumbnail title modify }
25
+ index.bottom.concat %w{ create search }
26
+ index.paginate.concat %w{ pagination }
27
+ end
28
+ image.searchearch = image.index
29
+ image.remove = image.index
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,171 @@
1
+ module Images
2
+ module Tags
3
+ module Image
4
+ include Radiant::Taggable
5
+
6
+ class TagError < StandardError; end
7
+
8
+ desc %{
9
+ The namespace for referencing images. You may specify the title
10
+ attribute for this tag to use only images with the specified title.
11
+
12
+ *Usage:*
13
+ <pre><code><r:images [title="image_title"]>...</r:images></code></pre>
14
+ }
15
+ tag 'images' do |tag|
16
+ tag.locals.image = ::Image.find_by_title(tag.attr['title']) || ::Image.find(tag.attr['id']) unless tag.attr.empty?
17
+ tag.expand
18
+ end
19
+
20
+ desc %{
21
+ Goes through each of the available images.
22
+ Use the limit and offset attributes to render a specific number of images.
23
+ Use the by and order attributes to control the order of images.
24
+
25
+ *Usage:*
26
+ <pre><code><r:images:each [limit=0] [offset=0] [order="asc|desc"] [by="position|title|..."]>...</r:images:each></code></pre>
27
+ }
28
+ tag 'images:each' do |tag|
29
+ options = tag.attr.dup
30
+ result = []
31
+ images = ::Image.find(:all, images_find_options(tag))
32
+ tag.locals.images = images
33
+ images.each do |image|
34
+ tag.locals.image = image
35
+ result << tag.expand
36
+ end
37
+ result
38
+ end
39
+
40
+ desc %{
41
+ Renders the first image.
42
+
43
+ *Usage:*
44
+ <pre><code><r:images:first>...</r:images:first></code></pre>
45
+ }
46
+ tag 'images:first' do |tag|
47
+ if first = ::Image.first
48
+ tag.locals.image = first
49
+ tag.expand
50
+ end
51
+ end
52
+
53
+ desc %{
54
+ Renders the contained elements only if the current image is the first.
55
+
56
+ *Usage:*
57
+ <pre><code><r:if_first>...</r:if_first></code></pre>
58
+ }
59
+ tag 'images:if_first' do |tag|
60
+ images = tag.locals.images
61
+ image = tag.locals.image
62
+ if image == images.first
63
+ tag.expand
64
+ end
65
+ end
66
+
67
+ desc %{
68
+ Renders the contained elements only if the current image is not the first.
69
+
70
+ *Usage:*
71
+ <pre><code><r:unless_first>...</r:unless_first></code></pre>
72
+ }
73
+ tag 'images:unless_first' do |tag|
74
+ images = tag.locals.images
75
+ image = tag.locals.image
76
+ if image != images.first
77
+ tag.expand
78
+ end
79
+ end
80
+
81
+ desc %{
82
+ Renders the contained elements only if the current contextual page has one or
83
+ more image. The min_count attribute specifies the minimum number of required
84
+ images.
85
+
86
+ *Usage:*
87
+ <pre><code><r:if_images [min_count="n"]>...</r:if_images></code></pre>
88
+ }
89
+ tag 'if_images' do |tag|
90
+ count = tag.attr['min_count'] && tag.attr['min_count'].to_i || 1
91
+ images = ::Image.count
92
+ tag.expand if images >= count
93
+ end
94
+
95
+ desc %{
96
+ Renders the contained elements only if the current contextual page does not
97
+ have one or more image. The min_count attribute specifies the minimum number
98
+ of required images.
99
+
100
+ *Usage:*
101
+ <pre><code><r:if_images [min_count="n"]>...</r:if_images></code></pre>
102
+ }
103
+ tag 'unless_images' do |tag|
104
+ count = tag.attr['min_count'] && tag.attr['min_count'].to_i || 1
105
+ images = ::Image.count
106
+ tag.expand unless images >= count
107
+ end
108
+
109
+ desc %{
110
+ Outputs the full URL of the image including the filename. Specify the style
111
+ using the style option.
112
+ }
113
+ tag 'images:url' do |tag|
114
+ style = tag.attr['style'] || :original
115
+ image, options = image_and_options(tag)
116
+ image.asset.url(style, false) rescue nil
117
+ end
118
+
119
+ desc %{
120
+ Outputs the title of the current image
121
+ }
122
+ tag 'images:title' do |tag|
123
+ image, option = image_and_options(tag)
124
+ image.title rescue nil
125
+ end
126
+
127
+ desc %{
128
+ Outputs the image tag for the current image. Use the size option to
129
+ specify which size version of the image is to be used. Use alt to
130
+ specify alt text.
131
+
132
+ *Usage:*
133
+ <pre><code><r:images:tag [title="image_title"] [size="icon|original"] [alt="alt_text"]></code></pre>
134
+ }
135
+ tag 'images:tag' do |tag|
136
+ image, options = image_and_options(tag)
137
+ size = options['size'] ? options.delete('size') : 'original'
138
+ alt = " alt=\"#{image.title}\"" unless tag.attr['alt'] rescue nil
139
+ attributes = options.inject('') { |s, (k, v)| s << %{#{k.downcase}="#{v}" } }.strip
140
+ attributes << alt unless alt.nil?
141
+ url = image.asset.url size
142
+ %{<img src=\"#{url}\" #{attributes unless attributes.empty?} />} rescue nil
143
+ end
144
+
145
+
146
+ private
147
+ def image_and_options(tag)
148
+ options = tag.attr.dup
149
+ [find_image(tag, options), options]
150
+ end
151
+
152
+ def find_image(tag, options)
153
+ raise TagError, "'title' attribute required" unless title = options.delete('title') or id = options.delete('id') or tag.locals.image
154
+ tag.locals.image || ::Image.find_by_title(title) || ::Image.find(id)
155
+ end
156
+
157
+ def images_find_options(tag)
158
+ attr = tag.attr.symbolize_keys
159
+ by = attr[:by] || 'position'
160
+ order = attr[:order] || 'asc'
161
+
162
+ options = {
163
+ :order => "#{by} #{order}",
164
+ :limit => attr[:limit] || nil,
165
+ :offset => attr[:offset] || nil
166
+ }
167
+ end
168
+
169
+ end
170
+ end
171
+ end
@@ -0,0 +1,77 @@
1
+ namespace :radiant do
2
+ namespace :extensions do
3
+ namespace :images do
4
+
5
+ desc "Runs the migration of the Images extension"
6
+ task :migrate => :environment do
7
+ require 'radiant/extension_migrator'
8
+ if ENV["VERSION"]
9
+ ImagesExtension.migrator.migrate(ENV["VERSION"].to_i)
10
+ Rake::Task['db:schema:dump'].invoke
11
+ else
12
+ ImagesExtension.migrator.migrate
13
+ Rake::Task['db:schema:dump'].invoke
14
+ end
15
+ end
16
+
17
+ desc "Migrates from Paperclipped to Images"
18
+ task :migrate_from_paperclipped => :environment do
19
+ Asset.all.each do |asset|
20
+ if asset.asset_type.name == :image
21
+ Image.create({
22
+ :title => asset.title,
23
+ :asset_file_name => asset.asset_file_name,
24
+ :asset_content_type => asset.asset_content_type,
25
+ :asset_file_size => asset.asset_file_size
26
+ })
27
+ end
28
+ end
29
+
30
+ Radiant::Config['s3.bucket'] = Radiant::Config['assets.s3.bucket']
31
+ Radiant::Config['s3.key'] = Radiant::Config['assets.s3.key']
32
+ Radiant::Config['s3.secret'] = Radiant::Config['assets.s3.secret']
33
+ Radiant::Config['s3.path'] = Radiant::Config['assets.s3.path'].gsub(':class', 'assets')
34
+
35
+ ENV['CLASS'] = 'Image'
36
+ Rake::Task['paperclip:refresh:thumbnails'].invoke
37
+ end
38
+
39
+ desc "Copies public assets of the Images to the instance public/ directory."
40
+ task :update => :environment do
41
+ is_svn_or_dir = proc {|path| path =~ /\.svn/ || File.directory?(path) }
42
+ puts "Copying assets from ImagesExtension"
43
+ Dir[ImagesExtension.root + "/public/**/*"].reject(&is_svn_or_dir).each do |file|
44
+ path = file.sub(ImagesExtension.root, '')
45
+ directory = File.dirname(path)
46
+ mkdir_p RAILS_ROOT + directory, :verbose => false
47
+ cp file, RAILS_ROOT + path, :verbose => false
48
+ end
49
+ unless ImagesExtension.root.starts_with? RAILS_ROOT # don't need to copy vendored tasks
50
+ puts "Copying rake tasks from ImagesExtension"
51
+ local_tasks_path = File.join(RAILS_ROOT, %w(lib tasks))
52
+ mkdir_p local_tasks_path, :verbose => false
53
+ Dir[File.join ImagesExtension.root, %w(lib tasks *.rake)].each do |file|
54
+ cp file, local_tasks_path, :verbose => false
55
+ end
56
+ end
57
+ end
58
+
59
+ desc "Syncs all available translations for this ext to the English ext master"
60
+ task :sync => :environment do
61
+ # The main translation root, basically where English is kept
62
+ language_root = ImagesExtension.root + "/config/locales"
63
+ words = TranslationSupport.get_translation_keys(language_root)
64
+
65
+ Dir["#{language_root}/*.yml"].each do |filename|
66
+ next if filename.match('_available_tags')
67
+ basename = File.basename(filename, '.yml')
68
+ puts "Syncing #{basename}"
69
+ (comments, other) = TranslationSupport.read_file(filename, basename)
70
+ words.each { |k,v| other[k] ||= words[k] } # Initializing hash variable as empty if it does not exist
71
+ other.delete_if { |k,v| !words[k] } # Remove if not defined in en.yml
72
+ TranslationSupport.write_file(filename, basename, comments, other)
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end