mno-enterprise-core 3.1.4 → 3.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (77) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/images/mno_enterprise/logo-intuit.png +0 -0
  3. data/app/assets/images/mno_enterprise/main-logo.png +0 -0
  4. data/app/assets/stylesheets/mno_enterprise/mail.css +1 -4
  5. data/app/controllers/mno_enterprise/application_controller.rb +1 -1
  6. data/app/helpers/mno_enterprise/application_helper.rb +7 -7
  7. data/app/models/mno_enterprise/app.rb +12 -3
  8. data/app/models/mno_enterprise/app_answer.rb +13 -0
  9. data/app/models/mno_enterprise/app_comment.rb +10 -0
  10. data/app/models/mno_enterprise/app_feedback.rb +7 -0
  11. data/app/models/mno_enterprise/app_question.rb +9 -0
  12. data/app/models/mno_enterprise/app_review.rb +7 -0
  13. data/app/models/mno_enterprise/base_resource.rb +6 -1
  14. data/app/models/mno_enterprise/identity.rb +24 -0
  15. data/app/models/mno_enterprise/impac/alert.rb +10 -0
  16. data/app/models/mno_enterprise/impac/dashboard.rb +10 -11
  17. data/app/models/mno_enterprise/impac/kpi.rb +4 -2
  18. data/app/models/mno_enterprise/impac/widget.rb +4 -1
  19. data/app/models/mno_enterprise/team.rb +1 -33
  20. data/app/models/mno_enterprise/user.rb +109 -28
  21. data/app/views/system_notifications/email-change.html.erb +27 -0
  22. data/app/views/system_notifications/email-change.text.erb +10 -0
  23. data/app/views/system_notifications/password-change.html.erb +25 -0
  24. data/app/views/system_notifications/password-change.text.erb +7 -0
  25. data/app/views/system_notifications/reconfirmation-instructions.html.erb +7 -5
  26. data/app/views/system_notifications/reconfirmation-instructions.text.erb +3 -2
  27. data/config/initializers/config.rb +5 -0
  28. data/config/locales/models/user/en.yml +5 -0
  29. data/config/locales/templates/components/en.yml +9 -0
  30. data/config/locales/templates/dashboard/en.yml +14 -0
  31. data/config/locales/templates/dashboard/marketplace/en.yml +111 -8
  32. data/config/locales/templates/dashboard/organization/en.yml +1 -0
  33. data/config/locales/templates/impac/dock/en.yml +28 -0
  34. data/config/locales/views/auth/shared/en.yml +1 -2
  35. data/config/locales/views/webhook/o_auth/providers/en.yml +11 -6
  36. data/lib/devise/hooks/lockable.rb +13 -0
  37. data/lib/devise/models/remote_authenticatable.rb +31 -5
  38. data/lib/generators/mno_enterprise/install/install_generator.rb +7 -0
  39. data/lib/generators/mno_enterprise/install/templates/Procfile +1 -1
  40. data/lib/generators/mno_enterprise/install/templates/Procfile.dev +1 -1
  41. data/lib/generators/mno_enterprise/install/templates/config/application.yml +17 -0
  42. data/lib/generators/mno_enterprise/install/templates/config/initializers/mno_enterprise.rb +8 -3
  43. data/lib/generators/mno_enterprise/install/templates/config/newrelic.yml +46 -0
  44. data/lib/generators/mno_enterprise/install/templates/config/puma.rb +56 -0
  45. data/lib/generators/mno_enterprise/install/templates/config/settings/production.yml +1 -1
  46. data/lib/generators/mno_enterprise/install/templates/config/settings/uat.yml +1 -1
  47. data/lib/generators/mno_enterprise/install/templates/config/settings.yml +21 -0
  48. data/lib/generators/mno_enterprise/install/templates/stylesheets/variables.less +3 -0
  49. data/lib/her_extension/model/associations/association_proxy.rb +10 -6
  50. data/lib/mno_enterprise/concerns/controllers/auth/confirmations_controller.rb +1 -1
  51. data/lib/mno_enterprise/concerns/controllers/auth/omniauth_callbacks_controller.rb +203 -0
  52. data/lib/mno_enterprise/concerns/models/ability.rb +28 -0
  53. data/lib/mno_enterprise/concerns/models/app_instance.rb +22 -4
  54. data/lib/mno_enterprise/concerns/models/intercom_user.rb +28 -0
  55. data/lib/mno_enterprise/concerns/models/organization.rb +14 -1
  56. data/lib/mno_enterprise/concerns/models/team.rb +67 -0
  57. data/lib/mno_enterprise/core.rb +22 -3
  58. data/lib/mno_enterprise/impac_client.rb +19 -0
  59. data/lib/mno_enterprise/mail_adapters/mandrill_adapter.rb +4 -1
  60. data/lib/mno_enterprise/testing_support/factories/app_review.rb +51 -0
  61. data/lib/mno_enterprise/testing_support/factories/apps.rb +12 -10
  62. data/lib/mno_enterprise/testing_support/factories/identity.rb +9 -0
  63. data/lib/mno_enterprise/testing_support/factories/impac/alerts.rb +18 -0
  64. data/lib/mno_enterprise/testing_support/factories/impac/kpis.rb +5 -9
  65. data/lib/mno_enterprise/testing_support/factories/users.rb +4 -0
  66. data/lib/mno_enterprise/testing_support/jpi_v1_test_helper.rb +39 -0
  67. data/lib/mno_enterprise/testing_support/organizations_shared_helpers.rb +2 -1
  68. data/lib/mno_enterprise/testing_support/shared_examples/jpi_v1_admin.rb +38 -0
  69. data/lib/mno_enterprise/version.rb +1 -1
  70. data/spec/lib/devise/model/remote_authenticable_spec.rb +76 -0
  71. data/spec/lib/mno_enterprise/impac_client_spec.rb +31 -0
  72. data/spec/mno_enterprise_spec.rb +7 -8
  73. data/spec/models/mno_enterprise/app_spec.rb +1 -1
  74. data/spec/models/mno_enterprise/identity_spec.rb +39 -0
  75. data/spec/models/mno_enterprise/organization_spec.rb +19 -2
  76. data/spec/models/mno_enterprise/user_spec.rb +238 -0
  77. metadata +86 -22
