faalis 0.26.3 → 1.0.0.alpha0
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/README.md +35 -6
- data/Rakefile +2 -25
- data/app/assets/javascripts/faalis/angular-manifest.js +8 -2
- data/app/assets/javascripts/faalis/application.js +0 -3
- data/app/assets/javascripts/faalis/dashboard/application.js.erb +0 -5
- data/app/assets/javascripts/faalis/dashboard/functions.js.erb +9 -0
- data/app/assets/javascripts/faalis/dashboard/init.js +1 -1
- data/app/assets/javascripts/faalis/dashboard/modules/auth/group.js +0 -6
- data/app/assets/javascripts/faalis/dashboard/modules/fields/fields.js +1 -1
- data/app/assets/javascripts/faalis/dashboard/modules/fields/image.js +115 -0
- data/app/assets/javascripts/faalis/dashboard/modules/fields/tag.js +76 -0
- data/app/assets/javascripts/faalis/dashboard/variables.js.erb +1 -1
- data/app/assets/locale/templates.pot +282 -0
- data/app/assets/stylesheets/faalis/dashboard/dashboard.css.scss +4 -0
- data/app/assets/stylesheets/faalis/dashboard/ltr/application.css +4 -4
- data/app/assets/stylesheets/faalis/dashboard/rtl/application.css +4 -4
- data/app/assets/stylesheets/faalis/dashboard/rtl/base.css.scss.erb +2 -2
- data/app/controllers/faalis/api/v1/permissions_controller.rb +8 -39
- data/app/controllers/faalis/api/v1/users_controller.rb +7 -7
- data/app/models/ability.rb +4 -6
- data/app/models/faalis/concerns/assignment.rb +2 -2
- data/app/models/faalis/group.rb +18 -4
- data/app/models/faalis/permission.rb +11 -2
- data/app/models/faalis/permissions/auth.rb +3 -2
- data/app/models/faalis/user.rb +27 -73
- data/app/models/faalis/user/auth_definitions.rb +94 -0
- data/app/models/faalis/user/mongoid_fields.rb +76 -0
- data/app/models/faalis/user/permission.rb +21 -0
- data/app/views/angularjs_templates/auth/groups/new.html +0 -20
- data/app/views/angularjs_templates/fields/image/image.html +1 -0
- data/app/views/angularjs_templates/fields/relation/relation.html +3 -1
- data/app/views/angularjs_templates/fields/tag/tag.html +1 -0
- data/app/views/angularjs_templates/nav.html.erb +0 -1
- data/app/views/faalis/api/v1/users/index.json.jbuilder +1 -1
- data/app/views/layouts/faalis/application.html.erb +2 -0
- data/app/views/layouts/faalis/dashboard.html.erb +1 -0
- data/app/views/layouts/faalis/simple.html.erb +1 -0
- data/config/initializers/devise.rb +0 -7
- data/db/migrate/20131013091000_devise_create_faalis_users.rb +3 -2
- data/db/migrate/20140613120923_add_users_groups_table.rb +8 -0
- data/db/migrate/20140617124019_faalis_groups_users.rb +4 -0
- data/db/seeds.rb +39 -11
- data/lib/faalis.rb +5 -4
- data/lib/faalis/concerns.rb +7 -0
- data/lib/faalis/concerns/authorizable.rb +76 -0
- data/lib/faalis/discovery.rb +8 -0
- data/lib/faalis/discovery/permissions.rb +51 -0
- data/lib/faalis/engine.rb +19 -5
- data/lib/faalis/extensions.rb +18 -0
- data/lib/faalis/extensions/base.rb +29 -0
- data/lib/faalis/fake_assets.rb +7 -0
- data/lib/faalis/generators/dashboard_scaffold.rb +23 -5
- data/lib/faalis/omniauth.rb +3 -0
- data/lib/faalis/orm.rb +29 -0
- data/lib/faalis/{active_record.rb → patches/models.rb} +1 -3
- data/lib/faalis/version.rb +1 -1
- data/lib/generators/faalis/install_generator.rb +7 -3
- data/lib/{faalis/plugins.rb → generators/faalis/js/install_i18n_generator.rb} +20 -17
- data/lib/generators/faalis/scaffold_generator.rb +139 -0
- data/lib/generators/faalis/templates/application.js +1 -1
- data/lib/generators/faalis/templates/i18n/Gruntfile.js.erb +30 -0
- data/lib/generators/faalis/templates/i18n/README +13 -0
- data/lib/generators/faalis/templates/i18n/fa.js +3 -0
- data/lib/generators/faalis/templates/js/list_view/README +1 -1
- data/lib/tasks/faalis_tasks.rake +26 -20
- data/lib/tasks/grunt/Gruntfile.js +7 -2
- data/spec/dummy/config/initializers/faalis.rb +40 -0
- metadata +134 -44
- data/app/assets/javascripts/faalis/dashboard/lib/angular-gettext.js +0 -202
- data/app/assets/javascripts/faalis/dashboard/lib/ng-grid.js +0 -3260
- data/app/assets/javascripts/faalis/dashboard/lib/ng-quick-date.js +0 -297
- data/app/assets/javascripts/faalis/dashboard/lib/restangular.js +0 -1066
- data/app/assets/javascripts/faalis/dashboard/lib/select2.js +0 -3255
- data/app/assets/javascripts/faalis/dashboard/lib/ui.select2.js +0 -217
- data/app/assets/stylesheets/faalis/dashboard/ng-grid.css.scss +0 -442
- data/app/assets/stylesheets/faalis/dashboard/ng-quick-date-default-theme.css.scss +0 -20
- data/app/assets/stylesheets/faalis/dashboard/ng-quick-date.css.scss +0 -19
- data/app/assets/stylesheets/faalis/dashboard/select2.css.scss.erb +0 -618
- data/app/controllers/faalis/api/v1/#conversations_controller.rb# +0 -120
- data/app/models/faalis/workflow.rb +0 -4
- data/db/migrate/20140413180202_create_faalis_workflows.rb +0 -9
- data/lib/faalis/permissions.rb +0 -72
- data/lib/faalis/workflows.rb +0 -7
- data/lib/faalis/workflows/base.rb +0 -70
- data/lib/faalis/workflows/discovery.rb +0 -42
- data/lib/generators/faalis/js_scaffold_generator.rb +0 -113
|
@@ -20,10 +20,10 @@
|
|
|
20
20
|
*= require faalis/dashboard/dashboard
|
|
21
21
|
*= require faalis/dashboard/sidebar
|
|
22
22
|
*= require faalis/dashboard/list_view
|
|
23
|
-
*= require
|
|
24
|
-
*= require
|
|
25
|
-
*= require
|
|
26
|
-
*= require
|
|
23
|
+
*= require ngQuickDate/ng-quick-date
|
|
24
|
+
*= require ngQuickDate/ng-quick-date-default-theme
|
|
25
|
+
*= require ng-grid/ng-grid
|
|
26
|
+
*= require select2/select2
|
|
27
27
|
*= require_tree ../share/
|
|
28
28
|
|
|
29
29
|
*/
|
|
@@ -19,9 +19,9 @@
|
|
|
19
19
|
*= require faalis/dashboard/dashboard
|
|
20
20
|
*= require faalis/dashboard/sidebar
|
|
21
21
|
*= require faalis/dashboard/list_view
|
|
22
|
-
*= require
|
|
23
|
-
*= require
|
|
24
|
-
*= require
|
|
25
|
-
*= require
|
|
22
|
+
*= require ngQuickDate/ng-quick-date
|
|
23
|
+
*= require ngQuickDate/ng-quick-date-default-theme
|
|
24
|
+
*= require ng-grid/ng-grid
|
|
25
|
+
*= require select2/select2
|
|
26
26
|
*= require_tree ../share/
|
|
27
27
|
*/
|
|
@@ -3,12 +3,12 @@
|
|
|
3
3
|
|
|
4
4
|
@font-face {
|
|
5
5
|
font-family: 'Droid Arabic Naskh';
|
|
6
|
-
src: url(<%= asset_path("DroidNaskh-Regular.ttf")%>) format('truetype'); /*
|
|
6
|
+
src: url(<%= asset_path("DroidNaskh-Regular.ttf")%>) format('truetype'); /* Chrome4+, FF3.5, Opera 10+ */
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
:not(.fa){
|
|
11
|
-
font-family: 'Droid Arabic Naskh', tahome, serif
|
|
11
|
+
font-family: 'Droid Arabic Naskh', tahome, serif;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
input[type="integer"] {
|
|
@@ -10,24 +10,8 @@ module Faalis
|
|
|
10
10
|
# @api GET permissions
|
|
11
11
|
# @return All permissions
|
|
12
12
|
def index
|
|
13
|
-
@permissions =
|
|
14
|
-
|
|
15
|
-
::ApplicationModels.all.each do |m|
|
|
16
|
-
|
|
17
|
-
model = m.model.constantize
|
|
18
|
-
if model.respond_to? :permission_strings
|
|
19
|
-
@permissions.concat(model.permission_strings(model))
|
|
20
|
-
end
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
Faalis::Engine.models_with_permission.each do |m|
|
|
24
|
-
model = m.constantize
|
|
25
|
-
if model.respond_to? :permission_strings
|
|
26
|
-
@permissions.concat(model.permission_strings(model))
|
|
27
|
-
end
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
respond_with(@permissions.uniq)
|
|
13
|
+
@permissions = Faalis::Discovery::Permissions.all_permissions
|
|
14
|
+
respond_with(@permissions)
|
|
31
15
|
end
|
|
32
16
|
|
|
33
17
|
# @api GET permissions/user
|
|
@@ -35,28 +19,13 @@ module Faalis
|
|
|
35
19
|
def user_permissions
|
|
36
20
|
@permissions = {}
|
|
37
21
|
perms = []
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
perm.model = model.model
|
|
44
|
-
perm.permission_type = p
|
|
45
|
-
perms << perm
|
|
46
|
-
end
|
|
47
|
-
end
|
|
48
|
-
Faalis::Engine.models_with_permission.each do |m|
|
|
49
|
-
m.constantize.possible_permissions.each do |p|
|
|
50
|
-
perm = DummyPerm.new
|
|
51
|
-
perm.model = m
|
|
52
|
-
perm.permission_type = p
|
|
53
|
-
perms << perm
|
|
54
|
-
end
|
|
55
|
-
end
|
|
56
|
-
perms.uniq!
|
|
57
|
-
else
|
|
58
|
-
perms = current_user.group.permissions
|
|
22
|
+
current_user.permissions do |perm|
|
|
23
|
+
tmp = DummyPerm.new
|
|
24
|
+
tmp.model = perm.model
|
|
25
|
+
tmp.permission_type = perm.permission_type
|
|
26
|
+
perms << tmp
|
|
59
27
|
end
|
|
28
|
+
perms.uniq!
|
|
60
29
|
|
|
61
30
|
# Generate a suitable Hash for permissions
|
|
62
31
|
perms.each do |perm|
|
|
@@ -4,8 +4,8 @@ module Faalis
|
|
|
4
4
|
class API::V1::UsersController < ::APIController
|
|
5
5
|
|
|
6
6
|
def index
|
|
7
|
-
@users = User.joins(:
|
|
8
|
-
authorize! :read, @users
|
|
7
|
+
@users = User.joins(:groups).all
|
|
8
|
+
#authorize! :read, @users
|
|
9
9
|
respond_with(@users)
|
|
10
10
|
end
|
|
11
11
|
|
|
@@ -35,8 +35,8 @@ module Faalis
|
|
|
35
35
|
user_fields[:password] = params[:password]
|
|
36
36
|
end
|
|
37
37
|
|
|
38
|
-
if params.include? :
|
|
39
|
-
user_fields[:
|
|
38
|
+
if params.include? :groups and params[:groups]
|
|
39
|
+
user_fields[:groups] = Group.find(params[:groups]) || nil
|
|
40
40
|
end
|
|
41
41
|
|
|
42
42
|
if @user.update(user_fields)
|
|
@@ -58,9 +58,9 @@ module Faalis
|
|
|
58
58
|
password: params[:password],
|
|
59
59
|
})
|
|
60
60
|
|
|
61
|
-
if params.include? :
|
|
62
|
-
group = Group.find(params[:
|
|
63
|
-
@user.
|
|
61
|
+
if params.include? :groups
|
|
62
|
+
group = Group.find(params[:groups]) || nil
|
|
63
|
+
@user.groups = group
|
|
64
64
|
end
|
|
65
65
|
|
|
66
66
|
if @user.save
|
data/app/models/ability.rb
CHANGED
|
@@ -31,12 +31,10 @@ class Ability
|
|
|
31
31
|
|
|
32
32
|
# IMPORTANT: Remove this snippet with more suitable one
|
|
33
33
|
# TODO: Remove this snippet with more suitable one
|
|
34
|
-
user.
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
if user and user.group_id == 1
|
|
39
|
-
can :manage, :all
|
|
34
|
+
user.groups.each do |group|
|
|
35
|
+
group.permissions.each do |permission|
|
|
36
|
+
can permission.permission_type.to_sym, permission.model.constantize
|
|
37
|
+
end
|
|
40
38
|
end
|
|
41
39
|
end
|
|
42
40
|
end
|
data/app/models/faalis/group.rb
CHANGED
|
@@ -18,10 +18,24 @@
|
|
|
18
18
|
# -----------------------------------------------------------------------------
|
|
19
19
|
|
|
20
20
|
module Faalis
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
# **Group** model for **Faalis** platform
|
|
22
|
+
class Group < Faalis::ORM.proper_base_class
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
# Make this model authorizable
|
|
25
|
+
include Faalis::Concerns::Authorizable
|
|
26
|
+
|
|
27
|
+
# Define **Group** fields if current ORM was **Mongoid**
|
|
28
|
+
if Faalis::ORM.mongoid?
|
|
29
|
+
include Mongoid::Document
|
|
30
|
+
include Mongoid::Timestamps
|
|
31
|
+
|
|
32
|
+
field :name, type: String
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
has_and_belongs_to_many :users, class_name: 'Faalis::User'
|
|
36
|
+
has_and_belongs_to_many :permissions, class_name: 'Faalis::Permission'
|
|
37
|
+
|
|
38
|
+
# Validations
|
|
39
|
+
validates :name, presence: true
|
|
26
40
|
end
|
|
27
41
|
end
|
|
@@ -1,7 +1,16 @@
|
|
|
1
1
|
module Faalis
|
|
2
|
-
class Permission <
|
|
2
|
+
class Permission < Faalis::ORM.proper_base_class
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
if Faalis::ORM.mongoid?
|
|
5
|
+
include Mongoid::Document
|
|
6
|
+
include Mongoid::Timestamps
|
|
7
|
+
|
|
8
|
+
field :model, type: String
|
|
9
|
+
field :permission_type, type: String
|
|
10
|
+
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
has_and_belongs_to_many :groups, class_name: 'Faalis::Group'
|
|
5
14
|
|
|
6
15
|
def string_repr
|
|
7
16
|
_("can %s %s") % [_(self.permission_type.to_s), self.model.underscore.humanize]
|
data/app/models/faalis/user.rb
CHANGED
|
@@ -18,85 +18,39 @@
|
|
|
18
18
|
# -----------------------------------------------------------------------------
|
|
19
19
|
|
|
20
20
|
module Faalis
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
#
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
:timeoutable,
|
|
39
|
-
:validatable,
|
|
40
|
-
:omniauthable]
|
|
41
|
-
|
|
42
|
-
if Rails.env.production?
|
|
43
|
-
@@devise_options << :confirmable
|
|
21
|
+
# **User** model for **Faalis** platform
|
|
22
|
+
class User < Faalis::ORM.proper_base_class
|
|
23
|
+
|
|
24
|
+
# AuthDefinitions contains all the **Devise** related configurations.
|
|
25
|
+
include Faalis::User::AuthDefinitions
|
|
26
|
+
# Permission related methods for user
|
|
27
|
+
include Faalis::User::Permission
|
|
28
|
+
|
|
29
|
+
# Make this model authorizable
|
|
30
|
+
include Faalis::Concerns::Authorizable
|
|
31
|
+
|
|
32
|
+
# Define **User** fields if current ORM was Mongoid -----------------------
|
|
33
|
+
if Faalis::ORM.mongoid?
|
|
34
|
+
include Mongoid::Document
|
|
35
|
+
include Mongoid::Timestamps
|
|
36
|
+
include Faalis::User::MongoidFields
|
|
37
|
+
# FIXME: Port mailboxer to work with mongoid
|
|
44
38
|
end
|
|
45
39
|
|
|
46
|
-
if
|
|
47
|
-
|
|
48
|
-
|
|
40
|
+
# Define **User** fields if current ORM was ActiveRecord-------------------
|
|
41
|
+
if Faalis::ORM.active_record?
|
|
42
|
+
# acts as messageable for mailboxer
|
|
43
|
+
#acts_as_messageable
|
|
49
44
|
end
|
|
50
45
|
|
|
51
|
-
|
|
46
|
+
has_and_belongs_to_many :groups, class_name: 'Faalis::Group'
|
|
52
47
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
"#{first_name} #{last_name}"
|
|
58
|
-
else
|
|
59
|
-
email
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
def full_name
|
|
65
|
-
name
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
def password_required?
|
|
69
|
-
# TODO: nil? is not suitable for here we should use empty? or blink?
|
|
70
|
-
if Devise.omniauth_configs.any?
|
|
71
|
-
return (provider.nil? || password.nil?) && super
|
|
72
|
-
else
|
|
73
|
-
password.nil? && super
|
|
74
|
-
end
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
def self.find_from_oauth(auth, signed_in_resource=nil)
|
|
78
|
-
user = User.where(:provider => auth.provider, :uid => auth.uid).first
|
|
79
|
-
|
|
80
|
-
first_name = auth.info.first_name
|
|
81
|
-
last_name = auth.info.last_name
|
|
82
|
-
|
|
83
|
-
if first_name.blank?
|
|
84
|
-
# With first_name being blank last_name is probably is blank too
|
|
85
|
-
name = auth.info.name.split(" ")
|
|
86
|
-
first_name = name[0]
|
|
87
|
-
last_name = name[1,] || ""
|
|
88
|
-
end
|
|
89
|
-
unless user
|
|
90
|
-
user = User.create(first_name: first_name,
|
|
91
|
-
last_name: last_name,
|
|
92
|
-
provider: auth.provider,
|
|
93
|
-
uid: auth.uid,
|
|
94
|
-
email: auth.info.email,
|
|
95
|
-
password: Devise.friendly_token[0,20])
|
|
96
|
-
end
|
|
97
|
-
user
|
|
48
|
+
# Validations
|
|
49
|
+
validates :password, presence: true, length: {minimum: 5, maximum: 120}, on: :create
|
|
50
|
+
validates :password, length: {minimum: 5, maximum: 120}, on: :update, allow_blank: true
|
|
51
|
+
validates :email, presence: true, length: {minimum: 6}
|
|
98
52
|
|
|
99
|
-
|
|
53
|
+
devise *@@devise_options
|
|
100
54
|
|
|
101
55
|
end
|
|
102
56
|
end
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
module Faalis
|
|
2
|
+
module User::AuthDefinitions
|
|
3
|
+
|
|
4
|
+
def self.included(base)
|
|
5
|
+
|
|
6
|
+
base.class_eval do
|
|
7
|
+
# Include default devise modules. Others available are:
|
|
8
|
+
# :token_authenticatable, :confirmable,
|
|
9
|
+
# :lockable, :timeoutable and :omniauthable
|
|
10
|
+
@@devise_options = [:database_authenticatable,
|
|
11
|
+
:registerable,
|
|
12
|
+
:recoverable,
|
|
13
|
+
:rememberable,
|
|
14
|
+
:trackable,
|
|
15
|
+
:lockable,
|
|
16
|
+
:timeoutable,
|
|
17
|
+
:validatable]
|
|
18
|
+
|
|
19
|
+
if Rails.env.production?
|
|
20
|
+
@@devise_options << :confirmable
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
if Devise.omniauth_configs.any?
|
|
24
|
+
@@devise_options << :omniauthable
|
|
25
|
+
@@devise_options << {:omniauth_providers => Devise.omniauth_configs.keys}
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
base.extend ClassMethods
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def name
|
|
32
|
+
if first_name or last_name
|
|
33
|
+
"#{first_name} #{last_name}"
|
|
34
|
+
else
|
|
35
|
+
email
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def full_name
|
|
41
|
+
name
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
# Confirmation not required when using omniauth
|
|
46
|
+
def confirmation_required?
|
|
47
|
+
super && identities.empty?
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def update_with_password(params, *options)
|
|
51
|
+
if encrypted_password.blank?
|
|
52
|
+
update_attributes(params, *options)
|
|
53
|
+
else
|
|
54
|
+
super
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def password_required?
|
|
59
|
+
# TODO: nil? is not suitable for here we should use empty? or blink?
|
|
60
|
+
if Devise.omniauth_configs.any?
|
|
61
|
+
return (provider.nil? || password.nil?) && super
|
|
62
|
+
else
|
|
63
|
+
password.nil? && super
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
module ClassMethods
|
|
68
|
+
def find_from_oauth(auth, signed_in_resource = nil)
|
|
69
|
+
user = User.where(:provider => auth.provider, :uid => auth.uid).first
|
|
70
|
+
|
|
71
|
+
first_name = auth.info.first_name
|
|
72
|
+
last_name = auth.info.last_name
|
|
73
|
+
|
|
74
|
+
if first_name.blank?
|
|
75
|
+
# With first_name being blank last_name is probably is blank too
|
|
76
|
+
name = auth.info.name.split(' ')
|
|
77
|
+
first_name = name[0]
|
|
78
|
+
last_name = name[1,] || ''
|
|
79
|
+
end
|
|
80
|
+
unless user
|
|
81
|
+
user = User.create(first_name: first_name,
|
|
82
|
+
last_name: last_name,
|
|
83
|
+
provider: auth.provider,
|
|
84
|
+
uid: auth.uid,
|
|
85
|
+
email: auth.info.email,
|
|
86
|
+
password: Devise.friendly_token[0,20])
|
|
87
|
+
end
|
|
88
|
+
user
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
end
|
|
94
|
+
end
|