From 433fe0be740eca8f8447c5af63fb3fe45b51b78d Mon Sep 17 00:00:00 2001 From: Steven Miers Date: Tue, 4 Nov 2014 14:55:12 -0600 Subject: [PATCH] Initial merge and manual conflict and fix for tests. --- admin/.rakeTasks | 7 + admin/app/admin/jam_track_licensors.rb | 8 + admin/app/admin/jam_tracks.rb | 57 ++ admin/app/assets/javascripts/jam_track.js | 43 ++ .../views/admin/jam_tracks/_form.html.haml | 31 ++ .../_jam_track_track_fields.html.haml | 18 + admin/config/application.rb | 5 + admin/config/environments/test.rb | 5 + admin/spec/factories.rb | 50 ++ db/manifest | 3 + db/up/jam_tracks.sql | 68 +++ db/up/recurly.sql | 2 + db/up/shopping_carts.sql | 10 + ruby/lib/jam_ruby.rb | 7 + .../app/uploaders/jam_track_track_uploader.rb | 28 + .../app/uploaders/jam_track_uploader.rb | 28 + .../jam_ruby/constants/validation_messages.rb | 5 + ruby/lib/jam_ruby/lib/s3_manager.rb | 8 + ruby/lib/jam_ruby/models/genre.rb | 2 + ruby/lib/jam_ruby/models/jam_track.rb | 106 ++++ .../lib/jam_ruby/models/jam_track_licensor.rb | 21 + ruby/lib/jam_ruby/models/jam_track_right.rb | 14 + ruby/lib/jam_ruby/models/jam_track_track.rb | 47 ++ ruby/lib/jam_ruby/models/recorded_track.rb | 1 + ruby/lib/jam_ruby/models/search.rb | 1 + ruby/lib/jam_ruby/models/shopping_cart.rb | 34 ++ ruby/lib/jam_ruby/models/user.rb | 7 + ruby/spec/factories.rb | 50 ++ .../models/jam_track_licensor_spec.rb | 10 + .../jam_ruby/models/jam_track_right_spec.rb | 31 ++ ruby/spec/jam_ruby/models/jam_track_spec.rb | 138 +++++ .../jam_ruby/models/jam_track_track_spec.rb | 70 +++ .../jam_ruby/models/shopping_cart_spec.rb | 23 + .../unused_music_notation_cleaner_spec.rb | 13 +- web/Gemfile | 1 + .../images/content/bkg_home_jamtracks.jpg | Bin 0 -> 2767 bytes .../images/content/bkg_home_jamtracks_x.jpg | Bin 0 -> 2850 bytes web/app/assets/images/content/checkmark.png | Bin 0 -> 1813 bytes .../assets/images/content/icon_jamtracks.png | Bin 0 -> 779 bytes .../images/content/icon_shopping_cart.png | Bin 0 -> 1316 bytes .../assets/images/content/shopping-cart.png | Bin 0 -> 7355 bytes web/app/assets/javascripts/checkout_signin.js | 106 ++++ web/app/assets/javascripts/dialog/banner.js | 2 +- .../dialog/jamtrackAvailabilityDialog.js | 46 ++ web/app/assets/javascripts/jam_rest.js | 112 +++- web/app/assets/javascripts/jamtrack.js | 256 +++++++++ web/app/assets/javascripts/order.js | 511 ++++++++++++++++++ .../javascripts/scheduled_session.js.erb | 2 +- web/app/assets/javascripts/shopping_cart.js | 112 ++++ .../assets/javascripts/web/signin_helper.js | 6 +- .../stylesheets/client/checkout.css.scss | 252 +++++++++ web/app/assets/stylesheets/client/client.css | 3 + .../assets/stylesheets/client/header.css.scss | 9 + .../assets/stylesheets/client/home.css.scss | 6 + .../stylesheets/client/jamtrack.css.scss | 116 ++++ .../stylesheets/client/shoppingCart.css.scss | 78 +++ .../dialogs/jamtrackAvailability.css.scss | 0 .../controllers/api_jamtracks_controller.rb | 15 + web/app/controllers/api_recurly_controller.rb | 155 ++++++ .../api_shopping_carts_controller.rb | 53 ++ web/app/views/api_jamtracks/index.rabl | 7 + web/app/views/api_jamtracks/show.rabl | 19 + .../api_shopping_carts/add_jamtrack.rabl | 1 + web/app/views/api_shopping_carts/index.rabl | 3 + web/app/views/api_shopping_carts/show.rabl | 3 + web/app/views/api_users/show.rabl | 4 +- web/app/views/clients/_header.html.erb | 5 + web/app/views/clients/_home.html.erb | 28 +- web/app/views/clients/_jamtrack.html.haml | 81 +++ web/app/views/clients/_order.html.slim | 268 +++++++++ .../views/clients/_shopping_cart.html.haml | 53 ++ web/app/views/clients/_signin.html.slim | 68 +++ web/app/views/clients/_web_filter.html.erb | 31 +- web/app/views/clients/index.html.erb | 18 + web/app/views/dialogs/_dialogs.html.haml | 1 + .../_jamtrackAvailabilityDialog.html.haml | 9 + web/config/application.rb | 1 - web/config/environments/development.rb | 2 + web/config/environments/test.rb | 6 + web/config/initializers/recurly.rb | 2 + web/config/routes.rb | 15 + web/lib/tasks/sample_data.rake | 8 + web/spec/controllers/api_recurly_spec.rb | 34 ++ web/spec/factories.rb | 58 ++ web/spec/features/jamtrack_shopping_spec.rb | 133 +++++ websocket-gateway/spec/factories.rb | 50 ++ 86 files changed, 3666 insertions(+), 34 deletions(-) create mode 100644 admin/.rakeTasks create mode 100644 admin/app/admin/jam_track_licensors.rb create mode 100644 admin/app/admin/jam_tracks.rb create mode 100644 admin/app/assets/javascripts/jam_track.js create mode 100644 admin/app/views/admin/jam_tracks/_form.html.haml create mode 100644 admin/app/views/admin/jam_tracks/_jam_track_track_fields.html.haml create mode 100644 db/up/jam_tracks.sql create mode 100644 db/up/recurly.sql create mode 100644 db/up/shopping_carts.sql create mode 100644 ruby/lib/jam_ruby/app/uploaders/jam_track_track_uploader.rb create mode 100644 ruby/lib/jam_ruby/app/uploaders/jam_track_uploader.rb create mode 100644 ruby/lib/jam_ruby/models/jam_track.rb create mode 100644 ruby/lib/jam_ruby/models/jam_track_licensor.rb create mode 100644 ruby/lib/jam_ruby/models/jam_track_right.rb create mode 100644 ruby/lib/jam_ruby/models/jam_track_track.rb create mode 100644 ruby/lib/jam_ruby/models/shopping_cart.rb create mode 100644 ruby/spec/jam_ruby/models/jam_track_licensor_spec.rb create mode 100644 ruby/spec/jam_ruby/models/jam_track_right_spec.rb create mode 100644 ruby/spec/jam_ruby/models/jam_track_spec.rb create mode 100644 ruby/spec/jam_ruby/models/jam_track_track_spec.rb create mode 100644 ruby/spec/jam_ruby/models/shopping_cart_spec.rb create mode 100644 web/app/assets/images/content/bkg_home_jamtracks.jpg create mode 100644 web/app/assets/images/content/bkg_home_jamtracks_x.jpg create mode 100644 web/app/assets/images/content/checkmark.png create mode 100644 web/app/assets/images/content/icon_jamtracks.png create mode 100644 web/app/assets/images/content/icon_shopping_cart.png create mode 100644 web/app/assets/images/content/shopping-cart.png create mode 100644 web/app/assets/javascripts/checkout_signin.js create mode 100644 web/app/assets/javascripts/dialog/jamtrackAvailabilityDialog.js create mode 100644 web/app/assets/javascripts/jamtrack.js create mode 100644 web/app/assets/javascripts/order.js create mode 100644 web/app/assets/javascripts/shopping_cart.js create mode 100644 web/app/assets/stylesheets/client/checkout.css.scss create mode 100644 web/app/assets/stylesheets/client/jamtrack.css.scss create mode 100644 web/app/assets/stylesheets/client/shoppingCart.css.scss create mode 100644 web/app/assets/stylesheets/dialogs/jamtrackAvailability.css.scss create mode 100644 web/app/controllers/api_jamtracks_controller.rb create mode 100644 web/app/controllers/api_recurly_controller.rb create mode 100644 web/app/controllers/api_shopping_carts_controller.rb create mode 100644 web/app/views/api_jamtracks/index.rabl create mode 100644 web/app/views/api_jamtracks/show.rabl create mode 100644 web/app/views/api_shopping_carts/add_jamtrack.rabl create mode 100644 web/app/views/api_shopping_carts/index.rabl create mode 100644 web/app/views/api_shopping_carts/show.rabl create mode 100644 web/app/views/clients/_jamtrack.html.haml create mode 100644 web/app/views/clients/_order.html.slim create mode 100644 web/app/views/clients/_shopping_cart.html.haml create mode 100644 web/app/views/clients/_signin.html.slim create mode 100644 web/app/views/dialogs/_jamtrackAvailabilityDialog.html.haml create mode 100644 web/config/initializers/recurly.rb create mode 100644 web/spec/controllers/api_recurly_spec.rb create mode 100644 web/spec/features/jamtrack_shopping_spec.rb diff --git a/admin/.rakeTasks b/admin/.rakeTasks new file mode 100644 index 000000000..c6865d9a1 --- /dev/null +++ b/admin/.rakeTasks @@ -0,0 +1,7 @@ + + diff --git a/admin/app/admin/jam_track_licensors.rb b/admin/app/admin/jam_track_licensors.rb new file mode 100644 index 000000000..65fdaed6d --- /dev/null +++ b/admin/app/admin/jam_track_licensors.rb @@ -0,0 +1,8 @@ +ActiveAdmin.register JamRuby::JamTrackLicensor, :as => 'JamTrack Licensors' do + + menu :label => 'JamTrack Licensors', :parent => 'JamTracks' + + config.sort_order = 'name_asc' + config.batch_actions = false + +end \ No newline at end of file diff --git a/admin/app/admin/jam_tracks.rb b/admin/app/admin/jam_tracks.rb new file mode 100644 index 000000000..0f6d99e08 --- /dev/null +++ b/admin/app/admin/jam_tracks.rb @@ -0,0 +1,57 @@ +ActiveAdmin.register JamRuby::JamTrack, :as => 'JamTracks' do + + menu :label => 'JamTracks', :parent => 'JamTracks' + + config.sort_order = 'name_asc' + config.batch_actions = false + + filter :genre + filter :status, :as => :select, collection: JamRuby::JamTrack::STATUS + + form :partial => 'form' + + index do + column :id + column :name + column :description + column :bpm + column :time_signature + column :status + column :recording_type + column :original_artist + column :songwriter + column :publisher + column :licensor + column :pro + column :genre + column :sales_region + column :price + column :reproduction_royalty + column :public_performance_royalty + column :reproduction_royalty_amount + column :licensor_royalty_amount + column :pro_royalty_amount + column :url + column :created_at + + column :jam_track_tracks do |jam_track| + table_for jam_track.jam_track_tracks.order('position ASC') do + column :id + column :track_type + column :instrument + column :part + column :track do |track| + link_to 'Play', '#' + end + end + end + + # default_actions # use this for all view/edit/delete links + column "Actions" do |jam_track| + links = ''.html_safe + links << link_to("Show Tracks", '#', :class => "member_link view_link show_tracks") + links << link_to("Update", edit_resource_path(jam_track), :class => "member_link edit_link") + links + end + end +end \ No newline at end of file diff --git a/admin/app/assets/javascripts/jam_track.js b/admin/app/assets/javascripts/jam_track.js new file mode 100644 index 000000000..a8781672a --- /dev/null +++ b/admin/app/assets/javascripts/jam_track.js @@ -0,0 +1,43 @@ +$(document).ready(function() { + $("th.jam_track_tracks").css('display', 'none'); + $("td.jam_track_tracks").css('display', 'none'); + + $(".show_tracks").click(function(e) { + e.preventDefault(); + + var $rowJamTrack = $(this).parents('tr'); + var $jamTrackTracks = $($rowJamTrack).find("td.jam_track_tracks"); + + var count = $jamTrackTracks.find("table tbody tr").length; + + if ($rowJamTrack.next().attr('id') == "jam_track_tracks_detail") { + $(this).html("Show Tracks"); + $rowJamTrack.next().remove(); + } + else { + $(this).html('Hide Tracks'); + if (count == 0) { + $rowJamTrack.after( + $("").html( + $("") + ).append( + $("").html( + "No Tracks" + ) + ) + ); + } + else { + $rowJamTrack.after( + $("").html( + $("") + ).append( + $("").html( + $jamTrackTracks.html() + ) + ) + ); + } + } + }) +}); \ No newline at end of file diff --git a/admin/app/views/admin/jam_tracks/_form.html.haml b/admin/app/views/admin/jam_tracks/_form.html.haml new file mode 100644 index 000000000..5c2028673 --- /dev/null +++ b/admin/app/views/admin/jam_tracks/_form.html.haml @@ -0,0 +1,31 @@ += semantic_form_for([:admin, resource], :html => {:multipart => true}, :url => resource.new_record? ? admin_jam_tracks_path : "#{ENV['RAILS_RELATIVE_URL_ROOT']}/admin/jam_tracks/#{resource.id}") do |f| + = f.semantic_errors *f.object.errors.keys + = f.inputs name: 'JamTrack fields' do + + = f.input :name + = f.input :description + = f.input :bpm + = f.input :time_signature, collection: JamRuby::JamTrack::TIME_SIGNATURES + = f.input :status, collection: JamRuby::JamTrack::STATUS + = f.input :recording_type, collection: JamRuby::JamTrack::RECORDING_TYPE + = f.input :original_artist + = f.input :songwriter + = f.input :publisher + = f.input :licensor, collection: JamRuby::JamTrackLicensor.all + = f.input :pro, collection: JamRuby::JamTrack::PRO + = f.input :genre, collection: JamRuby::Genre.all + = f.input :sales_region, collection: JamRuby::JamTrack::SALES_REGION + = f.input :price + = f.input :reproduction_royalty, :label => 'Reproduction Royalty' + = f.input :public_performance_royalty, :label => 'Public Performance Royalty' + = f.input :reproduction_royalty_amount + = f.input :licensor_royalty_amount + = f.input :pro_royalty_amount + = f.input :url, :as => :file, :label => 'Audio File' + + = f.semantic_fields_for :jam_track_tracks do |track| + = render 'jam_track_track_fields', f: track + .links + = link_to_add_association 'Add Track', f, :jam_track_tracks, class: 'button', style: 'margin:20px;padding:10px 20px' + + = f.actions \ No newline at end of file diff --git a/admin/app/views/admin/jam_tracks/_jam_track_track_fields.html.haml b/admin/app/views/admin/jam_tracks/_jam_track_track_fields.html.haml new file mode 100644 index 000000000..11200771b --- /dev/null +++ b/admin/app/views/admin/jam_tracks/_jam_track_track_fields.html.haml @@ -0,0 +1,18 @@ += f.inputs name: 'Track fields' do + + %ol.nested-fields + = f.input :track_type, :as => :select, collection: JamRuby::JamTrackTrack::TRACK_TYPE + = f.input :instrument, collection: Instrument.all + = f.input :part + = f.input :position + + - if f.object.new_record? + %p{style: 'margin-left:10px'} + %i before you can upload, you must select 'Update JamTrack' + - else + = f.input :url, :as => :file, :label => 'Track file' + - unless f.object.nil? || f.object[:url].nil? + .current_file_holder{style: 'margin-bottom:10px'} + %a{href: f.object.sign_url(3600), style: 'padding:0 0 0 20px'} Download + + = link_to_remove_association "Delete Track", f, class: 'button', style: 'margin-left:10px' \ No newline at end of file diff --git a/admin/config/application.rb b/admin/config/application.rb index 08b11d58c..1b9485036 100644 --- a/admin/config/application.rb +++ b/admin/config/application.rb @@ -134,5 +134,10 @@ module JamAdmin # recording upload/download configs config.max_track_upload_failures = 10 config.max_track_part_upload_failures = 3 + + # Use Private API Keys to communicate with Recurly's API v2. See https://docs.recurly.com/api/basics/authentication to learn more. + config.recurly_private_api_key = '7d623daabfc2434fa2a893bb008eb3e6' + # Use Public Keys to identify your site when using Recurly.js. See https://docs.recurly.com/js/#include to learn more. + config.recurly_public_api_key = 'sc-SZlO11shkeA1WMGuISLGg5' end end diff --git a/admin/config/environments/test.rb b/admin/config/environments/test.rb index 1f9229001..33880f53b 100644 --- a/admin/config/environments/test.rb +++ b/admin/config/environments/test.rb @@ -42,4 +42,9 @@ JamAdmin::Application.configure do config.twitter_app_secret = 'PfG1jAUMnyrimPcDooUVQaJrG1IuDjUyGg5KciOo' config.redis_host = "localhost:6379:1" # go to another db to not cross pollute into dev/production redis dbs + + # Use Private API Keys to communicate with Recurly's API v2. See https://docs.recurly.com/api/basics/authentication to learn more. + config.recurly_private_api_key = '4631527f203b41848523125b3ae51341' + # Use Public Keys to identify your site when using Recurly.js. See https://docs.recurly.com/js/#include to learn more. + config.recurly_public_api_key = 'sc-s6G2OA80Rwyvsb1RmS3mAE' end diff --git a/admin/spec/factories.rb b/admin/spec/factories.rb index 3ba6c50aa..08f668ff3 100644 --- a/admin/spec/factories.rb +++ b/admin/spec/factories.rb @@ -205,4 +205,54 @@ FactoryGirl.define do latency_tester.save end end + + factory :jam_track_licensor, :class => JamRuby::JamTrackLicensor do + sequence(:name) { |n| "licensor-#{n}" } + sequence(:description) { |n| "description-#{n}" } + sequence(:attention) { |n| "attention-#{n}" } + sequence(:address_line_1) { |n| "address1-#{n}" } + sequence(:address_line_2) { |n| "address2-#{n}" } + sequence(:city) { |n| "city-#{n}" } + sequence(:state) { |n| "state-#{n}" } + sequence(:zip_code) { |n| "zipcode-#{n}" } + sequence(:contact) { |n| "contact-#{n}" } + sequence(:email) { |n| "email-#{n}" } + sequence(:phone) { |n| "phone-#{n}" } + end + + factory :jam_track, :class => JamRuby::JamTrack do + sequence(:name) { |n| "jam-track-#{n}" } + sequence(:description) { |n| "description-#{n}" } + bpm 100.1 + time_signature '4/4' + status 'Production' + recording_type 'Cover' + sequence(:original_artist) { |n| "original-artist-#{n}" } + sequence(:songwriter) { |n| "songwriter-#{n}" } + sequence(:publisher) { |n| "publisher-#{n}" } + pro 'ASCAP' + sales_region 'United States' + price 1.99 + reproduction_royalty true + public_performance_royalty true + reproduction_royalty_amount 0.999 + licensor_royalty_amount 0.999 + pro_royalty_amount 0.999 + + genre JamRuby::Genre.first + association :licensor, factory: :jam_track_licensor + end + + factory :jam_track_track, :class => JamRuby::JamTrackTrack do + position 1 + part 'lead guitar' + track_type 'Track' + instrument JamRuby::Instrument.find('electric guitar') + association :jam_track, factory: :jam_track + end + + factory :jam_track_right, :class => JamRuby::JamTrackRight do + association :jam_track, factory: :jam_track + association :user, factory: :user + end end diff --git a/db/manifest b/db/manifest index c8652bebf..02cd17956 100755 --- a/db/manifest +++ b/db/manifest @@ -225,3 +225,6 @@ add_youtube_flag_to_claimed_recordings.sql add_session_create_type.sql user_syncs_and_quick_mix.sql user_syncs_fix_dup_tracks_2408.sql +jam_tracks.sql +shopping_carts.sql +recurly.sql diff --git a/db/up/jam_tracks.sql b/db/up/jam_tracks.sql new file mode 100644 index 000000000..a6e716fa2 --- /dev/null +++ b/db/up/jam_tracks.sql @@ -0,0 +1,68 @@ +CREATE TABLE jam_track_licensors ( + id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4() NOT NULL, + name VARCHAR NOT NULL UNIQUE, + description TEXT, + attention TEXT, + address_line_1 VARCHAR, + address_line_2 VARCHAR, + city VARCHAR, + state VARCHAR, + zip_code VARCHAR, + contact VARCHAR, + email VARCHAR, + phone VARCHAR, + created_at TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW() NOT NULL, + updated_at TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW() NOT NULL +); + +CREATE TABLE jam_tracks ( + id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4() NOT NULL, + name VARCHAR NOT NULL UNIQUE, + description TEXT, + bpm decimal, + time_signature VARCHAR, + status VARCHAR, + recording_type VARCHAR, + original_artist TEXT, + songwriter TEXT, + publisher TEXT, + pro VARCHAR, + sales_region VARCHAR, + price decimal, + reproduction_royalty BOOLEAN, + public_performance_royalty BOOLEAN, + reproduction_royalty_amount DECIMAL, + licensor_royalty_amount DECIMAL, + pro_royalty_amount DECIMAL, + url VARCHAR, + md5 VARCHAR, + length BIGINT, + licensor_id VARCHAR(64) REFERENCES jam_track_licensors (id) ON DELETE SET NULL, + genre_id VARCHAR(64) REFERENCES genres (id) ON DELETE SET NULL, + created_at TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW() NOT NULL, + updated_at TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW() NOT NULL +); + +CREATE TABLE jam_track_tracks ( + id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4() NOT NULL, + position INTEGER, + track_type VARCHAR, + jam_track_id VARCHAR(64) REFERENCES jam_tracks(id) ON DELETE CASCADE, + instrument_id VARCHAR(64) REFERENCES instruments(id) ON DELETE SET NULL, + part VARCHAR, + url VARCHAR, + md5 VARCHAR, + length BIGINT, + created_at TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW() NOT NULL, + updated_at TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW() NOT NULL +); + +CREATE INDEX jam_track_tracks_position_uniqkey ON jam_track_tracks (position, jam_track_id); + +CREATE TABLE jam_track_rights ( + id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4() NOT NULL, + user_id VARCHAR(64) NOT NULL REFERENCES users(id), + jam_track_id VARCHAR(64) NOT NULL REFERENCES jam_tracks(id) +); + +CREATE INDEX jam_tracks_rights_uniqkey ON jam_track_rights (user_id, jam_track_id); \ No newline at end of file diff --git a/db/up/recurly.sql b/db/up/recurly.sql new file mode 100644 index 000000000..4745fa14d --- /dev/null +++ b/db/up/recurly.sql @@ -0,0 +1,2 @@ +ALTER TABLE users ADD COLUMN recurly_code VARCHAR(50) DEFAULT NULL; +ALTER TABLE jam_tracks ADD COLUMN plan_code VARCHAR(50) DEFAULT NULL; \ No newline at end of file diff --git a/db/up/shopping_carts.sql b/db/up/shopping_carts.sql new file mode 100644 index 000000000..1ec83122f --- /dev/null +++ b/db/up/shopping_carts.sql @@ -0,0 +1,10 @@ +CREATE TABLE shopping_carts ( + id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4(), + quantity INTEGER NOT NULL DEFAULT 1, + user_id VARCHAR(64) NOT NULL REFERENCES users(id) ON DELETE CASCADE, + cart_id VARCHAR(64) NOT NULL, + cart_class_name VARCHAR(64), + cart_type VARCHAR(64), + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP +); diff --git a/ruby/lib/jam_ruby.rb b/ruby/lib/jam_ruby.rb index 649dc40e6..b616794bf 100755 --- a/ruby/lib/jam_ruby.rb +++ b/ruby/lib/jam_ruby.rb @@ -71,6 +71,8 @@ require "jam_ruby/app/uploaders/perf_data_uploader" require "jam_ruby/app/uploaders/recorded_track_uploader" require "jam_ruby/app/uploaders/mix_uploader" require "jam_ruby/app/uploaders/music_notation_uploader" +require "jam_ruby/app/uploaders/jam_track_uploader" +require "jam_ruby/app/uploaders/jam_track_track_uploader" require "jam_ruby/app/uploaders/max_mind_release_uploader" require "jam_ruby/lib/desk_multipass" require "jam_ruby/lib/ip" @@ -170,11 +172,16 @@ require "jam_ruby/models/email_batch_new_musician" require "jam_ruby/models/email_batch_progression" require "jam_ruby/models/email_batch_scheduled_sessions" require "jam_ruby/models/email_batch_set" +require "jam_ruby/models/jam_track_licensor" +require "jam_ruby/models/jam_track" +require "jam_ruby/models/jam_track_track" +require "jam_ruby/models/jam_track_right" require "jam_ruby/app/mailers/async_mailer" require "jam_ruby/app/mailers/batch_mailer" require "jam_ruby/app/mailers/progress_mailer" require "jam_ruby/models/affiliate_partner" require "jam_ruby/models/chat_message" +require "jam_ruby/models/shopping_cart" require "jam_ruby/models/generic_state" require "jam_ruby/models/score_history" require "jam_ruby/models/jam_company" diff --git a/ruby/lib/jam_ruby/app/uploaders/jam_track_track_uploader.rb b/ruby/lib/jam_ruby/app/uploaders/jam_track_track_uploader.rb new file mode 100644 index 000000000..4a4d8cc4f --- /dev/null +++ b/ruby/lib/jam_ruby/app/uploaders/jam_track_track_uploader.rb @@ -0,0 +1,28 @@ +class JamTrackTrackUploader < CarrierWave::Uploader::Base + # include CarrierWaveDirect::Uploader + include CarrierWave::MimeTypes + + process :set_content_type + + def initialize(*) + super + JamRuby::UploaderConfiguration.set_aws_private_configuration(self) + end + + # Add a white list of extensions which are allowed to be uploaded. + def extension_white_list + %w(ogg) + end + + def store_dir + nil + end + + def md5 + @md5 ||= ::Digest::MD5.file(current_path).hexdigest + end + + def filename + "#{model.store_dir}/#{model.filename}" if model.id + end +end diff --git a/ruby/lib/jam_ruby/app/uploaders/jam_track_uploader.rb b/ruby/lib/jam_ruby/app/uploaders/jam_track_uploader.rb new file mode 100644 index 000000000..784d646c4 --- /dev/null +++ b/ruby/lib/jam_ruby/app/uploaders/jam_track_uploader.rb @@ -0,0 +1,28 @@ +class JamTrackUploader < CarrierWave::Uploader::Base + # include CarrierWaveDirect::Uploader + include CarrierWave::MimeTypes + + process :set_content_type + + def initialize(*) + super + JamRuby::UploaderConfiguration.set_aws_private_configuration(self) + end + + # Add a white list of extensions which are allowed to be uploaded. + def extension_white_list + %w(jka) + end + + def store_dir + nil + end + + def md5 + @md5 ||= ::Digest::MD5.file(current_path).hexdigest + end + + def filename + "#{model.store_dir}/#{model.filename}" if model.id + end +end diff --git a/ruby/lib/jam_ruby/constants/validation_messages.rb b/ruby/lib/jam_ruby/constants/validation_messages.rb index 724eb5680..74070a6bc 100644 --- a/ruby/lib/jam_ruby/constants/validation_messages.rb +++ b/ruby/lib/jam_ruby/constants/validation_messages.rb @@ -40,6 +40,11 @@ module ValidationMessages EMAIL_MATCHES_CURRENT = "is same as your current email" INVALID_FPFILE = "is not valid" + # recurly + RECURLY_ERROR = "Error occurred during Recurly transaction." + RECURLY_ACCOUNT_ERROR = "You don't have Recurly account yet." + RECURLY_PARAMETER_ERROR = "You didn't input correct information for Recurly transaction." + #connection USER_OR_LATENCY_TESTER_PRESENT = "user or latency_tester must be present" SELECT_AT_LEAST_ONE = "Please select at least one track" # DO NOT CHANGE THIS TEXT MESSAGE UNLESS YOU CHANGE createSession.js.erb, which is looking for it diff --git a/ruby/lib/jam_ruby/lib/s3_manager.rb b/ruby/lib/jam_ruby/lib/s3_manager.rb index 8e31532d1..3b6a6bf77 100644 --- a/ruby/lib/jam_ruby/lib/s3_manager.rb +++ b/ruby/lib/jam_ruby/lib/s3_manager.rb @@ -88,6 +88,14 @@ module JamRuby end end + def exists?(filename) + s3_bucket.objects[filename].exists? + end + + def length(filename) + s3_bucket.objects[filename].content_length + end + private def s3_bucket diff --git a/ruby/lib/jam_ruby/models/genre.rb b/ruby/lib/jam_ruby/models/genre.rb index b8e61a270..e2e0faa1d 100644 --- a/ruby/lib/jam_ruby/models/genre.rb +++ b/ruby/lib/jam_ruby/models/genre.rb @@ -14,6 +14,8 @@ module JamRuby # genres has_and_belongs_to_many :recordings, :class_name => "JamRuby::Recording", :join_table => "recordings_genres" + # jam tracks + has_many :jam_tracks, :class_name => "JamRuby::JamTrack" def to_s description diff --git a/ruby/lib/jam_ruby/models/jam_track.rb b/ruby/lib/jam_ruby/models/jam_track.rb new file mode 100644 index 000000000..b3f11dbb1 --- /dev/null +++ b/ruby/lib/jam_ruby/models/jam_track.rb @@ -0,0 +1,106 @@ +module JamRuby + class JamTrack < ActiveRecord::Base + include JamRuby::S3ManagerMixin + + TIME_SIGNATURES = %w{4/4 3/4 2/4 6/8 5/8'} + STATUS = %w{Staging Production Retired} + RECORDING_TYPE = %w{Cover Original} + PRO = %w{ASCAP BMI SESAC} + SALES_REGION = ['United States', 'Worldwide'] + + PRODUCT_TYPE = 'JamTrack' + + mount_uploader :url, JamTrackUploader + + attr_accessible :name, :description, :bpm, :time_signature, :status, :recording_type, + :original_artist, :songwriter, :publisher, :licensor, :licensor_id, :pro, :genre, :genre_id, :sales_region, :price, + :reproduction_royalty, :public_performance_royalty, :reproduction_royalty_amount, + :licensor_royalty_amount, :pro_royalty_amount, :jam_track_tracks_attributes, as: :admin + + validates :name, presence: true, uniqueness: true, length: {maximum: 200} + validates :description, length: {maximum: 1000} + validates_format_of :bpm, with: /^\d+\.*\d{0,1}$/ + validates :time_signature, inclusion: {in: [nil] + TIME_SIGNATURES} + validates :status, inclusion: {in: [nil] + STATUS} + validates :recording_type, inclusion: {in: [nil] + RECORDING_TYPE} + validates :original_artist, length: {maximum: 200} + validates :songwriter, length: {maximum: 1000} + validates :publisher, length: {maximum: 1000} + validates :pro, inclusion: {in: [nil] + PRO} + validates :sales_region, inclusion: {in: [nil] + SALES_REGION} + validates_format_of :price, with: /^\d+\.*\d{0,2}$/ + + validates :reproduction_royalty, inclusion: {in: [nil, true, false]} + validates :public_performance_royalty, inclusion: {in: [nil, true, false]} + validates_format_of :reproduction_royalty_amount, with: /^\d+\.*\d{0,3}$/ + validates_format_of :licensor_royalty_amount, with: /^\d+\.*\d{0,3}$/ + validates_format_of :pro_royalty_amount, with: /^\d+\.*\d{0,3}$/ + + before_save :sanitize_active_admin + + belongs_to :genre, class_name: "JamRuby::Genre" + belongs_to :licensor , class_name: 'JamRuby::JamTrackLicensor', foreign_key: 'licensor_id' + + has_many :jam_track_tracks, :class_name => "JamRuby::JamTrackTrack", order: 'position ASC' + + has_many :jam_track_rights, :class_name => "JamRuby::JamTrackRight", inverse_of: 'jam_track', :foreign_key => "jam_track_id" + has_many :owners, :through => :jam_track_rights, :class_name => "JamRuby::User", :source => :user + + accepts_nested_attributes_for :jam_track_tracks, allow_destroy: true + + # create storage directory that will house this jam_track, as well as + def store_dir + "jam_tracks/#{created_at.strftime('%m-%d-%Y')}/#{id}" + end + + # create name of the file + def filename + "#{name}.jka" + end + + # creates a short-lived URL that has access to the object. + # the idea is that this is used when a user who has the rights to this tries to download this JamTrack + # we would verify their rights (can_download?), and generates a URL in response to the click so that they can download + # but the url is short lived enough so that it wouldn't be easily shared + def sign_url(expiration_time = 120) + s3_manager.sign_url(self[:url], {:expires => expiration_time, :response_content_type => 'audio/jka', :secure => false}) + end + + def can_download?(user) + owners.include?(user) + end + + def self.index user, options = {} + limit = options[:limit] + limit ||= 20 + limit = limit.to_i + + start = options[:start].presence + start = start.to_i || 0 + + query = JamTrack.joins(:jam_track_tracks) + .offset(start) + .limit(limit) + + query = query.where("jam_tracks.genre_id = '#{options[:genre]}'") unless options[:genre].blank? + query = query.where("jam_track_tracks.instrument_id = '#{options[:instrument]}'") unless options[:instrument].blank? + query = query.where("jam_tracks.sales_region = '#{options[:availability]}'") unless options[:availability].blank? + query = query.group("jam_tracks.id") + + if query.length == 0 + [query, nil] + elsif query.length < limit + [query, nil] + else + [query, start + limit] + end + end + + private + + def sanitize_active_admin + self.genre_id = nil if self.genre_id == '' + self.licensor_id = nil if self.licensor_id == '' + end + end +end diff --git a/ruby/lib/jam_ruby/models/jam_track_licensor.rb b/ruby/lib/jam_ruby/models/jam_track_licensor.rb new file mode 100644 index 000000000..d5ee3df75 --- /dev/null +++ b/ruby/lib/jam_ruby/models/jam_track_licensor.rb @@ -0,0 +1,21 @@ +module JamRuby + class JamTrackLicensor < ActiveRecord::Base + + attr_accessible :name, :description, :attention, :address_line_1, :address_line_2, + :city, :state, :zip_code, :contact, :email, :phone, as: :admin + + validates :name, presence: true, uniqueness: true, length: {maximum: 200} + validates :description, length: {maximum: 1000} + validates :attention, length: {maximum: 200} + validates :address_line_1, length: {maximum: 200} + validates :address_line_2, length: {maximum: 200} + validates :city, length: {maximum: 200} + validates :state, length: {maximum: 200} + validates :zip_code, length: {maximum: 200} + validates :contact, length: {maximum: 200} + validates :email, length: {maximum: 200} + validates :phone, length: {maximum: 200} + + has_many :jam_tracks, :class_name => "JamRuby::JamTrack", foreign_key: 'licensor_id' + end +end diff --git a/ruby/lib/jam_ruby/models/jam_track_right.rb b/ruby/lib/jam_ruby/models/jam_track_right.rb new file mode 100644 index 000000000..2a741a3a3 --- /dev/null +++ b/ruby/lib/jam_ruby/models/jam_track_right.rb @@ -0,0 +1,14 @@ +module JamRuby + + # describes what users have rights to which tracks + class JamTrackRight < ActiveRecord::Base + + belongs_to :user, class_name: "JamRuby::User" # the owner, or purchaser of the jam_track + belongs_to :jam_track, class_name: "JamRuby::JamTrack" + + validates :user, presence:true + validates :jam_track, presence:true + + validates_uniqueness_of :user_id, scope: :jam_track_id + end +end diff --git a/ruby/lib/jam_ruby/models/jam_track_track.rb b/ruby/lib/jam_ruby/models/jam_track_track.rb new file mode 100644 index 000000000..f9e8ebcb9 --- /dev/null +++ b/ruby/lib/jam_ruby/models/jam_track_track.rb @@ -0,0 +1,47 @@ +module JamRuby + + # describes an audio track (like the drums, or guitar) that comprises a JamTrack + class JamTrackTrack < ActiveRecord::Base + include JamRuby::S3ManagerMixin + + # there should only be one Master per JamTrack, but there can be N Track per JamTrack + TRACK_TYPE = %w{Master Track} + + mount_uploader :url, JamTrackTrackUploader + + attr_accessible :track_type, :instrument, :instrument_id, :position, :part, :url, as: :admin + + validates :position, presence: true, numericality: {only_integer: true}, length: {in: 1..1000} + validates :part, length: {maximum: 20} + validates :track_type, inclusion: {in: TRACK_TYPE } + validates_uniqueness_of :position, scope: :jam_track_id + validates_uniqueness_of :part, scope: :jam_track_id + # validates :jam_track, presence: true + + belongs_to :instrument, class_name: "JamRuby::Instrument" + belongs_to :jam_track, class_name: "JamRuby::JamTrack" + + # create storage directory that will house this jam_track, as well as + def store_dir + "#{jam_track.store_dir}/tracks" + end + + # create name of the file + def filename + track_type == 'Master' ? 'master.ogg' : "#{part}.ogg" + end + + # creates a short-lived URL that has access to the object. + # the idea is that this is used when a user who has the rights to this tries to download this JamTrack + # we would verify their rights (can_download?), and generates a URL in response to the click so that they can download + # but the url is short lived enough so that it wouldn't be easily shared + def sign_url(expiration_time = 120) + s3_manager.sign_url(self[:url], {:expires => expiration_time, :response_content_type => 'audio/ogg', :secure => false}) + end + + def can_download?(user) + # I think we have to make a special case for 'previews', but maybe that's just up to the controller to not check can_download? + jam_track.owners.include?(user) + end + end +end diff --git a/ruby/lib/jam_ruby/models/recorded_track.rb b/ruby/lib/jam_ruby/models/recorded_track.rb index ce3299d8b..d51605c5f 100644 --- a/ruby/lib/jam_ruby/models/recorded_track.rb +++ b/ruby/lib/jam_ruby/models/recorded_track.rb @@ -16,6 +16,7 @@ module JamRuby attr_writer :is_skip_mount_uploader attr_accessible :discard, :user, :user_id, :instrument_id, :sound, :client_id, :track_id, :client_track_id, :url, as: :admin + attr_writer :current_user SOUND = %w(mono stereo) diff --git a/ruby/lib/jam_ruby/models/search.rb b/ruby/lib/jam_ruby/models/search.rb index c83730172..97f6b80de 100644 --- a/ruby/lib/jam_ruby/models/search.rb +++ b/ruby/lib/jam_ruby/models/search.rb @@ -90,6 +90,7 @@ module JamRuby PARAM_MUSICIAN = :srch_m PARAM_BAND = :srch_b PARAM_FEED = :srch_f + PARAM_JAMTRACK = :srch_j F_PER_PAGE = B_PER_PAGE = M_PER_PAGE = 20 M_MILES_DEFAULT = 500 diff --git a/ruby/lib/jam_ruby/models/shopping_cart.rb b/ruby/lib/jam_ruby/models/shopping_cart.rb new file mode 100644 index 000000000..ccad1b9d4 --- /dev/null +++ b/ruby/lib/jam_ruby/models/shopping_cart.rb @@ -0,0 +1,34 @@ +module JamRuby + class ShoppingCart < ActiveRecord::Base + + attr_accessible :quantity, :cart_type, :product_info + + belongs_to :user, :inverse_of => :shopping_carts, :class_name => "JamRuby::User", :foreign_key => "user_id" + + validates :cart_id, presence: true + validates :cart_type, presence: true + validates :cart_class_name, presence: true + + default_scope order('created_at DESC') + + def product_info + product = self.cart_product + {name: product.name, price: product.price} unless product.nil? + end + + def cart_product + self.cart_class_name.classify.constantize.find_by_id self.cart_id unless self.cart_class_name.blank? + end + + def self.create user, product, quantity = 1 + cart = ShoppingCart.new + cart.user = user + cart.cart_type = product.class::PRODUCT_TYPE + cart.cart_class_name = product.class.name + cart.cart_id = product.id + cart.quantity = quantity + cart.save + cart + end + end +end \ No newline at end of file diff --git a/ruby/lib/jam_ruby/models/user.rb b/ruby/lib/jam_ruby/models/user.rb index 5216063db..3d3c52cce 100644 --- a/ruby/lib/jam_ruby/models/user.rb +++ b/ruby/lib/jam_ruby/models/user.rb @@ -121,6 +121,13 @@ module JamRuby # diagnostics has_many :diagnostics, :class_name => "JamRuby::Diagnostic" + # jam_tracks + has_many :jam_track_rights, :class_name => "JamRuby::JamTrackRight", :foreign_key => "user_id" + has_many :purchased_jam_tracks, :through => :jam_track_rights, :class_name => "JamRuby::JamTrack", :source => :jam_track, :order => :created_at + + # Shopping carts + has_many :shopping_carts, :class_name => "JamRuby::ShoppingCart" + # score history has_many :from_score_histories, :class_name => "JamRuby::ScoreHistory", foreign_key: 'from_user_id' has_many :to_score_histories, :class_name => "JamRuby::ScoreHistory", foreign_key: 'to_user_id' diff --git a/ruby/spec/factories.rb b/ruby/spec/factories.rb index b91b9dee1..78af1b599 100644 --- a/ruby/spec/factories.rb +++ b/ruby/spec/factories.rb @@ -650,4 +650,54 @@ FactoryGirl.define do factory :max_mind_release, :class => JamRuby::MaxMindRelease do released_at Time.now.to_date end + + factory :jam_track_licensor, :class => JamRuby::JamTrackLicensor do + sequence(:name) { |n| "licensor-#{n}" } + sequence(:description) { |n| "description-#{n}" } + sequence(:attention) { |n| "attention-#{n}" } + sequence(:address_line_1) { |n| "address1-#{n}" } + sequence(:address_line_2) { |n| "address2-#{n}" } + sequence(:city) { |n| "city-#{n}" } + sequence(:state) { |n| "state-#{n}" } + sequence(:zip_code) { |n| "zipcode-#{n}" } + sequence(:contact) { |n| "contact-#{n}" } + sequence(:email) { |n| "email-#{n}" } + sequence(:phone) { |n| "phone-#{n}" } + end + + factory :jam_track, :class => JamRuby::JamTrack do + sequence(:name) { |n| "jam-track-#{n}" } + sequence(:description) { |n| "description-#{n}" } + bpm 100.1 + time_signature '4/4' + status 'Production' + recording_type 'Cover' + sequence(:original_artist) { |n| "original-artist-#{n}" } + sequence(:songwriter) { |n| "songwriter-#{n}" } + sequence(:publisher) { |n| "publisher-#{n}" } + pro 'ASCAP' + sales_region 'United States' + price 1.99 + reproduction_royalty true + public_performance_royalty true + reproduction_royalty_amount 0.999 + licensor_royalty_amount 0.999 + pro_royalty_amount 0.999 + + genre JamRuby::Genre.first + association :licensor, factory: :jam_track_licensor + end + + factory :jam_track_track, :class => JamRuby::JamTrackTrack do + position 1 + part 'lead guitar' + track_type 'Track' + instrument JamRuby::Instrument.find('electric guitar') + association :jam_track, factory: :jam_track + end + + factory :jam_track_right, :class => JamRuby::JamTrackRight do + association :jam_track, factory: :jam_track + association :user, factory: :user + end end diff --git a/ruby/spec/jam_ruby/models/jam_track_licensor_spec.rb b/ruby/spec/jam_ruby/models/jam_track_licensor_spec.rb new file mode 100644 index 000000000..80bfc377c --- /dev/null +++ b/ruby/spec/jam_ruby/models/jam_track_licensor_spec.rb @@ -0,0 +1,10 @@ +require 'spec_helper' + +describe JamTrackLicensor do + + it "created" do + FactoryGirl.create(:jam_track_licensor) + end + +end + diff --git a/ruby/spec/jam_ruby/models/jam_track_right_spec.rb b/ruby/spec/jam_ruby/models/jam_track_right_spec.rb new file mode 100644 index 000000000..5d136d62a --- /dev/null +++ b/ruby/spec/jam_ruby/models/jam_track_right_spec.rb @@ -0,0 +1,31 @@ +require 'spec_helper' + +describe JamTrackRight do + + it "created" do + jam_track_right = FactoryGirl.create(:jam_track_right) + + user = jam_track_right.user + jam_track = jam_track_right.jam_track + + # verify that the user sees this as a purchased jam_track + user.purchased_jam_tracks.should == [jam_track] + + # verify that the jam_track sees the user as an owner + jam_track.owners.should == [user] + + end + + describe "validations" do + it "one purchase per user/jam_track combo" do + user = FactoryGirl.create(:user) + jam_track = FactoryGirl.create(:jam_track) + + right_1 = FactoryGirl.create(:jam_track_right, user: user, jam_track: jam_track) + right_2 = FactoryGirl.build(:jam_track_right, user: user, jam_track: jam_track) + right_2.valid?.should be_false + right_2.errors[:user_id].should == ['has already been taken'] + end + end +end + diff --git a/ruby/spec/jam_ruby/models/jam_track_spec.rb b/ruby/spec/jam_ruby/models/jam_track_spec.rb new file mode 100644 index 000000000..85cf9059c --- /dev/null +++ b/ruby/spec/jam_ruby/models/jam_track_spec.rb @@ -0,0 +1,138 @@ +require 'spec_helper' + +require 'carrierwave/test/matchers' + +describe JamTrack do + include CarrierWave::Test::Matchers + include UsesTempFiles + + + it "created" do + jam_track = FactoryGirl.create(:jam_track) + jam_track.licensor.should_not be_nil + jam_track.licensor.jam_tracks.should == [jam_track] + end + + describe "validations" do + describe "bpm" do + it "1" do + FactoryGirl.build(:jam_track, bpm: 1).valid?.should be_true + end + + it "100" do + FactoryGirl.build(:jam_track, bpm: 100).valid?.should be_true + end + + it "100.1" do + FactoryGirl.build(:jam_track, bpm: 100.1).valid?.should be_true + end + + it "100.12" do + jam_track = FactoryGirl.build(:jam_track, bpm: 100.12) + jam_track.valid?.should be_false + jam_track.errors[:bpm].should == ['is invalid'] + end + end + + describe "price" do + + it "0.99" do + FactoryGirl.build(:jam_track, price: 0.99).valid?.should be_true + end + + it "1" do + FactoryGirl.build(:jam_track, price: 1).valid?.should be_true + end + + it "100" do + FactoryGirl.build(:jam_track, price: 100).valid?.should be_true + end + + it "100.1" do + FactoryGirl.build(:jam_track, price: 100.1).valid?.should be_true + end + + it "100.12" do + FactoryGirl.build(:jam_track, price: 100.12).valid?.should be_true + end + + it "100.123" do + jam_track = FactoryGirl.build(:jam_track, price: 100.123) + jam_track.valid?.should be_false + jam_track.errors[:price].should == ['is invalid'] + end + end + + describe "reproduction_royalty_amount" do + it "0.99" do + FactoryGirl.build(:jam_track, reproduction_royalty_amount: 0.99).valid?.should be_true + end + + it "1" do + FactoryGirl.build(:jam_track, reproduction_royalty_amount: 1).valid?.should be_true + end + + it "100" do + FactoryGirl.build(:jam_track, reproduction_royalty_amount: 100).valid?.should be_true + end + + it "100.1" do + FactoryGirl.build(:jam_track, reproduction_royalty_amount: 100.1).valid?.should be_true + end + + it "100.12" do + FactoryGirl.build(:jam_track, reproduction_royalty_amount: 100.12).valid?.should be_true + end + + it "100.123" do + FactoryGirl.build(:jam_track, reproduction_royalty_amount: 100.123).valid?.should be_true + end + + it "100.1234" do + jam_track = FactoryGirl.build(:jam_track, reproduction_royalty_amount: 100.1234) + jam_track.valid?.should be_false + jam_track.errors[:reproduction_royalty_amount].should == ['is invalid'] + end + end + end + + describe "upload/download" do + JKA_NAME = 'blah.jka' + + in_directory_with_file(JKA_NAME) + + before(:all) do + original_storage = JamTrackUploader.storage = :fog + end + + after(:all) do + JamTrackUploader.storage = @original_storage + end + + before(:each) do + content_for_file('abc') + end + + it "uploads to s3 with correct name, and then downloads via signed URL" do + jam_track = FactoryGirl.create(:jam_track) + uploader = JamTrackUploader.new(jam_track, :url) + uploader.store!(File.open(JKA_NAME)) # uploads file + jam_track.save! + + # verify that the uploader stores the correct path + jam_track[:url].should == jam_track.store_dir + '/' + jam_track.filename + + # verify it's on S3 + s3 = S3Manager.new(APP_CONFIG.aws_bucket, APP_CONFIG.aws_access_key_id, APP_CONFIG.aws_secret_access_key) + s3.exists?(jam_track[:url]).should be_true + s3.length(jam_track[:url]).should == 'abc'.length + + # download it via signed URL, and check contents + url = jam_track.sign_url + downloaded_contents = open(url).read + downloaded_contents.should == 'abc' + end + + end +end + diff --git a/ruby/spec/jam_ruby/models/jam_track_track_spec.rb b/ruby/spec/jam_ruby/models/jam_track_track_spec.rb new file mode 100644 index 000000000..6cc5c3773 --- /dev/null +++ b/ruby/spec/jam_ruby/models/jam_track_track_spec.rb @@ -0,0 +1,70 @@ +require 'spec_helper' + +describe JamTrackTrack do + include CarrierWave::Test::Matchers + include UsesTempFiles + + it "created" do + jam_track_track = FactoryGirl.create(:jam_track_track) + jam_track_track.jam_track.should_not be_nil + jam_track_track.jam_track.jam_track_tracks.should == [jam_track_track] + end + + describe "validations" do + it "position" do + jam_track = FactoryGirl.create(:jam_track) + jam_track_track_1 = FactoryGirl.create(:jam_track_track, position: 1, jam_track: jam_track) + jam_track_track_2 = FactoryGirl.build(:jam_track_track, position: 1, jam_track: jam_track) + jam_track_track_2.valid?.should == false + jam_track_track_2.errors[:position].should == ['has already been taken'] + end + + it "jam_track required" do + pending "Need to be not mandatory because of activeadmin" + jam_track = FactoryGirl.build(:jam_track_track, jam_track: nil) + jam_track.valid?.should be_false + jam_track.errors[:jam_track].should == ["can't be blank"] + end + end + + + describe "upload/download" do + TRACK_NAME = 'lead guitar.ogg' + + in_directory_with_file(TRACK_NAME) + + before(:all) do + original_storage = JamTrackTrackUploader.storage = :fog + end + + after(:all) do + JamTrackTrackUploader.storage = @original_storage + end + + before(:each) do + content_for_file('abc') + end + + it "uploads to s3 with correct name, and then downloads via signed URL" do + jam_track_track = FactoryGirl.create(:jam_track_track) + uploader = JamTrackTrackUploader.new(jam_track_track, :url) + uploader.store!(File.open(TRACK_NAME)) # uploads file + jam_track_track.save! + + # verify that the uploader stores the correct path + jam_track_track[:url].should == jam_track_track.store_dir + '/' + jam_track_track.filename + + # verify it's on S3 + s3 = S3Manager.new(APP_CONFIG.aws_bucket, APP_CONFIG.aws_access_key_id, APP_CONFIG.aws_secret_access_key) + s3.exists?(jam_track_track[:url]).should be_true + s3.length(jam_track_track[:url]).should == 'abc'.length + + # download it via signed URL, and check contents + url = jam_track_track.sign_url + downloaded_contents = open(url).read + downloaded_contents.should == 'abc' + end + + end + +end diff --git a/ruby/spec/jam_ruby/models/shopping_cart_spec.rb b/ruby/spec/jam_ruby/models/shopping_cart_spec.rb new file mode 100644 index 000000000..db3f4d75c --- /dev/null +++ b/ruby/spec/jam_ruby/models/shopping_cart_spec.rb @@ -0,0 +1,23 @@ +require 'spec_helper' + +describe ShoppingCart do + + let(:user) { FactoryGirl.create(:user) } + let(:jam_track) {FactoryGirl.create(:jam_track) } + + before(:each) do + ShoppingCart.delete_all + end + + it "can reference a shopping cart" do + shopping_cart = ShoppingCart.create user, jam_track, 1 + + ShoppingCart.count.should == 1 + user.shopping_carts.count.should == 1 + user.shopping_carts[0].product_info[:name].should == jam_track.name + user.shopping_carts[0].product_info[:price].should == jam_track.price + user.shopping_carts[0].cart_type.should == jam_track.class::PRODUCT_TYPE + user.shopping_carts[0].quantity.should == 1 + end + +end diff --git a/ruby/spec/jam_ruby/resque/unused_music_notation_cleaner_spec.rb b/ruby/spec/jam_ruby/resque/unused_music_notation_cleaner_spec.rb index f47f186df..1f0ff3e55 100644 --- a/ruby/spec/jam_ruby/resque/unused_music_notation_cleaner_spec.rb +++ b/ruby/spec/jam_ruby/resque/unused_music_notation_cleaner_spec.rb @@ -5,9 +5,9 @@ require 'fileutils' describe UnusedMusicNotationCleaner do include UsesTempFiles - NOTATION_TEMP_FILE='detail.png' + UNUSED_NOTATION_TEMP_FILE='detail.png' - in_directory_with_file(NOTATION_TEMP_FILE) + in_directory_with_file(UNUSED_NOTATION_TEMP_FILE) before do content_for_file("this is music notation test file") @@ -30,7 +30,8 @@ describe UnusedMusicNotationCleaner do it "find no music notataions if music_session_id is nil and created at 1 hour ago" do notation = MusicNotation.new - notation.file_url = File.open(NOTATION_TEMP_FILE) + + notation.file_url = File.open(UNUSED_NOTATION_TEMP_FILE) notation.size = 10 notation.user = FactoryGirl.create(:user) notation.created_at = Time.now - 1.hours @@ -44,7 +45,7 @@ describe UnusedMusicNotationCleaner do music_session = FactoryGirl.create(:music_session, :session_removed_at => Time.now - 1.hours) notation = MusicNotation.new - notation.file_url = File.open(NOTATION_TEMP_FILE) + notation.file_url = File.open(UNUSED_NOTATION_TEMP_FILE) notation.size = 10 notation.user = FactoryGirl.create(:user) notation.created_at = Time.now - 1.hours @@ -56,7 +57,7 @@ describe UnusedMusicNotationCleaner do it "find music notataions if music_session_id is nil and created at 2 days ago" do notation = MusicNotation.new - notation.file_url = File.open(NOTATION_TEMP_FILE) + notation.file_url = File.open(UNUSED_NOTATION_TEMP_FILE) notation.size = 10 notation.user = FactoryGirl.create(:user) notation.created_at = Time.now - 2.days @@ -70,7 +71,7 @@ describe UnusedMusicNotationCleaner do music_session = FactoryGirl.create(:music_session, :session_removed_at => Time.now - 2.days) notation = MusicNotation.new - notation.file_url = File.open(NOTATION_TEMP_FILE) + notation.file_url = File.open(UNUSED_NOTATION_TEMP_FILE) notation.size = 10 notation.user = FactoryGirl.create(:user) notation.created_at = Time.now - 3.days diff --git a/web/Gemfile b/web/Gemfile index e2b8e8a44..284f2be0b 100644 --- a/web/Gemfile +++ b/web/Gemfile @@ -83,6 +83,7 @@ gem 'rubyzip' gem 'slim' gem 'htmlentities' gem 'sanitize' +gem 'recurly' group :development, :test do gem 'rspec-rails', '2.14.2' diff --git a/web/app/assets/images/content/bkg_home_jamtracks.jpg b/web/app/assets/images/content/bkg_home_jamtracks.jpg new file mode 100644 index 0000000000000000000000000000000000000000..700f012c6efbd0bdaaf0efb141e725bfaf463ec0 GIT binary patch literal 2767 zcma)7c{JNu8@{n`qxRI=1=Uu|sHOIP?Vv)Ztu3ljrq-4cTkLyNGiv)#(GXNoMKP+x zu2mEvu@plrwSSAeh)%GomnKmZ3TtPcQl4G3OzK{~ktm)EQagh>Wyz{SDA!O6kJ$;rjb#ajG4 zTwFZ-0(^Y@e0%~z0-qryAb3dVkf4C5sF;|jsEo9^B+YWuO{7ZdR-@3ItYIR4P z3GVG*yBP&%0`kJId+zkS@AfG-*Z*j0iMz5&n%{b^-R+dM=3sZ7_TTU7;?<(oz2puH zGf$gSHQP`d4bPRpR?x<8CM#PlIae@j7Mx!uI>(wuw4&cIUv%`w$BE&tf2LrQ_X=sRdL|pM z*ETD^!*-WXhTK)+%`gQ#Jw7Q7uppOS@{G)rk8tR)Lw9D5?kD+ic?Q8ClC$-#>BkiK z8ew5H23i37JHB(P7{;8kCFhw*sT}DW_s#di( zqy7Vx?rzwy2G7dqfBIUe(E}>3P)B!NZ_MMV&XBHk^%|Jz&Z(OiQr7lYi9Rqitl#-T z{4ejhBUcP8NvW-l#;uy>MKGcjD!oj%8mq5fBl7y-7z^BC_9bu8%@!L!PG4z}$U;2m z`WdcLtWxccNu#G)7igRK`lgk-EW#a}&RlPHgHv!00&B}FS&1)ZdIt{fzxPtKjafKv zHQ|kD{&7YQsa{!DoRUq!ys8K|eUSeL{Ceh=exvZGO~gnBQQJ}2XZ{XEvQ0f9F~dqp z_vMDLv*Bv7cc_C5b~_n(n zovm_<$jPnJ!BrNf!A>j2YM-T)^=InYT5qZN2Ha^`Z+l6nTDa?tWOP~Oop7v-tdZ~| z-F-IH;S@w5K+)0P*Z|;Iwa@6j>!5l(d!mIhWMu zsor0Y@p*?qpC}erysxd9tNMehDY_JSHj3J4`X_aI$e_cj5Q{0r!-X?<*3Vyh;Sd0 zFM@jgn*vm;R-@bx;aKD17ZkX$F6FMWfx8Vo^-H39Hd5HrIzQ5K!o-jGh)V23btg*_ z=qv&qOmNaNdO?#h>CCs1}sTyWOrltx8yTg*h) zS22D)d#T)dRkJs4{vQL^*P%o>8K$+Zn4qITLGgdg>5dYQ)oa*gxYG%?nex^eGiNO< z`>Z!m+D%&VLXX^6GTp9*Kdn9l`;FUNWGGOz{78JB%c7qhTrwk2{&!eTeJUkZ`sX2 zTSj(hzYMXBQn#CLuB=k;k#sL?2V754u!RteR;UOet8o71y-oj_2f;V~mATqig2<7O z8N1rI+ultuQA>0x5I0KZ_i7rcvPabwd3CJs=56chhPEta4d%ap0{F=nhGvN>Ot3N} zC);a#X}PH0FtJvt*O=C4AaN9Elt3-=cg^f%rY(NF7Xbb}V{_%QdozEvZjKttCSrUi zdoYd;T+O06%9VWF{zrjS;I@5=O9*+x-soXbcF^A>Mqclx8pBzd!bbHlQF~ZuBXJpv zJe!M-_G`vJ=s+l9iygg4`^x@3i8Ci*on}e0_3x`6_q3ZIvh*sf!^a+<;@p$4sbd1A z1nn?AgG$<{dMtzDt+|`agWB4&oDx%1IzdJuC4yjYmC(_^AQ4fR)#B^XydX{##+6yX>bi{}|4c5+_cn*xMnyuxlPmO9sd z+v1GoHMKebWGcsOkA1=j43$-nFah6^AE}^RXFllME%J^28uN)y$%boFkxFq`LOCH7 z4?PtuukW_BE`g7WAf(}NMe)|}B#aR-B;o7e#LF7xpV zrr$DmmK#OeztyQFBcbtB*jrXZlGR!t4pmlijLGww)6~g3D;iVXNzLxMh?%@~4Hj*B z(AHV+in4udCrT+3(RX2FmsVLxC@d_1o}gkB6&7fih-Kn@S@$o=YfYh@aQecXXuy#j zfUrM4;MX~9l9ngw$8ysAc|>)`t_306#%Gu4#_OX2<)eJ(y?f@%R5?@0iC0K*f%JnW zT=VkRhDaU?BPl}lw_1M<{|WP-v4)SfGna5n%9R3h{VtHwkfhfvOKfoqOfZjmya&aS zhewxnTI@U#b=j;5z-?}ugSSo(sWzA!J4d5_#2dbMX9AMM$4mCvgS*cgd{lFB9o}WB zr7RK!GR%o~e4xmKPsoBGN}8%~s!d1t6xoIu3de=p4ejpixi>sVo%~l5Vugxq-AI_( zxXQU<@ z6%3)Djh5;<-oKnvVpw)3)jAqbA|?mSJ<4b48q3&4$}K;V-q;sT09mm{q@_Py=&D_( tnPfCeNu+pb%LUdiQeK&&>%C=|K(xPf=WmUrIWpx|uzSu*G>Br3{SQ6+&!_+Z literal 0 HcmV?d00001 diff --git a/web/app/assets/images/content/bkg_home_jamtracks_x.jpg b/web/app/assets/images/content/bkg_home_jamtracks_x.jpg new file mode 100644 index 0000000000000000000000000000000000000000..22df61a3dc64660e6c90cf0881b069092a481f72 GIT binary patch literal 2850 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 Q5m1i7)78&qol`;+0Iv(`qW}N^ literal 0 HcmV?d00001 diff --git a/web/app/assets/images/content/icon_jamtracks.png b/web/app/assets/images/content/icon_jamtracks.png new file mode 100644 index 0000000000000000000000000000000000000000..4cdb1e1f5724e9cf45ecc379b69b91cbe6fbb5cb GIT binary patch literal 779 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's no jamtracks.") ; + } + + if(currentPage > 0) { + $noMoreJamtracks.show(); + // there are bugs with infinitescroll not removing the 'loading'. + // it's most noticeable at the end of the list, so whack all such entries + $('.infinite-scroll-loader').remove(); + } + } + else { + currentPage++; + buildQuery(); + registerInfiniteScroll(); + } + } + + function registerInfiniteScroll() { + $scroller.infinitescroll({ + behavior: 'local', + navSelector: '#jamtrackScreen .btn-next-pager', + nextSelector: '#jamtrackScreen .btn-next-pager', + binder: $scroller, + dataType: 'json', + appendCallback: false, + prefill: false, + bufferPx: 100, + loading: { + msg: $('
Loading ...
'), + img: '/assets/shared/spinner.gif' + }, + path: function(page) { + return '/api/jamtracks?' + $.param(buildQuery()); + } + },function(json, opts) { + handleJamtrackResponse(json); + }); + $scroller.infinitescroll('resume'); + } + + function playJamtrack(e) { + e.preventDefault(); + } + + function addToCartJamtrack(e) { + e.preventDefault(); + + var params = {id: $(e.target).attr("data-jamtrack-id")}; + + rest.addJamtrackToShoppingCart(params) + .done(function(response) { + context.location = "/client#/shoppingCart"; + }) + .fail(app.ajaxError); + } + + function licenseUSWhy(e) { + e.preventDefault(); + + app.layout.showDialog('jamtrack-availability-dialog'); + } + + function registerEvents() { + $screen.find('.jamtrack-detail-btn').on("click", showJamtrackDescription); + $screen.find('.play-button').on('click', playJamtrack); + $screen.find('.jamtrack-add-cart').on('click', addToCartJamtrack); + $screen.find('.license-us-why').on('click', licenseUSWhy); + } + + function renderJamtracks(data) { + $.each(data.jamtracks, function(i, jamtrack) { + $.each(jamtrack.tracks, function (index, track) { + var inst = '../assets/content/icon_instrument_default24.png'; + if (track.instrument.id in instrument_logo_map) { + inst = instrument_logo_map[track.instrument.id].asset; + } + track.instrument_url = inst; + + track.instrument_desc = track.instrument.description; + if (track.part != "") { + track.instrument_desc += " ( " + track.part + " )"; + } + }); + + var options = { + jamtrack: jamtrack + }; + + var $jamtrackItem = $( + context._.template( + $('#template-jamtrack').html(), + options, + {variable: 'data'} + ) + ); + renderJamtrack($jamtrackItem ); + }); + + registerEvents(); + } + + function showJamtrackDescription(e) { + e.preventDefault(); + + var $description = $(e.target).parent(".detail-arrow").next(); + if ($description.css("display") == "none") { + $description.show(); + } + else { + $description.hide(); + } + } + + function renderJamtrack(jamtrack) { + $content.append(jamtrack); + } + + function initialize() { + var screenBindings = { + 'beforeShow': beforeShow, + 'afterShow': afterShow + }; + app.bindScreen('jamtrack', screenBindings); + + $screen = $("#jamtrack-find-form"); + $scroller = $screen.find('.content-body-scroller'); + $content = $screen.find(".jamtrack-content"); + $genre = $screen.find("#jamtrack_genre"); + $instrument = $screen.find("#jamtrack_instrument"); + $availability = $screen.find("#jamtrack_availability"); + $nextPager = $screen.find("a.btn-next-pager"); + $noMoreJamtracks = $screen.find("#end-of-jamtrack-list"); + + if($screen.length == 0) throw "$screen must be specified"; + if($scroller.length == 0) throw "$scroller must be specified"; + if($content.length == 0) throw "$content must be specified"; + if($noMoreJamtracks.length == 0) throw "$noMoreJamtracks must be specified"; + if($genre.length == 0) throw "$genre must be specified"; + if($instrument.length == 0) throw "$instrument must be specified"; + if($availability.length ==0) throw "$availability must be specified"; + + events(); + } + + this.initialize = initialize; + + return this; + } +})(window,jQuery); \ No newline at end of file diff --git a/web/app/assets/javascripts/order.js b/web/app/assets/javascripts/order.js new file mode 100644 index 000000000..b6a78bff9 --- /dev/null +++ b/web/app/assets/javascripts/order.js @@ -0,0 +1,511 @@ +(function(context,$) { + + "use strict"; + context.JK = context.JK || {}; + context.JK.OrderScreen = function(app) { + + var logger = context.JK.logger; + + var $screen = null; + var $navigation = null; + var $billingInfo = null; + var $shippingInfo = null; + var $paymentMethod = null; + var $shippingAddress = null; + var $shippingAsBilling = null; + var $paymentInfoPanel = null; + var $orderPanel = null; + var $orderContent = null; + var userDetail = null; + var step = null; + var billing_info = null; + var shipping_info = null; + var shipping_as_billing = null; + + function beforeShow() { + beforeShowPaymentInfo(); +// moveToOrder(); + } + + function beforeShowPaymentInfo() { + step = 2; + renderNavigation(); + renderAccountInfo(); + } + + function renderAccountInfo() { + rest.getUserDetail() + .done(populateAccountInfo) + .error(app.ajaxError); + } + + function populateAccountInfo(user) { + userDetail = user; + + if (userDetail.has_recurly_account) { + rest.getBillingInfo() + .done(function(response) { + $billingInfo.find("#billing-first-name").val(response.first_name); + $billingInfo.find("#billing-last-name").val(response.last_name); + $billingInfo.find("#billing-address1").val(response.address1); + $billingInfo.find("#billing-address2").val(response.address2); + $billingInfo.find("#billing-city").val(response.city); + $billingInfo.find("#billing-state").val(response.state); + $billingInfo.find("#billing-zip").val(response.zip); + $billingInfo.find("#billing-country").val(response.country); + + $shippingAddress.find("#shipping-first-name").val(response.first_name); + $shippingAddress.find("#shipping-last-name").val(response.last_name); + $shippingAddress.find("#shipping-address1").val(response.address1); + $shippingAddress.find("#shipping-address2").val(response.address2); + $shippingAddress.find("#shipping-city").val(response.city); + $shippingAddress.find("#shipping-state").val(response.state); + $shippingAddress.find("#shipping-zip").val(response.zip); + $shippingAddress.find("#shipping-country").val(response.country); + }) + .error(app.ajaxError); + } + else { + $billingInfo.find("#billing-first-name").val(userDetail.first_name); + $billingInfo.find("#billing-last-name").val(userDetail.last_name); + $billingInfo.find("#billing-city").val(userDetail.city); + $billingInfo.find("#billing-state").val(userDetail.state); + $billingInfo.find("#billing-country").val(userDetail.country); + + $shippingAddress.find("#shipping-first-name").val(userDetail.first_name); + $shippingAddress.find("#shipping-last-name").val(userDetail.last_name); + $shippingAddress.find("#shipping-city").val(userDetail.city); + $shippingAddress.find("#shipping-state").val(userDetail.state); + $shippingAddress.find("#shipping-country").val(userDetail.country); + } + } + + function afterShow(data) { + } + + function next(e) { + e.preventDefault(); + + // validation + var billing_first_name = $billingInfo.find("#billing-first-name").val(); + var billing_last_name = $billingInfo.find("#billing-last-name").val(); + var billing_address1 = $billingInfo.find("#billing-address1").val(); + var billing_address2 = $billingInfo.find("#billing-address2").val(); + var billing_city = $billingInfo.find("#billing-city").val(); + var billing_state = $billingInfo.find("#billing-state").val(); + var billing_zip = $billingInfo.find("#billing-zip").val(); + var billing_country = $billingInfo.find("#billing-country").val(); + + if (!billing_first_name) { + $billingInfo.find('#divBillingFirstName .error-text').remove(); + $billingInfo.find('#divBillingFirstName').addClass("error"); + $billingInfo.find('#billing-first-name').after("
  • First Name is required