@@ -3,24 +3,28 @@ module Her
3
3
  module Model
4
4
  module Associations
5
5
  class AssociationProxy < (ActiveSupport.const_defined?('ProxyObject') ? ActiveSupport::ProxyObject : ActiveSupport::BasicObject)
6
-
6
+
7
7
  install_proxy_methods :association,
8
8
  :build, :create, :update, :destroy, :where, :find, :all, :assign_nested_attributes, :reload, :order, :order_by, :limit, :skip
9
-
10
-
9
+
10
+ # Returns true if the association has been loaded, otherwise false.
11
+ def loaded?
12
+ !!association.instance_variable_get('@cached_result')
13
+ end
14
+
11
15
  def method_missing(name, *args, &block)
12
16
  if :object_id == name # avoid redefining object_id
13
17
  return association.fetch.object_id
14
18
  end
15
-
19
+
16
20
  # Check if a class scope has previously been defined
17
21
  begin
18
22
  if Relation.scopes.keys.grep(::Regexp.new(name.to_s)).any?
19
23
  return self.association.send(name,*args,&block)
20
24
  end
21
- rescue ::NoMethodError => e
25
+ rescue ::NoMethodError => e
22
26
  end
23
-
27
+
24
28
  # create a proxy to the fetched object's method
25
29
  # https://github.com/remiprev/her/pull/377
26
30
  AssociationProxy.install_proxy_methods 'association.fetch', name
@@ -98,7 +98,7 @@ module MnoEnterprise::Concerns::Controllers::Auth::ConfirmationsController
98
98
  sign_in resource, bypass: true
99
99
  set_flash_message(:notice, :confirmed) if is_flashing_format?
100
100
  yield(:success,resource) if block_given?
101
- MnoEnterprise::EventLogger.info('user_confirm', resource.id, 'User confirmed', nil, resource)
101
+ MnoEnterprise::EventLogger.info('user_confirm', resource.id, 'User confirmed', resource)
102
102
  respond_with_navigational(resource){ redirect_to after_confirmation_path_for(resource_name, resource, new_user: true) }
103
103
  else
104
104
  yield(:error,resource) if block_given?
