X-Git-Url: https://projects.mako.cc/source/selectricity-live/blobdiff_plain/6a935d078f20d2b1b0d60f0a30c41a642d40758a..5e74f498cd2f8d765e309ca52a45a7c2db0a1a6f:/vendor/plugins/attachment_fu/lib/technoweenie/attachment_fu/backends/s3_backend.rb diff --git a/vendor/plugins/attachment_fu/lib/technoweenie/attachment_fu/backends/s3_backend.rb b/vendor/plugins/attachment_fu/lib/technoweenie/attachment_fu/backends/s3_backend.rb new file mode 100644 index 0000000..b81fbde --- /dev/null +++ b/vendor/plugins/attachment_fu/lib/technoweenie/attachment_fu/backends/s3_backend.rb @@ -0,0 +1,309 @@ +module Technoweenie # :nodoc: + module AttachmentFu # :nodoc: + module Backends + # = AWS::S3 Storage Backend + # + # Enables use of {Amazon's Simple Storage Service}[http://aws.amazon.com/s3] as a storage mechanism + # + # == Requirements + # + # Requires the {AWS::S3 Library}[http://amazon.rubyforge.org] for S3 by Marcel Molina Jr. installed either + # as a gem or a as a Rails plugin. + # + # == Configuration + # + # Configuration is done via RAILS_ROOT/config/amazon_s3.yml and is loaded according to the RAILS_ENV. + # The minimum connection options that you must specify are a bucket name, your access key id and your secret access key. + # If you don't already have your access keys, all you need to sign up for the S3 service is an account at Amazon. + # You can sign up for S3 and get access keys by visiting http://aws.amazon.com/s3. + # + # Example configuration (RAILS_ROOT/config/amazon_s3.yml) + # + # development: + # bucket_name: appname_development + # access_key_id: + # secret_access_key: + # + # test: + # bucket_name: appname_test + # access_key_id: + # secret_access_key: + # + # production: + # bucket_name: appname + # access_key_id: + # secret_access_key: + # + # You can change the location of the config path by passing a full path to the :s3_config_path option. + # + # has_attachment :storage => :s3, :s3_config_path => (RAILS_ROOT + '/config/s3.yml') + # + # === Required configuration parameters + # + # * :access_key_id - The access key id for your S3 account. Provided by Amazon. + # * :secret_access_key - The secret access key for your S3 account. Provided by Amazon. + # * :bucket_name - A unique bucket name (think of the bucket_name as being like a database name). + # + # If any of these required arguments is missing, a MissingAccessKey exception will be raised from AWS::S3. + # + # == About bucket names + # + # Bucket names have to be globaly unique across the S3 system. And you can only have up to 100 of them, + # so it's a good idea to think of a bucket as being like a database, hence the correspondance in this + # implementation to the development, test, and production environments. + # + # The number of objects you can store in a bucket is, for all intents and purposes, unlimited. + # + # === Optional configuration parameters + # + # * :server - The server to make requests to. Defaults to s3.amazonaws.com. + # * :port - The port to the requests should be made on. Defaults to 80 or 443 if :use_ssl is set. + # * :use_ssl - If set to true, :port will be implicitly set to 443, unless specified otherwise. Defaults to false. + # + # == Usage + # + # To specify S3 as the storage mechanism for a model, set the acts_as_attachment :storage option to :s3. + # + # class Photo < ActiveRecord::Base + # has_attachment :storage => :s3 + # end + # + # === Customizing the path + # + # By default, files are prefixed using a pseudo hierarchy in the form of :table_name/:id, which results + # in S3 urls that look like: http(s)://:server/:bucket_name/:table_name/:id/:filename with :table_name + # representing the customizable portion of the path. You can customize this prefix using the :path_prefix + # option: + # + # class Photo < ActiveRecord::Base + # has_attachment :storage => :s3, :path_prefix => 'my/custom/path' + # end + # + # Which would result in URLs like http(s)://:server/:bucket_name/my/custom/path/:id/:filename. + # + # === Permissions + # + # By default, files are stored on S3 with public access permissions. You can customize this using + # the :s3_access option to has_attachment. Available values are + # :private, :public_read_write, and :authenticated_read. + # + # === Other options + # + # Of course, all the usual configuration options apply, such as content_type and thumbnails: + # + # class Photo < ActiveRecord::Base + # has_attachment :storage => :s3, :content_type => ['application/pdf', :image], :resize_to => 'x50' + # has_attachment :storage => :s3, :thumbnails => { :thumb => [50, 50], :geometry => 'x50' } + # end + # + # === Accessing S3 URLs + # + # You can get an object's URL using the s3_url accessor. For example, assuming that for your postcard app + # you had a bucket name like 'postcard_world_development', and an attachment model called Photo: + # + # @postcard.s3_url # => http(s)://s3.amazonaws.com/postcard_world_development/photos/1/mexico.jpg + # + # The resulting url is in the form: http(s)://:server/:bucket_name/:table_name/:id/:file. + # The optional thumbnail argument will output the thumbnail's filename (if any). + # + # Additionally, you can get an object's base path relative to the bucket root using + # base_path: + # + # @photo.file_base_path # => photos/1 + # + # And the full path (including the filename) using full_filename: + # + # @photo.full_filename # => photos/ + # + # Niether base_path or full_filename include the bucket name as part of the path. + # You can retrieve the bucket name using the bucket_name method. + module S3Backend + class RequiredLibraryNotFoundError < StandardError; end + class ConfigFileNotFoundError < StandardError; end + + def self.included(base) #:nodoc: + mattr_reader :bucket_name, :s3_config + + begin + require 'aws/s3' + include AWS::S3 + rescue LoadError + raise RequiredLibraryNotFoundError.new('AWS::S3 could not be loaded') + end + + begin + @@s3_config_path = base.attachment_options[:s3_config_path] || (RAILS_ROOT + '/config/amazon_s3.yml') + @@s3_config = @@s3_config = YAML.load_file(@@s3_config_path)[RAILS_ENV].symbolize_keys + #rescue + # raise ConfigFileNotFoundError.new('File %s not found' % @@s3_config_path) + end + + @@bucket_name = s3_config[:bucket_name] + + Base.establish_connection!( + :access_key_id => s3_config[:access_key_id], + :secret_access_key => s3_config[:secret_access_key], + :server => s3_config[:server], + :port => s3_config[:port], + :use_ssl => s3_config[:use_ssl] + ) + + # Bucket.create(@@bucket_name) + + base.before_update :rename_file + end + + def self.protocol + @protocol ||= s3_config[:use_ssl] ? 'https://' : 'http://' + end + + def self.hostname + @hostname ||= s3_config[:server] || AWS::S3::DEFAULT_HOST + end + + def self.port_string + @port_string ||= s3_config[:port] == (s3_config[:use_ssl] ? 443 : 80) ? '' : ":#{s3_config[:port]}" + end + + module ClassMethods + def s3_protocol + Technoweenie::AttachmentFu::Backends::S3Backend.protocol + end + + def s3_hostname + Technoweenie::AttachmentFu::Backends::S3Backend.hostname + end + + def s3_port_string + Technoweenie::AttachmentFu::Backends::S3Backend.port_string + end + end + + # Overwrites the base filename writer in order to store the old filename + def filename=(value) + @old_filename = filename unless filename.nil? || @old_filename + write_attribute :filename, sanitize_filename(value) + end + + # The attachment ID used in the full path of a file + def attachment_path_id + ((respond_to?(:parent_id) && parent_id) || id).to_s + end + + # The pseudo hierarchy containing the file relative to the bucket name + # Example: :table_name/:id + def base_path + File.join(attachment_options[:path_prefix], attachment_path_id) + end + + # The full path to the file relative to the bucket name + # Example: :table_name/:id/:filename + def full_filename(thumbnail = nil) + File.join(base_path, thumbnail_name_for(thumbnail)) + end + + # All public objects are accessible via a GET request to the S3 servers. You can generate a + # url for an object using the s3_url method. + # + # @photo.s3_url + # + # The resulting url is in the form: http(s)://:server/:bucket_name/:table_name/:id/:file where + # the :server variable defaults to AWS::S3 URL::DEFAULT_HOST (s3.amazonaws.com) and can be + # set using the configuration parameters in RAILS_ROOT/config/amazon_s3.yml. + # + # The optional thumbnail argument will output the thumbnail's filename (if any). + def s3_url(thumbnail = nil) + File.join(s3_protocol + s3_hostname + s3_port_string, bucket_name, full_filename(thumbnail)) + end + alias :public_filename :s3_url + + # All private objects are accessible via an authenticated GET request to the S3 servers. You can generate an + # authenticated url for an object like this: + # + # @photo.authenticated_s3_url + # + # By default authenticated urls expire 5 minutes after they were generated. + # + # Expiration options can be specified either with an absolute time using the :expires option, + # or with a number of seconds relative to now with the :expires_in option: + # + # # Absolute expiration date (October 13th, 2025) + # @photo.authenticated_s3_url(:expires => Time.mktime(2025,10,13).to_i) + # + # # Expiration in five hours from now + # @photo.authenticated_s3_url(:expires_in => 5.hours) + # + # You can specify whether the url should go over SSL with the :use_ssl option. + # By default, the ssl settings for the current connection will be used: + # + # @photo.authenticated_s3_url(:use_ssl => true) + # + # Finally, the optional thumbnail argument will output the thumbnail's filename (if any): + # + # @photo.authenticated_s3_url('thumbnail', :expires_in => 5.hours, :use_ssl => true) + def authenticated_s3_url(*args) + thumbnail = args.first.is_a?(String) ? args.first : nil + options = args.last.is_a?(Hash) ? args.last : {} + S3Object.url_for(full_filename(thumbnail), bucket_name, options) + end + + def create_temp_file + write_to_temp_file current_data + end + + def current_data + S3Object.value full_filename, bucket_name + end + + def s3_protocol + Technoweenie::AttachmentFu::Backends::S3Backend.protocol + end + + def s3_hostname + Technoweenie::AttachmentFu::Backends::S3Backend.hostname + end + + def s3_port_string + Technoweenie::AttachmentFu::Backends::S3Backend.port_string + end + + protected + # Called in the after_destroy callback + def destroy_file + S3Object.delete full_filename, bucket_name + end + + def rename_file + return unless @old_filename && @old_filename != filename + + old_full_filename = File.join(base_path, @old_filename) + + S3Object.rename( + old_full_filename, + full_filename, + bucket_name, + :access => attachment_options[:s3_access] + ) + + @old_filename = nil + true + end + + def save_to_storage + if save_attachment? + S3Object.store( + full_filename, + (temp_path ? File.open(temp_path) : temp_data), + bucket_name, + :content_type => content_type, + :access => attachment_options[:s3_access] + ) + end + + @old_filename = nil + true + end + end + end + end +end \ No newline at end of file