"); + + return false; + } + else { + $billingInfo.find('#divBillingFirstName').removeClass("error"); + } + + if (!billing_last_name) { + $billingInfo.find('#divBillingLastName .error-text').remove(); + $billingInfo.find('#divBillingLastName').addClass("error"); + $billingInfo.find('#billing-last-name').after("
  • Last Name is required
"); + + return false; + } + else { + $billingInfo.find('#divBillingLastName').removeClass("error"); + } + + if (!billing_address1) { + $billingInfo.find('#divBillingAddress1 .error-text').remove(); + $billingInfo.find('#divBillingAddress1').addClass("error"); + $billingInfo.find('#billing-address1').after("
  • Address is required
"); + + return false; + } + else { + $billingInfo.find('#divBillingAddress1').removeClass("error"); + } + + if (!billing_zip) { + $billingInfo.find('#divBillingZip .error-text').remove(); + $billingInfo.find('#divBillingZip').addClass("error"); + $billingInfo.find('#billing-zip').after("
  • Zip code is required
"); + + return false; + } + else { + $billingInfo.find('#divBillingZip').removeClass("error"); + } + + if (!billing_state) { + $billingInfo.find('#divBillingState .error-text').remove(); + $billingInfo.find('#divBillingState').addClass("error"); + $billingInfo.find('#billing-zip').after("
  • State is required