@@ -0,0 +1,203 @@
1
+ # This controller is used to handle the authentication (+creation) of external
2
+ # users via OpenID (e.g: QuickBooks OpenID)
3
+ #
4
+ # When users click on the "sign in with <provider>" button, they get redirected
5
+ # to the authorize endpoint (/users/auth/:provider - e.g: /users/auth/intuit).
6
+ # The action (handled by parent controller OmniauthCallbacksController) prepares
7
+ # the callback url then redirects the user to the OpenID provider (E.g: Intuit)
8
+ # for authentication.
9
+ #
10
+ # Once authentication has been performed at the OpenID provider level (e.g: Intuit)
11
+ # the user gets redirected to the callback endpoint (/users/auth/:provider/callback)
12
+ # The provider parameter in the url (E.g: intuit) gets automatically redirected to a
13
+ # controller action with the same name (handled by parent controller OmniauthCallbacksController)
14
+ # as you can see below with intuit.
15
+ #
16
+ # Then provider specific action then handles the (creation +) authentication of the user.
17
+ # Also, it automatically adds the right applications to the user dashboard (e.g: QuickBooks for
18
+ # Intuit)
19
+ #
20
+ # Intuit:
21
+ # --------
22
+ # For intuit, the authorize endpoint is be bypassed when the user clicks "try Maestrano" from
23
+ # the Intuit marketplace. The user automatically lands on the callback endpoints with a parameter
24
+ # in the url called 'qb_initiated'. This parameter is used to automatically trigger the retrieval
25
+ # of the oauth token in the background via javascript (by storing the temporary grant url in session)
26
+ #
27
+ # On Intuit, it is also possible to directly choose one of the apps proposed by Maestrano (E.g: 'SugarCRM
28
+ # by Maestrano'). In this case, an 'app' attribute containing the app nid (named id - e.g: 'sugarcrm') is
29
+ # added to the url parameters. The action then setup the app automatically (along with QuickBooks).
30
+ #
31
+ #
32
+ module MnoEnterprise::Concerns::Controllers::Auth::OmniauthCallbacksController
33
+ extend ActiveSupport::Concern
34
+
35
+ #==================================================================
36
+ # Included methods
37
+ #==================================================================
38
+ included do
39
+ skip_filter :verify_authenticity_token, only: [:intuit]
40
+
41
+ providers = Devise.omniauth_providers & %i(linkedin google facebook)
42
+
43
+ providers.each do |provider|
44
+ provides_callback_for provider
45
+ end
46
+ end
47
+
48
+ #==================================================================
49
+ # Class methods
50
+ #==================================================================
51
+ module ClassMethods
52
+ def provides_callback_for(provider)
53
+ class_eval %Q{
54
+ def #{provider}
55
+ auth = env["omniauth.auth"]
56
+ opts = { orga_on_create: create_orga_on_user_creation(auth.info.email) }
57
+
58
+ @user = MnoEnterprise::User.find_for_oauth(auth, opts, current_user)
59
+
60
+ if @user.persisted?
61
+ sign_in_and_redirect @user, event: :authentication
62
+ set_flash_message(:notice, :success, kind: "#{provider}".capitalize) if is_navigational_format?
63
+ else
64
+ session["devise.#{provider}_data"] = env["omniauth.auth"]
65
+ redirect_to new_user_registration_url
66
+ end
67
+ end
68
+ }
69
+ end
70
+ end
71
+
72
+ #==================================================================
73
+ # Instance methods
74
+ #==================================================================
75
+ # GET|POST /users/auth/:action/callback
76
+ def intuit
77
+ auth = request.env['omniauth.auth']
78
+ opts = {
79
+ orga_on_create: create_orga_on_user_creation(auth.info.email),
80
+ authorized_link_to_email: session['omniauth.intuit.authorized_link_to_email']
81
+ }
82
+
83
+ # Try to find via intuit
84
+ begin
85
+ @user = MnoEnterprise::User.find_for_oauth(auth, opts, current_user)
86
+ rescue SecurityError
87
+ # Intuit email is NOT a confirmed email. Therefore we need to ask the user to
88
+ # login the old fashion to make sure it is the right user!
89
+ session["omniauth.intuit.request_account_link"] = true
90
+ redirect_to new_user_session_path, notice: "Please sign in using your regular Maestrano account to confirm that you want to link it to your Intuit account"
91
+ return
92
+ end
93
+
94
+ # Cleanup any temporary omniauth.intuit session
95
+ cleanup_intuit_session
96
+
97
+ if @user && @user.persisted?
98
+ # Automatically adds a QuickBooks app (and any other app passed via :app param)
99
+ # to the user orga
100
+ # Only for new users for which an orga was created (not an invited user
101
+ # typically)
102
+ app_instances = setup_apps(@user,['quickbooks',params[:app]], oauth_keyset: params[:app])
103
+ qb_instance = app_instances.first
104
+
105
+ # On Intuit, Mno is configured to add qb_initiated=true if the user
106
+ # comes directly from apps.com (This is a different workflow from using
107
+ # the QuickBooks connect button because we're supposed to trigger the
108
+ # oauth workflow directly via javascript using directConnectToIntuit)
109
+ # Here we store in session the fact that we need to trigger an oauth
110
+ # workflow via directConnectToIntuit
111
+ # ----
112
+ # See layouts/partners/intuit for more info. The session param set
113
+ # below get reset in the view.
114
+ #
115
+ if params[:qb_initiated] && qb_instance && !qb_instance.oauth_keys_valid?
116
+ session[:qb_direct_connect_grant_url] = authorize_webhook_oauth_url(qb_instance.uid)
117
+ end
118
+
119
+ # The above methods trigger many different hooks which
120
+ # may impact the user (typically user workspace). It is safer
121
+ # to reload the user before continuing so that the picture
122
+ # is up to date
123
+ @user.reload
124
+
125
+ # Check whether we should redirect the user to a specific
126
+ # url
127
+ redirect_url = session.delete(:openid_previous_url) || MnoEnterprise.router.dashboard_path || main_app.root_path
128
+
129
+ sign_in @user
130
+ redirect_to redirect_url, event: :authentication
131
+
132
+ set_flash_message(:notice, :success, kind: "Intuit") if is_navigational_format?
133
+ else
134
+ session["devise.intuit_data"] = request.env["omniauth.auth"]
135
+ redirect_to home_url, "ng-controller" => "MnoSignupProcessCtrl", "ng-click" => "startProcess()"
136
+ end
137
+ end
138
+
139
+ #================================================
140
+ # Private methods
141
+ #================================================
142
+ private
143
+
144
+ def cleanup_intuit_session
145
+ session.delete("omniauth.intuit.passthru_email")
146
+ session.delete("omniauth.intuit.request_account_link")
147
+ end
148
+
149
+ # Whether to create an orga on user creation
150
+ def create_orga_on_user_creation(user_email = nil)
151
+ return false if user_email.blank?
152
+ return false if MnoEnterprise::User.exists?(email: user_email)
153
+
154
+ # First check previous url to see if the user
155
+ # was trying to accept an orga
156
+ if !session[:previous_url].blank? && (r = session[:previous_url].match(/\/orga_invites\/(\d+)\?token=(\w+)/))
157
+ invite_params = { id: r.captures[0].to_i, token: r.captures[1] }
158
+ return false if OrgInvite.where(invite_params).any?
159
+ end
160
+
161
+ # Get remaining invites via email address
162
+ return MnoEnterprise::OrgInvite.where(user_email: user_email).empty?
163
+ end
164
+
165
+ # Create or find the apps provided in argument
166
+ # Accept an array of app nid (named id - e.g: 'quickbooks')
167
+ # opts:
168
+ # oauth_keyset: If a oauth_keyset is provided then it will be added to the
169
+ # oauth_keys of any app that is oauth ready (QuickBooks for example)
170
+ #
171
+ # Return an array of app instances (found or created)
172
+ def setup_apps(user = nil, app_nids = [], opts = {})
173
+ return [] unless user
174
+ return [] unless (user.organizations.reload.count == 1)
175
+ return [] unless (org = user.organizations.first)
176
+ return [] unless MnoEnterprise::Ability.new(user).can?(:edit,org)
177
+
178
+ results = []
179
+
180
+ apps = MnoEnterprise::App.where('nid.in' => app_nids.compact)
181
+ existing = org.app_instances.active.index_by(&:app_id)
182
+
183
+ # For each app nid (which is not nil), try to find an existing instance or create one
184
+ apps.each do |app|
185
+ if (app_instance = existing[app.id])
186
+ results << app_instance
187
+ else
188
+ # Provision instance and add to results
189
+ app_instance = org.app_instances.create(product: app.nid)
190
+ results << app_instance
191
+ MnoEnterprise::EventLogger.info('app_add', user.id, 'App added', app_instance)
192
+ end
193
+
194
+ # Add oauth keyset if defined and app_instance is
195
+ # oauth ready and does not have a valid set of oauth keys
196
+ if app_instance && opts[:oauth_keyset].present? && !app_instance.oauth_keys_valid?
197
+ app_instance.oauth_keys = { keyset: opts[:oauth_keyset] }
198
+ app_instance.save
199
+ end
200
+ end
201
+ return results
202
+ end
203
+ end
@@ -109,6 +109,34 @@ module MnoEnterprise::Concerns::Models::Ability
109
109
  !!user.role(org) && ['Super Admin', 'Admin'].include?(user.role(org))
