spree_google_products 1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/README.md +177 -0
- data/Rakefile +21 -0
- data/app/assets/config/spree_google_products_manifest.js +5 -0
- data/app/assets/images/app_icons/google_logo.svg +1 -0
- data/app/assets/images/app_icons/google_merchant_logo.svg +52 -0
- data/app/assets/images/app_icons/welcome_scene.svg +1 -0
- data/app/controllers/spree/admin/google_merchant_settings_controller.rb +182 -0
- data/app/controllers/spree/admin/google_shopping/dashboard_controller.rb +64 -0
- data/app/controllers/spree/admin/google_shopping/issues_controller.rb +42 -0
- data/app/controllers/spree/admin/google_shopping/products_controller.rb +79 -0
- data/app/controllers/spree/admin/google_shopping/taxons_controller.rb +39 -0
- data/app/helpers/spree/admin/google_shopping_helper.rb +39 -0
- data/app/javascript/spree_google_products/application.js +16 -0
- data/app/javascript/spree_google_products/controllers/spree_google_products_controller.js +7 -0
- data/app/jobs/spree/google_shopping/fetch_status_job.rb +23 -0
- data/app/jobs/spree/google_shopping/sync_all_job.rb +18 -0
- data/app/jobs/spree/google_shopping/sync_product_job.rb +35 -0
- data/app/jobs/spree_google_products/base_job.rb +5 -0
- data/app/models/spree/google_credential.rb +29 -0
- data/app/models/spree/google_product_attribute.rb +9 -0
- data/app/models/spree/google_taxon.rb +5 -0
- data/app/models/spree/google_variant_attribute.rb +6 -0
- data/app/models/spree/product_decorator.rb +29 -0
- data/app/models/spree/store_decorator.rb +9 -0
- data/app/models/spree/variant_decorator.rb +8 -0
- data/app/models/spree_google_products/ability.rb +10 -0
- data/app/services/spree/google_shopping/content_service.rb +215 -0
- data/app/services/spree/google_shopping/status_service.rb +150 -0
- data/app/services/spree/google_token_service.rb +59 -0
- data/app/views/spree/admin/google_merchant_settings/edit.html.erb +331 -0
- data/app/views/spree/admin/google_shopping/dashboard/index.html.erb +121 -0
- data/app/views/spree/admin/google_shopping/issues/index.html.erb +106 -0
- data/app/views/spree/admin/google_shopping/products/_filters.html.erb +19 -0
- data/app/views/spree/admin/google_shopping/products/edit.html.erb +336 -0
- data/app/views/spree/admin/google_shopping/products/index.html.erb +131 -0
- data/app/views/spree/admin/google_shopping/products/issues.html.erb +48 -0
- data/app/views/spree/admin/products/google_shopping.html.erb +63 -0
- data/app/views/spree_google_products/_head.html.erb +1 -0
- data/config/importmap.rb +6 -0
- data/config/initializers/assets.rb +8 -0
- data/config/initializers/force_encryption_keys.rb +24 -0
- data/config/initializers/spree.rb +32 -0
- data/config/initializers/spree_google_products.rb +29 -0
- data/config/locales/en.yml +35 -0
- data/config/routes.rb +33 -0
- data/db/migrate/20260112000000_add_spree_google_shopping_tables.rb +89 -0
- data/lib/generators/spree_google_products/install/install_generator.rb +84 -0
- data/lib/generators/spree_google_products/uninstall/uninstall_generator.rb +55 -0
- data/lib/spree_google_products/configuration.rb +13 -0
- data/lib/spree_google_products/engine.rb +37 -0
- data/lib/spree_google_products/factories.rb +6 -0
- data/lib/spree_google_products/version.rb +7 -0
- data/lib/spree_google_products.rb +13 -0
- data/lib/tasks/spree_google_products.rake +41 -0
- metadata +190 -0
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
<%= render 'spree/admin/shared/product_tabs', current: :google_shopping %>
|
|
2
|
+
|
|
3
|
+
<% content_for :page_title do %>
|
|
4
|
+
Google Shopping Attributes
|
|
5
|
+
<% end %>
|
|
6
|
+
|
|
7
|
+
<%= form_for [:admin, @product], url: admin_product_path(@product), method: :put do |f| %>
|
|
8
|
+
<div class="card mb-3">
|
|
9
|
+
<div class="card-header">
|
|
10
|
+
<h5 class="card-title mb-0">Basic Information</h5>
|
|
11
|
+
</div>
|
|
12
|
+
<div class="card-body">
|
|
13
|
+
<%= f.fields_for :google_product_attribute do |g| %>
|
|
14
|
+
|
|
15
|
+
<div class="form-group">
|
|
16
|
+
<%= g.label :brand, "Brand Name" %>
|
|
17
|
+
<%= g.text_field :brand, class: 'form-control' %>
|
|
18
|
+
</div>
|
|
19
|
+
|
|
20
|
+
<div class="row">
|
|
21
|
+
<div class="col-6">
|
|
22
|
+
<div class="form-group">
|
|
23
|
+
<%= g.label :gtin, "GTIN / UPC / EAN" %>
|
|
24
|
+
<%= g.text_field :gtin, class: 'form-control' %>
|
|
25
|
+
<small class="form-text text-muted">Leave empty if custom product.</small>
|
|
26
|
+
</div>
|
|
27
|
+
</div>
|
|
28
|
+
<div class="col-6">
|
|
29
|
+
<div class="form-group">
|
|
30
|
+
<%= g.label :mpn, "MPN (Manufacturer Part Number)" %>
|
|
31
|
+
<%= g.text_field :mpn, class: 'form-control' %>
|
|
32
|
+
</div>
|
|
33
|
+
</div>
|
|
34
|
+
</div>
|
|
35
|
+
|
|
36
|
+
<div class="row">
|
|
37
|
+
<div class="col-4">
|
|
38
|
+
<div class="form-group">
|
|
39
|
+
<%= g.label :condition %>
|
|
40
|
+
<%= g.select :condition, ['new', 'refurbished', 'used'], {}, class: 'select2' %>
|
|
41
|
+
</div>
|
|
42
|
+
</div>
|
|
43
|
+
<div class="col-4">
|
|
44
|
+
<div class="form-group">
|
|
45
|
+
<%= g.label :gender %>
|
|
46
|
+
<%= g.select :gender, ['', 'male', 'female', 'unisex'], {}, class: 'select2' %>
|
|
47
|
+
</div>
|
|
48
|
+
</div>
|
|
49
|
+
<div class="col-4">
|
|
50
|
+
<div class="form-group">
|
|
51
|
+
<%= g.label :age_group %>
|
|
52
|
+
<%= g.select :age_group, ['', 'adult', 'kids', 'toddler', 'infant'], {}, class: 'select2' %>
|
|
53
|
+
</div>
|
|
54
|
+
</div>
|
|
55
|
+
end %>
|
|
56
|
+
<% end %>
|
|
57
|
+
</div>
|
|
58
|
+
</div>
|
|
59
|
+
|
|
60
|
+
<div class="form-actions" data-hook="buttons">
|
|
61
|
+
<%= render partial: 'spree/admin/shared/edit_resource_links' %>
|
|
62
|
+
</div>
|
|
63
|
+
<% end %>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<%= javascript_import_module_tag 'application-spree-google-products' %>
|
data/config/importmap.rb
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
pin 'application-spree-google-products', to: 'spree_google_products/application.js', preload: false
|
|
2
|
+
|
|
3
|
+
pin_all_from SpreeGoogleProducts::Engine.root.join('app/javascript/spree_google_products/controllers'),
|
|
4
|
+
under: 'spree_google_products/controllers',
|
|
5
|
+
to: 'spree_google_products/controllers',
|
|
6
|
+
preload: 'application-spree-google-products'
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# Only run this if Sprockets is loaded (Spree 4.x / 5.0)
|
|
2
|
+
if Rails.configuration.respond_to?(:assets) && defined?(Sprockets)
|
|
3
|
+
Rails.application.config.assets.precompile += %w[
|
|
4
|
+
app_icons/welcome_scene.svg
|
|
5
|
+
app_icons/google_logo.svg
|
|
6
|
+
app_icons/google_merchant_logo.svg
|
|
7
|
+
]
|
|
8
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
if Rails.env.development? || Rails.env.test?
|
|
2
|
+
env_file = Rails.root.join('.env')
|
|
3
|
+
|
|
4
|
+
if File.exist?(env_file)
|
|
5
|
+
File.foreach(env_file) do |line|
|
|
6
|
+
next if line.strip.start_with?('#') || line.strip.empty?
|
|
7
|
+
key, value = line.strip.split('=', 2)
|
|
8
|
+
if key && value
|
|
9
|
+
ENV[key] = value
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
if ENV['ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY'].present?
|
|
15
|
+
ActiveRecord::Encryption.configure(
|
|
16
|
+
primary_key: ENV['ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY'],
|
|
17
|
+
deterministic_key: ENV['ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY'],
|
|
18
|
+
key_derivation_salt: ENV['ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT']
|
|
19
|
+
)
|
|
20
|
+
puts "🔐 Active Record Encryption Keys Loaded Successfully via Initializer."
|
|
21
|
+
else
|
|
22
|
+
puts "⚠️ WARNING: Active Record Encryption Keys are MISSING in .env"
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Uncomment lines below to add your own custom business logic
|
|
2
|
+
# such as promotions, shipping methods, etc.
|
|
3
|
+
Rails.application.config.after_initialize do
|
|
4
|
+
# Spree.shipping_methods << Spree::ShippingMethods::SuperExpensiveNotVeryFastShipping
|
|
5
|
+
# Spree.payment_methods << Spree::PaymentMethods::VerySafeAndReliablePaymentMethod
|
|
6
|
+
|
|
7
|
+
# Spree.calculators.tax_rates << Spree::TaxRates::FinanceTeamForcedMeToCodeThis
|
|
8
|
+
|
|
9
|
+
# Spree.stock_splitters << Spree::Stock::Splitters::SecretLogicSplitter
|
|
10
|
+
|
|
11
|
+
# Spree.adjusters << Spree::Adjustable::Adjuster::TaxTheRich
|
|
12
|
+
|
|
13
|
+
# Custom promotions
|
|
14
|
+
# Spree.calculators.promotion_actions_create_adjustments << Spree::Calculators::PromotionActions::CreateAdjustments::AddDiscountForFriends
|
|
15
|
+
# Spree.calculators.promotion_actions_create_item_adjustments << Spree::Calculators::PromotionActions::CreateItemAdjustments::FinanceTeamForcedMeToCodeThis
|
|
16
|
+
# Spree.promotions.rules << Spree::Promotions::Rules::OnlyForVIPCustomers
|
|
17
|
+
# Spree.promotions.actions << Spree::Promotions::Actions::GiftWithPurchase
|
|
18
|
+
|
|
19
|
+
# Spree.taxon_rules << Spree::TaxonRules::ProductsWithColor
|
|
20
|
+
|
|
21
|
+
# Spree.exports << Spree::Exports::Payments
|
|
22
|
+
# Spree.reports << Spree::Reports::MassivelyOvercomplexReportForCfo
|
|
23
|
+
|
|
24
|
+
# Themes and page builder
|
|
25
|
+
# Spree.page_builder.themes << Spree::Themes::NewShinyTheme
|
|
26
|
+
# Spree.page_builder.theme_layout_sections << Spree::PageSections::SuperImportantCeoBio
|
|
27
|
+
# Spree.page_builder.page_sections << Spree::PageSections::ContactFormToGetInTouch
|
|
28
|
+
# Spree.page_builder.page_blocks << Spree::PageBlocks::BigRedButtonToCallSales
|
|
29
|
+
|
|
30
|
+
# Storefront partials
|
|
31
|
+
Spree.storefront.partials.head << 'spree_google_products/head'
|
|
32
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
Rails.application.config.after_initialize do
|
|
2
|
+
if Spree.respond_to?(:admin) && Spree.admin.respond_to?(:navigation)
|
|
3
|
+
sidebar = Spree.admin.navigation.sidebar
|
|
4
|
+
sidebar.add :google_shopping,
|
|
5
|
+
label: 'Google Shopping',
|
|
6
|
+
icon: 'brand-google',
|
|
7
|
+
url: :admin_google_shopping_dashboard_path,
|
|
8
|
+
position: 85
|
|
9
|
+
sidebar.add :google_dashboard,
|
|
10
|
+
parent: :google_shopping,
|
|
11
|
+
label: 'Dashboard',
|
|
12
|
+
url: :admin_google_shopping_dashboard_path,
|
|
13
|
+
active: -> { params[:controller].include?('google_shopping/dashboard') }
|
|
14
|
+
sidebar.add :google_products,
|
|
15
|
+
parent: :google_shopping,
|
|
16
|
+
label: 'Products',
|
|
17
|
+
url: :admin_google_shopping_products_path,
|
|
18
|
+
active: -> { params[:controller].include?('google_shopping/products') }
|
|
19
|
+
sidebar.add :google_settings,
|
|
20
|
+
parent: :google_shopping,
|
|
21
|
+
label: :settings,
|
|
22
|
+
url: :edit_admin_google_merchant_settings_path,
|
|
23
|
+
active: -> { controller_name == 'google_merchant_settings' }
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
if defined?(Spree::Ability) && defined?(SpreeGoogleProducts::Ability)
|
|
27
|
+
Spree::Ability.register_ability(SpreeGoogleProducts::Ability)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
en:
|
|
2
|
+
spree:
|
|
3
|
+
# --- Page Titles & Headers ---
|
|
4
|
+
google_merchant_settings: "Google Merchant Settings"
|
|
5
|
+
google_shopping_settings: "Google Shopping Settings"
|
|
6
|
+
google_merchant: "Merchant Center"
|
|
7
|
+
google_merchant_products: "Google Products"
|
|
8
|
+
google_data_quality_issues: "Data Quality Issues"
|
|
9
|
+
google_shopping: "Google Shopping"
|
|
10
|
+
|
|
11
|
+
# --- Navigation & Tabs ---
|
|
12
|
+
admin:
|
|
13
|
+
tab:
|
|
14
|
+
google_shopping: "Google Shopping"
|
|
15
|
+
|
|
16
|
+
# --- Sidebar Menu ---
|
|
17
|
+
google_dashboard: "Dashboard"
|
|
18
|
+
google_products: "Products"
|
|
19
|
+
# 'settings' is already provided by Spree core
|
|
20
|
+
|
|
21
|
+
# --- Actions & Flash Messages ---
|
|
22
|
+
connect_merchant_center: "Connect Merchant Center"
|
|
23
|
+
sync_now: "Sync Now"
|
|
24
|
+
sync_all_products: "Sync All Products"
|
|
25
|
+
view_issues: "View Issues"
|
|
26
|
+
view_fixes: "View Fixes"
|
|
27
|
+
|
|
28
|
+
# --- Status Labels ---
|
|
29
|
+
# Used in Dashboard and Product Lists
|
|
30
|
+
google_status:
|
|
31
|
+
active: "Active"
|
|
32
|
+
pending: "Pending"
|
|
33
|
+
disapproved: "Disapproved"
|
|
34
|
+
limited: "Limited"
|
|
35
|
+
not_synced: "Not Synced"
|
data/config/routes.rb
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
Spree::Core::Engine.add_routes do
|
|
2
|
+
namespace :admin do
|
|
3
|
+
# Dashboard
|
|
4
|
+
namespace :google_shopping do
|
|
5
|
+
get 'dashboard', to: 'dashboard#index', as: :dashboard
|
|
6
|
+
resources :issues, only: [:index]
|
|
7
|
+
post 'sync', to: 'dashboard#sync', as: :sync
|
|
8
|
+
|
|
9
|
+
# Dedicated Product Manager
|
|
10
|
+
resources :products, only: [:index, :edit, :update] do
|
|
11
|
+
member do
|
|
12
|
+
post :sync_one
|
|
13
|
+
get :issues
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Taxonomy Search
|
|
18
|
+
resources :taxons, only: [] do
|
|
19
|
+
collection do
|
|
20
|
+
get :drill_down
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Settings Routes
|
|
26
|
+
resource :google_merchant_settings, only: [:edit, :update] do
|
|
27
|
+
get :connect
|
|
28
|
+
get :callback
|
|
29
|
+
delete :disconnect
|
|
30
|
+
post :sync_now
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
class AddSpreeGoogleShoppingTables < ActiveRecord::Migration[7.0]
|
|
2
|
+
def change
|
|
3
|
+
create_table :spree_google_credentials do |t|
|
|
4
|
+
t.references :store, null: false, index: true
|
|
5
|
+
|
|
6
|
+
# Auth & Security
|
|
7
|
+
t.string :merchant_center_id
|
|
8
|
+
t.string :ad_account_id
|
|
9
|
+
t.text :access_token
|
|
10
|
+
t.text :refresh_token
|
|
11
|
+
t.string :scope
|
|
12
|
+
t.string :email
|
|
13
|
+
t.datetime :token_expires_at
|
|
14
|
+
t.datetime :last_sync_at
|
|
15
|
+
|
|
16
|
+
# Targeting Configuration
|
|
17
|
+
t.string :target_country, default: 'US'
|
|
18
|
+
t.string :target_currency, default: 'USD'
|
|
19
|
+
|
|
20
|
+
# Global Defaults
|
|
21
|
+
t.string :default_google_product_category
|
|
22
|
+
t.string :default_product_type
|
|
23
|
+
t.integer :default_min_handling_time
|
|
24
|
+
t.integer :default_max_handling_time
|
|
25
|
+
|
|
26
|
+
t.timestamps
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
create_table :spree_google_product_attributes do |t|
|
|
30
|
+
t.references :product, null: false, index: true
|
|
31
|
+
|
|
32
|
+
# Core Attributes
|
|
33
|
+
t.string :brand
|
|
34
|
+
t.string :gtin
|
|
35
|
+
t.string :mpn
|
|
36
|
+
t.boolean :identifier_exists, default: true
|
|
37
|
+
|
|
38
|
+
# Categorization
|
|
39
|
+
t.string :google_product_category
|
|
40
|
+
t.string :product_type
|
|
41
|
+
|
|
42
|
+
# Demographics
|
|
43
|
+
t.string :condition, default: 'new'
|
|
44
|
+
t.string :gender
|
|
45
|
+
t.string :age_group
|
|
46
|
+
t.string :color
|
|
47
|
+
t.string :size
|
|
48
|
+
t.string :material
|
|
49
|
+
|
|
50
|
+
# Status & Feedback
|
|
51
|
+
t.string :google_status
|
|
52
|
+
t.text :google_issues
|
|
53
|
+
|
|
54
|
+
# Extra Data
|
|
55
|
+
t.string :custom_label_0
|
|
56
|
+
|
|
57
|
+
# Sale Logic
|
|
58
|
+
t.datetime :sale_start_at
|
|
59
|
+
t.datetime :sale_end_at
|
|
60
|
+
|
|
61
|
+
# Shipping Overrides
|
|
62
|
+
t.integer :min_handling_time
|
|
63
|
+
t.integer :max_handling_time
|
|
64
|
+
|
|
65
|
+
t.timestamps
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
create_table :spree_google_variant_attributes do |t|
|
|
69
|
+
t.references :variant, null: false, index: true
|
|
70
|
+
|
|
71
|
+
# Identification Overrides
|
|
72
|
+
t.string :gtin
|
|
73
|
+
t.string :mpn
|
|
74
|
+
|
|
75
|
+
# Status Syncing
|
|
76
|
+
t.string :google_status, index: true
|
|
77
|
+
t.text :google_issues
|
|
78
|
+
t.datetime :last_synced_at
|
|
79
|
+
|
|
80
|
+
t.timestamps
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
create_table :spree_google_taxons do |t|
|
|
84
|
+
t.string :name, index: true
|
|
85
|
+
t.string :google_id, index: true
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
end
|
|
89
|
+
end
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
require 'securerandom'
|
|
2
|
+
|
|
3
|
+
module SpreeGoogleProducts
|
|
4
|
+
module Generators
|
|
5
|
+
class InstallGenerator < Rails::Generators::Base
|
|
6
|
+
source_root File.expand_path('templates', __dir__)
|
|
7
|
+
|
|
8
|
+
class_option :auto_run_migrations, type: :boolean, default: false
|
|
9
|
+
|
|
10
|
+
def add_migrations
|
|
11
|
+
run 'bundle exec rake railties:install:migrations FROM=spree_google_products'
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def create_initializer
|
|
15
|
+
initializer_content = <<~RUBY
|
|
16
|
+
if Rails.env.development? || Rails.env.test?
|
|
17
|
+
env_file = Rails.root.join('.env')
|
|
18
|
+
|
|
19
|
+
if File.exist?(env_file)
|
|
20
|
+
File.foreach(env_file) do |line|
|
|
21
|
+
next if line.strip.start_with?('#') || line.strip.empty?
|
|
22
|
+
key, value = line.strip.split('=', 2)
|
|
23
|
+
ENV[key] = value if key && value
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
if ENV['ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY'].present?
|
|
28
|
+
ActiveRecord::Encryption.configure(
|
|
29
|
+
primary_key: ENV['ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY'],
|
|
30
|
+
deterministic_key: ENV['ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY'],
|
|
31
|
+
key_derivation_salt: ENV['ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT']
|
|
32
|
+
)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
RUBY
|
|
36
|
+
|
|
37
|
+
create_file "config/initializers/force_encryption_keys.rb", initializer_content
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def setup_encryption_keys
|
|
41
|
+
return unless Rails.env.development? || Rails.env.test?
|
|
42
|
+
env_file = File.join(Rails.root, '.env')
|
|
43
|
+
create_file '.env' unless File.exist?(env_file)
|
|
44
|
+
current_content = File.read(env_file)
|
|
45
|
+
keys_to_add = []
|
|
46
|
+
|
|
47
|
+
{
|
|
48
|
+
"ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY" => SecureRandom.alphanumeric(32),
|
|
49
|
+
"ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY" => SecureRandom.alphanumeric(32),
|
|
50
|
+
"ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT" => SecureRandom.alphanumeric(32)
|
|
51
|
+
}.each do |key_name, value|
|
|
52
|
+
unless current_content.include?(key_name)
|
|
53
|
+
keys_to_add << "#{key_name}=#{value}"
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
if keys_to_add.any?
|
|
58
|
+
append_to_file '.env', "\n# --- Added by Spree Google Shopping Installer ---\n#{keys_to_add.join("\n")}\n"
|
|
59
|
+
puts "🔑 Added missing Active Record Encryption keys to .env"
|
|
60
|
+
else
|
|
61
|
+
puts "✅ Active Record Encryption keys already present."
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def run_migrations
|
|
66
|
+
run_migrations = options[:auto_run_migrations] || ['', 'y', 'Y'].include?(ask('Would you like to run the migrations now? [Y/n]'))
|
|
67
|
+
if run_migrations
|
|
68
|
+
run 'bundle exec rails db:migrate'
|
|
69
|
+
else
|
|
70
|
+
puts 'Skipping rails db:migrate, don\'t forget to run it!'
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def run_seeds
|
|
75
|
+
run_seeds = options[:auto_run_migrations] || ['', 'y', 'Y'].include?(ask('Would you like to download and seed Google Taxonomies now? (Recommended) [Y/n]'))
|
|
76
|
+
if run_seeds
|
|
77
|
+
run 'bundle exec rake spree_google_shopping:seed_taxons'
|
|
78
|
+
else
|
|
79
|
+
puts 'Skipping seed. You can run it later with: bundle exec rake spree_google_shopping:seed_taxons'
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
module SpreeGoogleProducts
|
|
2
|
+
module Generators
|
|
3
|
+
class UninstallGenerator < Rails::Generators::Base
|
|
4
|
+
|
|
5
|
+
def drop_tables
|
|
6
|
+
return unless yes?("WARNING: This will destroy all Google Shopping data (Credentials, Product Settings, Taxonomies). Are you sure? [y/N]")
|
|
7
|
+
|
|
8
|
+
tables = %w[spree_google_credentials spree_google_product_attributes spree_google_variant_attributes spree_google_taxons]
|
|
9
|
+
|
|
10
|
+
tables.each do |table|
|
|
11
|
+
if ActiveRecord::Base.connection.table_exists?(table)
|
|
12
|
+
ActiveRecord::Base.connection.drop_table(table, force: :cascade)
|
|
13
|
+
puts "🔥 Dropped table: #{table}"
|
|
14
|
+
else
|
|
15
|
+
puts " Table #{table} does not exist, skipping."
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def remove_migrations
|
|
21
|
+
migration_files = Dir.glob("db/migrate/*add_spree_google_shopping_tables.spree_google_products.rb")
|
|
22
|
+
migration_files += Dir.glob("db/migrate/*spree_google_products.rb")
|
|
23
|
+
|
|
24
|
+
if migration_files.any?
|
|
25
|
+
migration_files.uniq.each do |file|
|
|
26
|
+
version = File.basename(file).split('_').first
|
|
27
|
+
if version.match?(/^\d+$/)
|
|
28
|
+
ActiveRecord::Base.connection.execute("DELETE FROM schema_migrations WHERE version = '#{version}'")
|
|
29
|
+
puts "🧹 Removed version #{version} from schema_migrations."
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
remove_file file
|
|
33
|
+
end
|
|
34
|
+
else
|
|
35
|
+
puts "⚠️ No specific migration files found to remove."
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def remove_initializer
|
|
40
|
+
remove_file "config/initializers/force_encryption_keys.rb"
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def clean_env_reminder
|
|
44
|
+
puts "\n"
|
|
45
|
+
puts "⚠️ IMPORTANT: We did not remove the Encryption Keys from your .env file."
|
|
46
|
+
puts " If you are not using them for other plugins, please manually remove:"
|
|
47
|
+
puts " - ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY"
|
|
48
|
+
puts " - ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY"
|
|
49
|
+
puts " - ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT"
|
|
50
|
+
puts "\n"
|
|
51
|
+
puts "✅ Spree Google Products has been uninstalled."
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
module SpreeGoogleProducts
|
|
2
|
+
class Configuration < Spree::Preferences::Configuration
|
|
3
|
+
|
|
4
|
+
# Some example preferences are shown below, for more information visit:
|
|
5
|
+
# https://docs.spreecommerce.org/developer/contributing/creating-an-extension
|
|
6
|
+
|
|
7
|
+
# preference :enabled, :boolean, default: true
|
|
8
|
+
# preference :dark_chocolate, :boolean, default: true
|
|
9
|
+
# preference :color, :string, default: 'Red'
|
|
10
|
+
# preference :favorite_number, :integer
|
|
11
|
+
# preference :supported_locales, :array, default: [:en]
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
module SpreeGoogleProducts
|
|
2
|
+
class Engine < Rails::Engine
|
|
3
|
+
require 'spree/core'
|
|
4
|
+
isolate_namespace Spree
|
|
5
|
+
engine_name 'spree_google_products'
|
|
6
|
+
|
|
7
|
+
# use rspec for tests
|
|
8
|
+
config.generators do |g|
|
|
9
|
+
g.test_framework :rspec
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
initializer 'spree_google_products.environment', before: :load_config_initializers do |_app|
|
|
13
|
+
SpreeGoogleProducts::Config = SpreeGoogleProducts::Configuration.new
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
initializer 'spree_google_products.assets' do |app|
|
|
17
|
+
app.config.assets.paths << root.join('app/javascript')
|
|
18
|
+
app.config.assets.paths << root.join('vendor/javascript')
|
|
19
|
+
app.config.assets.paths << root.join('vendor/stylesheets')
|
|
20
|
+
app.config.assets.precompile += %w[spree_google_products_manifest]
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
initializer 'spree_google_products.importmap', before: 'importmap' do |app|
|
|
24
|
+
app.config.importmap.paths << root.join('config/importmap.rb')
|
|
25
|
+
# https://github.com/rails/importmap-rails?tab=readme-ov-file#sweeping-the-cache-in-development-and-test
|
|
26
|
+
app.config.importmap.cache_sweepers << root.join('app/javascript')
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def self.activate
|
|
30
|
+
Dir.glob(File.join(File.dirname(__FILE__), '../../app/**/*_decorator*.rb')) do |c|
|
|
31
|
+
Rails.configuration.cache_classes ? require(c) : load(c)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
config.to_prepare(&method(:activate).to_proc)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
FactoryBot.define do
|
|
2
|
+
# Define your Spree extensions Factories within this file to enable applications, and other extensions to use and override them.
|
|
3
|
+
#
|
|
4
|
+
# Example adding this to your spec_helper will load these Factories for use:
|
|
5
|
+
# require 'spree_google_products/factories'
|
|
6
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
require 'spree_core'
|
|
2
|
+
require 'spree_extension'
|
|
3
|
+
require 'spree_google_products/engine'
|
|
4
|
+
require 'spree_google_products/version'
|
|
5
|
+
require 'spree_google_products/configuration'
|
|
6
|
+
|
|
7
|
+
module SpreeGoogleProducts
|
|
8
|
+
mattr_accessor :queue
|
|
9
|
+
|
|
10
|
+
def self.queue
|
|
11
|
+
@@queue ||= (defined?(Spree.queues) ? Spree.queues.default : :default)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
namespace :spree_google_shopping do
|
|
2
|
+
desc "Download and import official Google Taxonomy"
|
|
3
|
+
task seed_taxons: :environment do
|
|
4
|
+
require 'open-uri'
|
|
5
|
+
|
|
6
|
+
url = "https://www.google.com/basepages/producttype/taxonomy-with-ids.en-US.txt"
|
|
7
|
+
puts "⬇️ Downloading Google Taxonomy from: #{url}..."
|
|
8
|
+
|
|
9
|
+
ActiveRecord::Base.transaction do
|
|
10
|
+
Spree::GoogleTaxon.delete_all
|
|
11
|
+
|
|
12
|
+
count = 0
|
|
13
|
+
data = []
|
|
14
|
+
|
|
15
|
+
URI.open(url) do |file|
|
|
16
|
+
file.each_line do |line|
|
|
17
|
+
next if line.start_with?('#') || line.strip.empty?
|
|
18
|
+
|
|
19
|
+
parts = line.strip.split(' - ', 2)
|
|
20
|
+
|
|
21
|
+
if parts.length == 2
|
|
22
|
+
data << {
|
|
23
|
+
google_id: parts[0],
|
|
24
|
+
name: parts[1]
|
|
25
|
+
}
|
|
26
|
+
count += 1
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
if data.size >= 1000
|
|
30
|
+
Spree::GoogleTaxon.insert_all(data)
|
|
31
|
+
print "."
|
|
32
|
+
data = []
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
Spree::GoogleTaxon.insert_all(data) if data.any?
|
|
38
|
+
puts "\n✅ Successfully seeded #{count} Google Taxons!"
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|