"); + + return false; + } + else { + $billingInfo.find('#divBillingState').removeClass("error"); + } + + if (!billing_city) { + $billingInfo.find('#divBillingCity .error-text').remove(); + $billingInfo.find('#divBillingCity').addClass("error"); + $billingInfo.find('#billing-city').after("
  • City is required
"); + + return false; + } + else { + $billingInfo.find('#divBillingCity').removeClass("error"); + } + + if (!billing_country) { + $billingInfo.find('#divBillingCountry .error-text').remove(); + $billingInfo.find('#divBillingCountry').addClass("error"); + $billingInfo.find('#billing-country').after("
  • Country is required
"); + + return false; + } + else { + $billingInfo.find('#divBillingCountry').removeClass("error"); + } + + shipping_as_billing = $shippingAsBilling.is(":checked"); + var shipping_first_name, shipping_last_name, shipping_address1, shipping_address2; + var shipping_city, shipping_state, shipping_zip, shipping_country; + + if (!shipping_as_billing) { + shipping_first_name = $shippingAddress.find("#shipping-first-name").val(); + shipping_last_name = $shippingAddress.find("#shipping-last-name").val(); + shipping_address1 = $shippingAddress.find("#shipping-address1").val(); + shipping_address2 = $shippingAddress.find("#shipping-address2").val(); + shipping_city = $shippingAddress.find("#shipping-city").val(); + shipping_state = $shippingAddress.find("#shipping-state").val(); + shipping_zip = $shippingAddress.find("#shipping-zip").val(); + shipping_country = $shippingAddress.find("#shipping-country").val(); + + if (!shipping_first_name) { + $shippingAddress.find('#divShippingFirstName .error-text').remove(); + $shippingAddress.find('#divShippingFirstName').addClass("error"); + $shippingAddress.find('#shipping-first-name').after("
  • First Name is required