110
110
  end
111
111
  end
112
+
113
+ can :manage_dashboard, MnoEnterprise::Impac::Dashboard do |dashboard|
114
+ if dashboard.owner_type == "Organization"
115
+ # The current user is a member of the organization that owns the dashboard that has the kpi attached to
116
+ owner = MnoEnterprise::Organization.find(dashboard.owner_id)
117
+ owner && !!user.role(owner)
118
+ elsif dashboard.owner_type == "User"
119
+ # The current user is the owner of the dashboard that has the kpi attached to
120
+ dashboard.owner_id == user.id
121
+ else
122
+ false
123
+ end
124
+ end
125
+
126
+ can :manage_widget, MnoEnterprise::Impac::Widget do |widget|
127
+ dashboard = widget.dashboard
128
+ authorize! :manage_dashboard, dashboard
129
+ end
130
+
131
+ can :manage_kpi, MnoEnterprise::Impac::Kpi do |kpi|
132
+ dashboard = kpi.dashboard
133
+ authorize! :manage_dashboard, dashboard
134
+ end
135
+
136
+ can :manage_alert, MnoEnterprise::Impac::Alert do |alert|
137
+ kpi = alert.kpi
138
+ authorize! :manage_kpi, kpi
139
+ end
112
140
  end
113
141
 
