spree_mobility 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +22 -0
- data/.hound.yml +21 -0
- data/.rspec +3 -0
- data/.rubocop.yml +7 -0
- data/.travis.yml +45 -0
- data/Appraisals +8 -0
- data/CONTRIBUTING.md +81 -0
- data/Gemfile +8 -0
- data/Guardfile +12 -0
- data/LICENSE.md +26 -0
- data/README.md +92 -0
- data/Rakefile +24 -0
- data/app/assets/javascripts/spree/backend/spree_mobility.js +2 -0
- data/app/assets/javascripts/spree/backend/taxon_tree_menu.js +49 -0
- data/app/assets/javascripts/spree/backend/translations.js +50 -0
- data/app/controllers/concerns/spree_mobility/controller_mobility_helper.rb +18 -0
- data/app/controllers/spree/admin/option_values_controller_decorator.rb +23 -0
- data/app/controllers/spree/admin/product_properties_controller_decorator.rb +22 -0
- data/app/controllers/spree/admin/shipping_methods_controller_decorator.rb +18 -0
- data/app/controllers/spree/admin/translations_controller.rb +53 -0
- data/app/controllers/spree/api/base_controller_decorator.rb +5 -0
- data/app/controllers/spree/base_controller_decorator.rb +5 -0
- data/app/helpers/spree_mobility/locale_helper.rb +11 -0
- data/app/models/concerns/spree_mobility/translatable.rb +63 -0
- data/app/overrides/spree/admin/option_types/_option_value_fields/add_translation.rb +10 -0
- data/app/overrides/spree/admin/option_types/index/add_translation.rb +8 -0
- data/app/overrides/spree/admin/product_properties/_product_property_fields/add_translation.rb +10 -0
- data/app/overrides/spree/admin/products/index/search_by_name_or_sku.rb +21 -0
- data/app/overrides/spree/admin/promotions/index/add_translation_link.rb +8 -0
- data/app/overrides/spree/admin/properties/index/add_translation.rb +8 -0
- data/app/overrides/spree/admin/shared/_product_tabs/add_translations.rb +10 -0
- data/app/overrides/spree/admin/shared/_translations/translation.rb +8 -0
- data/app/overrides/spree/admin/shared/sub_menu/_configuration/store_translations.rb +8 -0
- data/app/overrides/spree/admin/shipping_methods/index/add_translation.rb +8 -0
- data/app/overrides/spree/admin/taxonomies/_list/add_translations.rb +8 -0
- data/app/views/spree/admin/translations/_fields.html.erb +12 -0
- data/app/views/spree/admin/translations/_form.html.erb +13 -0
- data/app/views/spree/admin/translations/_form_fields.html.erb +38 -0
- data/app/views/spree/admin/translations/_settings.html.erb +22 -0
- data/app/views/spree/admin/translations/option_type.html.erb +9 -0
- data/app/views/spree/admin/translations/option_value.html.erb +25 -0
- data/app/views/spree/admin/translations/product.html.erb +7 -0
- data/app/views/spree/admin/translations/product_property.html.erb +23 -0
- data/app/views/spree/admin/translations/promotion.html.erb +9 -0
- data/app/views/spree/admin/translations/property.html.erb +9 -0
- data/app/views/spree/admin/translations/shipping_method.html.erb +9 -0
- data/app/views/spree/admin/translations/store.html.erb +25 -0
- data/app/views/spree/admin/translations/taxon.html.erb +25 -0
- data/app/views/spree/admin/translations/taxonomy.html.erb +9 -0
- data/bin/rails +7 -0
- data/config/initializers/enable_extensions.rb +27 -0
- data/config/initializers/form_builder_mobility_patch.rb +53 -0
- data/config/initializers/mobility.rb +118 -0
- data/config/initializers/spree_ransack.rb +31 -0
- data/config/locales/be.yml +6 -0
- data/config/locales/bg.yml +6 -0
- data/config/locales/en.yml +6 -0
- data/config/locales/it.yml +6 -0
- data/config/locales/ru.yml +6 -0
- data/config/locales/zh-TW.yml +6 -0
- data/config/routes.rb +7 -0
- data/db/migrate/20220411041407_add_translations_to_main_models.rb +83 -0
- data/db/migrate/20220412095648_remove_null_constraints_from_spree_tables.rb +13 -0
- data/db/migrate/20220413095648_migrate_translation_data.rb +53 -0
- data/gemfiles/spree_4_2.gemfile +8 -0
- data/gemfiles/spree_master.gemfile +10 -0
- data/lib/generators/spree_mobility/install/install_generator.rb +23 -0
- data/lib/spree_mobility/configuration.rb +12 -0
- data/lib/spree_mobility/core_ext/mobility/backends/active_record/table/mobility_acts_as_paranoid_decorator.rb +31 -0
- data/lib/spree_mobility/core_ext/spree/option_type_decorator.rb +11 -0
- data/lib/spree_mobility/core_ext/spree/option_value_decorator.rb +30 -0
- data/lib/spree_mobility/core_ext/spree/product_decorator.rb +87 -0
- data/lib/spree_mobility/core_ext/spree/product_property_decorator.rb +6 -0
- data/lib/spree_mobility/core_ext/spree/product_scopes_with_mobility_decorator.rb +16 -0
- data/lib/spree_mobility/core_ext/spree/products/find_with_mobility_decorator.rb +12 -0
- data/lib/spree_mobility/core_ext/spree/promotion_decorator.rb +11 -0
- data/lib/spree_mobility/core_ext/spree/property_decorator.rb +26 -0
- data/lib/spree_mobility/core_ext/spree/shipping_method_decorator.rb +10 -0
- data/lib/spree_mobility/core_ext/spree/store_decorator.rb +12 -0
- data/lib/spree_mobility/core_ext/spree/taxon_decorator.rb +54 -0
- data/lib/spree_mobility/core_ext/spree/taxonomy_decorator.rb +29 -0
- data/lib/spree_mobility/core_ext/spree/variant_decorator.rb +19 -0
- data/lib/spree_mobility/engine.rb +33 -0
- data/lib/spree_mobility/fallbacks.rb +45 -0
- data/lib/spree_mobility/translation_query.rb +36 -0
- data/lib/spree_mobility/version.rb +18 -0
- data/lib/spree_mobility.rb +47 -0
- data/spec/features/admin/products_spec.rb +15 -0
- data/spec/features/admin/translations_spec.rb +259 -0
- data/spec/features/translations_spec.rb +35 -0
- data/spec/models/product_spec.rb +77 -0
- data/spec/models/taxon_spec.rb +16 -0
- data/spec/models/translated_models_spec.rb +33 -0
- data/spec/models/variant_spec.rb +33 -0
- data/spec/spec_helper.rb +10 -0
- data/spec/support/i18n.rb +5 -0
- data/spec/support/matchers/be_a_thorough_translation_of_matcher.rb +21 -0
- data/spec/support/matchers/have_text_like.rb +3 -0
- data/spec/support/shared_contexts/translatable_context.rb +63 -0
- data/spree_mobility.gemspec +37 -0
- metadata +316 -0
@@ -0,0 +1,118 @@
|
|
1
|
+
require 'mobility'
|
2
|
+
|
3
|
+
Mobility.configure do
|
4
|
+
# PLUGINS
|
5
|
+
plugins do
|
6
|
+
# Backend
|
7
|
+
#
|
8
|
+
# Sets the default backend to use in models. This can be overridden in models
|
9
|
+
# by passing +backend: ...+ to +translates+.
|
10
|
+
#
|
11
|
+
# To default to a different backend globally, replace +:key_value+ by another
|
12
|
+
# backend name.
|
13
|
+
#
|
14
|
+
backend :table
|
15
|
+
|
16
|
+
# ActiveRecord
|
17
|
+
#
|
18
|
+
# Defines ActiveRecord as ORM, and enables ActiveRecord-specific plugins.
|
19
|
+
active_record
|
20
|
+
|
21
|
+
# Accessors
|
22
|
+
#
|
23
|
+
# Define reader and writer methods for translated attributes. Remove either
|
24
|
+
# to disable globally, or pass +reader: false+ or +writer: false+ to
|
25
|
+
# +translates+ in any translated model.
|
26
|
+
#
|
27
|
+
reader
|
28
|
+
writer
|
29
|
+
|
30
|
+
# Backend Reader
|
31
|
+
#
|
32
|
+
# Defines reader to access the backend for any attribute, of the form
|
33
|
+
# +<attribute>_backend+.
|
34
|
+
#
|
35
|
+
backend_reader
|
36
|
+
#
|
37
|
+
# Or pass an interpolation string to define a different pattern:
|
38
|
+
# backend_reader "%s_translations"
|
39
|
+
|
40
|
+
# Query
|
41
|
+
#
|
42
|
+
# Defines a scope on the model class which allows querying on
|
43
|
+
# translated attributes. The default scope is named +i18n+, pass a different
|
44
|
+
# name as default to change the global default, or to +translates+ in any
|
45
|
+
# model to change it for that model alone.
|
46
|
+
#
|
47
|
+
query
|
48
|
+
|
49
|
+
# Cache
|
50
|
+
#
|
51
|
+
# Comment out to disable caching reads and writes.
|
52
|
+
#
|
53
|
+
cache
|
54
|
+
|
55
|
+
# Dirty
|
56
|
+
#
|
57
|
+
# Uncomment this line to include and enable globally:
|
58
|
+
dirty
|
59
|
+
#
|
60
|
+
# Or uncomment this line to include but disable by default, and only enable
|
61
|
+
# per model by passing +dirty: true+ to +translates+.
|
62
|
+
# dirty false
|
63
|
+
|
64
|
+
# Fallbacks
|
65
|
+
#
|
66
|
+
# Uncomment line below to enable fallbacks, using +I18n.fallbacks+.
|
67
|
+
fallbacks
|
68
|
+
#
|
69
|
+
# Or uncomment this line to enable fallbacks with a global default.
|
70
|
+
# fallbacks { :pt => :en }
|
71
|
+
|
72
|
+
# Presence
|
73
|
+
#
|
74
|
+
# Converts blank strings to nil on reads and writes. Comment out to
|
75
|
+
# disable.
|
76
|
+
#
|
77
|
+
presence
|
78
|
+
|
79
|
+
# Default
|
80
|
+
#
|
81
|
+
# Set a default translation per attributes. When enabled, passing +default:
|
82
|
+
# 'foo'+ sets a default translation string to show in case no translation is
|
83
|
+
# present. Can also be passed a proc.
|
84
|
+
#
|
85
|
+
# default 'foo'
|
86
|
+
|
87
|
+
# Fallthrough Accessors
|
88
|
+
#
|
89
|
+
# Uses method_missing to define locale-specific accessor methods like
|
90
|
+
# +title_en+, +title_en=+, +title_fr+, +title_fr=+ for each translated
|
91
|
+
# attribute. If you know what set of locales you want to support, it's
|
92
|
+
# generally better to use Locale Accessors (or both together) since
|
93
|
+
# +method_missing+ is very slow. (You can use both fallthrough and locale
|
94
|
+
# accessor plugins together without conflict.)
|
95
|
+
#
|
96
|
+
# fallthrough_accessors
|
97
|
+
|
98
|
+
# Locale Accessors
|
99
|
+
#
|
100
|
+
# Uses +def+ to define accessor methods for a set of locales. By default uses
|
101
|
+
# +I18n.available_locales+, but you can pass the set of locales with
|
102
|
+
# +translates+ and/or set a global default here.
|
103
|
+
#
|
104
|
+
locale_accessors
|
105
|
+
#
|
106
|
+
# Or define specific defaults by uncommenting line below
|
107
|
+
# locale_accessors [:en, :ja]
|
108
|
+
|
109
|
+
# Attribute Methods
|
110
|
+
#
|
111
|
+
# Adds translated attributes to +attributes+ hash, and defines methods
|
112
|
+
# +translated_attributes+ and +untranslated_attributes+ which return hashes
|
113
|
+
# with translated and untranslated attributes, respectively. Be aware that
|
114
|
+
# this plugin can create conflicts with other gems.
|
115
|
+
#
|
116
|
+
# attribute_methods
|
117
|
+
end
|
118
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# Core Spree is biased towards SpreeGlobalize, so we need to prepend our versions
|
2
|
+
Rails.application.config.to_prepare do
|
3
|
+
module SpreeMobilityAdminTaxonSearch
|
4
|
+
private
|
5
|
+
def load_taxonomy
|
6
|
+
@taxonomy = scope.includes(:translations, taxons: [:translations]).find(params[:taxonomy_id])
|
7
|
+
end
|
8
|
+
end
|
9
|
+
Spree::Admin::TaxonsController.prepend SpreeMobilityAdminTaxonSearch
|
10
|
+
|
11
|
+
module SpreeMobilityAdminStoreSearch
|
12
|
+
def load_stores_by_query
|
13
|
+
@stores =
|
14
|
+
stores_scope.joins(:translations).where("LOWER(#{Spree::Store::Translation.table_name}.name) LIKE LOWER(:query)",
|
15
|
+
query: "%#{params[:q]}%")
|
16
|
+
end
|
17
|
+
end
|
18
|
+
Spree::Admin::StoresController.prepend SpreeMobilityAdminStoreSearch
|
19
|
+
|
20
|
+
module SpreeMobilityApiV1TaxonSearch
|
21
|
+
private
|
22
|
+
def taxonomy
|
23
|
+
if params[:taxonomy_id].present?
|
24
|
+
@taxonomy ||=
|
25
|
+
Spree::Taxonomy.includes(:translations, taxons: [:translations]).
|
26
|
+
accessible_by(current_ability, :show).find(params[:taxonomy_id])
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
Spree::Api::V1::TaxonsController.prepend SpreeMobilityApiV1TaxonSearch
|
31
|
+
end
|
data/config/routes.rb
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
Spree::Core::Engine.add_routes do
|
2
|
+
namespace :admin, path: Spree.admin_path do
|
3
|
+
get '/:resource/:resource_id/translations' => 'translations#index', as: :translations
|
4
|
+
patch '/option_values/:id' => 'option_values#update', as: :option_type_option_value
|
5
|
+
patch 'product/:id/product_properties/:id' => "product_properties#translate", as: :translate_product_property
|
6
|
+
end
|
7
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
class AddTranslationsToMainModels < SpreeExtension::Migration[4.2]
|
2
|
+
def up
|
3
|
+
unless table_exists?(:spree_product_translations)
|
4
|
+
create_helper :spree_product, { name: :string, description: :text, meta_title: :string,
|
5
|
+
meta_description: :string, meta_keywords: :string, slug: :string, deleted_at: :datetime }
|
6
|
+
add_index :spree_product_translations, :deleted_at
|
7
|
+
end
|
8
|
+
|
9
|
+
unless table_exists?(:spree_promotion_translations)
|
10
|
+
create_helper :spree_promotion, { name: :string, description: :string }
|
11
|
+
end
|
12
|
+
|
13
|
+
unless table_exists?(:spree_option_type_translations)
|
14
|
+
create_helper :spree_option_type, { name: :string, presentation: :string }
|
15
|
+
end
|
16
|
+
|
17
|
+
unless table_exists?(:spree_property_translations)
|
18
|
+
create_helper :spree_property, { name: :string, presentation: :string }
|
19
|
+
end
|
20
|
+
|
21
|
+
unless table_exists?(:spree_taxonomy_translations)
|
22
|
+
create_helper :spree_taxonomy, { name: :string }
|
23
|
+
end
|
24
|
+
|
25
|
+
unless table_exists?(:spree_taxon_translations)
|
26
|
+
create_helper :spree_taxon, { name: :string, description: :text, meta_title: :string,
|
27
|
+
meta_description: :string, meta_keywords: :string,
|
28
|
+
permalink: :string }
|
29
|
+
end
|
30
|
+
|
31
|
+
unless table_exists?(:spree_option_value_translations)
|
32
|
+
create_helper :spree_option_value, { name: :string, presentation: :string }
|
33
|
+
end
|
34
|
+
|
35
|
+
unless table_exists?(:spree_product_property_translations)
|
36
|
+
create_helper :spree_product_property, { value: :string }
|
37
|
+
end
|
38
|
+
|
39
|
+
unless table_exists?(:spree_store_translations)
|
40
|
+
create_helper :spree_store, { name: :string, meta_description: :text, meta_keywords: :text, seo_title: :string }
|
41
|
+
end
|
42
|
+
|
43
|
+
unless table_exists?(:spree_shipping_method_translations)
|
44
|
+
create_helper :spree_shipping_method, { name: :string }
|
45
|
+
end
|
46
|
+
|
47
|
+
add_column :friendly_id_slugs, :locale, :string
|
48
|
+
end
|
49
|
+
|
50
|
+
def down
|
51
|
+
drop_table :spree_product_translations
|
52
|
+
drop_table :spree_promotion_translations
|
53
|
+
drop_table :spree_option_type_translations
|
54
|
+
drop_table :spree_property_translations
|
55
|
+
drop_table :spree_taxonomy_translations
|
56
|
+
drop_table :spree_taxon_translations
|
57
|
+
drop_table :spree_option_value_translations
|
58
|
+
drop_table :spree_product_property_translations
|
59
|
+
drop_table :spree_store_translations
|
60
|
+
drop_table :spree_shipping_method_translations
|
61
|
+
remove_column :friendly_id_slugs, :locale
|
62
|
+
end
|
63
|
+
|
64
|
+
protected
|
65
|
+
|
66
|
+
def create_helper(table, params)
|
67
|
+
create_table :"#{table}_translations" do |t|
|
68
|
+
|
69
|
+
# Translated attribute(s)
|
70
|
+
params.each_pair do |attr, attr_type|
|
71
|
+
t.send attr_type, attr
|
72
|
+
end
|
73
|
+
|
74
|
+
t.string :locale, null: false
|
75
|
+
t.references table, null: false, foreign_key: true, index: false
|
76
|
+
|
77
|
+
t.timestamps null: false
|
78
|
+
end
|
79
|
+
|
80
|
+
add_index :"#{table}_translations", :locale, name: :"index_#{table}_translations_on_locale"
|
81
|
+
add_index :"#{table}_translations", [:"#{table}_id", :locale], name: :"index_#{table}_translations_on_id_and_locale", unique: true
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class RemoveNullConstraintsFromSpreeTables < SpreeExtension::Migration[4.2]
|
2
|
+
def up
|
3
|
+
change_column :spree_properties, :presentation, :string, null: true
|
4
|
+
change_column :spree_taxonomies, :name, :string, null: true
|
5
|
+
change_column :spree_taxons, :name, :string, null: true
|
6
|
+
end
|
7
|
+
|
8
|
+
def down
|
9
|
+
change_column :spree_properties, :presentation, :string, null: false
|
10
|
+
change_column :spree_taxonomies, :name, :string, null: false
|
11
|
+
change_column :spree_taxons, :name, :string, null: false
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
class MigrateTranslationData < SpreeExtension::Migration[4.2]
|
2
|
+
def up
|
3
|
+
create_helper Spree::Product, { name: :string, description: :text, meta_title: :string,
|
4
|
+
meta_description: :string, meta_keywords: :string, slug: :string, deleted_at: :datetime }
|
5
|
+
|
6
|
+
create_helper Spree::Promotion, { name: :string, description: :string }
|
7
|
+
|
8
|
+
create_helper Spree::OptionType, { name: :string, presentation: :string }
|
9
|
+
|
10
|
+
create_helper Spree::Property, { name: :string, presentation: :string }
|
11
|
+
|
12
|
+
create_helper Spree::Taxonomy, { name: :string }
|
13
|
+
|
14
|
+
create_helper Spree::Taxon, { name: :string, description: :text, meta_title: :string,
|
15
|
+
meta_description: :string, meta_keywords: :string,
|
16
|
+
permalink: :string }
|
17
|
+
|
18
|
+
create_helper Spree::OptionValue, { name: :string, presentation: :string }
|
19
|
+
|
20
|
+
create_helper Spree::ProductProperty, { value: :string }
|
21
|
+
|
22
|
+
create_helper Spree::Store, { name: :string, meta_description: :text, meta_keywords: :text, seo_title: :string }
|
23
|
+
|
24
|
+
create_helper Spree::ShippingMethod, { name: :string }
|
25
|
+
end
|
26
|
+
|
27
|
+
def down
|
28
|
+
# no need to do anything
|
29
|
+
end
|
30
|
+
|
31
|
+
protected
|
32
|
+
|
33
|
+
def create_helper(model_klass, fields)
|
34
|
+
store = Spree::Store.unscoped.first
|
35
|
+
return unless store
|
36
|
+
default_locale = store.default_locale.to_s
|
37
|
+
field_names = fields.keys + ['created_at', 'updated_at']
|
38
|
+
translation_table = "#{model_klass.table_name.singularize}_translations"
|
39
|
+
foreign_key = "#{model_klass.table_name.singularize}_id"
|
40
|
+
|
41
|
+
# In case we are migrating from Globalize with existing translations, skip this step
|
42
|
+
return if ActiveRecord::Base.connection.execute("SELECT id FROM #{translation_table}").any?
|
43
|
+
ActiveRecord::Base.connection.execute("SELECT id, #{field_names.join(',')} FROM #{model_klass.table_name}").each do |r|
|
44
|
+
field_values =
|
45
|
+
field_names.each_with_object([]) do |field_name, a|
|
46
|
+
a << r[field_name.to_s]
|
47
|
+
end
|
48
|
+
|
49
|
+
ActiveRecord::Base.connection.execute(ActiveRecord::Base.sanitize_sql_array(["INSERT INTO #{translation_table} (locale, #{foreign_key}, #{field_names.join(',')}) VALUES (?,?,#{(['?'] * field_names.size).join(',')})",
|
50
|
+
default_locale.to_s, r['id'], *field_values]))
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
# This file was generated by Appraisal
|
2
|
+
|
3
|
+
source "https://rubygems.org"
|
4
|
+
|
5
|
+
gem 'spree', github: 'spree/spree', branch: 'main'
|
6
|
+
gem 'spree_frontend', github: 'spree/spree', branch: 'main'
|
7
|
+
gem 'spree_backend', github: 'spree/spree', branch: 'main'
|
8
|
+
gem 'spree_i18n', github: 'spree-contrib/spree_i18n', branch: 'main'
|
9
|
+
|
10
|
+
gemspec path: '../'
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module SpreeMobility
|
2
|
+
module Generators
|
3
|
+
class InstallGenerator < Rails::Generators::Base
|
4
|
+
class_option :migrate, type: :boolean, default: true
|
5
|
+
|
6
|
+
def add_javascripts
|
7
|
+
append_file "vendor/assets/javascripts/spree/backend/all.js", "//= require spree/backend/spree_mobility\n"
|
8
|
+
end
|
9
|
+
|
10
|
+
def add_migrations
|
11
|
+
run 'bundle exec rake spree_mobility:install:migrations'
|
12
|
+
end
|
13
|
+
|
14
|
+
def run_migrations
|
15
|
+
if options[:migrate]
|
16
|
+
run 'bundle exec rake db:migrate'
|
17
|
+
else
|
18
|
+
puts "Skiping rake db:migrate, don't forget to run it!"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module SpreeMobility
|
2
|
+
class Configuration < Spree::Preferences::Configuration
|
3
|
+
# These configs intend to, respectively:
|
4
|
+
#
|
5
|
+
# Say which mobility inputs are displayed on backend
|
6
|
+
# Set locales that should be available for end users
|
7
|
+
#
|
8
|
+
# e.g. If available_locales are [:en, :es] admin can translate model records
|
9
|
+
# to spanish as well. Once it's done :es can be added to supported_locales
|
10
|
+
preference :supported_locales, :array, default: [:en]
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module SpreeMobility::CoreExt::Mobility::Backends::ActiveRecord::Table
|
2
|
+
module MobilityActsAsParanoidDecorator
|
3
|
+
private
|
4
|
+
def using_acts_as_paranoid?
|
5
|
+
translation_class = model_class.const_get(subclass_name)
|
6
|
+
return false unless translation_class
|
7
|
+
translation_class.column_names.include?('deleted_at')
|
8
|
+
end
|
9
|
+
|
10
|
+
# If joining a table with a deleted_at column (acts_as_paranoid), then
|
11
|
+
# filter the join with deleted_at IS NULL
|
12
|
+
def join_translations(relation, locale, join_type)
|
13
|
+
if using_acts_as_paranoid?
|
14
|
+
# check if joins changed
|
15
|
+
old_joins = relation.joins_values
|
16
|
+
relation = super
|
17
|
+
new_joins = relation.joins_values - old_joins
|
18
|
+
return relation if new_joins.empty?
|
19
|
+
fail "join_translations new_joins.size > 1" unless new_joins.size == 1
|
20
|
+
# append deleted_at IS NULL to joins ON clause using Arel
|
21
|
+
new_join = new_joins.first
|
22
|
+
joined_table = model_class.const_get(subclass_name).arel_table.alias(table_alias(locale))
|
23
|
+
relation.joins_values -= [new_join]
|
24
|
+
new_join.right = new_join.right.and(joined_table[:deleted_at].eq(nil))
|
25
|
+
relation.joins(new_join)
|
26
|
+
else
|
27
|
+
super
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module SpreeMobility::CoreExt::Spree::OptionTypeDecorator
|
2
|
+
def self.prepended(base)
|
3
|
+
base.include SpreeMobility::Translatable
|
4
|
+
SpreeMobility.translates_for base, :name, :presentation
|
5
|
+
|
6
|
+
base.translation_class.class_eval do
|
7
|
+
validates :name, presence: true, uniqueness: { scope: :locale, case_sensitive: false, allow_blank: true }
|
8
|
+
validates :presentation, presence: true
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module SpreeMobility::CoreExt::Spree::OptionValueDecorator
|
2
|
+
module TranslationMethods
|
3
|
+
def name_uniqueness_validation
|
4
|
+
return unless name.present?
|
5
|
+
return unless translated_model
|
6
|
+
check_scope =
|
7
|
+
::Spree::OptionValue.
|
8
|
+
where.not(id: translated_model.id).
|
9
|
+
where(option_type_id: translated_model.option_type_id).
|
10
|
+
joins(:translations).
|
11
|
+
where(spree_option_value_translations: { locale: locale }).
|
12
|
+
where('LOWER(spree_option_value_translations.name) = LOWER(?)', name)
|
13
|
+
if check_scope.exists?
|
14
|
+
errors.add(:name, :taken, value: name)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.prepended(base)
|
20
|
+
base.include SpreeMobility::Translatable
|
21
|
+
SpreeMobility.translates_for base, :name, :presentation
|
22
|
+
|
23
|
+
base.translation_class.class_eval do
|
24
|
+
include TranslationMethods
|
25
|
+
validate :name_uniqueness_validation
|
26
|
+
validates :name, presence: true
|
27
|
+
validates :presentation, presence: true
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
module SpreeMobility::CoreExt::Spree
|
2
|
+
module ProductDecorator
|
3
|
+
|
4
|
+
|
5
|
+
module ClassMethods
|
6
|
+
def search_by_name(query)
|
7
|
+
helper = SpreeMobility::TranslationQuery.new(all.model.mobility_backend_class(:name))
|
8
|
+
|
9
|
+
helper.add_joins(self.all).
|
10
|
+
where("LOWER(#{helper.col_name(:name)}) LIKE LOWER(:query)", query: "%#{query}%").distinct
|
11
|
+
end
|
12
|
+
|
13
|
+
def search_by_name_or_sku(query)
|
14
|
+
helper = SpreeMobility::TranslationQuery.new(all.model.mobility_backend_class(:name))
|
15
|
+
|
16
|
+
helper.add_joins(self.all).
|
17
|
+
joins(:variants_including_master).
|
18
|
+
where("(LOWER(#{helper.col_name(:name)}) LIKE LOWER(:query)) OR (LOWER(#{::Spree::Variant.table_name}.sku) LIKE LOWER(:query))", query: "%#{query}%").distinct
|
19
|
+
end
|
20
|
+
|
21
|
+
def like_any(fields, values)
|
22
|
+
mobility_fields = fields.select { |field| mobility_attributes.include?(field.to_s) }
|
23
|
+
other_fields = fields - mobility_fields
|
24
|
+
|
25
|
+
helper = SpreeMobility::TranslationQuery.new(all.model.mobility_backend_class(:name))
|
26
|
+
conditions = mobility_fields.product(values).map do |(field, value)|
|
27
|
+
sanitize_sql_array(["LOWER(#{helper.col_name(field)}) LIKE LOWER(?)", "%#{value}%"])
|
28
|
+
end
|
29
|
+
|
30
|
+
scope = other_fields.empty? ? self.all : super(other_fields, values)
|
31
|
+
|
32
|
+
helper.add_joins(scope).where(conditions.join(' OR '))
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.prepended(base)
|
37
|
+
base.include SpreeMobility::Translatable
|
38
|
+
SpreeMobility.translates_for base, :name, :description, :meta_title, :meta_description, :meta_keywords, :slug
|
39
|
+
base.friendly_id :slug_candidates, use: [:history, :mobility]
|
40
|
+
base.whitelisted_ransackable_scopes << 'search_by_name_or_sku'
|
41
|
+
|
42
|
+
base.translation_class.class_eval do
|
43
|
+
acts_as_paranoid
|
44
|
+
after_destroy :punch_slug
|
45
|
+
default_scopes = []
|
46
|
+
validates :slug, presence: true, uniqueness: { scope: :locale, case_sensitive: false }
|
47
|
+
|
48
|
+
with_options length: { maximum: 255 }, allow_blank: true do
|
49
|
+
validates :meta_keywords
|
50
|
+
validates :meta_title
|
51
|
+
end
|
52
|
+
with_options presence: true do
|
53
|
+
validates :name
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
if RUBY_VERSION.to_f >= 2.5
|
58
|
+
base.translation_class.define_method(:punch_slug) { update(slug: "#{Time.current.to_i}_#{slug}"[0..254]) }
|
59
|
+
else
|
60
|
+
base.translation_class.send(:define_method, :punch_slug) { update(slug: "#{Time.current.to_i}_#{slug}"[0..254]) }
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# Don't punch slug on original product as it prevents bulk deletion.
|
65
|
+
# Also we don't need it, as it is translated.
|
66
|
+
def punch_slug; end
|
67
|
+
|
68
|
+
def duplicate_extra(old_product)
|
69
|
+
duplicate_translations(old_product)
|
70
|
+
end
|
71
|
+
|
72
|
+
def property(property_name)
|
73
|
+
product_properties.joins(:property).find_by(spree_properties: { id: ::Spree::Property.find_by(name: property_name) }).try(:value)
|
74
|
+
end
|
75
|
+
|
76
|
+
private
|
77
|
+
|
78
|
+
def duplicate_translations(old_product)
|
79
|
+
self.translations.clear
|
80
|
+
old_product.translations.each do |translation|
|
81
|
+
translation.slug = nil # slug must be regenerated
|
82
|
+
translation.name = "COPY OF #{translation.name}" unless translation.name&.start_with?('COPY OF ')
|
83
|
+
self.translations << translation.dup
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module SpreeMobility::CoreExt::Spree::ProductScopesWithMobilityDecorator
|
2
|
+
def ascend_by_taxons_min_position(taxon_ids)
|
3
|
+
# order() must not refer to select(), because select could be removed
|
4
|
+
# in Spree::Products::Find
|
5
|
+
joins(:classifications).
|
6
|
+
where(::Spree::Classification.table_name => { taxon_id: taxon_ids }).
|
7
|
+
select(
|
8
|
+
[
|
9
|
+
"#{::Spree::Product.table_name}.*",
|
10
|
+
"MIN(#{::Spree::Classification.table_name}.position) AS min_position"
|
11
|
+
].join(', ')
|
12
|
+
).
|
13
|
+
group(:id).
|
14
|
+
order(Arel.sql("MIN(#{::Spree::Classification.table_name}.position) ASC"))
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module SpreeMobility::CoreExt::Spree::Products::FindWithMobilityDecorator
|
2
|
+
# The issue here is that ordering by translated attr (e.g. name) will add
|
3
|
+
# an ORDER BY translations_table.name, but the query has a SELECT DISTINCT,
|
4
|
+
# which would require the translations_table.name to be added to the SELECT
|
5
|
+
# Instead of using DISTINCT, we select the ID only and use that as a subquery
|
6
|
+
# in a simple SELECT ... FROM products WHERE products.id IN (SUBQUERY).
|
7
|
+
# Performance-wise should be almost the same, or the same.
|
8
|
+
def execute
|
9
|
+
::Spree::Product.where(id: super.except(:select).select(:id).distinct(false))
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module SpreeMobility::CoreExt::Spree::PromotionDecorator
|
2
|
+
def self.prepended(base)
|
3
|
+
base.include SpreeMobility::Translatable
|
4
|
+
SpreeMobility.translates_for base, :name, :description
|
5
|
+
|
6
|
+
base.translation_class.class_eval do
|
7
|
+
validates :name, presence: true
|
8
|
+
validates :description, length: { maximum: 255 }, allow_blank: true
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module SpreeMobility::CoreExt::Spree::PropertyDecorator
|
2
|
+
def self.prepended(base)
|
3
|
+
base.include SpreeMobility::Translatable
|
4
|
+
SpreeMobility.translates_for base, :name, :presentation
|
5
|
+
|
6
|
+
base.translation_class.class_eval do
|
7
|
+
validates :name, :presentation, presence: true
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
# Currently, mobilize does not yet support fallbacks in query scopes,
|
12
|
+
# so we update this method to fetch property IDs instead of value and then let
|
13
|
+
# mobilize logic determine the correct translation.
|
14
|
+
# The performance impact in this case should be minimal.
|
15
|
+
def uniq_values(product_properties_scope: nil)
|
16
|
+
with_uniq_values_cache_key(product_properties_scope) do
|
17
|
+
properties = product_properties
|
18
|
+
properties = properties.where(id: product_properties_scope) if product_properties_scope.present?
|
19
|
+
|
20
|
+
filter_params_scope = properties.reorder(nil).group(:filter_param).select(::Arel.sql('MAX(id)'))
|
21
|
+
properties.where(id: filter_params_scope).
|
22
|
+
includes(:translations).sort_by(&:value).
|
23
|
+
map { |property| [property.filter_param, property.value] }
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
module SpreeMobility::CoreExt::Spree::ShippingMethodDecorator
|
2
|
+
def self.prepended(base)
|
3
|
+
base.include SpreeMobility::Translatable
|
4
|
+
SpreeMobility.translates_for base, :name
|
5
|
+
|
6
|
+
base.translation_class.class_eval do
|
7
|
+
validates :name, presence: true
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|