"); + + return false; + } + else { + $shippingInfo.find('#divShippingFirstName').removeClass("error"); + } + + if (!shipping_last_name) { + $shippingAddress.find('#divShippingLastName .error-text').remove(); + $shippingAddress.find('#divShippingLastName').addClass("error"); + $shippingAddress.find('#shipping-last-name').after("
  • Last Name is required
"); + + return false; + } + else { + $shippingInfo.find('#divShippingLastName').removeClass("error"); + } + + if (!shipping_address1) { + $shippingAddress.find('#divShippingAddress1 .error-text').remove(); + $shippingAddress.find('#divShippingAddress1').addClass("error"); + $shippingAddress.find('#shipping-address1').after("
  • Address is required
"); + + return false; + } + else { + $shippingInfo.find('#divShippingAddress1').removeClass("error"); + } + + if (!shipping_zip) { + $shippingAddress.find('#divShippingZip .error-text').remove(); + $shippingAddress.find('#divShippingZip').addClass("error"); + $shippingAddress.find('#shipping-zip').after("
  • Zip code is required
"); + + return false; + } + else { + $shippingInfo.find('#divShippingZip').removeClass("error"); + } + + if (!shipping_state) { + $shippingAddress.find('#divShippingState .error-text').remove(); + $shippingAddress.find('#divShippingState').addClass("error"); + $shippingAddress.find('#shipping-zip').after("
  • State is required