114
142
  # Abilities for admin user
@@ -40,13 +40,13 @@ module MnoEnterprise::Concerns::Models::AppInstance
40
40
  included do
41
41
  attributes :id, :uid, :name, :status, :app_id, :created_at, :updated_at, :started_at, :stack, :owner_id,
42
42
  :owner_type, :terminated_at, :stopped_at, :billing_type, :autostop_at, :autostop_interval,
43
- :next_status, :soa_enabled, :oauth_keys_valid, :oauth_company
43
+ :next_status, :soa_enabled, :oauth_company, :oauth_keys, :oauth_keys_valid, :free_trial_end_at, :per_user_licence, :active_licences_count
44
44
 
45
45
  #==============================================================
46
46
  # Constants
47
47
  #==============================================================
48
- ACTIVE_STATUSES = [:running,:stopped,:staged,:provisioning,:starting,:stopping,:updating]
49
- TERMINATION_STATUSES = [:terminating,:terminated]
48
+ ACTIVE_STATUSES = [:running, :stopped, :staged, :provisioning, :starting, :stopping, :updating]
49
+ TERMINATION_STATUSES = [:terminating, :terminated]
50
50
 
51
51
  #==============================================================
52
52
  # Associations
@@ -55,7 +55,7 @@ module MnoEnterprise::Concerns::Models::AppInstance
55
55
  belongs_to :app, class_name: 'MnoEnterprise::App'
56
56
 
57
57
  # Define connector_stack?, cloud_stack? etc. methods
58
- [:cube,:cloud,:connector].each do |stackname|
58
+ [:cube, :cloud, :connector].each do |stackname|
59
59
  define_method("#{stackname}_stack?") do
60
60
  self.stack == stackname.to_s
61
61
  end
@@ -89,8 +89,26 @@ module MnoEnterprise::Concerns::Models::AppInstance
89
89
  ACTIVE_STATUSES.include?(self.status.to_sym)
90
90
  end
91
91
 
92
+ def per_user_licence?
93
+ self.per_user_licence
94
+ end
95
+
96
+ def under_free_trial?
97
+ self.under_free_trial
98
+ end
99
+
92
100
  def running?
93
101
  self.status == 'running'
94
102
  end
95
103
 
104
+ def to_audit_event
105
+ {
106
+ id: id,
107
+ uid: uid,
108
+ name: name,
109
+ app_nid: app ? app.nid : nil
110
+ }
111
+ end
112
+
113
+
96
114
  end
