spree_core 5.2.0 → 5.2.1
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/app/models/spree/ability.rb +75 -10
- data/app/models/spree/permission_sets/base.rb +81 -0
- data/app/models/spree/permission_sets/configuration_management.rb +47 -0
- data/app/models/spree/permission_sets/dashboard_display.rb +17 -0
- data/app/models/spree/permission_sets/default_customer.rb +66 -0
- data/app/models/spree/permission_sets/order_display.rb +27 -0
- data/app/models/spree/permission_sets/order_management.rb +33 -0
- data/app/models/spree/permission_sets/product_display.rb +27 -0
- data/app/models/spree/permission_sets/product_management.rb +27 -0
- data/app/models/spree/permission_sets/promotion_management.rb +22 -0
- data/app/models/spree/permission_sets/role_management.rb +21 -0
- data/app/models/spree/permission_sets/stock_display.rb +19 -0
- data/app/models/spree/permission_sets/stock_management.rb +19 -0
- data/app/models/spree/permission_sets/super_user.rb +28 -0
- data/app/models/spree/permission_sets/user_display.rb +19 -0
- data/app/models/spree/permission_sets/user_management.rb +20 -0
- data/config/locales/en.yml +1 -0
- data/lib/friendly_id/history_decorator.rb +26 -0
- data/lib/generators/spree/install/templates/config/initializers/spree.rb +25 -0
- data/lib/spree/core/permission_configuration.rb +102 -0
- data/lib/spree/core/version.rb +1 -1
- data/lib/spree/core.rb +17 -0
- data/lib/spree_core.rb +1 -0
- metadata +24 -7
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 77f4a7f3643f4b9263ab029e06d7870f5f45397e295f4116a485bc82a280d15f
|
|
4
|
+
data.tar.gz: 482208c5fd5725818a539a4daa4d566bd4c57c1c82595166e6232c91c56b876f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6424ad89c3f5a35cade4d6fc2dc67a74bbf1e0fab459727630c49582f5f94581924b16fba3ce54844d9fddac8cf2f2c2e442c4937f00db98d4276f1e5fb45649
|
|
7
|
+
data.tar.gz: 5dbaa89fd0648e3b35d5f4d7d48ee1066d3bf2da71cae97ee2c9769600380c95054f0aee146f845d4c92910f9b1944bc52155b98508345482ba3bb47224c689f
|
data/app/models/spree/ability.rb
CHANGED
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
# Implementation class for Cancan gem. Instead of overriding this class, consider adding new permissions
|
|
2
2
|
# using the special +register_ability+ method which allows extensions to add their own abilities.
|
|
3
3
|
#
|
|
4
|
+
# The preferred way to add permissions is now through permission sets. See Spree::PermissionSets::Base
|
|
5
|
+
# for more details on creating custom permission sets.
|
|
6
|
+
#
|
|
7
|
+
# @example Configuring role permissions
|
|
8
|
+
# Spree.permissions.assign(:customer_service, [
|
|
9
|
+
# Spree::PermissionSets::OrderDisplay,
|
|
10
|
+
# Spree::PermissionSets::UserManagement
|
|
11
|
+
# ])
|
|
12
|
+
#
|
|
4
13
|
# See https://github.com/CanCanCommunity/cancancan for more details.
|
|
5
14
|
require 'cancan'
|
|
6
15
|
|
|
@@ -11,6 +20,12 @@ module Spree
|
|
|
11
20
|
class_attribute :abilities
|
|
12
21
|
self.abilities = Set.new
|
|
13
22
|
|
|
23
|
+
# @return [Object] the current user
|
|
24
|
+
attr_reader :user
|
|
25
|
+
|
|
26
|
+
# @return [Spree::Store, nil] the current store
|
|
27
|
+
attr_reader :store
|
|
28
|
+
|
|
14
29
|
# Allows us to go beyond the standard cancan initialize method which makes it difficult for engines to
|
|
15
30
|
# modify the default +Ability+ of an application. The +ability+ argument must be a class that includes
|
|
16
31
|
# the +CanCan::Ability+ module. The registered ability should behave properly as a stand-alone class
|
|
@@ -26,22 +41,16 @@ module Spree
|
|
|
26
41
|
def initialize(user, options = {})
|
|
27
42
|
alias_cancan_delete_action
|
|
28
43
|
|
|
29
|
-
user
|
|
30
|
-
store
|
|
44
|
+
@user = user || Spree.user_class.new
|
|
45
|
+
@store = options[:store] || Spree::Current.store
|
|
31
46
|
|
|
32
|
-
|
|
33
|
-
apply_admin_permissions(user, options)
|
|
34
|
-
else
|
|
35
|
-
apply_user_permissions(user, options)
|
|
36
|
-
end
|
|
47
|
+
apply_permissions_from_sets
|
|
37
48
|
|
|
38
49
|
# Include any abilities registered by extensions, etc.
|
|
39
50
|
# this is legacy behaviour and should be removed in Spree 5.0
|
|
40
51
|
Ability.abilities.merge(abilities_to_register).each do |clazz|
|
|
41
|
-
merge clazz.new(user)
|
|
52
|
+
merge clazz.new(@user)
|
|
42
53
|
end
|
|
43
|
-
|
|
44
|
-
protect_admin_role
|
|
45
54
|
end
|
|
46
55
|
|
|
47
56
|
protected
|
|
@@ -57,6 +66,62 @@ module Spree
|
|
|
57
66
|
alias_action :create, :update, :destroy, to: :modify
|
|
58
67
|
end
|
|
59
68
|
|
|
69
|
+
# Applies permissions based on the user's roles and the configured permission sets.
|
|
70
|
+
def apply_permissions_from_sets
|
|
71
|
+
role_names = determine_role_names
|
|
72
|
+
permission_sets = Spree.permissions.permission_sets_for_roles(role_names)
|
|
73
|
+
|
|
74
|
+
# If no permission sets are configured for the user's roles, use legacy behavior
|
|
75
|
+
if permission_sets.empty?
|
|
76
|
+
apply_legacy_permissions
|
|
77
|
+
else
|
|
78
|
+
activate_permission_sets(permission_sets)
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Determines the role names for the current user.
|
|
83
|
+
#
|
|
84
|
+
# @return [Array<Symbol>] the role names
|
|
85
|
+
def determine_role_names
|
|
86
|
+
return [:default] unless @user.persisted?
|
|
87
|
+
|
|
88
|
+
# First, try to get roles from the spree_roles association
|
|
89
|
+
if @user.respond_to?(:spree_roles)
|
|
90
|
+
role_names = @user.spree_roles.pluck(:name).map(&:to_sym)
|
|
91
|
+
return role_names if role_names.any?
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Fall back to checking spree_admin? for backward compatibility
|
|
95
|
+
# This supports cases where roles are mocked or admin status is determined differently
|
|
96
|
+
if @user.try(:spree_admin?, @store)
|
|
97
|
+
[:admin]
|
|
98
|
+
else
|
|
99
|
+
[:default]
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# Activates the given permission sets.
|
|
104
|
+
#
|
|
105
|
+
# @param permission_sets [Array<Class>] the permission set classes to activate
|
|
106
|
+
def activate_permission_sets(permission_sets)
|
|
107
|
+
permission_sets.each do |permission_set_class|
|
|
108
|
+
permission_set = permission_set_class.new(self)
|
|
109
|
+
permission_set.activate!
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# Legacy permission application for backward compatibility.
|
|
114
|
+
# This is used when no permission sets are configured for the user's roles.
|
|
115
|
+
def apply_legacy_permissions
|
|
116
|
+
if @user.persisted? && @user.is_a?(Spree.admin_user_class) && @user.try(:spree_admin?, @store)
|
|
117
|
+
apply_admin_permissions(@user, { store: @store })
|
|
118
|
+
else
|
|
119
|
+
apply_user_permissions(@user, { store: @store })
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
protect_admin_role
|
|
123
|
+
end
|
|
124
|
+
|
|
60
125
|
def apply_admin_permissions(_user, _options)
|
|
61
126
|
can :manage, :all
|
|
62
127
|
cannot :cancel, Spree::Order
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# Base class for all permission sets.
|
|
2
|
+
#
|
|
3
|
+
# Permission sets are reusable groups of permissions that can be assigned to roles.
|
|
4
|
+
# They provide a clean abstraction over CanCanCan abilities, making it easier to
|
|
5
|
+
# manage permissions in a modular way.
|
|
6
|
+
#
|
|
7
|
+
# @example Creating a custom permission set
|
|
8
|
+
# class Spree::PermissionSets::InventoryManagement < Spree::PermissionSets::Base
|
|
9
|
+
# def activate!
|
|
10
|
+
# can :manage, Spree::StockItem
|
|
11
|
+
# can :manage, Spree::StockLocation
|
|
12
|
+
# can :manage, Spree::StockMovement
|
|
13
|
+
# end
|
|
14
|
+
# end
|
|
15
|
+
#
|
|
16
|
+
# @example Assigning the permission set to a role
|
|
17
|
+
# Spree.permissions.assign(:warehouse_manager, Spree::PermissionSets::InventoryManagement)
|
|
18
|
+
#
|
|
19
|
+
module Spree
|
|
20
|
+
module PermissionSets
|
|
21
|
+
class Base
|
|
22
|
+
# @return [CanCan::Ability] the ability instance to add permissions to
|
|
23
|
+
attr_reader :ability
|
|
24
|
+
|
|
25
|
+
# @param ability [CanCan::Ability] the ability instance to add permissions to
|
|
26
|
+
def initialize(ability)
|
|
27
|
+
@ability = ability
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Activates this permission set by adding its permissions to the ability.
|
|
31
|
+
# Override this method in subclasses to define the permissions.
|
|
32
|
+
#
|
|
33
|
+
# @abstract
|
|
34
|
+
# @return [void]
|
|
35
|
+
def activate!
|
|
36
|
+
raise NotImplementedError, "#{self.class} must implement #activate!"
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
protected
|
|
40
|
+
|
|
41
|
+
# Delegates the `can` method to the ability instance.
|
|
42
|
+
#
|
|
43
|
+
# @param args [Array] arguments to pass to CanCan::Ability#can
|
|
44
|
+
# @param block [Proc] optional block for conditional permissions
|
|
45
|
+
def can(*args, &block)
|
|
46
|
+
ability.can(*args, &block)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Delegates the `cannot` method to the ability instance.
|
|
50
|
+
#
|
|
51
|
+
# @param args [Array] arguments to pass to CanCan::Ability#cannot
|
|
52
|
+
# @param block [Proc] optional block for conditional permissions
|
|
53
|
+
def cannot(*args, &block)
|
|
54
|
+
ability.cannot(*args, &block)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Delegates the `can?` method to the ability instance.
|
|
58
|
+
#
|
|
59
|
+
# @param args [Array] arguments to pass to CanCan::Ability#can?
|
|
60
|
+
# @return [Boolean]
|
|
61
|
+
def can?(*args)
|
|
62
|
+
ability.can?(*args)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Returns the user from the ability instance.
|
|
66
|
+
# This method assumes the ability has a user accessor.
|
|
67
|
+
#
|
|
68
|
+
# @return [Object] the current user
|
|
69
|
+
def user
|
|
70
|
+
ability.user
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Returns the store from the ability instance options.
|
|
74
|
+
#
|
|
75
|
+
# @return [Spree::Store, nil] the current store
|
|
76
|
+
def store
|
|
77
|
+
ability.store
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Permission set for managing store configuration and settings.
|
|
2
|
+
#
|
|
3
|
+
# This permission set provides access to manage store settings,
|
|
4
|
+
# payment methods, shipping methods, and other configuration.
|
|
5
|
+
#
|
|
6
|
+
# @example
|
|
7
|
+
# Spree.permissions.assign(:store_admin, Spree::PermissionSets::ConfigurationManagement)
|
|
8
|
+
#
|
|
9
|
+
module Spree
|
|
10
|
+
module PermissionSets
|
|
11
|
+
class ConfigurationManagement < Base
|
|
12
|
+
def activate!
|
|
13
|
+
# Store settings
|
|
14
|
+
can :manage, Spree::Store
|
|
15
|
+
|
|
16
|
+
# Payment configuration
|
|
17
|
+
can :manage, Spree::PaymentMethod
|
|
18
|
+
can :manage, Spree::Gateway
|
|
19
|
+
|
|
20
|
+
# Shipping configuration
|
|
21
|
+
can :manage, Spree::ShippingMethod
|
|
22
|
+
can :manage, Spree::ShippingCategory
|
|
23
|
+
can :manage, Spree::Zone
|
|
24
|
+
can :manage, Spree::ZoneMember
|
|
25
|
+
|
|
26
|
+
# Tax configuration
|
|
27
|
+
can :manage, Spree::TaxCategory
|
|
28
|
+
can :manage, Spree::TaxRate
|
|
29
|
+
|
|
30
|
+
# General configuration
|
|
31
|
+
can :manage, Spree::RefundReason
|
|
32
|
+
can :manage, Spree::ReimbursementType
|
|
33
|
+
can :manage, Spree::ReturnReason
|
|
34
|
+
|
|
35
|
+
# Restrictions on immutable types
|
|
36
|
+
cannot [:edit, :update], Spree::RefundReason, mutable: false
|
|
37
|
+
cannot [:edit, :update], Spree::ReimbursementType, mutable: false
|
|
38
|
+
|
|
39
|
+
# Metafield configuration
|
|
40
|
+
can :manage, Spree::MetafieldDefinition
|
|
41
|
+
|
|
42
|
+
# Policies
|
|
43
|
+
can :manage, Spree::Policy
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Permission set for viewing the admin dashboard.
|
|
2
|
+
#
|
|
3
|
+
# This permission set provides access to view the admin dashboard
|
|
4
|
+
# and basic admin navigation.
|
|
5
|
+
#
|
|
6
|
+
# @example
|
|
7
|
+
# Spree.permissions.assign(:viewer, Spree::PermissionSets::DashboardDisplay)
|
|
8
|
+
#
|
|
9
|
+
module Spree
|
|
10
|
+
module PermissionSets
|
|
11
|
+
class DashboardDisplay < Base
|
|
12
|
+
def activate!
|
|
13
|
+
can [:admin, :index, :show], :dashboard
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# Permission set for default storefront customers (both authenticated and guests).
|
|
2
|
+
#
|
|
3
|
+
# This permission set provides the standard permissions needed for browsing
|
|
4
|
+
# the store and making purchases.
|
|
5
|
+
#
|
|
6
|
+
# @example
|
|
7
|
+
# Spree.permissions.assign(:default, Spree::PermissionSets::DefaultCustomer)
|
|
8
|
+
#
|
|
9
|
+
module Spree
|
|
10
|
+
module PermissionSets
|
|
11
|
+
class DefaultCustomer < Base
|
|
12
|
+
def activate!
|
|
13
|
+
# Read-only access to catalog
|
|
14
|
+
can :read, Spree::Country
|
|
15
|
+
can :read, Spree::OptionType
|
|
16
|
+
can :read, Spree::OptionValue
|
|
17
|
+
can :read, Spree::Product
|
|
18
|
+
can :read, Spree::ProductProperty
|
|
19
|
+
can :read, Spree::Property
|
|
20
|
+
can :read, Spree::State
|
|
21
|
+
can :read, Spree::Store
|
|
22
|
+
can :read, Spree::Taxon
|
|
23
|
+
can :read, Spree::Taxonomy
|
|
24
|
+
can :read, Spree::Variant
|
|
25
|
+
can :read, Spree::Zone
|
|
26
|
+
|
|
27
|
+
# Content pages
|
|
28
|
+
can :read, Spree::Policy
|
|
29
|
+
can :read, Spree::Page
|
|
30
|
+
can :read, Spree::Post
|
|
31
|
+
can :read, Spree::PostCategory
|
|
32
|
+
|
|
33
|
+
# Order management for the user's own orders
|
|
34
|
+
can :create, Spree::Order
|
|
35
|
+
can :show, Spree::Order do |order, token|
|
|
36
|
+
order.user == user || order.token && token == order.token
|
|
37
|
+
end
|
|
38
|
+
can :update, Spree::Order do |order, token|
|
|
39
|
+
!order.completed? && (order.user == user || order.token && token == order.token)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# User account management - available to all users (including guests for their own record)
|
|
43
|
+
can :create, Spree.user_class
|
|
44
|
+
can [:show, :update, :destroy], Spree.user_class, id: user.id
|
|
45
|
+
|
|
46
|
+
# Address management
|
|
47
|
+
can :manage, Spree::Address, user_id: user.id
|
|
48
|
+
|
|
49
|
+
# Credit card management
|
|
50
|
+
can [:read, :destroy], Spree::CreditCard, user_id: user.id
|
|
51
|
+
|
|
52
|
+
# Wishlist management
|
|
53
|
+
can :manage, Spree::Wishlist, user_id: user.id
|
|
54
|
+
can :show, Spree::Wishlist do |wishlist|
|
|
55
|
+
wishlist.user == user || wishlist.is_private == false
|
|
56
|
+
end
|
|
57
|
+
can [:create, :update, :destroy], Spree::WishedItem do |wished_item|
|
|
58
|
+
wished_item.wishlist.user == user
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Invitation acceptance
|
|
62
|
+
can :accept, Spree::Invitation, invitee_id: [user.id, nil], invitee_type: user.class.name, status: 'pending'
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Permission set for viewing orders and related resources.
|
|
2
|
+
#
|
|
3
|
+
# This permission set provides read-only access to orders and associated
|
|
4
|
+
# models like payments, shipments, and refunds.
|
|
5
|
+
#
|
|
6
|
+
# @example
|
|
7
|
+
# Spree.permissions.assign(:customer_service, Spree::PermissionSets::OrderDisplay)
|
|
8
|
+
#
|
|
9
|
+
module Spree
|
|
10
|
+
module PermissionSets
|
|
11
|
+
class OrderDisplay < Base
|
|
12
|
+
def activate!
|
|
13
|
+
can [:read, :admin, :index], Spree::Order
|
|
14
|
+
can [:read, :admin], Spree::Payment
|
|
15
|
+
can [:read, :admin], Spree::Shipment
|
|
16
|
+
can [:read, :admin], Spree::Adjustment
|
|
17
|
+
can [:read, :admin], Spree::LineItem
|
|
18
|
+
can [:read, :admin], Spree::ReturnAuthorization
|
|
19
|
+
can [:read, :admin], Spree::CustomerReturn
|
|
20
|
+
can [:read, :admin], Spree::Reimbursement
|
|
21
|
+
can [:read, :admin], Spree::Refund
|
|
22
|
+
can [:read, :admin], Spree::StoreCredit
|
|
23
|
+
can [:read, :admin], Spree::GiftCard
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Permission set for full order management.
|
|
2
|
+
#
|
|
3
|
+
# This permission set provides complete access to manage orders,
|
|
4
|
+
# including creating, updating, and processing payments and shipments.
|
|
5
|
+
#
|
|
6
|
+
# @example
|
|
7
|
+
# Spree.permissions.assign(:order_manager, Spree::PermissionSets::OrderManagement)
|
|
8
|
+
#
|
|
9
|
+
module Spree
|
|
10
|
+
module PermissionSets
|
|
11
|
+
class OrderManagement < Base
|
|
12
|
+
def activate!
|
|
13
|
+
can :manage, Spree::Order
|
|
14
|
+
can :manage, Spree::Payment
|
|
15
|
+
can :manage, Spree::Shipment
|
|
16
|
+
can :manage, Spree::Adjustment
|
|
17
|
+
can :manage, Spree::LineItem
|
|
18
|
+
can :manage, Spree::ReturnAuthorization
|
|
19
|
+
can :manage, Spree::CustomerReturn
|
|
20
|
+
can :manage, Spree::Reimbursement
|
|
21
|
+
can :manage, Spree::Refund
|
|
22
|
+
can :manage, Spree::StoreCredit
|
|
23
|
+
can :manage, Spree::GiftCard
|
|
24
|
+
|
|
25
|
+
# Order-specific restrictions
|
|
26
|
+
cannot :cancel, Spree::Order
|
|
27
|
+
can :cancel, Spree::Order, &:allow_cancel?
|
|
28
|
+
cannot :destroy, Spree::Order
|
|
29
|
+
can :destroy, Spree::Order, &:can_be_deleted?
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Permission set for viewing products and catalog information.
|
|
2
|
+
#
|
|
3
|
+
# This permission set provides read-only access to products, variants,
|
|
4
|
+
# and related catalog models.
|
|
5
|
+
#
|
|
6
|
+
# @example
|
|
7
|
+
# Spree.permissions.assign(:content_editor, Spree::PermissionSets::ProductDisplay)
|
|
8
|
+
#
|
|
9
|
+
module Spree
|
|
10
|
+
module PermissionSets
|
|
11
|
+
class ProductDisplay < Base
|
|
12
|
+
def activate!
|
|
13
|
+
can [:read, :admin, :index], Spree::Product
|
|
14
|
+
can [:read, :admin], Spree::Variant
|
|
15
|
+
can [:read, :admin], Spree::OptionType
|
|
16
|
+
can [:read, :admin], Spree::OptionValue
|
|
17
|
+
can [:read, :admin], Spree::Property
|
|
18
|
+
can [:read, :admin], Spree::ProductProperty
|
|
19
|
+
can [:read, :admin], Spree::Metafield
|
|
20
|
+
can [:read, :admin], Spree::Taxon
|
|
21
|
+
can [:read, :admin], Spree::Taxonomy
|
|
22
|
+
can [:read, :admin], Spree::Classification
|
|
23
|
+
can [:read, :admin], Spree::Price
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Permission set for full product and catalog management.
|
|
2
|
+
#
|
|
3
|
+
# This permission set provides complete access to manage products, variants,
|
|
4
|
+
# and related catalog models like taxonomies and properties.
|
|
5
|
+
#
|
|
6
|
+
# @example
|
|
7
|
+
# Spree.permissions.assign(:merchandiser, Spree::PermissionSets::ProductManagement)
|
|
8
|
+
#
|
|
9
|
+
module Spree
|
|
10
|
+
module PermissionSets
|
|
11
|
+
class ProductManagement < Base
|
|
12
|
+
def activate!
|
|
13
|
+
can :manage, Spree::Product
|
|
14
|
+
can :manage, Spree::Variant
|
|
15
|
+
can :manage, Spree::OptionType
|
|
16
|
+
can :manage, Spree::OptionValue
|
|
17
|
+
can :manage, Spree::Property
|
|
18
|
+
can :manage, Spree::ProductProperty
|
|
19
|
+
can :manage, Spree::Taxon
|
|
20
|
+
can :manage, Spree::Taxonomy
|
|
21
|
+
can :manage, Spree::Classification
|
|
22
|
+
can :manage, Spree::Price
|
|
23
|
+
can :manage, Spree::Asset
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Permission set for managing promotions and discounts.
|
|
2
|
+
#
|
|
3
|
+
# This permission set provides access to create and manage promotions,
|
|
4
|
+
# coupon codes, and promotion rules.
|
|
5
|
+
#
|
|
6
|
+
# @example
|
|
7
|
+
# Spree.permissions.assign(:marketing, Spree::PermissionSets::PromotionManagement)
|
|
8
|
+
#
|
|
9
|
+
module Spree
|
|
10
|
+
module PermissionSets
|
|
11
|
+
class PromotionManagement < Base
|
|
12
|
+
def activate!
|
|
13
|
+
can :manage, Spree::Promotion
|
|
14
|
+
can :manage, Spree::PromotionRule
|
|
15
|
+
can :manage, Spree::PromotionAction
|
|
16
|
+
can :manage, Spree::PromotionCategory
|
|
17
|
+
can :manage, Spree::CouponCode
|
|
18
|
+
can [:read, :admin], Spree::Metafield
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Permission set for managing roles and permissions.
|
|
2
|
+
#
|
|
3
|
+
# This permission set provides access to manage roles and user assignments.
|
|
4
|
+
# Note: The admin role cannot be modified.
|
|
5
|
+
#
|
|
6
|
+
# @example
|
|
7
|
+
# Spree.permissions.assign(:admin, Spree::PermissionSets::RoleManagement)
|
|
8
|
+
#
|
|
9
|
+
module Spree
|
|
10
|
+
module PermissionSets
|
|
11
|
+
class RoleManagement < Base
|
|
12
|
+
def activate!
|
|
13
|
+
can :manage, Spree::Role
|
|
14
|
+
can :manage, Spree::RoleUser
|
|
15
|
+
|
|
16
|
+
# Protect the admin role from modification
|
|
17
|
+
cannot [:update, :destroy], Spree::Role, name: ['admin']
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Permission set for viewing stock and inventory information.
|
|
2
|
+
#
|
|
3
|
+
# This permission set provides read-only access to stock items,
|
|
4
|
+
# locations, and movements.
|
|
5
|
+
#
|
|
6
|
+
# @example
|
|
7
|
+
# Spree.permissions.assign(:warehouse_viewer, Spree::PermissionSets::StockDisplay)
|
|
8
|
+
#
|
|
9
|
+
module Spree
|
|
10
|
+
module PermissionSets
|
|
11
|
+
class StockDisplay < Base
|
|
12
|
+
def activate!
|
|
13
|
+
can [:read, :admin, :index], Spree::StockItem
|
|
14
|
+
can [:read, :admin], Spree::StockLocation
|
|
15
|
+
can [:read, :admin], Spree::StockMovement
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Permission set for full stock and inventory management.
|
|
2
|
+
#
|
|
3
|
+
# This permission set provides complete access to manage stock items,
|
|
4
|
+
# locations, and movements.
|
|
5
|
+
#
|
|
6
|
+
# @example
|
|
7
|
+
# Spree.permissions.assign(:warehouse_manager, Spree::PermissionSets::StockManagement)
|
|
8
|
+
#
|
|
9
|
+
module Spree
|
|
10
|
+
module PermissionSets
|
|
11
|
+
class StockManagement < Base
|
|
12
|
+
def activate!
|
|
13
|
+
can :manage, Spree::StockItem
|
|
14
|
+
can :manage, Spree::StockLocation
|
|
15
|
+
can :manage, Spree::StockMovement
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Permission set granting full administrative access.
|
|
2
|
+
#
|
|
3
|
+
# This permission set provides unrestricted access to all resources,
|
|
4
|
+
# with some safety restrictions for critical operations.
|
|
5
|
+
#
|
|
6
|
+
# @example
|
|
7
|
+
# Spree.permissions.assign(:admin, Spree::PermissionSets::SuperUser)
|
|
8
|
+
#
|
|
9
|
+
module Spree
|
|
10
|
+
module PermissionSets
|
|
11
|
+
class SuperUser < Base
|
|
12
|
+
def activate!
|
|
13
|
+
can :manage, :all
|
|
14
|
+
|
|
15
|
+
# Safety restrictions
|
|
16
|
+
cannot :cancel, Spree::Order
|
|
17
|
+
can :cancel, Spree::Order, &:allow_cancel?
|
|
18
|
+
cannot :destroy, Spree::Order
|
|
19
|
+
can :destroy, Spree::Order, &:can_be_deleted?
|
|
20
|
+
cannot [:edit, :update], Spree::RefundReason, mutable: false
|
|
21
|
+
cannot [:edit, :update], Spree::ReimbursementType, mutable: false
|
|
22
|
+
|
|
23
|
+
# Protect the admin role from modification
|
|
24
|
+
cannot [:update, :destroy], Spree::Role, name: ['admin']
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Permission set for viewing users and related information.
|
|
2
|
+
#
|
|
3
|
+
# This permission set provides read-only access to user accounts,
|
|
4
|
+
# addresses, and credit cards.
|
|
5
|
+
#
|
|
6
|
+
# @example
|
|
7
|
+
# Spree.permissions.assign(:support_staff, Spree::PermissionSets::UserDisplay)
|
|
8
|
+
#
|
|
9
|
+
module Spree
|
|
10
|
+
module PermissionSets
|
|
11
|
+
class UserDisplay < Base
|
|
12
|
+
def activate!
|
|
13
|
+
can [:read, :admin, :index], Spree.user_class
|
|
14
|
+
can [:read, :admin], Spree::Address
|
|
15
|
+
can [:read, :admin], Spree::CreditCard
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Permission set for full user management.
|
|
2
|
+
#
|
|
3
|
+
# This permission set provides complete access to manage user accounts,
|
|
4
|
+
# addresses, and credit cards.
|
|
5
|
+
#
|
|
6
|
+
# @example
|
|
7
|
+
# Spree.permissions.assign(:customer_service, Spree::PermissionSets::UserManagement)
|
|
8
|
+
#
|
|
9
|
+
module Spree
|
|
10
|
+
module PermissionSets
|
|
11
|
+
class UserManagement < Base
|
|
12
|
+
def activate!
|
|
13
|
+
can :manage, Spree.user_class
|
|
14
|
+
can :manage, Spree::Address
|
|
15
|
+
can :manage, Spree::CreditCard
|
|
16
|
+
can [:read, :admin], Spree::Metafield
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
data/config/locales/en.yml
CHANGED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
module FriendlyId
|
|
2
|
+
module HistoryDecorator
|
|
3
|
+
private
|
|
4
|
+
|
|
5
|
+
# This a patch to friendly_id history module.
|
|
6
|
+
# Originally, it removes and re-creates a slug record if it already exists. The issue is that we use `acts_as_paranoid`
|
|
7
|
+
# for slugs so it sets the `deleted_at` timestamp instead of deleting the record. We need to delete the record instead.
|
|
8
|
+
def create_slug
|
|
9
|
+
return unless friendly_id
|
|
10
|
+
return if history_is_up_to_date?
|
|
11
|
+
# Allow reversion back to a previously used slug
|
|
12
|
+
relation = slugs.where(slug: friendly_id)
|
|
13
|
+
if friendly_id_config.uses?(:scoped)
|
|
14
|
+
relation = relation.where(scope: serialized_scope)
|
|
15
|
+
end
|
|
16
|
+
# Use `delete_all` instead of `destroy_all` to avoid the unique index error.
|
|
17
|
+
relation.delete_all unless relation.empty?
|
|
18
|
+
slugs.create! do |record|
|
|
19
|
+
record.slug = friendly_id
|
|
20
|
+
record.scope = serialized_scope if friendly_id_config.uses?(:scoped)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
FriendlyId::History.prepend(FriendlyId::HistoryDecorator)
|
|
@@ -95,6 +95,31 @@ Rails.application.config.after_initialize do
|
|
|
95
95
|
|
|
96
96
|
# Admin partials
|
|
97
97
|
# Spree.admin.partials.product_form << 'spree/admin/products/custom_section'
|
|
98
|
+
|
|
99
|
+
# Role-based permissions
|
|
100
|
+
# Configure which permission sets are assigned to each role
|
|
101
|
+
# More on permission sets: https://spreecommerce.org/docs/developer/customization/permissions
|
|
102
|
+
Spree.permissions.assign(:default, [Spree::PermissionSets::DefaultCustomer])
|
|
103
|
+
Spree.permissions.assign(:admin, [Spree::PermissionSets::SuperUser])
|
|
104
|
+
|
|
105
|
+
# Example: Create a custom role with specific permissions
|
|
106
|
+
# Spree.permissions.assign(:customer_service, [
|
|
107
|
+
# Spree::PermissionSets::DashboardDisplay,
|
|
108
|
+
# Spree::PermissionSets::OrderManagement,
|
|
109
|
+
# Spree::PermissionSets::UserDisplay
|
|
110
|
+
# ])
|
|
111
|
+
#
|
|
112
|
+
# Available permission sets:
|
|
113
|
+
# - Spree::PermissionSets::SuperUser (full admin access)
|
|
114
|
+
# - Spree::PermissionSets::DefaultCustomer (storefront access)
|
|
115
|
+
# - Spree::PermissionSets::DashboardDisplay (view admin dashboard)
|
|
116
|
+
# - Spree::PermissionSets::OrderDisplay / OrderManagement
|
|
117
|
+
# - Spree::PermissionSets::ProductDisplay / ProductManagement
|
|
118
|
+
# - Spree::PermissionSets::UserDisplay / UserManagement
|
|
119
|
+
# - Spree::PermissionSets::StockDisplay / StockManagement
|
|
120
|
+
# - Spree::PermissionSets::PromotionManagement
|
|
121
|
+
# - Spree::PermissionSets::ConfigurationManagement
|
|
122
|
+
# - Spree::PermissionSets::RoleManagement
|
|
98
123
|
end
|
|
99
124
|
|
|
100
125
|
Spree.user_class = <%= (options[:user_class].blank? ? 'Spree::LegacyUser' : options[:user_class]).inspect %>
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# Manages the mapping between roles and permission sets.
|
|
2
|
+
#
|
|
3
|
+
# This configuration allows you to define which permission sets are assigned to each role.
|
|
4
|
+
# Permission sets are reusable groups of permissions that can be applied to roles.
|
|
5
|
+
#
|
|
6
|
+
# @example Assigning permission sets to a role
|
|
7
|
+
# Spree.permissions.assign(:customer_service, [
|
|
8
|
+
# Spree::PermissionSets::OrderDisplay,
|
|
9
|
+
# Spree::PermissionSets::UserManagement
|
|
10
|
+
# ])
|
|
11
|
+
#
|
|
12
|
+
# @example Clearing permission sets from a role
|
|
13
|
+
# Spree.permissions.clear(:customer_service)
|
|
14
|
+
#
|
|
15
|
+
# @example Getting permission sets for a role
|
|
16
|
+
# Spree.permissions.permission_sets_for(:admin)
|
|
17
|
+
# # => [Spree::PermissionSets::SuperUser]
|
|
18
|
+
#
|
|
19
|
+
module Spree
|
|
20
|
+
class PermissionConfiguration
|
|
21
|
+
# Default role used for unauthenticated users
|
|
22
|
+
DEFAULT_ROLE = :default
|
|
23
|
+
|
|
24
|
+
# Admin role with full access
|
|
25
|
+
ADMIN_ROLE = :admin
|
|
26
|
+
|
|
27
|
+
def initialize
|
|
28
|
+
@role_permissions = {}
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Assigns permission sets to a role.
|
|
32
|
+
#
|
|
33
|
+
# @param role_name [Symbol, String] the name of the role
|
|
34
|
+
# @param permission_sets [Array<Class>, Class] permission set class(es) to assign
|
|
35
|
+
# @return [Array<Class>] the assigned permission sets
|
|
36
|
+
#
|
|
37
|
+
# @example
|
|
38
|
+
# Spree.permissions.assign(:customer_service, Spree::PermissionSets::OrderDisplay)
|
|
39
|
+
# Spree.permissions.assign(:admin, [
|
|
40
|
+
# Spree::PermissionSets::SuperUser
|
|
41
|
+
# ])
|
|
42
|
+
def assign(role_name, permission_sets)
|
|
43
|
+
role_key = normalize_role_name(role_name)
|
|
44
|
+
@role_permissions[role_key] ||= []
|
|
45
|
+
@role_permissions[role_key] |= Array(permission_sets)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Clears all permission sets from a role.
|
|
49
|
+
#
|
|
50
|
+
# @param role_name [Symbol, String] the name of the role
|
|
51
|
+
# @return [Array<Class>] the removed permission sets
|
|
52
|
+
def clear(role_name)
|
|
53
|
+
role_key = normalize_role_name(role_name)
|
|
54
|
+
@role_permissions.delete(role_key)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Returns the permission sets assigned to a role.
|
|
58
|
+
#
|
|
59
|
+
# @param role_name [Symbol, String] the name of the role
|
|
60
|
+
# @return [Array<Class>] the assigned permission sets
|
|
61
|
+
def permission_sets_for(role_name)
|
|
62
|
+
role_key = normalize_role_name(role_name)
|
|
63
|
+
@role_permissions[role_key] || []
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Returns all permission sets for multiple roles.
|
|
67
|
+
#
|
|
68
|
+
# @param role_names [Array<Symbol, String>] the names of the roles
|
|
69
|
+
# @return [Array<Class>] the combined permission sets (deduplicated)
|
|
70
|
+
def permission_sets_for_roles(role_names)
|
|
71
|
+
role_names.flat_map { |role_name| permission_sets_for(role_name) }.uniq
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Returns all configured roles.
|
|
75
|
+
#
|
|
76
|
+
# @return [Array<Symbol>] the configured role names
|
|
77
|
+
def roles
|
|
78
|
+
@role_permissions.keys
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Checks if a role has any permission sets assigned.
|
|
82
|
+
#
|
|
83
|
+
# @param role_name [Symbol, String] the name of the role
|
|
84
|
+
# @return [Boolean]
|
|
85
|
+
def role_configured?(role_name)
|
|
86
|
+
role_key = normalize_role_name(role_name)
|
|
87
|
+
@role_permissions.key?(role_key) && @role_permissions[role_key].any?
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Resets all role permissions to empty state.
|
|
91
|
+
# Useful for testing.
|
|
92
|
+
def reset!
|
|
93
|
+
@role_permissions = {}
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
private
|
|
97
|
+
|
|
98
|
+
def normalize_role_name(role_name)
|
|
99
|
+
role_name.to_s.downcase.to_sym
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
data/lib/spree/core/version.rb
CHANGED
data/lib/spree/core.rb
CHANGED
|
@@ -358,6 +358,22 @@ module Spree
|
|
|
358
358
|
end
|
|
359
359
|
end
|
|
360
360
|
|
|
361
|
+
# Permission configuration accessor for managing role-to-permission-set mappings.
|
|
362
|
+
#
|
|
363
|
+
# @example Assigning permission sets to a role
|
|
364
|
+
# Spree.permissions.assign(:customer_service, [
|
|
365
|
+
# Spree::PermissionSets::OrderDisplay,
|
|
366
|
+
# Spree::PermissionSets::UserManagement
|
|
367
|
+
# ])
|
|
368
|
+
#
|
|
369
|
+
# @example Clearing permission sets from a role
|
|
370
|
+
# Spree.permissions.clear(:customer_service)
|
|
371
|
+
#
|
|
372
|
+
# @return [Spree::PermissionConfiguration] the permission configuration instance
|
|
373
|
+
def self.permissions
|
|
374
|
+
@permissions ||= PermissionConfiguration.new
|
|
375
|
+
end
|
|
376
|
+
|
|
361
377
|
module Core
|
|
362
378
|
autoload :ProductFilters, 'spree/core/product_filters'
|
|
363
379
|
autoload :TokenGenerator, 'spree/core/token_generator'
|
|
@@ -400,3 +416,4 @@ require 'spree/core/preferences/scoped_store'
|
|
|
400
416
|
require 'spree/core/preferences/runtime_configuration'
|
|
401
417
|
|
|
402
418
|
require 'spree/core/webhooks'
|
|
419
|
+
require 'spree/core/permission_configuration'
|
data/lib/spree_core.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: spree_core
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 5.2.
|
|
4
|
+
version: 5.2.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Sean Schofield
|
|
@@ -335,28 +335,28 @@ dependencies:
|
|
|
335
335
|
requirements:
|
|
336
336
|
- - "~>"
|
|
337
337
|
- !ruby/object:Gem::Version
|
|
338
|
-
version: '0.
|
|
338
|
+
version: '0.100'
|
|
339
339
|
type: :runtime
|
|
340
340
|
prerelease: false
|
|
341
341
|
version_requirements: !ruby/object:Gem::Requirement
|
|
342
342
|
requirements:
|
|
343
343
|
- - "~>"
|
|
344
344
|
- !ruby/object:Gem::Version
|
|
345
|
-
version: '0.
|
|
345
|
+
version: '0.100'
|
|
346
346
|
- !ruby/object:Gem::Dependency
|
|
347
347
|
name: state_machines-activemodel
|
|
348
348
|
requirement: !ruby/object:Gem::Requirement
|
|
349
349
|
requirements:
|
|
350
350
|
- - "~>"
|
|
351
351
|
- !ruby/object:Gem::Version
|
|
352
|
-
version: '0.
|
|
352
|
+
version: '0.100'
|
|
353
353
|
type: :runtime
|
|
354
354
|
prerelease: false
|
|
355
355
|
version_requirements: !ruby/object:Gem::Requirement
|
|
356
356
|
requirements:
|
|
357
357
|
- - "~>"
|
|
358
358
|
- !ruby/object:Gem::Version
|
|
359
|
-
version: '0.
|
|
359
|
+
version: '0.100'
|
|
360
360
|
- !ruby/object:Gem::Dependency
|
|
361
361
|
name: stringex
|
|
362
362
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -1050,6 +1050,21 @@ files:
|
|
|
1050
1050
|
- app/models/spree/payment_method/check.rb
|
|
1051
1051
|
- app/models/spree/payment_method/store_credit.rb
|
|
1052
1052
|
- app/models/spree/payment_source.rb
|
|
1053
|
+
- app/models/spree/permission_sets/base.rb
|
|
1054
|
+
- app/models/spree/permission_sets/configuration_management.rb
|
|
1055
|
+
- app/models/spree/permission_sets/dashboard_display.rb
|
|
1056
|
+
- app/models/spree/permission_sets/default_customer.rb
|
|
1057
|
+
- app/models/spree/permission_sets/order_display.rb
|
|
1058
|
+
- app/models/spree/permission_sets/order_management.rb
|
|
1059
|
+
- app/models/spree/permission_sets/product_display.rb
|
|
1060
|
+
- app/models/spree/permission_sets/product_management.rb
|
|
1061
|
+
- app/models/spree/permission_sets/promotion_management.rb
|
|
1062
|
+
- app/models/spree/permission_sets/role_management.rb
|
|
1063
|
+
- app/models/spree/permission_sets/stock_display.rb
|
|
1064
|
+
- app/models/spree/permission_sets/stock_management.rb
|
|
1065
|
+
- app/models/spree/permission_sets/super_user.rb
|
|
1066
|
+
- app/models/spree/permission_sets/user_display.rb
|
|
1067
|
+
- app/models/spree/permission_sets/user_management.rb
|
|
1053
1068
|
- app/models/spree/policy.rb
|
|
1054
1069
|
- app/models/spree/post.rb
|
|
1055
1070
|
- app/models/spree/post_category.rb
|
|
@@ -1457,6 +1472,7 @@ files:
|
|
|
1457
1472
|
- db/migrate/20250915093930_add_metadata_to_spree_newsletter_subscribers.rb
|
|
1458
1473
|
- db/migrate/20250923141845_create_spree_imports.rb
|
|
1459
1474
|
- db/seeds.rb
|
|
1475
|
+
- lib/friendly_id/history_decorator.rb
|
|
1460
1476
|
- lib/friendly_id/paranoia.rb
|
|
1461
1477
|
- lib/generators/spree/authentication/custom/custom_generator.rb
|
|
1462
1478
|
- lib/generators/spree/authentication/custom/templates/authentication_helpers.rb.tt
|
|
@@ -1504,6 +1520,7 @@ files:
|
|
|
1504
1520
|
- lib/spree/core/importer/product.rb
|
|
1505
1521
|
- lib/spree/core/number_generator.rb
|
|
1506
1522
|
- lib/spree/core/partials.rb
|
|
1523
|
+
- lib/spree/core/permission_configuration.rb
|
|
1507
1524
|
- lib/spree/core/preferences/configuration.rb
|
|
1508
1525
|
- lib/spree/core/preferences/preferable.rb
|
|
1509
1526
|
- lib/spree/core/preferences/preferable_class_methods.rb
|
|
@@ -1660,9 +1677,9 @@ licenses:
|
|
|
1660
1677
|
- BSD-3-Clause
|
|
1661
1678
|
metadata:
|
|
1662
1679
|
bug_tracker_uri: https://github.com/spree/spree/issues
|
|
1663
|
-
changelog_uri: https://github.com/spree/spree/releases/tag/v5.2.
|
|
1680
|
+
changelog_uri: https://github.com/spree/spree/releases/tag/v5.2.1
|
|
1664
1681
|
documentation_uri: https://docs.spreecommerce.org/
|
|
1665
|
-
source_code_uri: https://github.com/spree/spree/tree/v5.2.
|
|
1682
|
+
source_code_uri: https://github.com/spree/spree/tree/v5.2.1
|
|
1666
1683
|
rdoc_options: []
|
|
1667
1684
|
require_paths:
|
|
1668
1685
|
- lib
|