"); + + return false; + } + else { + $shippingInfo.find('#divShippingState').removeClass("error"); + } + + if (!shipping_city) { + $shippingAddress.find('#divShippingCity .error-text').remove(); + $shippingAddress.find('#divShippingCity').addClass("error"); + $shippingAddress.find('#shipping-city').after("
  • City is required
"); + + return false; + } + else { + $shippingInfo.find('#divShippingCity').removeClass("error"); + } + + if (!shipping_country) { + $shippingAddress.find('#divShippingCountry .error-text').remove(); + $shippingAddress.find('#divShippingCountry').addClass("error"); + $shippingAddress.find('#shipping-country').after("
  • Country is required
"); + + return false; + } + else { + $shippingAddress.find('#divShippingCountry').removeClass("error"); + } + } + + var card_name = $paymentMethod.find("#card-name").val(); + var card_number = $paymentMethod.find("#card-number").val(); + var card_year = $paymentMethod.find("#card_expire-date_3i").val(); + var card_month = $paymentMethod.find("#card_expire-date_2i").val(); + var card_verify = $paymentMethod.find("#card-verify").val(); + + if (!card_name) { + $paymentMethod.find('#divCardName .error-text').remove(); + $paymentMethod.find('#divCardName').addClass("error"); + $paymentMethod.find('#card-name').after("
  • Card Name is required