@@ -0,0 +1,28 @@
1
+ require 'openssl'
2
+
3
+ module MnoEnterprise::Concerns::Models::IntercomUser
4
+ extend ActiveSupport::Concern
5
+
6
+ #==================================================================
7
+ # Included methods
8
+ #==================================================================
9
+ # 'included do' causes the included code to be evaluated in the
10
+ # context where it is included rather than being executed in the module's context
11
+ included do
12
+ end
13
+
14
+ #==================================================================
15
+ # Class methods
16
+ #==================================================================
17
+ module ClassMethods
18
+ end
19
+
20
+ #==================================================================
21
+ # Instance methods
22
+ #==================================================================
23
+ # Return intercom user hash
24
+ # This is used in secure mode
25
+ def intercom_user_hash
26
+ OpenSSL::HMAC.hexdigest('sha256', MnoEnterprise.intercom_api_secret, (self.id || self.email).to_s) if MnoEnterprise.intercom_api_secret
27
+ end
28
+ end
@@ -51,6 +51,7 @@ module MnoEnterprise::Concerns::Models::Organization
51
51
  has_one :credit_card, class_name: 'MnoEnterprise::CreditCard'
52
52
  has_many :teams, class_name: 'MnoEnterprise::Team'
53
53
  has_many :dashboards, class_name: 'MnoEnterprise::Impac::Dashboard'
54
+ has_many :widgets, class_name: 'MnoEnterprise::Impac::Widget'
54
55
  has_one :raw_last_invoice, class_name: 'MnoEnterprise::Invoice', path: '/last_invoice'
55
56
  has_one :app_instances_sync, class_name: 'MnoEnterprise::AppInstancesSync'
56
57
  end
@@ -73,7 +74,7 @@ module MnoEnterprise::Concerns::Models::Organization
73
74
  # @params [Boolean] show_staged Also displayed staged invites (ie: not sent)
74
75
  def members(show_staged=false)
75
76
  invites = show_staged ? self.org_invites.active_or_staged : self.org_invites.active
76
- [self.users, invites].flatten
77
+ [self.users, invites.to_a].flatten
77
78
  end
78
79
 
79
80
  # Add a user to the organization with the provided role
@@ -102,4 +103,16 @@ module MnoEnterprise::Concerns::Models::Organization
102
103
  def update_user(user, role = 'Member')
103
104
  self.users.update(id: user.id, role: role)
104
105
  end
106
+
107
+ def to_audit_event
108
+ {
109
+ id: id,
110
+ uid: uid,
111
+ name: name
112
+ }
113
+ end
114
+
115
+ def payment_restriction
116
+ meta_data && meta_data['payment_restriction']
117
+ end
105
118
  end
@@ -0,0 +1,67 @@
1
+ # == Schema Information
2
+ #
3
+ # Endpoint:
4
+ # - /v1/teams
5
+ # - /v1/organizations/:organization_id/teams
6
+ #
7
+ # id :integer not null, primary key
8
+ # name :string(255)
9
+ # created_at :datetime not null
10
+ # updated_at :datetime not null
11
+ # organization_id :integer
12
+ #
13
+
14
+ module MnoEnterprise::Concerns::Models::Team
15
+ extend ActiveSupport::Concern
16
+
17
+ #==================================================================
18
+ # Included methods
19
+ #==================================================================
20
+ # 'included do' causes the included code to be evaluated in the
21
+ # context where it is included rather than being executed in the module's context
22
+ included do
23
+ attributes :id, :name, :organization_id
24
+
25
+ #=====================================
26
+ # Associations
27
+ #=====================================
28
+ belongs_to :organization, class_name: 'MnoEnterprise::Organization'
29
+ has_many :users, class_name: 'MnoEnterprise::User'
30
+ has_many :app_instances, class_name: 'MnoEnterprise::AppInstance'
31
+ end
32
+
33
+ #==================================================================
34
+ # Class methods
35
+ #==================================================================
36
+ module ClassMethods
37
+ # def some_class_method
38
+ # 'some text'
39
+ # end
40
+ end
41
+
42
+ #==================================================================
43
+ # Instance methods
44
+ #==================================================================
45
+ # Add a user to the team
46
+ # TODO: specs
47
+ def add_user(user)
48
+ self.users.create(id: user.id)
49
+ end
50
+
51
+ # Remove a user from the team
52
+ # TODO: specs
53
+ def remove_user(user)
54
+ self.users.destroy(id: user.id)
55
+ end
56
+
57
+ # Set the app_instance permissions of this team
58
+ # Accept a collection of hashes or an array of ids
59
+ # TODO: specs
60
+ def set_access_to(collection_or_array)
61
+ # Empty arrays do not seem to be passed in the request. Force value in this case
62
+ list = collection_or_array.empty? ? [""] : collection_or_array
63
+ self.put(data: { set_access_to: list })
64
+ self.reload
65
+ self
66
+ end
67
+ end
@@ -21,6 +21,7 @@ require "her_extension/model/associations/belongs_to_association"
21
21
  require "her_extension/middleware/mnoe_api_v1_parse_json"
