spree_embedded_videos 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.
@@ -0,0 +1,12 @@
1
+ \#*
2
+ *~
3
+ .#*
4
+ .DS_Store
5
+ .bundle
6
+ .idea
7
+ .project
8
+ .rvmrc
9
+ spec/test_app
10
+ tmp
11
+ nbproject
12
+ *.swp
data/LICENSE ADDED
@@ -0,0 +1,23 @@
1
+ Redistribution and use in source and binary forms, with or without modification,
2
+ are permitted provided that the following conditions are met:
3
+
4
+ * Redistributions of source code must retain the above copyright notice,
5
+ this list of conditions and the following disclaimer.
6
+ * Redistributions in binary form must reproduce the above copyright notice,
7
+ this list of conditions and the following disclaimer in the documentation
8
+ and/or other materials provided with the distribution.
9
+ * Neither the name of the Rails Dog LLC nor the names of its
10
+ contributors may be used to endorse or promote products derived from this
11
+ software without specific prior written permission.
12
+
13
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
14
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
15
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
16
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
17
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
21
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
22
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
23
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,27 @@
1
+ Spree Embedded Videos Extension
2
+ ===================
3
+
4
+ This extension adds videos to the product thumbnails displayed when showing a product. It uses
5
+ the oembed protocol and ruby-oembed and currently supports YouTube, Vimeo, Hulu and Viddler. It should
6
+ be fairly trivial to add additional video services if they support oembed.
7
+
8
+
9
+ Install
10
+ =======
11
+
12
+ To install just include the 'spree_embedded_videos' gem in your Gemfile and run:
13
+ ```sh
14
+ rake spree_embedded_videos:install
15
+ ```
16
+
17
+ Warning: This extension completely overwrites _thumbnails.html.erb and product.js from core spree, so if you have
18
+ modified these files be careful.
19
+
20
+ Configuration
21
+ =======
22
+
23
+ This extension supports Embed.ly in order to support video services that don't implement an oembed endpoint
24
+ (e.g. Brightcove). To enable embedly support, copy config/spree_embedded_videos.yml.example file into your
25
+ Rails project's config folder. Rename it to spree_embedded_videos.yml and configure your embedly API key.
26
+
27
+ Copyright (c) 2011 Vitrue, released under the New BSD License
@@ -0,0 +1,75 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'rake/testtask'
4
+ require 'rake/packagetask'
5
+ require 'rake/gempackagetask'
6
+
7
+ gemfile = File.expand_path('../spec/test_app/Gemfile', __FILE__)
8
+ if File.exists?(gemfile) && (%w(spec cucumber).include?(ARGV.first.to_s) || ARGV.size == 0)
9
+ require 'bundler'
10
+ ENV['BUNDLE_GEMFILE'] = gemfile
11
+ Bundler.setup
12
+
13
+ require 'rspec'
14
+ require 'rspec/core/rake_task'
15
+ RSpec::Core::RakeTask.new
16
+
17
+ require 'cucumber/rake/task'
18
+ Cucumber::Rake::Task.new do |t|
19
+ t.cucumber_opts = %w{--format progress}
20
+ end
21
+ end
22
+
23
+ desc "Default Task"
24
+ task :default => [:spec, :cucumber ]
25
+
26
+ spec = eval(File.read('spree_embedded_videos.gemspec'))
27
+
28
+ Rake::GemPackageTask.new(spec) do |p|
29
+ p.gem_spec = spec
30
+ end
31
+
32
+ desc "Release to gemcutter"
33
+ task :release => :package do
34
+ require 'rake/gemcutter'
35
+ Rake::Gemcutter::Tasks.new(spec).define
36
+ Rake::Task['gem:push'].invoke
37
+ end
38
+
39
+ desc "Default Task"
40
+ task :default => [ :spec ]
41
+
42
+ desc "Regenerates a rails 3 app for testing"
43
+ task :test_app do
44
+ require '../spree/lib/generators/spree/test_app_generator'
45
+ class SpreeEmbeddedVideoTestAppGenerator < Spree::Generators::TestAppGenerator
46
+
47
+ def install_gems
48
+ inside "test_app" do
49
+ run 'bundle exec rake spree_core:install'
50
+ run 'bundle exec rake spree_embedded_videos:install'
51
+ end
52
+ end
53
+
54
+ def migrate_db
55
+ run_migrations
56
+ end
57
+
58
+ protected
59
+ def full_path_for_local_gems
60
+ <<-gems
61
+ gem 'spree_core', :path => \'#{File.join(File.dirname(__FILE__), "..", "spree", "core")}\'
62
+ gem 'spree_embedded_videos', :path => \'#{File.dirname(__FILE__)}\'
63
+ gems
64
+ end
65
+
66
+ end
67
+ SpreeEmbeddedVideoTestAppGenerator.start
68
+ end
69
+
70
+ namespace :test_app do
71
+ desc 'Rebuild test and cucumber databases'
72
+ task :rebuild_dbs do
73
+ system("cd spec/test_app && bundle exec rake db:drop db:migrate RAILS_ENV=test && rake db:drop db:migrate RAILS_ENV=cucumber")
74
+ end
75
+ end
@@ -0,0 +1,6 @@
1
+ # This file is used to designate compatibilty with different versions of Spree
2
+ # Please see http://spreecommerce.com/documentation/extensions.html#versionfile for details
3
+
4
+ "0.60.x" => { :branch => "master" }
5
+
6
+
@@ -0,0 +1,49 @@
1
+ class Admin::VideosController < Admin::ResourceController
2
+ before_filter :load_data
3
+
4
+ create.before :set_viewable
5
+ update.before :set_viewable
6
+ destroy.before :destroy_before
7
+
8
+ def update_positions
9
+ params[:positions].each do |id, index|
10
+ Video.update_all(['position=?', index], ['id=?', id])
11
+ end
12
+
13
+ respond_to do |format|
14
+ format.js { render :text => 'Ok' }
15
+ end
16
+ end
17
+
18
+ private
19
+
20
+ def location_after_save
21
+ admin_product_videos_url(@product)
22
+ end
23
+
24
+ def load_data
25
+ @product = Product.find_by_permalink(params[:product_id])
26
+ @variants = @product.variants.collect do |variant|
27
+ [variant.options_text, variant.id ]
28
+ end
29
+ @variants.insert(0, [I18n.t("all"), "All"])
30
+ end
31
+
32
+ def set_viewable
33
+ if params[:video].has_key? :viewable_id
34
+ if params[:video][:viewable_id] == "All"
35
+ @video.viewable = @product
36
+ else
37
+ @video.viewable_type = 'Variant'
38
+ @video.viewable_id = params[:video][:viewable_id]
39
+ end
40
+ else
41
+ @video.viewable = @product
42
+ end
43
+ end
44
+
45
+ def destroy_before
46
+ @viewable = @video.viewable
47
+ end
48
+
49
+ end
@@ -0,0 +1,3 @@
1
+ Product.class_eval do
2
+ has_many :videos, :as => :viewable, :order => :position, :dependent => :destroy
3
+ end
@@ -0,0 +1,3 @@
1
+ Variant.class_eval do
2
+ has_many :videos, :as => :viewable, :order => :position, :dependent => :destroy
3
+ end
@@ -0,0 +1,74 @@
1
+ require 'ostruct'
2
+
3
+ class Video < ActiveRecord::Base
4
+ belongs_to :viewable, :polymorphic => true
5
+
6
+ before_save :update_metadata
7
+
8
+ validates :url, :presence => true
9
+ validates_each :url do |model, attr, value|
10
+ if value.present?
11
+ begin
12
+ oembed_response = model.oembed_cached()
13
+ unless oembed_response.type == 'video'
14
+ model.errors.add(attr, I18n.t("video_invalid_url"))
15
+ end
16
+ rescue OEmbed::UnknownResponse
17
+ model.errors.add(attr, I18n.t("video_unknown_response"))
18
+ rescue OEmbed::NotFound
19
+ model.errors.add(attr, I18n.t("video_invalid_url"))
20
+ end
21
+ end
22
+ end
23
+
24
+ def embed_html(maxwidth = nil)
25
+ oembed_cached(maxwidth).html
26
+ end
27
+
28
+ def oembed_cached(maxwidth = nil)
29
+ Rails.cache.fetch(oembed_cache_key(maxwidth), :expires_in => 1.hour) do
30
+ options = {}
31
+ options["maxwidth"] = maxwidth if maxwidth
32
+
33
+ provider = OEmbed::Providers.find(url)
34
+
35
+ # If embedly is the chosen provider and is configured, set an api key in the options
36
+ if provider && provider.endpoint.include?("embed.ly") && SpreeEmbeddedVideos::Config.embedly_api_key
37
+ options["key"] = SpreeEmbeddedVideos::Config.embedly_api_key
38
+ end
39
+
40
+ response = nil
41
+ if provider
42
+ response = provider.get(url, options)
43
+ end
44
+ response.nil? ? nil : OpenStruct.new(response.fields).freeze
45
+ end
46
+ end
47
+
48
+ private
49
+ def update_metadata
50
+ if url.present?
51
+ oembed_response = oembed_cached
52
+
53
+ Rails.logger.info(oembed_response.inspect)
54
+
55
+ if oembed_response.type == 'video'
56
+ self.title = oembed_response.title
57
+ self.thumbnail_url = oembed_response.thumbnail_url
58
+ self.thumbnail_width = oembed_response.thumbnail_width
59
+ self.thumbnail_height = oembed_response.thumbnail_height
60
+ else
61
+ raise "Error updating metadata - URL is not a video URL."
62
+ end
63
+ end
64
+ end
65
+
66
+ def oembed_cache_key(maxwidth = nil)
67
+ if maxwidth
68
+ "video:oembed:#{url}:#{maxwidth}"
69
+ else
70
+ "video:oembed:#{url}"
71
+ end
72
+ end
73
+
74
+ end
@@ -0,0 +1,12 @@
1
+ <% if @product.has_variants? %>
2
+ <tr>
3
+ <td><%= Variant.model_name.human %>:</td>
4
+ <td><%= f.select :viewable_id, @variants %></td>
5
+ </tr>
6
+ <% else %>
7
+ <%= hidden_field_tag :product_id, @product.id %>
8
+ <% end %>
9
+ <tr>
10
+ <td><%= t("url") %>:</td>
11
+ <td><%= f.text_field :url %></td>
12
+ </tr>
@@ -0,0 +1,3 @@
1
+ <li<%== ' class="active"' if current == "Videos" %>>
2
+ <%= link_to t("videos"), admin_product_videos_url(@product) %>
3
+ </li>
@@ -0,0 +1,25 @@
1
+ <%= render :partial => 'admin/shared/product_sub_menu' %>
2
+
3
+ <%= render :partial => 'admin/shared/product_tabs', :locals => {:current => "Videos"} %>
4
+
5
+ <p><%=t("video_description")%></p>
6
+
7
+ <%= render "shared/error_messages", :target => @video %>
8
+
9
+ <%= form_for(:video, :url => admin_product_video_url(@product), :html => { :method => :put, :multipart => true }) do |f| %>
10
+ <table class="basic-table">
11
+ <% if @video.thumbnail_url and @video.errors.empty? %>
12
+ <tr>
13
+ <td><%= t("video") %>:</td>
14
+ <td>
15
+ <%= @video.embed_html(240).html_safe %>
16
+ </td>
17
+ </tr>
18
+ <% end %>
19
+ <%= render :partial => "form", :locals => { :f => f } %>
20
+ </table>
21
+ <p class="form-buttons">
22
+ <%= button t("update") %>
23
+ <%= t('or') %> <%= link_to t("cancel"), admin_product_videos_url(@product), :id => "cancel_link" %>
24
+ </p>
25
+ <% end %>
@@ -0,0 +1,76 @@
1
+ <%= render :partial => 'admin/shared/product_sub_menu' %>
2
+
3
+ <%= render :partial => 'admin/shared/product_tabs', :locals => {:current => "Videos"} %>
4
+
5
+ <table class="index sortable">
6
+ <thead>
7
+ <tr>
8
+ <th><%= t("video") %></th>
9
+ <% if @product.has_variants? %>
10
+ <th><%= Variant.model_name.human %></th>
11
+ <% end %>
12
+ <th><%= t("title") %></th>
13
+ <th><%= t("action") %></th>
14
+ </tr>
15
+ </thead>
16
+ <tbody>
17
+ <% @product.videos.each do |video| %>
18
+ <tr id="<%= dom_id(video) %>">
19
+ <td>
20
+ <span class="handle"></span>
21
+ <%= image_tag(video.thumbnail_url, :width => 100) %>
22
+ </td>
23
+ <% if @product.has_variants? %>
24
+ <td><%= t('all') %></td>
25
+ <% end %>
26
+ <td><%= video.title %></td>
27
+
28
+ <td class="actions">
29
+ <%= link_to_with_icon('edit', t("edit"), edit_admin_product_video_url(@product, video), :class => 'edit') %>
30
+ &nbsp;
31
+ <%= link_to_delete video, {:url => admin_product_video_url(@product, video) }%>
32
+ </td>
33
+ </tr>
34
+ <% end %>
35
+
36
+ <% @product.variants.each do |variant| %>
37
+ <% variant.videos.each do |video| %>
38
+ <tr id="<%= dom_id(video) %>">
39
+ <td>
40
+ <span class="handle"></span>
41
+ <%= image_tag(video.thumbnail_url, :width => 100) %>
42
+ </td>
43
+ <% if @product.has_variants? %>
44
+ <td><%= variant.options_text %></td>
45
+ <% end %>
46
+ <td><%= video.title %></td>
47
+ <td class="actions">
48
+ <%= link_to_with_icon('edit', t("edit"), edit_admin_product_video_url(@product, video)) %>
49
+ &nbsp;
50
+ <%= link_to_delete video, {:url => admin_product_video_url(@product, video) }%>
51
+ </td>
52
+ </tr>
53
+ <% end %>
54
+ <% end %>
55
+ </tbody>
56
+ </table>
57
+
58
+ <div id="videos"></div>
59
+ <br/>
60
+ <p>
61
+ <%= link_to icon('add') + ' ' + t("new_video"), new_admin_product_video_url(@product), :id => "new_video_link" %>
62
+ </p>
63
+
64
+ <% content_for :head do %>
65
+ <script type="text/javascript">
66
+ jQuery(document).ready(function(){
67
+
68
+ jQuery('#new_video_link').click(function (event) {
69
+ event.preventDefault();
70
+ jQuery(this).hide();
71
+ jQuery.ajax({type: 'GET', url: this.href, data: ({authenticity_token: AUTH_TOKEN}), success: function(r){ jQuery('#videos').html(r);} });
72
+ });
73
+
74
+ });
75
+ </script>
76
+ <% end %>
@@ -0,0 +1,25 @@
1
+ <h4><%= t("new_video") %></h4>
2
+ <p><%=t("video_description")%></p>
3
+
4
+ <%= render "shared/error_messages", :target => @video %>
5
+
6
+ <%= form_for(:video, :url => { :controller => 'videos', :action => 'create' }, :html => { :multipart => true }) do |form| %>
7
+ <table class="basic-table">
8
+ <%= render :partial => "form", :locals => { :f => form } %>
9
+ </table>
10
+ <p class="form-buttons">
11
+ <%= button t("update") %>
12
+ <%= t('or') %> <%= link_to t("cancel"), "#", :id => "cancel_link" %>
13
+ </p>
14
+ <% end %>
15
+
16
+
17
+ <script type="text/javascript">
18
+ jQuery('#cancel_link').click(function (event) {
19
+ jQuery('#new_video_link').show();
20
+ jQuery('#videos').html('');
21
+ });
22
+ </script>
23
+
24
+
25
+
@@ -0,0 +1,20 @@
1
+ <% hidden ||= false %>
2
+
3
+ <ul id="<%= list_id %>" class="thumbnails" style="<%= hidden ? 'display: none' : '' %>">
4
+ <% images.each do |i| %>
5
+ <li id="image_<%=i.id%>" class="image"><%= link_to(image_tag(i.attachment.url(:mini)), i.attachment.url(:product)) %></li>
6
+ <script type="text/javascript">
7
+ $('#image_<%=i.id%>').data('content', <%==image_tag(i.attachment.url(:product)).to_json %>);
8
+ </script>
9
+ <% end %>
10
+
11
+ <% videos.each do |v| %>
12
+ <li id="video_<%=v.id%>" class="video">
13
+ <%= link_to(image_tag(v.thumbnail_url), v.thumbnail_url) %>
14
+ <span>Video</span>
15
+ </li>
16
+ <script type="text/javascript">
17
+ $('#video_<%=v.id%>').data('content', <%== v.embed_html(240).to_json %>);
18
+ </script>
19
+ <% end %>
20
+ </ul>
@@ -0,0 +1,13 @@
1
+ <%= render :partial => 'thumbnail_items', :locals => {:list_id => 'product-thumbnails', :images => @product.images, :videos => @product.videos } %>
2
+
3
+ <% if @product.has_variants? %>
4
+ <% @variants.each do |v| %>
5
+ <% if v.available? %>
6
+ <%= render :partial => 'thumbnail_items', :locals => {:list_id => "variant-thumbnails-#{v.id}", :hidden => true, :images => v.images, :videos => v.videos } %>
7
+ <% end %>
8
+ <% end %>
9
+ <% end %>
10
+
11
+ <% content_for :head do %>
12
+ <%= stylesheet_link_tag 'videos' %>
13
+ <% end %>
@@ -0,0 +1,10 @@
1
+ ---
2
+ en:
3
+ are_you_sure_delete_video: "Are you sure you want to delete this video?"
4
+ video: Video
5
+ videos: Videos
6
+ new_video: "New Video"
7
+ images_for: "Images/Videos for"
8
+ video_description: "Enter a YouTube, Vimeo, Viddler or Hulu video URL to add that video to your product. (e.g. http://www.youtube.com/watch?v=nTasT5h0LEg)"
9
+ video_invalid_url: "doesn't appear to be a valid video URL."
10
+ video_unknown_response: "could not be processed. It is possible that you are not authorized to embed that video. Please check the sharing settings on this video."
@@ -0,0 +1,11 @@
1
+ Rails.application.routes.draw do
2
+ namespace :admin do
3
+ resources :products do
4
+ resources :videos do
5
+ collection do
6
+ post :update_positions
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ # This is an example configuration file for spree embedded videos.
2
+ # Place this file in your Rails config folder and rename it to spree_embedded_videos.yml
3
+ # Then edit the file with your environment configuration.
4
+
5
+ defaults: &defaults
6
+ embedly_api_key: "EMBEDLY API KEY"
7
+
8
+ development:
9
+ <<: *defaults
10
+ production:
11
+ <<: *defaults
@@ -0,0 +1,19 @@
1
+ class CreateVideos < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :videos do |t|
4
+ t.integer :viewable_id, :null => false
5
+ t.string :viewable_type, :limit => 50, :null => false
6
+ t.string :url, :null => false
7
+ t.string :title
8
+ t.string :thumbnail_url
9
+ t.string :thumbnail_width
10
+ t.string :thumbnail_height
11
+ t.integer :position
12
+ t.timestamps
13
+ end
14
+ end
15
+
16
+ def self.down
17
+ drop_table :videos
18
+ end
19
+ end
@@ -0,0 +1,39 @@
1
+ require 'spree_core'
2
+ require 'spree_embedded_videos_hooks'
3
+
4
+ module SpreeEmbeddedVideos
5
+ module Config
6
+ mattr_accessor :embedly_api_key
7
+ @@embedly_api_key = nil
8
+ end
9
+
10
+ class Engine < Rails::Engine
11
+
12
+ config.autoload_paths += %W(#{config.root}/lib)
13
+
14
+ def self.activate
15
+ Dir.glob(File.join(File.dirname(__FILE__), "../app/**/*_decorator*.rb")) do |c|
16
+ Rails.env.production? ? require(c) : load(c)
17
+ end
18
+
19
+ require 'oembed'
20
+ OEmbed::Providers.unregister_all
21
+ OEmbed::Providers.register_all
22
+
23
+ # Add short URL form of brightcove to embedly
24
+ OEmbed::Providers::Embedly << "http://bcove.me/*"
25
+ OEmbed::Providers.register(OEmbed::Providers::Embedly)
26
+
27
+ config_file = Rails.root.join("config", "spree_embedded_videos.yml")
28
+ if config_file.file?
29
+ config = YAML.load(ERB.new(config_file.read).result)[Rails.env]
30
+ if config
31
+ SpreeEmbeddedVideos::Config.embedly_api_key = config["embedly_api_key"]
32
+ end
33
+ end
34
+
35
+ end
36
+
37
+ config.to_prepare &method(:activate).to_proc
38
+ end
39
+ end
@@ -0,0 +1,3 @@
1
+ class SpreeEmbeddedVideosHooks < Spree::ThemeSupport::HookListener
2
+ insert_after :admin_product_tabs, 'admin/videos/menu_item'
3
+ end
@@ -0,0 +1,25 @@
1
+ namespace :spree_embedded_videos do
2
+ desc "Copies all migrations and assets (NOTE: This will be obsolete with Rails 3.1)"
3
+ task :install do
4
+ Rake::Task['spree_embedded_videos:install:migrations'].invoke
5
+ Rake::Task['spree_embedded_videos:install:assets'].invoke
6
+ end
7
+
8
+ namespace :install do
9
+ desc "Copies all migrations (NOTE: This will be obsolete with Rails 3.1)"
10
+ task :migrations do
11
+ source = File.join(File.dirname(__FILE__), '..', '..', 'db')
12
+ destination = File.join(Rails.root, 'db')
13
+ Spree::FileUtilz.mirror_files(source, destination)
14
+ end
15
+
16
+ desc "Copies all assets (NOTE: This will be obsolete with Rails 3.1)"
17
+ task :assets do
18
+ source = File.join(File.dirname(__FILE__), '..', '..', 'public')
19
+ destination = File.join(Rails.root, 'public')
20
+ puts "INFO: Mirroring assets from #{source} to #{destination}"
21
+ Spree::FileUtilz.mirror_files(source, destination)
22
+ end
23
+ end
24
+
25
+ end
@@ -0,0 +1 @@
1
+ # add custom rake tasks here
@@ -0,0 +1,51 @@
1
+ var update_main_image = function(link) {
2
+ var li = $(link).closest('li');
3
+ if( !li.is('.selected') ) {
4
+ $('#main-image').html( li.data('content') );
5
+ $('ul.thumbnails li').removeClass('selected');
6
+ li.addClass('selected');
7
+ }
8
+ }
9
+
10
+ var add_image_handlers = function() {
11
+ $('ul.thumbnails li').eq(0).addClass('selected');
12
+
13
+ $('ul.thumbnails li a')
14
+ .click(function(e) {
15
+ e.preventDefault();
16
+ })
17
+ .bind('mouseenter',
18
+ function() {
19
+ update_main_image(this);
20
+ }
21
+ );
22
+
23
+ set_default_image(null);
24
+ };
25
+
26
+ var set_default_image = function(vid) {
27
+ var link = jQuery("#variant-thumbnails-" + vid + " a")[0];
28
+ if(link == null) {
29
+ link = jQuery("#product-thumbnails a")[0];
30
+ }
31
+ if( link != null ) {
32
+ update_main_image(link);
33
+ }
34
+ }
35
+
36
+ var variant_changed = function () {
37
+ var el = jQuery('#product-variants input[type=radio]');
38
+ if(el) {
39
+ var vid = $('input[type=radio]:checked').val();
40
+ $('.thumbnails').hide();
41
+ $('#product-thumbnails').show();
42
+ $('#variant-thumbnails-' + vid).show();
43
+ set_default_image(vid);
44
+ }
45
+ };
46
+
47
+ jQuery(document).ready(function() {
48
+ jQuery('#product-variants input[type=radio]').change(variant_changed);
49
+ add_image_handlers();
50
+ variant_changed();
51
+ });
@@ -0,0 +1,24 @@
1
+ #product-images #main-image iframe {
2
+ position: absolute;
3
+ top: 0;
4
+ bottom: 0;
5
+ left: 0;
6
+ right: 0;
7
+ margin: auto;
8
+ }
9
+
10
+ ul.thumbnails li.video img { width: 48px; }
11
+ ul.thumbnails li.video span {
12
+ z-index: 999;
13
+ background: rgba(50,50,50,0.9);
14
+ color: #ddd;
15
+ font-size: 10px;
16
+ height: 1.2em;
17
+ text-align: center;
18
+ padding: 2px 0 3px 0;
19
+ position: absolute;
20
+ bottom: 0;
21
+ left: 0;
22
+ right: 0;
23
+ margin: auto;
24
+ }
@@ -0,0 +1,95 @@
1
+ require 'spec_helper'
2
+
3
+ describe Video do
4
+
5
+ before do
6
+ @v = Video.new(:url => "http://youtube.com")
7
+ end
8
+
9
+ describe :validation do
10
+ it 'validates urls required' do
11
+ @v = Video.new(:url => nil)
12
+ @v.should_not_receive(:oembed_cached)
13
+ @v.valid?.should be_false
14
+ end
15
+
16
+ it 'validates urls are found by oembed' do
17
+ @v.should_receive(:oembed_cached).and_raise OEmbed::NotFound.new("Not found")
18
+ @v.valid?.should be_false
19
+ end
20
+
21
+ it 'validates response is a video' do
22
+ @v.should_receive(:oembed_cached).and_return OpenStruct.new(:type => 'notavideo')
23
+ @v.valid?.should be_false
24
+ end
25
+ end
26
+
27
+ describe :embed_html do
28
+ it 'should call oembed_cached and return html' do
29
+ meta = mock("meta")
30
+ @v.should_receive(:oembed_cached).with(200).and_return(meta)
31
+ meta.should_receive(:html)
32
+ @v.embed_html(200)
33
+ end
34
+ end
35
+
36
+ describe :oembed_cached do
37
+ it 'should call provider to get response and return open struct' do
38
+ Rails.cache.should_receive(:fetch).and_yield
39
+ response = mock("response", :fields => {abc: 123})
40
+ OEmbed::Providers.should_receive(:get).with("http://youtube.com", {}).and_return(response)
41
+
42
+ oembed = @v.oembed_cached()
43
+ oembed.abc.should eql(123)
44
+ end
45
+
46
+ it 'should call provider to get response and return open struct with a max width' do
47
+ Rails.cache.should_receive(:fetch).and_yield
48
+ response = mock("response", :fields => {abc: 123})
49
+ OEmbed::Providers.should_receive(:get).with("http://youtube.com", {"maxwidth" => 300}).and_return(response)
50
+
51
+ oembed = @v.oembed_cached(300)
52
+ oembed.abc.should eql(123)
53
+ end
54
+ end
55
+
56
+ describe :update_metadata do
57
+ it 'should do nothing if no url is present' do
58
+ @v = Video.new(:url => nil)
59
+ @v.should_not_receive(:oembed_cached)
60
+
61
+ @v.send(:update_metadata)
62
+ end
63
+
64
+ it 'should update meta_data if url is present' do
65
+ response = mock("response", :title => "Title", :thumbnail_url => "http://thumbnail.com/thumbnail.jpg", :thumbnail_height => 50, :thumbnail_width => 75, :type => 'video')
66
+ @v.should_receive(:oembed_cached).and_return(response)
67
+
68
+ @v.send(:update_metadata)
69
+
70
+ @v.title.should eql("Title")
71
+ @v.thumbnail_url.should eql("http://thumbnail.com/thumbnail.jpg")
72
+ @v.thumbnail_height.should eql(50)
73
+ @v.thumbnail_width.should eql(75)
74
+ end
75
+
76
+ it 'should raise error if not a video' do
77
+ response = mock("response", :title => "Title", :thumbnail_url => "http://thumbnail.com/thumbnail.jpg", :thumbnail_height => 50, :thumbnail_width => 75, :type => 'image')
78
+ @v.should_receive(:oembed_cached).and_return(response)
79
+
80
+ lambda {@v.send(:update_metadata)}.should raise_error
81
+ end
82
+ end
83
+
84
+ describe :oembed_cache_key do
85
+ it 'should return embed key with maxwidth if specified' do
86
+ @v.send(:oembed_cache_key, 300).should eql("video:oembed:http://youtube.com:300")
87
+ end
88
+
89
+ it 'should return embed key without maxwidth if not specified' do
90
+ @v.send(:oembed_cache_key).should eql("video:oembed:http://youtube.com")
91
+ end
92
+ end
93
+
94
+
95
+ end
@@ -0,0 +1,30 @@
1
+ # This file is copied to ~/spec when you run 'ruby script/generate rspec'
2
+ # from the project root directory.
3
+ ENV["RAILS_ENV"] ||= 'test'
4
+ require File.expand_path("../test_app/config/environment", __FILE__)
5
+ require 'rspec/rails'
6
+
7
+ # Requires supporting files with custom matchers and macros, etc,
8
+ # in ./support/ and its subdirectories.
9
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
10
+
11
+ RSpec.configure do |config|
12
+ # == Mock Framework
13
+ #
14
+ # If you prefer to use mocha, flexmock or RR, uncomment the appropriate line:
15
+ #
16
+ # config.mock_with :mocha
17
+ # config.mock_with :flexmock
18
+ # config.mock_with :rr
19
+ config.mock_with :rspec
20
+
21
+ config.fixture_path = "#{::Rails.root}/spec/fixtures"
22
+
23
+ #config.include Devise::TestHelpers, :type => :controller
24
+ # If you're not using ActiveRecord, or you'd prefer not to run each of your
25
+ # examples within a transaction, comment the following line or assign false
26
+ # instead of true.
27
+ config.use_transactional_fixtures = true
28
+ end
29
+
30
+ @configuration ||= AppConfiguration.find_or_create_by_name("Default configuration")
@@ -0,0 +1,21 @@
1
+ Gem::Specification.new do |s|
2
+ s.platform = Gem::Platform::RUBY
3
+ s.name = 'spree_embedded_videos'
4
+ s.version = '0.1.0'
5
+ s.summary = 'A Spree extension that adds embedded videos to the product pages.'
6
+ s.description = 'This extension uses oembed to support a variety of video services, such as YouTube, Vimeo, Brightcove, Viddler, Hulu, and more.'
7
+ s.required_ruby_version = '>= 1.8.7'
8
+
9
+ s.author = 'Jeremy Haile'
10
+ s.email = 'jhaile@gmail.com'
11
+ s.homepage = 'http://www.github.com/jeremyhaile/spree_embedded_videos'
12
+ s.rubyforge_project = 'spree_embedded_videos'
13
+
14
+ s.files = `git ls-files`.split("\n")
15
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
16
+ s.require_path = 'lib'
17
+ s.requirements << 'none'
18
+
19
+ s.add_dependency('spree_core', '>= 0.60.4')
20
+ s.add_dependency('ruby-oembed', '>= 0.8.5')
21
+ end
metadata ADDED
@@ -0,0 +1,99 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: spree_embedded_videos
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Jeremy Haile
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-12-06 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: spree_core
16
+ requirement: &70344224577960 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: 0.60.4
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *70344224577960
25
+ - !ruby/object:Gem::Dependency
26
+ name: ruby-oembed
27
+ requirement: &70344224577500 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: 0.8.5
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *70344224577500
36
+ description: This extension uses oembed to support a variety of video services, such
37
+ as YouTube, Vimeo, Brightcove, Viddler, Hulu, and more.
38
+ email: jhaile@gmail.com
39
+ executables: []
40
+ extensions: []
41
+ extra_rdoc_files: []
42
+ files:
43
+ - .gitignore
44
+ - LICENSE
45
+ - README.md
46
+ - Rakefile
47
+ - Versionfile
48
+ - app/controllers/admin/videos_controller.rb
49
+ - app/models/product_decorator.rb
50
+ - app/models/variant_decorator.rb
51
+ - app/models/video.rb
52
+ - app/views/admin/videos/_form.html.erb
53
+ - app/views/admin/videos/_menu_item.html.erb
54
+ - app/views/admin/videos/edit.html.erb
55
+ - app/views/admin/videos/index.html.erb
56
+ - app/views/admin/videos/new.html.erb
57
+ - app/views/products/_thumbnail_items.html.erb
58
+ - app/views/products/_thumbnails.html.erb
59
+ - config/locales/en.yml
60
+ - config/routes.rb
61
+ - config/spree_embedded_videos.yml.example
62
+ - db/migrate/20111130200938_create_videos.rb
63
+ - lib/spree_embedded_videos.rb
64
+ - lib/spree_embedded_videos_hooks.rb
65
+ - lib/tasks/install.rake
66
+ - lib/tasks/spree_embedded_videos.rake
67
+ - public/javascripts/product.js
68
+ - public/stylesheets/videos.css
69
+ - spec/models/video_spec.rb
70
+ - spec/spec_helper.rb
71
+ - spree_embedded_videos.gemspec
72
+ homepage: http://www.github.com/jeremyhaile/spree_embedded_videos
73
+ licenses: []
74
+ post_install_message:
75
+ rdoc_options: []
76
+ require_paths:
77
+ - lib
78
+ required_ruby_version: !ruby/object:Gem::Requirement
79
+ none: false
80
+ requirements:
81
+ - - ! '>='
82
+ - !ruby/object:Gem::Version
83
+ version: 1.8.7
84
+ required_rubygems_version: !ruby/object:Gem::Requirement
85
+ none: false
86
+ requirements:
87
+ - - ! '>='
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ requirements:
91
+ - none
92
+ rubyforge_project: spree_embedded_videos
93
+ rubygems_version: 1.8.12
94
+ signing_key:
95
+ specification_version: 3
96
+ summary: A Spree extension that adds embedded videos to the product pages.
97
+ test_files:
98
+ - spec/models/video_spec.rb
99
+ - spec/spec_helper.rb