dugway 1.0.14 → 1.2.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 +4 -4
- data/.bundle/config +2 -0
- data/.github/workflows/main.yml +1 -1
- data/.gitignore +1 -0
- data/README.md +9 -0
- data/lib/dugway/application.rb +5 -3
- data/lib/dugway/assets/big_cartel_logo.svg +4 -0
- data/lib/dugway/cli/build.rb +18 -1
- data/lib/dugway/cli/server.rb +2 -2
- data/lib/dugway/cli/templates/source/settings.json +8 -0
- data/lib/dugway/cli/validate.rb +20 -2
- data/lib/dugway/controller.rb +5 -1
- data/lib/dugway/liquid/drops/account_drop.rb +4 -0
- data/lib/dugway/liquid/drops/features_drop.rb +144 -0
- data/lib/dugway/liquid/drops/product_drop.rb +8 -0
- data/lib/dugway/liquid/drops/products_drop.rb +1 -1
- data/lib/dugway/liquid/drops/related_products_drop.rb +88 -0
- data/lib/dugway/liquid/drops/theme_drop.rb +23 -0
- data/lib/dugway/liquid/drops/translations_drop.rb +122 -0
- data/lib/dugway/liquifier.rb +44 -8
- data/lib/dugway/store.rb +7 -2
- data/lib/dugway/theme.rb +169 -3
- data/lib/dugway/version.rb +1 -1
- data/lib/dugway.rb +31 -1
- data/locales/storefront.de.yml +79 -0
- data/locales/storefront.en-CA.yml +79 -0
- data/locales/storefront.en-GB.yml +79 -0
- data/locales/storefront.en-US.yml +79 -0
- data/locales/storefront.es-ES.yml +79 -0
- data/locales/storefront.es-MX.yml +79 -0
- data/locales/storefront.fr-CA.yml +79 -0
- data/locales/storefront.fr-FR.yml +79 -0
- data/locales/storefront.id.yml +79 -0
- data/locales/storefront.it.yml +79 -0
- data/locales/storefront.ja.yml +79 -0
- data/locales/storefront.ko.yml +79 -0
- data/locales/storefront.nl.yml +79 -0
- data/locales/storefront.pl.yml +79 -0
- data/locales/storefront.pt-BR.yml +79 -0
- data/locales/storefront.pt-PT.yml +79 -0
- data/locales/storefront.ro.yml +79 -0
- data/locales/storefront.sv.yml +79 -0
- data/locales/storefront.tr.yml +79 -0
- data/locales/storefront.zh-CN.yml +79 -0
- data/locales/storefront.zh-TW.yml +79 -0
- data/log/dugway.log +1 -0
- data/spec/features/page_rendering_spec.rb +4 -4
- data/spec/fixtures/theme/layout.html +2 -0
- data/spec/fixtures/theme/settings.json +6 -0
- data/spec/spec_helper.rb +4 -0
- data/spec/units/dugway/liquid/drops/features_drop_spec.rb +182 -0
- data/spec/units/dugway/liquid/drops/product_drop_spec.rb +36 -0
- data/spec/units/dugway/liquid/drops/related_products_drop_spec.rb +80 -0
- data/spec/units/dugway/liquid/drops/theme_drop_spec.rb +45 -0
- data/spec/units/dugway/liquid/drops/translations_drop_spec.rb +292 -0
- data/spec/units/dugway/store_spec.rb +37 -0
- data/spec/units/dugway/theme_spec.rb +456 -0
- metadata +35 -2
@@ -0,0 +1,144 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
3
|
+
module Dugway
|
4
|
+
module Drops
|
5
|
+
# Provides access to specific account feature flags within Liquid templates,
|
6
|
+
# mirroring the behavior of the storefront's FeaturesDrop but reading
|
7
|
+
# configuration from the Dugway store configuration (e.g., dugway.json).
|
8
|
+
#
|
9
|
+
# Feature flags can have a default status (enabled or disabled). This status,
|
10
|
+
# along with the list of exposed features, opt-ins, and opt-outs, should be
|
11
|
+
# defined under a top-level "features" key in the store configuration file,
|
12
|
+
# making it a peer to "store" and "customization".
|
13
|
+
#
|
14
|
+
# Example structure expected within the store configuration (e.g., dugway.json):
|
15
|
+
# {
|
16
|
+
# "store": { ... },
|
17
|
+
# "customization": { ... },
|
18
|
+
# "features": {
|
19
|
+
# "definitions": {
|
20
|
+
# "theme_bnpl_messaging": "enabled_by_default",
|
21
|
+
# "theme_category_collages": "disabled_by_default"
|
22
|
+
# },
|
23
|
+
# "opt_ins": ["some_opted_in_feature"],
|
24
|
+
# "opt_outs": ["some_opted_out_feature"]
|
25
|
+
# }
|
26
|
+
# }
|
27
|
+
# The `FeaturesDrop` receives the top-level `features` hash as its `source`
|
28
|
+
# via the ThemeDrop.
|
29
|
+
#
|
30
|
+
# Usage in Liquid:
|
31
|
+
# {% if features.has_theme_bnpl_messaging %} ... {% endif %}
|
32
|
+
# Note: Liquid might require the trailing '?' depending on context,
|
33
|
+
# but this implementation handles calls without it.
|
34
|
+
#
|
35
|
+
class FeaturesDrop < BaseDrop
|
36
|
+
# Memoized hash of feature definitions (name => default_status_string)
|
37
|
+
# read from the source (store configuration data).
|
38
|
+
# @return [Hash<String, String>]
|
39
|
+
def feature_definitions
|
40
|
+
@feature_definitions ||= source&.fetch('definitions', {}) || {}
|
41
|
+
end
|
42
|
+
|
43
|
+
# Returns a memoized list of features this account has explicitly opted *into*,
|
44
|
+
# filtered to only include features defined in the definitions.
|
45
|
+
# @return [Array<String>] List of opted-in feature names.
|
46
|
+
def opt_ins
|
47
|
+
return @opt_ins if defined?(@opt_ins)
|
48
|
+
|
49
|
+
source_opt_ins = source&.fetch('opt_ins', []) || []
|
50
|
+
# Ensure source data is treated as a Set for efficient intersection
|
51
|
+
source_set = source_opt_ins.respond_to?(:to_set) ? source_opt_ins.to_set : Set.new(Array(source_opt_ins))
|
52
|
+
|
53
|
+
# Only keep opt-ins that correspond to defined features
|
54
|
+
@opt_ins = (Set.new(feature_definitions.keys) & source_set).to_a
|
55
|
+
end
|
56
|
+
|
57
|
+
# Returns a memoized list of features this account has explicitly opted *out of*,
|
58
|
+
# filtered to only include features defined in the definitions.
|
59
|
+
# @return [Array<String>] List of opted-out feature names.
|
60
|
+
def opt_outs
|
61
|
+
return @opt_outs if defined?(@opt_outs)
|
62
|
+
|
63
|
+
source_opt_outs = source&.fetch('opt_outs', []) || []
|
64
|
+
# Ensure source data is treated as a Set for efficient intersection
|
65
|
+
source_set = source_opt_outs.respond_to?(:to_set) ? source_opt_outs.to_set : Set.new(Array(source_opt_outs))
|
66
|
+
|
67
|
+
# Only keep opt-outs that correspond to defined features
|
68
|
+
@opt_outs = (Set.new(feature_definitions.keys) & source_set).to_a
|
69
|
+
end
|
70
|
+
|
71
|
+
# Override BaseDrop's `before_method` to intercept `has_...` calls directly.
|
72
|
+
# This is called by BaseDrop's `method_missing` and allows us to intercept
|
73
|
+
# `has_...` calls before BaseDrop tries to resolve them against the source hash.
|
74
|
+
def before_method(method_or_key)
|
75
|
+
method_str = method_or_key.to_s
|
76
|
+
|
77
|
+
if method_str.start_with?('has_')
|
78
|
+
# Extract feature name, removing 'has_' prefix and optional trailing '?'
|
79
|
+
# to handle calls like `features.has_feature_name` or `features.has_feature_name?`
|
80
|
+
feature_name = method_str.sub(/^has_/, '').chomp('?')
|
81
|
+
|
82
|
+
# Check if this feature is defined
|
83
|
+
# If the extracted name doesn't match a defined feature, it's not available.
|
84
|
+
unless feature_definitions.key?(feature_name)
|
85
|
+
return false
|
86
|
+
end
|
87
|
+
|
88
|
+
# If defined, calculate availability based on defaults and overrides.
|
89
|
+
is_feature_available?(feature_name)
|
90
|
+
else
|
91
|
+
# If it's not a 'has_' method, delegate to BaseDrop's original logic.
|
92
|
+
super(method_or_key)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
# Keep liquid_method_missing for compatibility or explicit Liquid contexts,
|
97
|
+
# We keep liquid_method_missing defined as it's the official Liquid hook,
|
98
|
+
# although our `before_method` override likely handles most cases in Liquid 3.
|
99
|
+
# This provides potential compatibility if Liquid internals change or call this directly.
|
100
|
+
def liquid_method_missing(method_name)
|
101
|
+
method_str = method_name.to_s
|
102
|
+
|
103
|
+
if method_str.start_with?('has_')
|
104
|
+
feature_name = method_str.sub(/^has_/, '').chomp('?')
|
105
|
+
# Check definition and calculate availability, returning false if undefined.
|
106
|
+
feature_definitions.key?(feature_name) ? is_feature_available?(feature_name) : false
|
107
|
+
else
|
108
|
+
# Fallback for non-'has_' methods.
|
109
|
+
super
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
private
|
114
|
+
# Determines if a specific feature is available for the account based on its
|
115
|
+
# default status (from store customization) and the account's explicit opt-ins/outs
|
116
|
+
# (also from store customization).
|
117
|
+
#
|
118
|
+
# @param feature_name [String] The name of the feature to check.
|
119
|
+
# @return [Boolean] True if the feature is available, false otherwise.
|
120
|
+
def is_feature_available?(feature_name)
|
121
|
+
default_behavior_str = feature_definitions[feature_name]
|
122
|
+
# Should not happen if called via before_method/liquid_method_missing,
|
123
|
+
# but return false if the definition was somehow missing.
|
124
|
+
return false unless default_behavior_str
|
125
|
+
|
126
|
+
is_opted_in = opt_ins.include?(feature_name)
|
127
|
+
is_opted_out = opt_outs.include?(feature_name)
|
128
|
+
|
129
|
+
# Determine final availability:
|
130
|
+
# - If explicitly opted in, it's available.
|
131
|
+
# - If explicitly opted out, it's unavailable.
|
132
|
+
# - Otherwise, availability depends on the defined default behavior string.
|
133
|
+
# Precedence: Opt-out > Opt-in > Default
|
134
|
+
if is_opted_out
|
135
|
+
false
|
136
|
+
elsif is_opted_in
|
137
|
+
true
|
138
|
+
else
|
139
|
+
default_behavior_str == 'enabled_by_default'
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
@@ -104,6 +104,14 @@ module Dugway
|
|
104
104
|
end
|
105
105
|
end
|
106
106
|
|
107
|
+
def related_products
|
108
|
+
@related_products ||= begin
|
109
|
+
drop = RelatedProductsDrop.new(source)
|
110
|
+
drop.context = @context if @context
|
111
|
+
drop.products
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
107
115
|
private
|
108
116
|
|
109
117
|
def price_min_max
|
@@ -0,0 +1,88 @@
|
|
1
|
+
module Dugway
|
2
|
+
module Drops
|
3
|
+
class RelatedProductsDrop < BaseDrop
|
4
|
+
def initialize(product)
|
5
|
+
super()
|
6
|
+
@product = product
|
7
|
+
end
|
8
|
+
|
9
|
+
def products
|
10
|
+
fetch_related_products
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def settings
|
16
|
+
@settings ||= theme.customization
|
17
|
+
end
|
18
|
+
|
19
|
+
def limit
|
20
|
+
@limit ||= begin
|
21
|
+
if settings
|
22
|
+
limit = settings['similar_products'] ||
|
23
|
+
settings['related_items'] ||
|
24
|
+
settings['related_products'] ||
|
25
|
+
settings['number_related_products'] ||
|
26
|
+
4
|
27
|
+
limit
|
28
|
+
else
|
29
|
+
4
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def sort_order
|
35
|
+
@sort_order ||= begin
|
36
|
+
if settings
|
37
|
+
order = settings['related_products_order'] ||
|
38
|
+
settings['similar_products_order'] ||
|
39
|
+
"position"
|
40
|
+
order
|
41
|
+
else
|
42
|
+
"position"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def fetch_related_products
|
48
|
+
return [] unless @product
|
49
|
+
|
50
|
+
category_products = sort_products(fetch_category_products).take(limit)
|
51
|
+
return category_products if category_products.size >= limit
|
52
|
+
|
53
|
+
remaining_limit = limit - category_products.size
|
54
|
+
fallback_products = sort_products(fetch_fallback_products(category_products, remaining_limit)).take(remaining_limit)
|
55
|
+
|
56
|
+
category_products + fallback_products
|
57
|
+
end
|
58
|
+
|
59
|
+
def fetch_category_products
|
60
|
+
# Filter Dugway's product data to match the categories of the current product
|
61
|
+
Dugway.store.products.select do |product|
|
62
|
+
product_cats = product['category_ids'] || []
|
63
|
+
current_cats = @product['category_ids'] || []
|
64
|
+
(product_cats & current_cats).any? && product['id'] != @product['id']
|
65
|
+
end.map { |p| ProductDrop.new(p) }
|
66
|
+
end
|
67
|
+
|
68
|
+
def fetch_fallback_products(category_products, limit)
|
69
|
+
# Get additional products excluding already included ones
|
70
|
+
excluded_ids = category_products.map { |p| p['id'] } + [@product['id']]
|
71
|
+
Dugway.store.products
|
72
|
+
.reject { |product| excluded_ids.include?(product['id']) }
|
73
|
+
.map { |p| ProductDrop.new(p) }
|
74
|
+
end
|
75
|
+
|
76
|
+
def sort_products(products)
|
77
|
+
case sort_order
|
78
|
+
when 'date', 'newest'
|
79
|
+
products.sort { |a,b| b.source['id'] <=> a.source['id'] }
|
80
|
+
when 'sales', 'sells', 'top-selling', 'views'
|
81
|
+
products.shuffle
|
82
|
+
else
|
83
|
+
products.sort_by { |p| p.source['position'] }
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -1,6 +1,13 @@
|
|
1
1
|
module Dugway
|
2
2
|
module Drops
|
3
3
|
class ThemeDrop < BaseDrop
|
4
|
+
# @param customization [Hash] Theme customization values.
|
5
|
+
# @param definitions [Hash] Theme setting definitions (from settings.json).
|
6
|
+
def initialize(customization, definitions = {})
|
7
|
+
super(customization)
|
8
|
+
@definitions = definitions || {}
|
9
|
+
end
|
10
|
+
|
4
11
|
def before_method(method_or_key)
|
5
12
|
# We should try to get away from this api and use the newer one below
|
6
13
|
if source.respond_to?('has_key?') && source.has_key?(method_or_key) && settings_images.find { |image| image['variable'] == method_or_key.to_s }
|
@@ -20,6 +27,22 @@ module Dugway
|
|
20
27
|
Drops::ThemeImageSetsDrop.new(source)
|
21
28
|
end
|
22
29
|
|
30
|
+
def features
|
31
|
+
# Fetch features config from the top-level Dugway options hash,
|
32
|
+
# which should contain the parsed store configuration (e.g., dugway.json).
|
33
|
+
# This allows 'features' to be a peer to 'store' and 'customization'.
|
34
|
+
feature_data = Dugway.options&.fetch('features', {}) || {}
|
35
|
+
Drops::FeaturesDrop.new(feature_data)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Provides access to theme translations via theme.translations
|
39
|
+
def translations
|
40
|
+
# Instantiate and return the TranslationsDrop, passing settings.
|
41
|
+
# Memoize the instance for efficiency within a single render cycle.
|
42
|
+
# Pass both customization (source) and definitions to TranslationsDrop.
|
43
|
+
@translations_drop ||= Drops::TranslationsDrop.new(source, @definitions)
|
44
|
+
end
|
45
|
+
|
23
46
|
private
|
24
47
|
def settings_images
|
25
48
|
@settings_images ||= settings.has_key?('images') ? settings['images'] : []
|
@@ -0,0 +1,122 @@
|
|
1
|
+
require 'i18n'
|
2
|
+
|
3
|
+
module Dugway
|
4
|
+
module Drops
|
5
|
+
class TranslationsDrop < BaseDrop
|
6
|
+
class MissingTranslationError < StandardError; end
|
7
|
+
|
8
|
+
# @param settings [Hash] Theme customization values (resolved settings).
|
9
|
+
# @param definitions [Hash] Raw theme setting definitions (from settings.json).
|
10
|
+
def initialize(settings, definitions = {})
|
11
|
+
@settings = settings || {}
|
12
|
+
@definitions = definitions || {} # Store definitions
|
13
|
+
# Read settings using both symbol and string keys for flexibility.
|
14
|
+
@translation_mode = (@settings[:translation_mode] || @settings['translation_mode'])&.to_s
|
15
|
+
# Use explicit theme setting for locale if present, otherwise default to current I18n.locale
|
16
|
+
@translation_locale = (@settings[:translation_locale] || @settings['translation_locale'])&.to_s.presence || I18n.locale.to_s
|
17
|
+
super()
|
18
|
+
end
|
19
|
+
|
20
|
+
# Liquid access: `{{ translations['key.name'] }}`
|
21
|
+
# Returns the translation string (which could be an empty string),
|
22
|
+
# or a "[MISSING TRANSLATION: ...]" string if the key is not found or its value is nil.
|
23
|
+
#
|
24
|
+
# @param key [String, Symbol] The translation key.
|
25
|
+
# @return [String, nil] The translation or missing translation indicator.
|
26
|
+
def [](key)
|
27
|
+
key_str = key.to_s
|
28
|
+
translation = find_translation(key_str)
|
29
|
+
|
30
|
+
if translation.nil?
|
31
|
+
# Key is missing, or explicitly nil in manual mode - treat both as missing.
|
32
|
+
log_missing_translation(key_str)
|
33
|
+
return missing_translation_message(key_str)
|
34
|
+
end
|
35
|
+
|
36
|
+
# Key found, return its value.
|
37
|
+
translation
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def manual_mode?
|
43
|
+
@translation_mode == 'manual'
|
44
|
+
end
|
45
|
+
|
46
|
+
# Looks up translation using I18n.
|
47
|
+
# Returns nil if the key is not found or the locale is invalid.
|
48
|
+
def lookup_automatic_translation(key)
|
49
|
+
locale_to_use = @translation_locale
|
50
|
+
# `default: nil` prevents I18n from raising an error on missing keys.
|
51
|
+
I18n.t("storefront.#{key}", locale: locale_to_use, default: nil)
|
52
|
+
rescue I18n::InvalidLocale
|
53
|
+
nil # Treat invalid locale as a missing translation.
|
54
|
+
end
|
55
|
+
|
56
|
+
# Converts a canonical key ('a.b.c') to the theme setting key format (:a_b_c_tr_text).
|
57
|
+
def derive_manual_setting_key(key)
|
58
|
+
"#{key.tr('.', '_')}_tr_text".to_sym
|
59
|
+
end
|
60
|
+
|
61
|
+
# Finds the translation based on the current mode.
|
62
|
+
# In manual mode, respects the 'allow_blank' setting definition.
|
63
|
+
# Returns the translation string, or nil if not found or disallowed blank.
|
64
|
+
def find_translation(key_str)
|
65
|
+
if manual_mode?
|
66
|
+
manual_setting_key_sym = derive_manual_setting_key(key_str)
|
67
|
+
manual_setting_key_str = manual_setting_key_sym.to_s
|
68
|
+
custom_value = nil
|
69
|
+
setting_exists = false
|
70
|
+
|
71
|
+
# Check both symbol and string keys for manual overrides.
|
72
|
+
if @settings.key?(manual_setting_key_sym)
|
73
|
+
custom_value = @settings[manual_setting_key_sym]
|
74
|
+
setting_exists = true
|
75
|
+
elsif @settings.key?(manual_setting_key_str)
|
76
|
+
custom_value = @settings[manual_setting_key_str]
|
77
|
+
setting_exists = true
|
78
|
+
end
|
79
|
+
|
80
|
+
if setting_exists
|
81
|
+
# Find the corresponding definition to check allow_blank
|
82
|
+
# Assuming manual translations are defined similarly to 'options'
|
83
|
+
definition = find_setting_definition(manual_setting_key_str) || {}
|
84
|
+
allow_blank = definition['allow_blank'].to_s == 'true'
|
85
|
+
|
86
|
+
# Return custom value if it's present, or if it's blank but allowed
|
87
|
+
return custom_value if custom_value.present? || (allow_blank && custom_value == '')
|
88
|
+
|
89
|
+
# If we reach here, the value is blank but blank is not allowed.
|
90
|
+
# Fall through to automatic lookup.
|
91
|
+
end
|
92
|
+
|
93
|
+
# If setting key didn't exist, fall through to automatic lookup.
|
94
|
+
|
95
|
+
end # End manual_mode? check
|
96
|
+
|
97
|
+
# Automatic mode or fallback from manual mode
|
98
|
+
lookup_automatic_translation(key_str)
|
99
|
+
end
|
100
|
+
|
101
|
+
# Helper to find a setting definition by its variable name across common sections.
|
102
|
+
def find_setting_definition(variable_name)
|
103
|
+
# Look primarily in 'options' as manual translations often mimic them.
|
104
|
+
# Add other potential sections if needed in the future.
|
105
|
+
(@definitions['options'] || []).find { |d| d['variable'] == variable_name }
|
106
|
+
end
|
107
|
+
|
108
|
+
# Logs a warning message for a missing translation.
|
109
|
+
def log_missing_translation(key_str)
|
110
|
+
mode = manual_mode? ? 'manual' : 'automatic'
|
111
|
+
locale_info = manual_mode? ? '' : " locale='#{@translation_locale}'"
|
112
|
+
message = "Missing translation: key='#{key_str}'#{locale_info} mode='#{mode}'"
|
113
|
+
Dugway.logger.warn(message)
|
114
|
+
end
|
115
|
+
|
116
|
+
# Returns the standard string indicating a missing translation.
|
117
|
+
def missing_translation_message(key_str)
|
118
|
+
"[MISSING TRANSLATION: #{key_str}]"
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
data/lib/dugway/liquifier.rb
CHANGED
@@ -16,6 +16,7 @@ module Dugway
|
|
16
16
|
class Liquifier
|
17
17
|
def initialize(request)
|
18
18
|
@request = request
|
19
|
+
load_theme_locales
|
19
20
|
end
|
20
21
|
|
21
22
|
def render(content, variables={})
|
@@ -41,7 +42,7 @@ module Dugway
|
|
41
42
|
|
42
43
|
def self.render_styles(css)
|
43
44
|
Liquid::Template.parse(css).render!(
|
44
|
-
{ 'theme' => Drops::ThemeDrop.new(Dugway.theme.customization) },
|
45
|
+
{ 'theme' => Drops::ThemeDrop.new(Dugway.theme.customization, Dugway.theme.settings) },
|
45
46
|
:registers => { :settings => Dugway.theme.settings }
|
46
47
|
)
|
47
48
|
end
|
@@ -68,7 +69,8 @@ module Dugway
|
|
68
69
|
{
|
69
70
|
'store' => Drops::AccountDrop.new(store.account),
|
70
71
|
'cart' => Drops::CartDrop.new(cart),
|
71
|
-
|
72
|
+
# Pass both customization and definitions to ThemeDrop
|
73
|
+
'theme' => Drops::ThemeDrop.new(theme.customization, theme.settings),
|
72
74
|
'pages' => Drops::PagesDrop.new(store.pages.map { |p| Drops::PageDrop.new(p) }),
|
73
75
|
'categories' => Drops::CategoriesDrop.new(store.categories.map { |c| Drops::CategoryDrop.new(c) }),
|
74
76
|
'artists' => Drops::ArtistsDrop.new(store.artists.map { |a| Drops::ArtistDrop.new(a) }),
|
@@ -77,6 +79,10 @@ module Dugway
|
|
77
79
|
'head_content' => [window_bigcartel_script, head_content].join,
|
78
80
|
'bigcartel_credit' => bigcartel_credit,
|
79
81
|
'powered_by_big_cartel' => powered_by_big_cartel,
|
82
|
+
'big_cartel_credit_logo' => big_cartel_credit_logo,
|
83
|
+
# Pass both customization and definitions to TranslationsDrop
|
84
|
+
'translations' => Drops::TranslationsDrop.new(theme.customization, theme.settings),
|
85
|
+
't' => Drops::TranslationsDrop.new(theme.customization, theme.settings),
|
80
86
|
}
|
81
87
|
end
|
82
88
|
|
@@ -109,17 +115,47 @@ module Dugway
|
|
109
115
|
"<script>#{script}</script>"
|
110
116
|
end
|
111
117
|
|
118
|
+
def read_svg_asset(filename)
|
119
|
+
File.read(File.join(File.dirname(__FILE__), 'assets', filename))
|
120
|
+
end
|
121
|
+
|
112
122
|
def bigcartel_credit
|
113
123
|
'<a href="http://bigcartel.com/" title="Start your own store at Big Cartel now">Online Store by Big Cartel</a>'
|
114
124
|
end
|
115
125
|
|
116
126
|
def powered_by_big_cartel
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
127
|
+
build_powered_by_link(include_text: true)
|
128
|
+
end
|
129
|
+
|
130
|
+
def big_cartel_credit_logo
|
131
|
+
build_powered_by_link(include_text: false)
|
132
|
+
end
|
133
|
+
|
134
|
+
def build_powered_by_link(include_text: true)
|
135
|
+
svg_content = read_svg_asset('big_cartel_logo.svg')
|
136
|
+
|
137
|
+
text_span = include_text ? ' <span class="bigcartel-credit__text" aria-hidden="true">Powered by</span>' : ''
|
138
|
+
<<~HTML
|
139
|
+
<a class="bigcartel-credit" href="https://www.bigcartel.com/?utm_source=bigcartel&utm_medium=storefront&utm_campaign=123}" title="Powered by Big Cartel" data-bc-hook="attribution">
|
140
|
+
#{text_span}
|
141
|
+
#{svg_content}
|
142
|
+
</a>
|
143
|
+
HTML
|
144
|
+
end
|
145
|
+
|
146
|
+
# Load theme-specific override locales
|
147
|
+
def load_theme_locales
|
148
|
+
return unless Dugway.source_dir && Dir.exist?(File.dirname(Dugway.source_dir))
|
149
|
+
|
150
|
+
parent_dir = File.dirname(Dugway.source_dir)
|
151
|
+
theme_locales_dir = File.join(parent_dir, 'locales')
|
152
|
+
|
153
|
+
if Dir.exist?(theme_locales_dir)
|
154
|
+
theme_locales_path = File.expand_path(theme_locales_dir)
|
155
|
+
# Ensure theme locales are loaded *after* defaults to allow overrides
|
156
|
+
I18n.load_path |= Dir[File.join(theme_locales_path, 'storefront.*.yml')]
|
157
|
+
I18n.backend.reload!
|
158
|
+
end
|
123
159
|
end
|
124
160
|
end
|
125
161
|
end
|
data/lib/dugway/store.rb
CHANGED
@@ -8,8 +8,9 @@ module Dugway
|
|
8
8
|
default_timeout 5
|
9
9
|
headers 'User-Agent' => "Dugway #{ Dugway::VERSION }"
|
10
10
|
|
11
|
-
def initialize(subdomain)
|
11
|
+
def initialize(subdomain, store_options = {})
|
12
12
|
self.class.base_uri "https://api.bigcartel.com/#{ subdomain }"
|
13
|
+
@store_options = store_options || {}
|
13
14
|
end
|
14
15
|
|
15
16
|
def account
|
@@ -124,7 +125,11 @@ module Dugway
|
|
124
125
|
end
|
125
126
|
|
126
127
|
def locale
|
127
|
-
currency['locale']
|
128
|
+
@store_options[:locale] || currency['locale']
|
129
|
+
end
|
130
|
+
|
131
|
+
def website
|
132
|
+
@store_options[:website] || account['website']
|
128
133
|
end
|
129
134
|
|
130
135
|
def instant_checkout?
|