radiant-images-extension 0.1.0

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