"); + + return false; + } + else { + $paymentMethod.find('#divCardName').removeClass("error"); + } + + if (!card_number) { + $paymentMethod.find('#divCardNumber .error-text').remove(); + $paymentMethod.find('#divCardNumber').addClass("error"); + $paymentMethod.find('#card-number').after("
  • Card Number is required
"); + + return false; + } + else { + $paymentMethod.find('#divCardNumber').removeClass("error"); + } + + if (!card_verify) { + $paymentMethod.find('#divCardVerify .error-text').remove(); + $paymentMethod.find('#divCardVerify').addClass("error"); + $paymentMethod.find('#card_verify').after("
  • Card Verification Value is required
"); + + return false; + } + else { + $paymentMethod.find('#divCardVerify').removeClass("error"); + } + + billing_info = {}; + shipping_info = {}; + billing_info.first_name = billing_first_name; + billing_info.last_name = billing_last_name; + billing_info.address1 = billing_address1; + billing_info.address2 = billing_address2; + billing_info.city = billing_city; + billing_info.state = billing_state; + billing_info.country = billing_country; + billing_info.zip = billing_zip; + billing_info.number = card_number; + billing_info.month = card_month; + billing_info.year = card_year; + billing_info.verification_value = card_verify; + + if (shipping_as_billing) { + shipping_info = billing_info; + delete shipping_info.number; + delete shipping_info.month; + delete shipping_info.year; + delete shipping_info.verification_value; + } + else { + shipping_info.first_name = shipping_first_name; + shipping_info.last_name = shipping_last_name; + shipping_info.address1 = shipping_address1; + shipping_info.address2 = shipping_address2; + shipping_info.city = shipping_city; + shipping_info.state = shipping_state; + shipping_info.country = shipping_country; + shipping_info.zip = shipping_zip; + } + + $paymentInfoPanel.find("#payment-info-next").addClass("disabled"); + $paymentInfoPanel.find("#payment-info-next").off("click"); + + if (userDetail.has_recurly_account) { + rest.updateBillingInfo(billing_info) + .done(function() { + }) + .fail(errorHandling); + } + else { + rest.createRecurlyAccount({billing_info: billing_info}) + .done(function() { + }) + .fail(errorHandling); + } + } + + function errorHandling(xhr, ajaxOptions, thrownError) { + $.each(xhr.responseJSON.errors, function(key, error) { + if (key == 'number') { + $paymentMethod.find('#divCardNumber .error-text').remove(); + $paymentMethod.find('#divCardNumber').addClass("error"); + $paymentMethod.find('#card-number').after("
  • " + error + "
"); + } + else if (key == 'verification_value') { + $paymentMethod.find('#divCardVerify .error-text').remove(); + $paymentMethod.find('#divCardVerify').addClass("error"); + $paymentMethod.find('#card-verify').after("
  • " + error + "