22
22
  require "her_extension/middleware/mnoe_raise_error"
23
23
  require "faraday_middleware"
24
+ require "httparty"
24
25
  require "mno_enterprise/engine"
25
26
 
26
27
  require 'mno_enterprise/database_extendable'
@@ -29,8 +30,7 @@ require 'mno_enterprise/database_extendable'
29
30
  require 'config'
30
31
  require 'figaro'
31
32
 
32
- require "mandrill_client"
33
-
33
+ require 'mandrill_client'
34
34
  require 'accountingjs_serializer'
35
35
 
36
36
  module MnoEnterprise
@@ -52,7 +52,7 @@ module MnoEnterprise
52
52
  end
53
53
 
54
54
  def terms_url
55
- @terms_url || '#'
55
+ @terms_url || '/mnoe/terms'
56
56
  end
57
57
 
58
58
  def admin_path
@@ -213,6 +213,23 @@ module MnoEnterprise
213
213
  mattr_accessor :google_tag_container
214
214
  @@google_tag_container = nil
215
215
 
216
+ mattr_accessor :intercom_app_id
217
+ @@intercom_app_id = nil
218
+
219
+ mattr_accessor :intercom_api_secret
220
+ @@intercom_api_secret = nil
221
+
222
+ mattr_accessor :intercom_api_key
223
+ @@intercom_api_key = nil
224
+
225
+ mattr_accessor :intercom_token
226
+ @@intercom_token = nil
227
+
228
+ # Define if Intercom is enabled. Only if the gem intercom is present
229
+ def self.intercom_enabled?
230
+ defined?(::Intercom) && ((intercom_app_id && intercom_api_key) || intercom_token)
231
+ end
232
+
216
233
  #====================================
217
234
  # Layout & Styling
218
235
  #====================================
@@ -248,6 +265,8 @@ module MnoEnterprise
248
265
  @@style
249
266
  end
250
267
 
268
+
269
+
251
270
  # Default way to setup MnoEnterprise. Run rails generate mno-enterprise:install to create
252
271
  # a fresh initializer with all configuration values.
253
272
  def self.configure
@@ -0,0 +1,19 @@
1
+ module MnoEnterprise
2
+ class ImpacClient
3
+ include HTTParty
4
+
5
+ def self.host
6
+ "#{Settings.impac.protocol}://#{Settings.impac.host}"
7
+ end
8
+
9
+ def self.endpoint_url(endpoint, params)
10
+ "#{File.join(host,endpoint)}?#{params.to_query}"
11
+ end
12
+
13
+ def self.send_get(endpoint, params, opts={})
14
+ url = endpoint_url(endpoint, params)
15
+ get(url, opts)
16
+ end
17
+
18
+ end
19
+ end
@@ -16,7 +16,10 @@ module MnoEnterprise
16
16
  # Prepare message from args
17
17
  message = { from_name: from[:name], from_email: from[:email]}
18
18
  message[:to] = [to].flatten.map { |t| {name: t[:name], email: t[:email], type: (t[:type] || :to) } }
19
- message[:global_merge_vars] = vars.map { |k,v| {name: k.to_s, content: v} }
19
+
20
+ # Sanitize merge vars
21
+ full_sanitizer = Rails::Html::FullSanitizer.new
22
+ message[:global_merge_vars] = vars.map { |k,v| {name: k.to_s, content: full_sanitizer.sanitize(v)} }
20
23
 
21
24
  # Merge additional mandrill options
22
25
  message.merge!(opts)
