radiant-images-extension 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/LICENSE +19 -0
- data/README.md +41 -0
- data/Rakefile +141 -0
- data/VERSION +1 -0
- data/app/controllers/admin/images_controller.rb +50 -0
- data/app/models/image.rb +91 -0
- data/app/views/admin/images/_bottom.haml +13 -0
- data/app/views/admin/images/_fields.haml +22 -0
- data/app/views/admin/images/_fields_bottom.haml +10 -0
- data/app/views/admin/images/_fields_top.haml +7 -0
- data/app/views/admin/images/_image.haml +12 -0
- data/app/views/admin/images/edit.haml +14 -0
- data/app/views/admin/images/index.haml +16 -0
- data/app/views/admin/images/new.haml +12 -0
- data/app/views/admin/images/remove.haml +12 -0
- data/config/locales/en.yml +8 -0
- data/config/routes.rb +7 -0
- data/cucumber.yml +1 -0
- data/db/migrate/20100601042237_create_images.rb +22 -0
- data/db/migrate/20100602044124_add_position_to_images.rb +13 -0
- data/features/support/env.rb +16 -0
- data/features/support/paths.rb +14 -0
- data/images_extension.rb +53 -0
- data/lib/images/interface/admin/images.rb +37 -0
- data/lib/images/tags/image.rb +171 -0
- data/lib/tasks/images_extension_tasks.rake +77 -0
- data/public/images/extensions/images/missing_icon.png +0 -0
- data/public/images/extensions/images/missing_normal.png +0 -0
- data/public/images/extensions/images/missing_preview.png +0 -0
- data/public/javascripts/admin/extensions/images/edit.js +0 -0
- data/public/stylesheets/sass/admin/extensions/images/edit.sass +8 -0
- data/public/stylesheets/sass/admin/extensions/images/index.sass +103 -0
- data/radiant-images-extension.gemspec +99 -0
- data/spec/controllers/admin/images_controller_spec.rb +135 -0
- data/spec/datasets/images.rb +15 -0
- data/spec/lib/images/tags/image_spec.rb +240 -0
- data/spec/models/image_spec.rb +65 -0
- data/spec/spec.opts +4 -0
- data/spec/spec_helper.rb +22 -0
- 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
|
data/config/routes.rb
ADDED
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
|
data/images_extension.rb
ADDED
@@ -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
|