solidus_multi_domain 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (103) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +33 -0
  3. data/.rspec +4 -0
  4. data/.ruby-version +1 -0
  5. data/Gemfile +9 -0
  6. data/README.md +72 -0
  7. data/Rakefile +21 -0
  8. data/app/controllers/concerns/spree_multi_domain/create_line_item_support.rb +16 -0
  9. data/app/controllers/concerns/spree_multi_domain/show_product_support.rb +17 -0
  10. data/app/controllers/spree/admin/products_controller_decorator.rb +12 -0
  11. data/app/controllers/spree/admin/stores_controller.rb +24 -0
  12. data/app/controllers/spree/api/line_items_controller_decorator.rb +1 -0
  13. data/app/controllers/spree/api/orders_controller_decorator.rb +21 -0
  14. data/app/controllers/spree/api/products_controller_decorator.rb +1 -0
  15. data/app/controllers/spree/api/shipments_controller_decorator.rb +17 -0
  16. data/app/controllers/spree/home_controller_decorator.rb +7 -0
  17. data/app/controllers/spree/products_controller_decorator.rb +1 -0
  18. data/app/controllers/spree/taxons_controller_decorator.rb +10 -0
  19. data/app/helpers/spree/products_helper_decorator.rb +11 -0
  20. data/app/mailers/spree/carton_mailer_decorator.rb +13 -0
  21. data/app/mailers/spree/order_mailer.rb +35 -0
  22. data/app/models/spree/order_decorator.rb +8 -0
  23. data/app/models/spree/payment_method_decorator.rb +13 -0
  24. data/app/models/spree/product_decorator.rb +4 -0
  25. data/app/models/spree/promotion/rules/store.rb +30 -0
  26. data/app/models/spree/shipping_method_decorator.rb +18 -0
  27. data/app/models/spree/store_decorator.rb +25 -0
  28. data/app/models/spree/store_payment_method.rb +6 -0
  29. data/app/models/spree/store_shipping_method.rb +6 -0
  30. data/app/models/spree/taxon_decorator.rb +5 -0
  31. data/app/models/spree/taxonomy_decorator.rb +3 -0
  32. data/app/models/spree/tracker_decorator.rb +7 -0
  33. data/app/models/spree_multi_domain/line_item_decorator.rb +20 -0
  34. data/app/overrides/decorate_admin_configurations_index.rb +5 -0
  35. data/app/overrides/decorate_admin_products_form.rb +6 -0
  36. data/app/overrides/decorate_admin_products_index.rb +20 -0
  37. data/app/overrides/decorate_admin_trackers_form.rb +6 -0
  38. data/app/overrides/decorate_admin_trackers_index.rb +13 -0
  39. data/app/views/spree/admin/products/_index_headers.html.erb +1 -0
  40. data/app/views/spree/admin/products/_index_rows.html.erb +1 -0
  41. data/app/views/spree/admin/products/_index_search_fields.html.erb +4 -0
  42. data/app/views/spree/admin/products/_stores.html.erb +8 -0
  43. data/app/views/spree/admin/promotions/rules/_store.html.erb +8 -0
  44. data/app/views/spree/admin/stores/_form.html.erb +90 -0
  45. data/app/views/spree/admin/stores/edit.html.erb +29 -0
  46. data/app/views/spree/admin/stores/index.html.erb +43 -0
  47. data/app/views/spree/admin/stores/new.html.erb +29 -0
  48. data/app/views/spree/admin/taxonomies/_form.html.erb +12 -0
  49. data/app/views/spree/admin/trackers/_index_rows.html.erb +1 -0
  50. data/app/views/spree/admin/trackers/_store.html.erb +6 -0
  51. data/app/views/spree/shared/_google_analytics.html.erb +39 -0
  52. data/bin/rails +7 -0
  53. data/circle.yml +6 -0
  54. data/config/locales/en.yml +26 -0
  55. data/config/locales/fr.yml +9 -0
  56. data/config/locales/pl.yml +10 -0
  57. data/config/locales/ru.yml +9 -0
  58. data/config/routes.rb +5 -0
  59. data/db/migrate/20091202123245_add_order_store.rb +17 -0
  60. data/db/migrate/20100114020535_add_store_to_tracker.rb +25 -0
  61. data/db/migrate/20100227180338_create_products_stores.rb +13 -0
  62. data/db/migrate/20100616204303_store_id_for_taxonomies.rb +19 -0
  63. data/db/migrate/20110601125650_remove_datetime_columns_from_products_stores.rb +13 -0
  64. data/db/migrate/20110601145424_add_indexes_to_products_stores.rb +11 -0
  65. data/db/migrate/20120223183401_namespace.rb +9 -0
  66. data/db/migrate/20121210224018_add_store_payment_methods.rb +10 -0
  67. data/db/migrate/20130308084714_add_logo_to_stores.rb +13 -0
  68. data/db/migrate/20130325231147_add_store_shipping_methods.rb +14 -0
  69. data/db/migrate/20130412212659_add_spree_promotion_rules_stores.rb +8 -0
  70. data/db/migrate/20150304200122_add_indexes.rb +6 -0
  71. data/lib/assets/javascripts/spree/backend/solidus_multi_domain.js +1 -0
  72. data/lib/assets/javascripts/spree/backend/store_picker.js +34 -0
  73. data/lib/assets/javascripts/spree/frontend/solidus_multi_domain.js +0 -0
  74. data/lib/assets/stylesheets/spree/frontend/solidus_multi_domain.css +5 -0
  75. data/lib/generators/spree_multi_domain/install/install_generator.rb +24 -0
  76. data/lib/solidus_multi_domain.rb +3 -0
  77. data/lib/spree/controller_helpers/order_decorator.rb +11 -0
  78. data/lib/spree/search/multi_domain.rb +19 -0
  79. data/lib/spree_multi_domain/engine.rb +65 -0
  80. data/lib/spree_multi_domain/multi_domain_helpers.rb +31 -0
  81. data/solidus_multi_domain.gemspec +31 -0
  82. data/spec/controllers/spree/admin/products_controller_spec.rb +39 -0
  83. data/spec/controllers/spree/admin/stores_controller_spec.rb +12 -0
  84. data/spec/controllers/spree/api/line_items_controller_spec.rb +35 -0
  85. data/spec/controllers/spree/api/orders_controller_spec.rb +44 -0
  86. data/spec/controllers/spree/api/products_controller_spec.rb +52 -0
  87. data/spec/controllers/spree/api/shipments_controller_spec.rb +63 -0
  88. data/spec/controllers/spree/products_controller_spec.rb +49 -0
  89. data/spec/helpers/products_helper_decorator_spec.rb +22 -0
  90. data/spec/mailers/carton_mailer_spec.rb +41 -0
  91. data/spec/models/spree/line_item_spec.rb +39 -0
  92. data/spec/models/spree/order_spec.rb +18 -0
  93. data/spec/models/spree/payment_method_spec.rb +33 -0
  94. data/spec/models/spree/product_spec.rb +18 -0
  95. data/spec/models/spree/shipping_method_decorator_spec.rb +27 -0
  96. data/spec/models/spree/store_spec.rb +42 -0
  97. data/spec/models/spree/taxon_decorator_spec.rb +30 -0
  98. data/spec/models/spree/tracker_spec.rb +19 -0
  99. data/spec/requests/global_controller_helpers_spec.rb +65 -0
  100. data/spec/requests/template_renderer_spec.rb +25 -0
  101. data/spec/spec_helper.rb +33 -0
  102. data/spec/support/factory_overrides.rb +10 -0
  103. metadata +278 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 52d337168f77ee0a5c1645598299ae808ab415e4