"); + } + }); + + $paymentInfoPanel.find("#payment-info-next").removeClass("disabled"); + $paymentInfoPanel.find("#payment-info-next").on("click", next); +// moveToOrder(); + } + + function beforeShowOrder() { + step = 3; + renderNavigation(); + populateOrderPage(); + } + + function clearOrderPage() { + $orderContent.empty(); + } + + function populateOrderPage() { + clearOrderPage(); + + rest.getShoppingCarts() + .done(renderOrderPage) + .fail(app.ajaxError); + } + + function renderOrderPage(carts) { + var data = {}; + + var sub_total = 0; + $.each(carts, function(index, cart) { + sub_total += parseFloat(cart.product_info.price) * parseFloat(cart.quantity); + }); + data.sub_total = sub_total.toFixed(2); + data.taxes = 12.01; + + data.carts = carts; + data.billing_info = billing_info; + data.shipping_info = shipping_info; + data.shipping_as_billing = shipping_as_billing; + var orderContentHtml = $( + context._.template( + $('#template-order-content').html(), + data, + {variable: 'data'} + ) + ); + + $orderContent.append(orderContentHtml); + + $orderPanel.find(".change-payment-info").on('click', moveToPaymentInfo); + $orderContent.find(".place-order").on('click', placeOrder); + } + + function moveToOrder() { + $paymentInfoPanel.addClass("hidden"); + $orderPanel.removeClass("hidden"); + beforeShowOrder(); + } + + function moveToPaymentInfo(e) { + e.preventDefault(); + $paymentInfoPanel.removeClass("hidden"); + $orderPanel.addClass("hidden"); + beforeShowPaymentInfo(); + } + + function toggleShippingAsBilling(e) { + e.preventDefault(); + + var shipping_as_billing = $(e.target).is(':checked'); + + if (!shipping_as_billing) { + $shippingAddress.removeClass("hidden"); + } + else { + $shippingAddress.addClass("hidden"); + } + } + + function placeOrder(e) { + e.preventDefault(); + } + + function events() { + $paymentInfoPanel.find("#payment-info-next").on('click', next); + $shippingAsBilling.on('ifChanged', toggleShippingAsBilling); + } + + function reset() { + } + + function renderNavigation() { + $navigation.html(""); + var navigationHtml = $( + context._.template( + $('#template-checkout-navigation').html(), + {current: step}, + {variable: 'data'} + ) + ); + + $navigation.append(navigationHtml); + } + + function initializeControls() { + $("form.payment-info").iCheck({ + checkboxClass: 'icheckbox_minimal', + radioClass: 'iradio_minimal', + inheritClass: true + }); + } + + function initialize() { + var screenBindings = { + 'beforeShow': beforeShow, + 'afterShow': afterShow + }; + app.bindScreen('order', screenBindings); + + $screen = $("#orderScreen"); + $paymentInfoPanel = $screen.find(".checkout-payment-info"); + $orderPanel = $screen.find(".order-panel"); + $navigation = $screen.find(".checkout-navigation-bar"); + $billingInfo = $paymentInfoPanel.find(".billing-address"); + $shippingInfo = $paymentInfoPanel.find(".shipping-address"); + $paymentMethod = $paymentInfoPanel.find(".payment-method"); + $shippingAddress = $paymentInfoPanel.find(".shipping-address-detail"); + $shippingAsBilling = $paymentInfoPanel.find("#shipping-as-billing"); + $orderContent = $orderPanel.find(".order-content"); + + if($screen.length == 0) throw "$screen must be specified"; + if($navigation.length == 0) throw "$navigation must be specified"; + + initializeControls(); + + events(); + } + + this.initialize = initialize; + + return this; + } +})(window,jQuery); \ No newline at end of file diff --git a/web/app/assets/javascripts/scheduled_session.js.erb b/web/app/assets/javascripts/scheduled_session.js.erb index e113e4b86..eaad204f4 100644 --- a/web/app/assets/javascripts/scheduled_session.js.erb +++ b/web/app/assets/javascripts/scheduled_session.js.erb @@ -862,7 +862,7 @@ } function onEditSessions(event) { - window.location = "/client#/account/sessions" + window.location = "/client#/account/sessions"; return false; } diff --git a/web/app/assets/javascripts/shopping_cart.js b/web/app/assets/javascripts/shopping_cart.js new file mode 100644 index 000000000..ac02b8b39 --- /dev/null +++ b/web/app/assets/javascripts/shopping_cart.js @@ -0,0 +1,112 @@ +(function(context,$) { + + "use strict"; + context.JK = context.JK || {}; + context.JK.ShoppingCartScreen = function(app) { + + var logger = context.JK.logger; + + var $screen = null; + var $content = null; + + function beforeShow(data) { + loadShoppingCarts(); + } + + function afterShow(data) { + } + + function events() { + $screen.find("a.remove-cart").on('click', removeCart); + $screen.find("a.proceed-checkout").on('click', proceedCheckout); + } + + function proceedCheckout(e) { + e.preventDefault(); + + if (!context.JK.currentUserId) { + window.location = '/client#/signin'; + } + else { + window.location = '/client#/order'; + } + } + + function removeCart(e) { + e.preventDefault(); + + var options = {}; + options.id = $(e.target).attr("cart-id"); + + rest.removeShoppingCart(options) + .done(loadShoppingCarts) + .fail(app.ajaxError); + } + + function clearContent() { + $content.empty(); + } + + function loadShoppingCarts() { + clearContent(); + + rest.getShoppingCarts() + .done(renderShoppingCarts) + .fail(app.ajaxError); + } + + function renderShoppingCarts(carts) { + var data = {}; + var latest_cart = carts[carts.length - 1]; + + var $latestCartHtml = ""; + + if (latest_cart) { + $latestCartHtml = $( + context._.template( + $('#template-shopping-cart-header').html(), + latest_cart, + {variable: 'data'} + ) + ); + } + + var sub_total = 0; + $.each(carts, function(index, cart) { + sub_total += parseFloat(cart.product_info.price) * parseFloat(cart.quantity); + }); + data.sub_total = sub_total.toFixed(2); + + data.carts = carts; + var $cartsHtml = $( + context._.template( + $('#template-shopping-cart-body').html(), + data, + {variable: 'data'} + ) + ); + + $content.append($latestCartHtml).append($cartsHtml); + + events(); + } + + function initialize() { + var screenBindings = { + 'beforeShow': beforeShow, + 'afterShow': afterShow + }; + app.bindScreen('shoppingCart', screenBindings); + + $screen = $("#shoppingCartScreen"); + $content = $screen.find(".shopping-cart-content"); + + if($screen.length == 0) throw "$screen must be specified"; + if($content.length == 0) throw "$content must be specified"; + } + + this.initialize = initialize; + + return this; + } +})(window,jQuery); \ No newline at end of file diff --git a/web/app/assets/javascripts/web/signin_helper.js b/web/app/assets/javascripts/web/signin_helper.js index 76a5dfaab..67eb0fd29 100644 --- a/web/app/assets/javascripts/web/signin_helper.js +++ b/web/app/assets/javascripts/web/signin_helper.js @@ -19,10 +19,9 @@ var $password = null; var $rememberMe = null; var useAjax = false; - var EVENTS = context.JK.EVENTS; - + function reset() { - $signinForm.removeClass('login-error') + $signinForm.removeClass('login-error'); $email.val(''); $password.val(''); @@ -32,7 +31,6 @@ function login() { var email = $email.val(); var password = $password.val(); - var rememberMe = $rememberMe.is(':checked') reset(); diff --git a/web/app/assets/stylesheets/client/checkout.css.scss b/web/app/assets/stylesheets/client/checkout.css.scss new file mode 100644 index 000000000..7cb65eac0 --- /dev/null +++ b/web/app/assets/stylesheets/client/checkout.css.scss @@ -0,0 +1,252 @@ +.checkout-navigation { + padding: 20px 0px; + .nav-signin, .nav-payment-info, .nav-place-order { + width: 30%; + float: left; + } + + .nav-signin { + margin-left: 5%; + } + + .nav-place-order { + margin-right: 5%; + } + + .nav-text { + font-size: 17px; + float: left; + } + + .nav-text.selected { + font-weight: bold; + } + + .nav-arrow { + float: left; + margin-left: 30px; + } +} + +.checkout-signin, .checkout-payment-info, .checkout-place-order { + padding: 30px; + + .signin-form { + padding: 10px; + + strong { + font-weight: bold; + } + + label { + display: inline; + } + + .signin-password { + margin-left: 33px; + } + + .login-error { + background-color: #330000; + border: 1px solid #990000; + padding:4px; + + div.actions { + margin-top:10px; + } + } + + .login-error-msg { + display:none; + margin-top:10px; + text-align:center; + color:#F00; + font-size:11px; + } + + .login-error .login-error-msg { + display:block; + } + } + + form.payment-info { + width: 100%; + + input[type="text"] { + width: 90%; + } + + .billing-address { + float: left; + width: 50%; + + h2.billing-caption { + margin: 20px 5px; + font-size: 16px; + } + + .billing-label { + padding-top: 8px; + width: 30%; + float: left; + text-align: right; + margin-right: 5px; + } + + .billing-value { + width: 65%; + text-align: left; + float: left; + } + } + + .payment-method { + float: left; + width: 50%; + + h2.payment-method-caption { + margin: 20px 5px; + font-size: 16px; + } + + .card-label { + padding-top: 8px; + width: 35%; + float: left; + text-align: right; + margin-right: 5px; + } + + .card-value { + width: 60%; + text-align: left; + float: left; + } + + .save-card-checkbox { + float:left; + display:block; + margin-right:5px; + } + } + + .shipping-address { + float: left; + width: 50%; + + h2.shipping-address-label { + margin: 20px 5px; + font-size: 16px; + } + + .shipping-as-billing { + float:left; + display:block; + margin-right:5px; + } + + .divBillingHelper { + padding-top: 2px; + } + + .shipping-label { + padding-top: 8px; + width: 30%; + float: left; + text-align: right; + margin-right: 5px; + } + + .shipping-value { + width: 65%; + text-align: left; + float: left; + } + } + } +} + +.order-panel { + padding: 30px; + + .order-header { + h2 { + font-size: 16px; + } + } + + .order-content { + margin-top: 20px; + } + + .order-left-page { + float: left; + width: 60%; + + .payment-info-page { + padding: 5px; + + .info-caption-link { + .caption-text { + float: left; + } + .caption-link { + float: left; + margin-left: 5px; + } + } + + .address-info { + width: 50%; + float: left; + } + + .payment-method-info { + width: 50%; + float: left; + } + } + .order-items-page { + padding: 5px; + + .cart-item-caption { + width: 50%; + text-align: left; + float: left; + } + + .cart-item-caption#header { + font-weight: bold; + } + + .cart-item-price { + width: 25%; + text-align: right; + float: left; + } + + .cart-item-quantity { + width: 25%; + text-align: right; + float: left; + } + + .cart-items { + margin-top: 10px; + } + + .cart-item { + margin-top: 10px; + } + } + } + .order-right-page { + float: right; + width: 35%; + text-align: center; + + .order-total { + color: #ed3618; + } + } +} \ No newline at end of file diff --git a/web/app/assets/stylesheets/client/client.css b/web/app/assets/stylesheets/client/client.css index 75d262cbe..c5dd4d21c 100644 --- a/web/app/assets/stylesheets/client/client.css +++ b/web/app/assets/stylesheets/client/client.css @@ -48,6 +48,9 @@ *= require ./terms *= require ./createSession *= require ./feed + *= require ./jamtrack + *= require ./shoppingCart + *= require ./checkout *= require ./genreSelector *= require ./sessionList *= require ./searchResults diff --git a/web/app/assets/stylesheets/client/header.css.scss b/web/app/assets/stylesheets/client/header.css.scss index 2812d087f..8bdedd4ea 100644 --- a/web/app/assets/stylesheets/client/header.css.scss +++ b/web/app/assets/stylesheets/client/header.css.scss @@ -7,6 +7,15 @@ z-index:5; } +.header-shopping-cart { + float: right; + margin-right: 20px; + + img { + width: 60px; + } +} + div[layout="header"] h1 { cursor:pointer; width: 247px; diff --git a/web/app/assets/stylesheets/client/home.css.scss b/web/app/assets/stylesheets/client/home.css.scss index 953d7f87e..d9b7d9d45 100644 --- a/web/app/assets/stylesheets/client/home.css.scss +++ b/web/app/assets/stylesheets/client/home.css.scss @@ -40,6 +40,9 @@ .homecard.musicians { background-image: url(/assets/content/bkg_home_musicians.jpg); } +.homecard.jamtrack { + background-image: url(/assets/content/bkg_home_jamtracks.jpg); +} .homebox-info { position: absolute; @@ -93,6 +96,9 @@ .homecard.musicians.hover { background-image: url(/assets/content/bkg_home_musicians_x.jpg); } +.homecard.jamtrack.hover { + background-image: url(/assets/content/bkg_home_jamtracks_x.jpg); +} diff --git a/web/app/assets/stylesheets/client/jamtrack.css.scss b/web/app/assets/stylesheets/client/jamtrack.css.scss new file mode 100644 index 000000000..7d69b1b6f --- /dev/null +++ b/web/app/assets/stylesheets/client/jamtrack.css.scss @@ -0,0 +1,116 @@ +#jamtrackScreen { + a.jamtrack_help { + color: #fff; + text-decoration: none; + margin: 4px 0px 0px 60px; + + &:hover { + text-decoration: underline; + } + } + + .jamtrack-content { + text-align: center; + } + + .no-jamtracks-msg { + margin-top: 10px; + } + + .jamtrack-record { + border-bottom: 1px solid black; + text-align: left; + } + + .jamtrack-detail { + float: left; + width: 50%; + padding: 10px 0px; + + .detail-label { + width: 40%; + float: left; + margin-top: 5px; + } + + .detail-value { + width: 50%; + float: left; + margin-top: 5px; + } + + .copyright-value { + width: 40%; + float: left; + margin-top: 5px; + } + + .detail-arrow { + float: left; + margin-left: 10px; + } + + .jamtrack-description { + display: none; + } + + .jamtrack-detail-btn { + cursor: pointer; + margin-top: 5px; + margin-right: 5px; + padding-top: 5px; + } + } + + .jamtrack-tracks { + float: left; + width: 25%; + padding: 10px 0px; + + .tracks-caption { + margin-top: 5px; + margin-bottom: 10px; + } + + .track-instrument { + margin-top: 5px; + } + + .instrument-image { + float: left; + } + + .instrument-desc { + margin-top: 6px; + float: left; + margin-left: 10px; + } + } + + .jamtrack-action { + float: left; + width: 25%; + padding: 10px 0px; + text-align: center; + + .play-button { + margin-top: 5px; + } + + .jamtrack-price { + margin-top: 5px; + font-size: 20px; + } + + .jamtrack-add-cart, .jamtrack-add-cart-disabled { + margin: 8px 0px; + } + + .jamtrack-license { + margin-left: 20%; + margin-right: 20%; + font-size: 13px; + width: 60%; + } + } +} \ No newline at end of file diff --git a/web/app/assets/stylesheets/client/shoppingCart.css.scss b/web/app/assets/stylesheets/client/shoppingCart.css.scss new file mode 100644 index 000000000..63f333543 --- /dev/null +++ b/web/app/assets/stylesheets/client/shoppingCart.css.scss @@ -0,0 +1,78 @@ +#shoppingCartScreen { + + .content-body { + padding: 50px 20px 20px 20px; + + .checkout-image { + width: 10%; + float: left; + } + + .checkout-desc { + width: 90%; + float: left; + + div { + margin-bottom: 7px; + } + + div#note { + font-style: italic; + font-size: 13px; + } + } + + .cart-item-caption { + width: 50%; + text-align: left; + float: left; + } + + .cart-item-caption#header { + font-weight: bold; + } + + .cart-item-price { + width: 15%; + text-align: right; + float: left; + } + + .cart-item-quantity { + width: 15%; + text-align: right; + float: left; + } + + .cart-item-actions { + width: 20%; + text-align: center; + float: left; + } + + .cart-items { + width: 100%; + height: 300px; + overflow: auto; + margin-top: 30px; + } + + .cart-item { + margin-top: 10px; + } + + .shopping-sub-total { + width: 65%; + float: left; + text-align: right; + margin-bottom: 20px; + font-weight: bold; + font-size: 17px; + } + + .no-cart-items { + margin-top: 30px; + text-align: center; + } + } +} \ No newline at end of file diff --git a/web/app/assets/stylesheets/dialogs/jamtrackAvailability.css.scss b/web/app/assets/stylesheets/dialogs/jamtrackAvailability.css.scss new file mode 100644 index 000000000..e69de29bb diff --git a/web/app/controllers/api_jamtracks_controller.rb b/web/app/controllers/api_jamtracks_controller.rb new file mode 100644 index 000000000..8bbc2c179 --- /dev/null +++ b/web/app/controllers/api_jamtracks_controller.rb @@ -0,0 +1,15 @@ +class ApiJamtracksController < ApiController + + # have to be signed in currently to see this screen + before_filter :api_signed_in_user + + respond_to :json + + def index + data = JamTrack.index current_user, params + + @jamtracks = data[0] + @next = data[1] + end + +end diff --git a/web/app/controllers/api_recurly_controller.rb b/web/app/controllers/api_recurly_controller.rb new file mode 100644 index 000000000..4c841320d --- /dev/null +++ b/web/app/controllers/api_recurly_controller.rb @@ -0,0 +1,155 @@ +class ApiRecurlyController < ApiController + + before_filter :api_signed_in_user + respond_to :json + + # create Recurly account + def create_account + logger.debug(params[:billing_info]) + if current_user.recurly_code.nil? + @account = Recurly::Account.create( + account_code: current_user.id, + email: current_user.email, + first_name: current_user.first_name, + last_name: current_user.last_name, + address: { + city: current_user.city, + state: current_user.state, + country: current_user.country + } + ) + else + @account = Recurly::Account.find(current_user.recurly_code) + end + + if @account.errors.any? + response.status = 404 + else + current_user.recurly_code = @account.account_code + current_user.save + + @account.billing_info = params[:billing_info] + @account.billing_info.save + + logger.debug @account + end + respond_with @account + + rescue Recurly::Error, NoMethodError => e + render :json => { :message => e.inspect }, :status => 404 + end + + # get Recurly account + def get_account + @account = Recurly::Account.find(current_user.reculry_code) + respond_with @account + rescue Recurly::Error, NoMethodError => e + render :json => { message: ValidationMessages::RECURLY_ERROR}, :status => 404 + end + + # update Recurly account + def update_account + if current_user.recurly_code.nil? + @account = Recurly::Account.create( + account_code: current_user.id, + email: current_user.email, + first_name: current_user.first_name, + last_name: current_user.last_name, + address: { + city: current_user.city, + state: current_user.state, + country: current_user.country + } + ) + else + @account = Recurly::Account.get(current_user.recurly_code) + end + + @account.first_name = current_user.first_name + @account.last_name = current_user.last_name + @account.email = current_user.email + @account.update + + if @account.errors.any? + response.status = 404 + else + current_user.recurly_code = @account.account_code + current_user.save + end + respond_with @account + + rescue Recurly::Error, NoMethodError => e + render :json => { message: ValidationMessages::RECURLY_ERROR}, :status => 404 + end + + # get subscription + def get_subscription + @account = Recurly::Acount.find(current_user.reculry_code) + respond_with @account.subscriptions.last + rescue Recurly::Error, NoMethodError => e + render :json => { message: ValidationMessages::RECURLY_GET_ACCOUNT_ERROR}, :status => 404 + end + + # create subscription + def create_subscription + end + + # get Billing Information + def billing_info + if current_user.recurly_code.nil? + render :json => { message: ValidationMessages::RECURLY_ACCOUNT_ERROR }, :status => 404 and return + else + @account = Recurly::Account.find(current_user.recurly_code) + logger.debug @account + respond_with @account.billing_info + end + rescue Recurly::Error, NoMethodError => e + render :json => { message: ValidationMessages::RECURLY_ERROR}, :status => 404 + end + + # update Billing Information + def update_billing_info + if current_user.recurly_code.nil? + render :json => { message: ValidationMessages::RECURLY_ACCOUNT_ERROR }, :status => 404 and return + else + if params[:first_name].blank? or params[:last_name].blank? or params[:number].blank? or params[:year].blank? or params[:month].blank? or params[:verification_value].blank? + render :json => { message: ValidationMessages::RECURLY_PARAMETER_ERROR }, :status => 404 and return + end + @account = Recurly::Acount.find(current_user.reculry_code) + @account.billing_info = params + @account.billing_info.save + + if @account.erros.any? + response.status = :unprocessable_entity + end + + respond_with @account + end + rescue Recurly::Error, NoMethodError => e + render :json => { message: ValidationMessages::RECURLY_ERROR}, :status => 404 + end + + def place_order + if current_user.recurly_code.nil? + render :json => { message: ValidationMessages::RECURLY_ACCOUNT_ERROR }, :status => 404 and return + else + if params[:first_name].blank? or params[:last_name].blank? or params[:number].blank? or params[:year].blank? or params[:month].blank? or params[:verification_value].blank? + render :json => { message: ValidationMessages::RECURLY_PARAMETER_ERROR }, :status => 404 and return + end + @account = Recurly::Account.find(current_user.recurly_code) + @account.billing_info = params + @account.billing_info.save + + # create subscription. + + if @account.erros.any? + response.status = :unprocessable_entity + end + + respond_with @account + end + rescue Recurly::Error, NoMethodError => e + render :json => { message: ValidationMessages::RECURLY_ERROR}, :status => 404 + end + +end \ No newline at end of file diff --git a/web/app/controllers/api_shopping_carts_controller.rb b/web/app/controllers/api_shopping_carts_controller.rb new file mode 100644 index 000000000..d1109e821 --- /dev/null +++ b/web/app/controllers/api_shopping_carts_controller.rb @@ -0,0 +1,53 @@ +class ApiShoppingCartsController < ApiController + + before_filter :api_signed_in_user + + respond_to :json + + def index + @carts = current_user.shopping_carts + end + + def add_jamtrack + jam_track = JamTrack.find_by_id(params[:id]) + + # verify JamTrack exists + if jam_track.nil? + raise StateError, "Invalid JamTrack." + end + + @cart = ShoppingCart.create current_user, jam_track + + if @cart.errors.any? + response.status = :unprocessable_entity + respond_with @cart + else + respond_with @cart, responder: ApiResponder, :statue => 201 + end + end + + def update_cart + @cart = ShoppingCart.find_by_id params[:id] + + #verify Cart exists + raise StateError, "Invalid Cart." if @cart.nil? + + @cart.quantity = params[:quantity] + + if @cart.errors.any? + response.statue = :unprocessable_entity + respond_with @cart + else + respond_with @cart, responder: ApiResponder, :status => 200 + end + end + + def remove_cart + @cart = current_user.shopping_carts.find_by_id(params[:id]) + raise StateError, "Invalid Cart." if @cart.nil? + + @cart.destroy + respond_with responder: ApiResponder, :status => 204 + end + +end diff --git a/web/app/views/api_jamtracks/index.rabl b/web/app/views/api_jamtracks/index.rabl new file mode 100644 index 000000000..25c3f9da5 --- /dev/null +++ b/web/app/views/api_jamtracks/index.rabl @@ -0,0 +1,7 @@ +node :next do |page| + @next +end + +node :jamtracks do |page| + partial "api_jamtracks/show", object: @jamtracks +end \ No newline at end of file diff --git a/web/app/views/api_jamtracks/show.rabl b/web/app/views/api_jamtracks/show.rabl new file mode 100644 index 000000000..dde7bb3dd --- /dev/null +++ b/web/app/views/api_jamtracks/show.rabl @@ -0,0 +1,19 @@ +object @jamtrack + +attributes :id, :name, :description, :recording_type, :original_artist, :songwriter, :publisher, :sales_region, :price + +node :genres do |item| + [item.genre.description] # XXX: need to return single genre; not array +end + +node :added_cart do |item| + current_user.shopping_carts.map(&:cart_id).include? item.id +end + +child(:jam_track_tracks => :tracks) { + attributes :id, :part, :instrument +} + +child(:licensor => :licensor) { + attributes :id, :name +} \ No newline at end of file diff --git a/web/app/views/api_shopping_carts/add_jamtrack.rabl b/web/app/views/api_shopping_carts/add_jamtrack.rabl new file mode 100644 index 000000000..c61e7b52b --- /dev/null +++ b/web/app/views/api_shopping_carts/add_jamtrack.rabl @@ -0,0 +1 @@ +extends "api_shopping_carts/show" \ No newline at end of file diff --git a/web/app/views/api_shopping_carts/index.rabl b/web/app/views/api_shopping_carts/index.rabl new file mode 100644 index 000000000..bc63bab24 --- /dev/null +++ b/web/app/views/api_shopping_carts/index.rabl @@ -0,0 +1,3 @@ +object @carts + +extends "api_shopping_carts/show" \ No newline at end of file diff --git a/web/app/views/api_shopping_carts/show.rabl b/web/app/views/api_shopping_carts/show.rabl new file mode 100644 index 000000000..e371ba4c9 --- /dev/null +++ b/web/app/views/api_shopping_carts/show.rabl @@ -0,0 +1,3 @@ +object @cart + +attributes :id, :quantity, :cart_type, :product_info \ No newline at end of file diff --git a/web/app/views/api_users/show.rabl b/web/app/views/api_users/show.rabl index fd8b9b87e..17cbfa149 100644 --- a/web/app/views/api_users/show.rabl +++ b/web/app/views/api_users/show.rabl @@ -17,6 +17,8 @@ if @user == current_user geoiplocation.info if geoiplocation end + node :has_recurly_account do @user.recurly_code == @user.id end + elsif current_user node :is_friend do |uu| current_user.friends?(@user) @@ -81,4 +83,4 @@ end node :last_jam_audio_latency do |user| user.last_jam_audio_latency.round if user.last_jam_audio_latency -end \ No newline at end of file +end diff --git a/web/app/views/clients/_header.html.erb b/web/app/views/clients/_header.html.erb index 5195512ff..bad8f5229 100644 --- a/web/app/views/clients/_header.html.erb +++ b/web/app/views/clients/_header.html.erb @@ -11,6 +11,11 @@ <%= render "users/user_dropdown" %> + + + + +