@@ -0,0 +1,51 @@
1
+ # Read about factories at https://github.com/thoughtbot/factory_girl
2
+
3
+ FactoryGirl.define do
4
+ factory :mno_enterprise_app_review, :class => 'AppReview' do
5
+
6
+ factory :app_review, class: MnoEnterprise::AppReview do
7
+ sequence(:id)
8
+ description 'Some Description'
9
+ status 'approved'
10
+ rating 3
11
+ app_id 'app-id'
12
+ app_name 'the app'
13
+ user_id 'usr-11'
14
+ user_name 'Jean Bon'
15
+ organization_id 'org-11'
16
+ organization_name 'Organization 11'
17
+ created_at 3.days.ago
18
+ updated_at 1.hour.ago
19
+ edited true
20
+ edited_by_name 'Jane Dale'
21
+ edited_by_admin_role 'admin'
22
+ edited_by_id 1
23
+
24
+ user_admin_role 'admin'
25
+ # Properly build the resource with Her
26
+ initialize_with { new(attributes).tap { |e| e.clear_attribute_changes! } }
27
+
28
+ factory :app_feedback, class: MnoEnterprise::AppFeedback do
29
+ type 'Feedback'
30
+ sequence(:description) { |n| "Feedback ##{n}" }
31
+ end
32
+
33
+ factory :app_question, class: MnoEnterprise::AppQuestion do
34
+ type 'Question'
35
+ sequence(:description) { |n| "Question ##{n}" }
36
+ end
37
+
38
+ factory :app_comment, class: MnoEnterprise::AppComment do
39
+ type 'Comment'
40
+ sequence(:description) { |n| "Comment ##{n}" }
41
+ feedback_id 'feedback-id'
42
+ end
43
+
44
+ factory :app_answer, class: MnoEnterprise::AppAnswer do
45
+ type 'Answer'
46
+ sequence(:description) { |n| "Answer ##{n}" }
47
+ question_id 'question-id'
48
+ end
49
+ end
50
+ end
51
+ end
@@ -5,7 +5,7 @@ FactoryGirl.define do
5
5
  sequence(:id) { |n| n }
6
6
  sequence(:name) { |n| "TestApp#{n}" }
7
7
  nid { name.parameterize }
8
-
8
+
9
9
  description "This is a description"
10
10
  created_at 1.day.ago
11
11
  updated_at 2.hours.ago
@@ -14,30 +14,32 @@ FactoryGirl.define do
14
14
  slug { "#{id}-myapp" }
15
15
  categories ["CRM"]
16
16
  tags ['Foo', 'Bar']
17
- key_benefits ['Super','Hyper','Good']
18
- key_features ['Super','Hyper','Good']
19
- testimonials [{text:'Bla', company:'Doe Pty Ltd', author: 'John'}]
17
+ key_benefits ['Super', 'Hyper', 'Good']
18
+ key_features ['Super', 'Hyper', 'Good']
19
+ testimonials [{text: 'Bla', company: 'Doe Pty Ltd', author: 'John'}]
20
20
  worldwide_usage 120000
21
21
  tiny_description "A great app"
22
22
  stack 'cube'
23
23
  terms_url "http://opensource.org/licenses/MIT"
24
24
  appinfo { {} }
25
+ average_rating { rand(1..5) }
25
26
  sequence(:rank) { |n| n }
26
- pricing_plans {{
27
- 'default' =>[{name: 'Monthly Plan', price: '20.0', currency: 'AUD', factor: '/month'}]
28
- }}
27
+ running_instances_count { rand(0..10) }
28
+ pricing_plans { {
29
+ 'default' => [{name: 'Monthly Plan', price: '20.0', currency: 'AUD', factor: '/month'}]
30
+ } }
29
31
 
30
32
  trait :cloud do
31
33
  stack 'cloud'
32
34
  end
33
-
35
+
34
36
  trait :connector do
35
37
  stack 'connector'
36
38
  end
37
-
39
+
38
40
  factory :cloud_app, traits: [:cloud]
39
41
  factory :connector_app, traits: [:connector]
40
-
42
+
41
43
  # Properly build the resource with Her
42
44
  initialize_with { new(attributes).tap { |e| e.clear_attribute_changes! } }
43
45
  end
@@ -0,0 +1,9 @@
1
+ FactoryGirl.define do
2
+ factory :identity, class: MnoEnterprise::Identity do
3
+ provider 'someprovider'
4
+ uid '123456'
5
+
6
+ # Properly build the resource with Her
7
+ initialize_with { new(attributes).tap { |e| e.clear_attribute_changes! } }
8
+ end
9
+ end