spree_mobility 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|