4
+ data.tar.gz: 2bc617558995dfb85db2a816feb05e8be58efab9
5
+ SHA512:
6
+ metadata.gz: 2d4b32d871c50b2e5a8b8abba49bd16103904da4037a7789d6b849720231712ecf151954d192aef5e3660223c67ba17be04912da400ede556457cbff5068b913
7
+ data.tar.gz: f491bf9f85566ac2f0b2be660e02cba7ba3654b78b0f3c768591a1d108a3886ed33ff26f4523b87cb8bfeb21dff72b124902428bc6931ed35c9ca6e81644f413
data/.gitignore ADDED
@@ -0,0 +1,33 @@
1
+ \#*
2
+ **/*.db
3
+ *~
4
+ .#*
5
+ .DS_Store
6
+ .dotest
7
+ .idea
8
+ .loadpath
9
+ .project
10
+ *.swp
11
+ spec/dummy
12
+ config/*-public.asc
13
+ config/database.yml
14
+ config/mongrel_cluster.yml
15
+ db/*.sql
16
+ db/*.sqlite3*
17
+ db/schema.rb
18
+ doc/**/*
19
+ log/*.log
20
+ public/assets
21
+ public/images
22
+ public/javascripts
23
+ public/stylesheets
24
+ tmp
25
+ vendor/rails
26
+ vendor/extensions/google_base
27
+ public/google_base.xml
28
+ public/template_google_base.xml
29
+ coverage/*
30
+ .bundle
31
+ Gemfile.lock
32
+ .ruby-version
33
+ .ruby-gemset
data/.rspec ADDED
@@ -0,0 +1,4 @@
1
+ --color
2
+ --format
3
+ progress
4
+ --backtrace
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.1.6
data/Gemfile ADDED
@@ -0,0 +1,9 @@
1
+ source "https://rubygems.org"
2
+
3
+ gem "solidus", git: "git@github.com:solidusio/solidus.git", branch: "master"
4
+
5
+ group :development, :test do
6
+ gem "pry-rails"
7
+ end
8
+
9
+ gemspec
data/README.md ADDED
@@ -0,0 +1,72 @@
1
+ Multi Domain Store
2
+ ==================
3
+
4
+ This extension allows a single Solidus instance to have several customer facing
5
+ stores, with a single shared backend administration system (i.e. multi-store,
6
+ single-vendor).
7
+
8
+ Current features:
9
+ ------------------
10
+
11
+ 1. **Store** model which has the following attributes:
12
+
13
+ 1.1 name - The display name for the store.
14
+
15
+ 1.2 domains - a line separated list of fully qualified domain names used to
16
+ associate a customers session with a particular store (you can use
17
+ localhost and/or IP addresses too).
18
+
19
+ 1.3 code - which is a abbreviated version of the store's name (used as the
20
+ layout directory name, and also helpful for separating partials by store).
21
+
22
+ 1.4 default - one store should be marked a default as a fallback in case
23
+ the URL being used to access the site cannot be matched against any of the
24
+ configured stores.
25
+
26
+ 2. Stores and domains can be maintained via the configuration section of the
27
+ Admin interface
28
+
29
+ 2. Each store can have its own layout(s) - these layouts should be located in
30
+ your site's theme extension in the
31
+ app/views/spree/layouts/_store#code_/directory. So, if you have a store
32
+ with a code of "alpha" you should store its default layout in
33
+ app/views/spree/layouts/alpha/spree_application.html.erb
34
+
35
+ 3. Each product can be assigned to one or more stores.
36
+
37
+ 4. All orders are associated with the store / domain that they were placed on.
38
+
39
+ 5. Google analytics trackers can be associated with a store.
40
+
41
+ Install Instructions
42
+ --------------------
43
+
44
+ Add to your Gemfile:
45
+
46
+ ```ruby
47
+ gem "solidus_multi_domain"
48
+ ```
49
+
50
+ Then run from the command line:
51
+
52
+ ```shell
53
+ bundle install
54
+ rails g spree_multi_domain:install
55
+ ```
56
+
57
+ You should see 'Stores & Domains' in Configuration tab of Spree Admin.
58
+
59
+ Features To-do
60
+ --------------
61
+
62
+ 1. Taxonomies - associate stores with taxonomies.
63
+
64
+ Testing
65
+ -------
66
+
67
+ Then just run the following to automatically build a dummy app if necessary and
68
+ run the tests:
69
+
70
+ ```shell
71
+ bundle exec rake
72
+ ```
data/Rakefile ADDED
@@ -0,0 +1,21 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
3
+
4
+ require 'rspec/core/rake_task'
5
+ require 'spree/testing_support/common_rake'
6
+
7
+ RSpec::Core::RakeTask.new
8
+
9
+ task :default do
10
+ if Dir["spec/dummy"].empty?
11
+ Rake::Task[:test_app].invoke
12
+ Dir.chdir("../../")
13
+ end
14
+ Rake::Task[:spec].invoke
15
+ end
16
+
17
+ desc "Generates a dummy app for testing"
18
+ task :test_app do
19
+ ENV['LIB_NAME'] = 'solidus_multi_domain'
20
+ Rake::Task['common:test_app'].invoke
21
+ end
@@ -0,0 +1,16 @@
1
+ module SpreeMultiDomain::CreateLineItemSupport
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ prepend(InstanceMethods)
6
+ rescue_from SpreeMultiDomain::LineItemDecorator::ProductDoesNotBelongToStoreError, with: :product_does_not_belong_to_store
7
+ end
8
+
9
+ module InstanceMethods
10
+ private
11
+
12
+ def product_does_not_belong_to_store
13
+ render json: { message: Spree.t('errors.products_from_different_stores_may_not_be_added_to_this_order') }, status: 422
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,17 @@
1
+ module SpreeMultiDomain::ShowProductSupport
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ prepend(InstanceMethods)
6
+ before_filter :can_show_product, :only => :show
7
+ end
8
+
9
+ module InstanceMethods
10
+ def can_show_product
11
+ @product ||= Spree::Product.friendly.find(params[:id])
12
+ if @product.stores.empty? || !@product.stores.include?(current_store)
13
+ raise ActiveRecord::RecordNotFound
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,12 @@
1
+ Spree::Admin::ProductsController.class_eval do
2
+ update.before :set_stores
3
+
4
+ private
5
+ def set_stores
6
+ # Remove all store associations if store data is being passed and no stores are selected
7
+ if params[:update_store_ids] && !params[:product].key?(:store_ids)
8
+ @product.stores.clear
9
+ end
10
+ end
11
+
12
+ end
@@ -0,0 +1,24 @@
1
+ class Spree::Admin::StoresController < Spree::Admin::ResourceController
2
+
3
+ before_filter :load_payment_methods
4
+ before_filter :load_shipping_methods
5
+
6
+ def index
7
+ @stores = @stores.ransack({ name_or_domains_or_code_cont: params[:q] }).result if params[:q]
8
+ @stores = @stores.where(id: params[:ids].split(',')) if params[:ids]
9
+
10
+ respond_with(@stores) do |format|
11
+ format.html
12
+ format.json
13
+ end
14
+ end
15
+
16
+ private
17
+ def load_payment_methods
18
+ @payment_methods = Spree::PaymentMethod.all
19
+ end
20
+
21
+ def load_shipping_methods
22
+ @shipping_methods = Spree::ShippingMethod.all
23
+ end
24
+ end
@@ -0,0 +1 @@
1
+ Spree::Api::LineItemsController.include SpreeMultiDomain::CreateLineItemSupport
@@ -0,0 +1,21 @@
1
+ module SpreeMultiStore
2
+ module Api
3
+ module OrdersControllerDecorator
4
+ def self.included(base)
5
+ base.alias_method_chain :mine, :store_scope
6
+ base.alias_method_chain :find_current_api_user_orders, :store_scope
7
+ end
8
+
9
+ def mine_with_store_scope
10
+ mine_without_store_scope
11
+ @orders = @orders.where(store: current_store) if @orders
12
+ end
13
+
14
+ def find_current_api_user_orders_with_store_scope
15
+ find_current_api_user_orders_without_store_scope.where(store: current_store)
16
+ end
17
+ end
18
+ end
19
+ end
20
+
21
+ Spree::Api::OrdersController.include SpreeMultiStore::Api::OrdersControllerDecorator
@@ -0,0 +1 @@
1
+ Spree::Api::ProductsController.include(SpreeMultiDomain::ShowProductSupport)
@@ -0,0 +1,17 @@
1
+ module SpreeMultiStore
2
+ module Api
3
+ module ShipmentsControllerDecorator
4
+ def self.included(base)
5
+ base.alias_method_chain :mine, :store_scope
6
+ end
7
+
8
+ def mine_with_store_scope
9
+ mine_without_store_scope
10
+ @shipments = @shipments.where(spree_orders: { store_id: current_store.id }) if @shipments
11
+ end
12
+ end
13
+ end
14
+ end
15
+
16
+ Spree::Api::ShipmentsController.include SpreeMultiStore::Api::ShipmentsControllerDecorator
17
+ Spree::Api::ShipmentsController.include SpreeMultiDomain::CreateLineItemSupport
@@ -0,0 +1,7 @@
1
+ Spree::HomeController.class_eval do
2
+ def index
3
+ @searcher = build_searcher(params)
4
+ @products = @searcher.retrieve_products
5
+ @taxonomies = get_taxonomies
6
+ end
7
+ end
@@ -0,0 +1 @@
1
+ Spree::ProductsController.include(SpreeMultiDomain::ShowProductSupport)
@@ -0,0 +1,10 @@
1
+ Spree::TaxonsController.class_eval do
2
+ def show
3
+ @taxon = Spree::Taxon.find_by_store_id_and_permalink!(current_store.id, params[:id])
4
+ return unless @taxon
5
+
6
+ @searcher = build_searcher(params.merge(:taxon => @taxon.id))
7
+ @products = @searcher.retrieve_products
8
+ @taxonomies = get_taxonomies
9
+ end
10
+ end
@@ -0,0 +1,11 @@
1
+ module Spree
2
+ module Api
3
+ ProductsHelper.class_eval do
4
+ def get_taxonomies
5
+ @taxonomies ||= current_store.present? ? Spree::Taxonomy.where(["store_id = ?", current_store.id]) : Spree::Taxonomy
6
+ @taxonomies = @taxonomies.includes(:root => :children)
7
+ @taxonomies
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,13 @@
1
+ Spree::CartonMailer.class_eval do
2
+ def from_address_with_multidomain
3
+ order = @carton.orders.first
4
+
5
+ if order.store
6
+ order.store.mail_from_address
7
+ else
8
+ from_address_without_multidomain
9
+ end
10
+ end
11
+
12
+ alias_method_chain :from_address, :multidomain
13
+ end
@@ -0,0 +1,35 @@
1
+ module Spree
2
+ class OrderMailer < BaseMailer
3
+ def confirm_email(order, resend = false)
4
+ find_order(order)
5
+ subject = (resend ? "[#{Spree.t(:resend).upcase}] " : '')
6
+ subject += "#{Spree::Store.default.name} #{Spree.t('order_mailer.confirm_email.subject')} ##{@order.number}"
7
+ mail_params = {:to => @order.email, :subject => subject}
8
+ if @order.store.present? && @order.store.mail_from_address.present?
9
+ mail_params[:from] = @order.store.mail_from_address
10
+ else
11
+ mail_params[:from] = Spree::Store.default.mail_from_address
12
+ end
13
+ mail(mail_params)
14
+ end
15
+
16
+ def cancel_email(order, resend = false)
17
+ find_order(order)
18
+ subject = (resend ? "[#{Spree.t(:resend).upcase}] " : '')
19
+ subject += "#{Spree::Store.default.name} #{Spree.t('order_mailer.cancel_email.subject')} ##{@order.number}"
20
+ mail_params = {:to => @order.email, :subject => subject}
21
+ if @order.store.present? && @order.store.mail_from_address.present?
22
+ mail_params[:from] = @order.store.mail_from_address
23
+ else
24
+ mail_params[:from] = Spree::Store.default.mail_from_address
25
+ end
26
+ mail(mail_params)
27
+ end
28
+
29
+ private
30
+
31
+ def find_order(order)
32
+ @order = order.is_a?(Spree::Order) ? order : Spree::Order.find(order)
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,8 @@
1
+ Spree::Order.class_eval do
2
+ belongs_to :store
3
+ scope :by_store, lambda { |store| where(:store_id => store.id) }
4
+
5
+ def available_payment_methods
6
+ @available_payment_methods ||= Spree::PaymentMethod.available(:front_end, store)
7
+ end
8
+ end
@@ -0,0 +1,13 @@
1
+ Spree::PaymentMethod.class_eval do
2
+ has_many :store_payment_methods
3
+ has_many :stores, :through => :store_payment_methods
4
+
5
+ def self.available(display_on = 'both', store = nil)
6
+ result = all.select do |p|
7
+ p.active &&
8
+ (p.environment == Rails.env || p.environment.blank?) &&
9
+ (store.nil? || store.payment_methods.empty? || store.payment_methods.include?(p)) &&
10
+ (p.display_on == display_on.to_s || p.display_on.blank?)
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,4 @@
1
+ Spree::Product.class_eval do
2
+ has_and_belongs_to_many :stores, :join_table => 'spree_products_stores'
3
+ scope :by_store, lambda { |store| joins(:stores).where("spree_products_stores.store_id = ?", store) }
4
+ end
@@ -0,0 +1,30 @@
1
+ module Spree
2
+ class Promotion
3
+ module Rules
4
+ class Store < PromotionRule
5
+ has_and_belongs_to_many :stores, :class_name => 'Spree::Store', :join_table => 'spree_promotion_rules_stores', :foreign_key => 'promotion_rule_id'
6
+
7
+ def applicable?(promotable)
8
+ promotable.is_a?(Spree::Order)
9
+ end
10
+
11
+ def eligible?(order, options = {})
12
+ stores.none? or stores.include?(order.store)
13
+ end
14
+
15
+ def applicable?(promotable)
16
+ promotable.is_a?(Spree::Order)
17
+ end
18
+
19
+ def store_ids_string
20
+ store_ids.join(',')
21
+ end
22
+
23
+ def store_ids_string=(s)
24
+ self.store_ids = s.to_s.split(',').map(&:strip)
25
+ end
26
+
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,18 @@
1
+ Spree::ShippingMethod.class_eval do
2
+ has_many :store_shipping_methods
3
+ has_many :stores, :through => :store_shipping_methods
4
+
5
+ # This adds store_match to the list of requirements.
6
+ # This will need to be fixed for Spree 2.0 when split shipments is added
7
+ def available_to_order?(order, display_on= nil)
8
+ available?(order, display_on) &&
9
+ within_zone?(order) &&
10
+ category_match?(order) &&
11
+ currency_match?(order) &&
12
+ store_match?(order)
13
+ end
14
+
15
+ def store_match?(order)
16
+ order.store.shipping_methods.empty? || stores.include?(order.store)
17
+ end
18
+ end