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
data/.gitignore ADDED
@@ -0,0 +1 @@
1
+ .DS_Store
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2010 SquareTalent
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,41 @@
1
+ # Radiant Images
2
+
3
+ Radiant Images is an IMAGE management tool, meant only to be useful to pages and extensions that need to require images.
4
+
5
+ ## Primary (only) Goals
6
+
7
+ ### Light Weight
8
+
9
+ > We're not replacing paperclipped, we're just building a simple image manager extension.
10
+
11
+ ### Uncomplicated
12
+
13
+ > People need to be able to look at the model and instantly know what it's doing, being able to do so means they can easily extend it
14
+
15
+ ### Easily Extendable
16
+
17
+ > Images is a base for things like galleries and shop, where they don't need additional assets
18
+
19
+ ### Strictly aws and paperclip
20
+
21
+ > We don't work with images locally because we use Heroku. So we're not going to give you the option to start locally
22
+
23
+ ### Migrate from paperclipped easily
24
+
25
+ > paperclipped is freakin' awesome, we use it and we need to be able to migrate the images over
26
+
27
+ rake radiant:extensions:images:migrate_from_paperclipped
28
+
29
+ ## Host Alias Settings
30
+
31
+ By default the images extension uses the following url for images:
32
+
33
+ http://s3.amazonaws.com/bucketname/images/name_of_image-style.(png|jpg|gif)
34
+
35
+ There is a radiant configuration option called s3.host_alias. By default it is blank, if you give it a value, the images extension will use the FQDN method to access your images. Setting your host alias to domain.name.com for example would produce a URL like this:
36
+
37
+ http://domain.name.com/images/name_of_image-style.(png|jpg|gif)
38
+
39
+ ## License
40
+
41
+ Radiant Images is licensed under the MIT standard license. See LICENSE for further information.
data/Rakefile ADDED
@@ -0,0 +1,141 @@
1
+ begin
2
+ require 'jeweler'
3
+ Jeweler::Tasks.new do |gem|
4
+ gem.name = "radiant-images-extension"
5
+ gem.summary = 'Images Extension for Radiant CMS'
6
+ gem.description = 'Image Radiant Extension management tool, meant only to be useful to pages and extensions that need to require images.'
7
+ gem.email = "info@squaretalent.com"
8
+ gem.homepage = "http://github.com/squaretalent/radiant-images-extension"
9
+ gem.authors = ['squaretalent']
10
+ gem.add_dependency 'paperclip', '~> 2.3.1.1'
11
+ gem.add_dependency 'aws-s3', '>= 0.6.2'
12
+ gem.add_dependency 'acts_as_list', '>= 0.1.2'
13
+ gem.add_development_dependency 'rspec', '>= 1.3.0'
14
+ gem.add_development_dependency 'rr', '>= 1.0.0'
15
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
16
+ end
17
+ rescue LoadError
18
+ puts "Jeweler (or a dependency) not available. This is only required if you plan to package images as a gem."
19
+ end
20
+
21
+ # In rails 1.2, plugins aren't available in the path until they're loaded.
22
+ # Check to see if the rspec plugin is installed first and require
23
+ # it if it is. If not, use the gem version.
24
+
25
+ # Determine where the RSpec plugin is by loading the boot
26
+ unless defined? RADIANT_ROOT
27
+ ENV["RAILS_ENV"] = "test"
28
+ case
29
+ when ENV["RADIANT_ENV_FILE"]
30
+ require File.dirname(ENV["RADIANT_ENV_FILE"]) + "/boot"
31
+ when File.dirname(__FILE__) =~ %r{vendor/radiant/vendor/extensions}
32
+ require "#{File.expand_path(File.dirname(__FILE__) + "/../../../../../")}/config/boot"
33
+ else
34
+ require "#{File.expand_path(File.dirname(__FILE__) + "/../../../")}/config/boot"
35
+ end
36
+ end
37
+
38
+ require 'rake'
39
+ require 'rake/rdoctask'
40
+ require 'rake/testtask'
41
+
42
+ rspec_base = File.expand_path(RADIANT_ROOT + '/vendor/plugins/rspec/lib')
43
+ $LOAD_PATH.unshift(rspec_base) if File.exist?(rspec_base)
44
+ require 'spec/rake/spectask'
45
+ require 'cucumber'
46
+ require 'cucumber/rake/task'
47
+
48
+ # Cleanup the RADIANT_ROOT constant so specs will load the environment
49
+ Object.send(:remove_const, :RADIANT_ROOT)
50
+
51
+ extension_root = File.expand_path(File.dirname(__FILE__))
52
+
53
+ task :default => :spec
54
+ task :stats => "spec:statsetup"
55
+
56
+ desc "Run all specs in spec directory"
57
+ Spec::Rake::SpecTask.new(:spec) do |t|
58
+ t.spec_opts = ['--options', "\"#{extension_root}/spec/spec.opts\""]
59
+ t.spec_files = FileList['spec/**/*_spec.rb']
60
+ end
61
+
62
+ task :features => 'spec:integration'
63
+
64
+ namespace :spec do
65
+ desc "Run all specs in spec directory with RCov"
66
+ Spec::Rake::SpecTask.new(:rcov) do |t|
67
+ t.spec_opts = ['--options', "\"#{extension_root}/spec/spec.opts\""]
68
+ t.spec_files = FileList['spec/**/*_spec.rb']
69
+ t.rcov = true
70
+ t.rcov_opts = ['--exclude', 'spec', '--rails']
71
+ end
72
+
73
+ desc "Print Specdoc for all specs"
74
+ Spec::Rake::SpecTask.new(:doc) do |t|
75
+ t.spec_opts = ["--format", "specdoc", "--dry-run"]
76
+ t.spec_files = FileList['spec/**/*_spec.rb']
77
+ end
78
+
79
+ [:models, :controllers, :views, :helpers].each do |sub|
80
+ desc "Run the specs under spec/#{sub}"
81
+ Spec::Rake::SpecTask.new(sub) do |t|
82
+ t.spec_opts = ['--options', "\"#{extension_root}/spec/spec.opts\""]
83
+ t.spec_files = FileList["spec/#{sub}/**/*_spec.rb"]
84
+ end
85
+ end
86
+
87
+ desc "Run the Cucumber features"
88
+ Cucumber::Rake::Task.new(:integration) do |t|
89
+ t.fork = true
90
+ t.cucumber_opts = ['--format', (ENV['CUCUMBER_FORMAT'] || 'pretty')]
91
+ # t.feature_pattern = "#{extension_root}/features/**/*.feature"
92
+ t.profile = "default"
93
+ end
94
+
95
+ # Setup specs for stats
96
+ task :statsetup do
97
+ require 'code_statistics'
98
+ ::STATS_DIRECTORIES << %w(Model\ specs spec/models)
99
+ ::STATS_DIRECTORIES << %w(View\ specs spec/views)
100
+ ::STATS_DIRECTORIES << %w(Controller\ specs spec/controllers)
101
+ ::STATS_DIRECTORIES << %w(Helper\ specs spec/views)
102
+ ::CodeStatistics::TEST_TYPES << "Model specs"
103
+ ::CodeStatistics::TEST_TYPES << "View specs"
104
+ ::CodeStatistics::TEST_TYPES << "Controller specs"
105
+ ::CodeStatistics::TEST_TYPES << "Helper specs"
106
+ ::STATS_DIRECTORIES.delete_if {|a| a[0] =~ /test/}
107
+ end
108
+
109
+ namespace :db do
110
+ namespace :fixtures do
111
+ desc "Load fixtures (from spec/fixtures) into the current environment's database. Load specific fixtures using FIXTURES=x,y"
112
+ task :load => :environment do
113
+ require 'active_record/fixtures'
114
+ ActiveRecord::Base.establish_connection(RAILS_ENV.to_sym)
115
+ (ENV['FIXTURES'] ? ENV['FIXTURES'].split(/,/) : Dir.glob(File.join(RAILS_ROOT, 'spec', 'fixtures', '*.{yml,csv}'))).each do |fixture_file|
116
+ Fixtures.create_fixtures('spec/fixtures', File.basename(fixture_file, '.*'))
117
+ end
118
+ end
119
+ end
120
+ end
121
+ end
122
+
123
+ desc 'Generate documentation for the images extension.'
124
+ Rake::RDocTask.new(:rdoc) do |rdoc|
125
+ rdoc.rdoc_dir = 'rdoc'
126
+ rdoc.title = 'ImagesExtension'
127
+ rdoc.options << '--line-numbers' << '--inline-source'
128
+ rdoc.rdoc_files.include('README')
129
+ rdoc.rdoc_files.include('lib/**/*.rb')
130
+ end
131
+
132
+ # For extensions that are in transition
133
+ desc 'Test the images extension.'
134
+ Rake::TestTask.new(:test) do |t|
135
+ t.libs << 'lib'
136
+ t.pattern = 'test/**/*_test.rb'
137
+ t.verbose = true
138
+ end
139
+
140
+ # Load any custom rakefiles for extension
141
+ Dir[File.dirname(__FILE__) + '/tasks/*.rake'].sort.each { |f| require f }
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,50 @@
1
+ class Admin::ImagesController < Admin::ResourceController
2
+
3
+ before_filter :index_assets, :only => [ :index ]
4
+ before_filter :edit_assets, :only => [ :show, :edit ]
5
+ around_filter :rescue_exceptions, :only => [:create, :edit, :destroy]
6
+
7
+ # GET /admin/images
8
+ # GET /admin/images.js
9
+ # GET /admin/images.xml
10
+ # GET /admin/images.json AJAX and HTML
11
+ #----------------------------------------------------------------------------
12
+
13
+ def index
14
+ @images = Image.paginate :page => params[:page], :per_page => 20
15
+ end
16
+
17
+
18
+ def search
19
+ @images = Image.search params[:search], params[:p]
20
+
21
+ respond_to do |format|
22
+ format.html { render }
23
+ format.js { render @images }
24
+ format.xml { render :xml => @images.to_xml }
25
+ format.json { render :json => @images.to_json }
26
+ end
27
+ end
28
+
29
+ private
30
+
31
+ def index_assets
32
+ include_javascript 'admin/extensions/images/index'
33
+ include_stylesheet 'admin/extensions/images/index'
34
+ end
35
+
36
+ def edit_assets
37
+ include_javascript 'admin/extensions/images/edit'
38
+ include_stylesheet 'admin/extensions/images/edit'
39
+ end
40
+
41
+ def rescue_exceptions
42
+ begin
43
+ yield
44
+ rescue AWS::S3::ResponseError => e
45
+ flash[:error] = e.to_s
46
+ redirect_to admin_images_url
47
+ end
48
+ end
49
+
50
+ end
@@ -0,0 +1,91 @@
1
+ class Image < ActiveRecord::Base
2
+
3
+ belongs_to :created_by, :class_name => 'User'
4
+ belongs_to :updated_by, :class_name => 'User'
5
+
6
+ before_save :assign_title
7
+ validates_uniqueness_of :asset_file_name, :message => 'This file already exists', :allow_nil => true
8
+ validates_uniqueness_of :title
9
+
10
+ default_scope :order => 'position ASC'
11
+ acts_as_list
12
+
13
+ has_attached_file :asset,
14
+ :styles => lambda { Image.config_styles },
15
+ :whiny_thumbnails => false,
16
+ :default_url => '/images/extensions/images/missing_:style.png',
17
+
18
+ :storage => :s3,
19
+ :s3_credentials => {
20
+ :access_key_id => Radiant::Config['s3.key'],
21
+ :secret_access_key => Radiant::Config['s3.secret']
22
+ },
23
+ :bucket => Radiant::Config['s3.bucket'],
24
+ :path => Radiant::Config['s3.path']
25
+
26
+ validates_attachment_presence :asset
27
+ validates_attachment_content_type :asset, :content_type => ['image/jpeg', 'image/png', 'image/gif']
28
+
29
+ def assign_title
30
+ self.title = self.asset_file_name if title.blank?
31
+ end
32
+
33
+
34
+ # We need to ovveried the url method for our attachment so
35
+ # we can dynamically swap between aliased and non aliased domain names
36
+ module ::Paperclip
37
+ class Attachment
38
+
39
+ def url(style = :original, include_updated_timestamp = true, secure = false)
40
+ style = style.to_s
41
+ prefix = secure ? 'https://' : 'http://'
42
+ domain = Radiant::Config['s3.host_alias'].empty? ? 's3.amazonaws.com' : Radiant::Config['s3.host_alias']
43
+ bucket = Radiant::Config['s3.host_alias'].empty? ? '/' + Radiant::Config['s3.bucket'] : ''
44
+
45
+ url = prefix + domain + bucket + '/images/' + basename + '-' + style + '.' + extension
46
+ include_updated_timestamp && updated_at ? [url, updated_at].compact.join(url.include?("?") ? "&" : "?") : url
47
+ end
48
+
49
+ def basename
50
+ File.basename(instance.asset_file_name, ".*") if instance.asset_file_name
51
+ end
52
+
53
+ def extension
54
+ instance.asset_file_name.split('.').last.downcase if instance.asset_file_name
55
+ end
56
+
57
+ end
58
+ end
59
+
60
+ private
61
+
62
+ class << self
63
+ def search(search, page)
64
+ unless search.blank?
65
+ queries = []
66
+ queries << 'LOWER(title) LIKE (:term)'
67
+ queries << 'LOWER(caption) LIKE (:term)'
68
+ queries << 'LOWER(asset_file_name) LIKE (:term)'
69
+
70
+ sql = queries.join(' OR ')
71
+ @conditions = [sql, {:term => "%#{search.downcase}%" }]
72
+ else
73
+ @conditions = []
74
+ end
75
+
76
+ self.all :conditions => @conditions
77
+
78
+ end
79
+
80
+ def config_styles
81
+ styles = []
82
+
83
+ if Radiant::Config['images.styles']
84
+ styles = Radiant::Config['images.styles'].gsub(/\s+/,'').split(',')
85
+ styles = styles.collect{|s| s.split('=')}.inject({}) {|ha, (k, v)| ha[k.to_sym] = v; ha}
86
+ end
87
+ styles
88
+ end
89
+ end
90
+
91
+ end
@@ -0,0 +1,13 @@
1
+ %ul{ :style =>'float: left'}
2
+ - render_region :bottom do |bottom|
3
+ - bottom.create do
4
+ %li
5
+ = link_to image('plus') + " " + t("new_image"), new_admin_image_path
6
+ - bottom.search do
7
+ %li
8
+ - form_tag nil, :method => 'get' do
9
+ = text_field_tag 'search', params[:search], :type => 'search'
10
+ = observe_field 'search', :frequency => 2, :update => 'images', :url => { :controller => 'images', :action=> 'search' }, :method => 'get', :with => "'search=' + escape(value)"
11
+ - render_region :paginate do |paginate|
12
+ - paginate.pagination do
13
+ = will_paginate(@images)
@@ -0,0 +1,22 @@
1
+ - render_region :form do |form|
2
+ - form.title do
3
+ %p.title
4
+ = fields.label :title
5
+ = fields.text_field :title, :class => 'textbox'
6
+ - form.drawer do
7
+ .drawer
8
+ .drawer_contents#attributes
9
+ %table.fieldset
10
+ %tr
11
+ %th.label
12
+ = fields.label :caption
13
+ %td.field
14
+ = fields.text_field :caption, :class => 'textbox'
15
+ .drawer_handle
16
+ = link_to 'More', '#attributes', { :class => 'more toggle', :rel => 'toggle[attributes]' }
17
+ - form.upload do
18
+ %p.update
19
+ = fields.label :asset
20
+ = fields.file_field :asset
21
+ - form.popups do
22
+ - nil
@@ -0,0 +1,10 @@
1
+ - render_region :form_bottom do |form_bottom|
2
+ - form_bottom.buttons do
3
+ %p.buttons
4
+ = save_model_button(@image)
5
+ = save_model_and_continue_editing_button(@image)
6
+ = t('or')
7
+ = link_to t('cancel'), admin_images_url
8
+
9
+ - form_bottom.timestamps do
10
+ = updated_stamp @image
@@ -0,0 +1,7 @@
1
+ - render_region :form_top do |form_top|
2
+ - form_top.asset_image do
3
+ - unless @image.new_record?
4
+ = link_to image_tag(@image.asset.url(:preview)), @image.asset.url(:original)
5
+ - @image.asset.styles.each do |style, attrs|
6
+ - unless style.to_sym == :preview
7
+ = link_to style.to_s, @image.asset.url(style.to_sym)
@@ -0,0 +1,12 @@
1
+ %li.image{:id => "image#{image.id}", :'data-id' => image.id}
2
+ .attributes
3
+ - render_region :attributes do |attributes|
4
+ - attributes.thumbnail do
5
+ .image.attribute
6
+ %a{ :href => edit_admin_image_path(image), :title => image.title }
7
+ %img.icon{ :src => image.asset.url(:icon) }
8
+ - attributes.title do
9
+ %span.title.attribute= link_to image.title, edit_admin_image_path(image)
10
+ - attributes.modify do
11
+ .modify.attribute
12
+ %span.remove= link_to t('remove'), remove_admin_image_path(image)
@@ -0,0 +1,14 @@
1
+ - @page_title = @image.title.titleize + ' - ' + default_page_title
2
+
3
+ - render_region :top do |top|
4
+ - top.title do
5
+ %h1 Edit Image
6
+
7
+ - form_for :image, @image, :url => admin_image_path(@image), :html => {:method => :put, :multipart => true, :id => 'edit_image', 'data-onsubmit_status'=>"Saving Changes&#8230;"} do |fields|
8
+ #asset
9
+ = render :partial => 'fields_top', :locals => { :fields => fields }
10
+ #fields
11
+ = render :partial => 'fields', :locals => { :fields => fields }
12
+
13
+ #fields_bottom
14
+ = render :partial => 'fields_bottom'
@@ -0,0 +1,16 @@
1
+ - @page_title = t('images') + ' - ' + default_page_title
2
+
3
+ .outset
4
+
5
+ #images_map
6
+
7
+ #images_headings
8
+ %h2.images Image
9
+
10
+ %h2.modify Modify
11
+
12
+ %ul#images
13
+ = render @images
14
+
15
+ #actions
16
+ = render 'bottom'