mno-enterprise-core 3.2.1 → 3.3.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.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/mno_enterprise/application_controller.rb +5 -5
  3. data/app/helpers/mno_enterprise/image_helper.rb +32 -0
  4. data/app/models/mno_enterprise/app.rb +11 -2
  5. data/app/models/mno_enterprise/base_resource.rb +2 -0
  6. data/app/models/mno_enterprise/credit_card.rb +7 -4
  7. data/app/models/mno_enterprise/impac/dashboard.rb +4 -1
  8. data/app/models/mno_enterprise/impac/widget.rb +10 -4
  9. data/app/models/mno_enterprise/org_invite.rb +7 -3
  10. data/app/models/mno_enterprise/shared_entity.rb +17 -0
  11. data/app/models/mno_enterprise/user.rb +7 -2
  12. data/app/pdf/mno_enterprise/invoice_pdf.rb +176 -183
  13. data/app/views/system_notifications/email-change.html.erb +1 -1
  14. data/app/views/system_notifications/email-change.text.erb +1 -1
  15. data/config/initializers/audit_log.rb +28 -1
  16. data/config/locales/templates/components/en.yml +29 -4
  17. data/config/locales/templates/dashboard/en.yml +2 -2
  18. data/config/locales/templates/dashboard/marketplace/en.yml +5 -5
  19. data/config/locales/templates/dashboard/organization/en.yml +14 -8
  20. data/config/locales/templates/dashboard/organization/id.yml +4 -4
  21. data/config/locales/templates/dashboard/organization/zh.yml +4 -4
  22. data/config/locales/templates/dashboard/teams/en.yml +3 -3
  23. data/config/locales/templates/dashboard/teams/id.yml +3 -3
  24. data/config/locales/templates/dashboard/teams/zh.yml +3 -3
  25. data/config/locales/templates/onboarding/en.yml +45 -0
  26. data/config/locales/views/auth/confirmations/en.yml +5 -3
  27. data/config/locales/views/webhook/o_auth/providers/en.yml +2 -2
  28. data/lib/generators/mno_enterprise/install/install_generator.rb +19 -1
  29. data/lib/generators/mno_enterprise/install/templates/Procfile.dev +1 -0
  30. data/lib/generators/mno_enterprise/install/templates/config/initializers/mno_enterprise.rb +7 -50
  31. data/lib/generators/mno_enterprise/install/templates/config/newrelic.yml +3 -3
  32. data/lib/generators/mno_enterprise/install/templates/config/settings.yml +49 -2
  33. data/lib/generators/mno_enterprise/install/templates/nginx.conf +71 -0
  34. data/lib/generators/mno_enterprise/install/templates/stylesheets/variables.less +148 -141
  35. data/lib/html_processor.rb +14 -14
  36. data/lib/mno_enterprise/concerns/controllers/auth/registrations_controller.rb +18 -15
  37. data/lib/mno_enterprise/concerns/models/ability.rb +6 -3
  38. data/lib/mno_enterprise/concerns/models/app_instance.rb +2 -3
  39. data/lib/mno_enterprise/concerns/models/intercom_user.rb +22 -0
  40. data/lib/mno_enterprise/concerns/models/organization.rb +8 -0
  41. data/lib/mno_enterprise/concerns/models/shared_entity.rb +36 -0
  42. data/lib/mno_enterprise/concerns/models/team.rb +7 -0
  43. data/lib/mno_enterprise/testing_support/common_rake.rb +1 -1
  44. data/lib/mno_enterprise/testing_support/factories/apps.rb +6 -0
  45. data/lib/mno_enterprise/testing_support/factories/audit_event.rb +2 -0
  46. data/lib/mno_enterprise/testing_support/mno_enterprise_api_test_helper.rb +47 -16
  47. data/lib/mno_enterprise/testing_support/organizations_shared_helpers.rb +6 -9
  48. data/lib/mno_enterprise/version.rb +1 -1
  49. data/spec/config/initializers/audit_log_spec.rb +5 -0
  50. data/spec/controllers/mno_enterprise/application_controller_spec.rb +4 -4
  51. data/spec/helpers/image_helper_spec.rb +69 -0
  52. data/spec/models/mno_enterprise/ability_spec.rb +5 -0
  53. data/spec/models/mno_enterprise/app_spec.rb +1 -1
  54. data/spec/models/mno_enterprise/base_resource_spec.rb +37 -0
  55. data/spec/models/mno_enterprise/credit_card_spec.rb +18 -0
  56. data/spec/models/mno_enterprise/organization_spec.rb +16 -0
  57. data/spec/models/mno_enterprise/shared_entity_spec.rb +7 -0
  58. data/spec/models/mno_enterprise/user_spec.rb +83 -15
  59. metadata +15 -2
@@ -8,13 +8,13 @@ require 'sanitize'
8
8
  # You can initialize it with html or markdown text
9
9
  class HtmlProcessor
10
10
  attr_reader :html, :original
11
-
11
+
12
12
  #======================================
13
13
  # Constants
14
14
  #======================================
15
15
  DESCRIPTION_PROCESSING_ORDER = %w( p h1 h2 h3 h4 h5 h6 )
16
-
17
-
16
+
17
+
18
18
  # Define Youtube transformer for Sanitize
19
19
  YOUTUBE_TRANSFORMER = lambda do |env|
20
20
  node = env[:node]
@@ -45,7 +45,7 @@ class HtmlProcessor
45
45
  # to whitelist the current node.
46
46
  {:node_whitelist => [node]}
47
47
  end
48
-
48
+
49
49
  # Default options for Sanitize
50
50
  SANITIZER_OPTS = Sanitize::Config::RELAXED.merge(
51
51
  attributes: Sanitize::Config::RELAXED[:attributes].merge(
@@ -53,18 +53,18 @@ class HtmlProcessor
53
53
  ),
54
54
  transformers: YOUTUBE_TRANSFORMER
55
55
  )
56
-
56
+
57
57
  #======================================
58
58
  # Methods
59
59
  #======================================
60
60
  def initialize(text, options = { })
61
61
  @original = text
62
-
62
+
63
63
  # Process markdown or leave original
64
64
  if options[:format].to_s == 'markdown' && text
65
65
  html_options = { :safe_links_only => true, :hard_wrap => true, :filter_html => false }
66
66
  renderer_options = { :autolink => true, :no_intraemphasis => true, :fenced_code_blocks => true, :superscript => true }
67
-
67
+
68
68
  renderer = Redcarpet::Markdown.new(Redcarpet::Render::HTML.new(html_options), renderer_options)
69
69
  raw_html = renderer.render(text)
70
70
  @html = Sanitize.fragment(raw_html, SANITIZER_OPTS)
@@ -72,27 +72,27 @@ class HtmlProcessor
72
72
  @html = text
73
73
  end
74
74
  end
75
-
75
+
76
76
  # Return a Nokogiri document based
77
77
  # on processor html
78
78
  def document
79
79
  @document ||= Nokogiri::HTML(@html)
80
80
  end
81
-
81
+
82
82
  # Return a description of the document
83
83
  # by returning the first sentence of the
84
84
  # first DESCRIPTION_PROCESSING_ORDER found
85
85
  def description
86
86
  # Return cached value if one
87
87
  return @description if @description
88
-
88
+
89
89
  # Parse the html document to try to find
90
90
  # a description
91
91
  @description = ''
92
92
  DESCRIPTION_PROCESSING_ORDER.each do |selector|
93
- elem = self.document.css(selector).select { |e| e && !e.content.blank? }.first
93
+ elem = self.document.css(selector).detect { |e| e && !e.content.blank? }
94
94
  next if elem.blank? #skip if nil or empty
95
-
95
+
96
96
  # Try to get the first two sentences
97
97
  match = elem.content.match(/([^.!?]+[.!?]?)([^.!?]+[.!?]?)?/)
98
98
  if match && match.captures.any?
@@ -100,7 +100,7 @@ class HtmlProcessor
100
100
  end
101
101
  break if !@description.empty?
102
102
  end
103
-
103
+
104
104
  return @description
105
105
  end
106
- end
106
+ end
@@ -1,6 +1,6 @@
1
1
  module MnoEnterprise::Concerns::Controllers::Auth::RegistrationsController
2
2
  extend ActiveSupport::Concern
3
-
3
+
4
4
  #==================================================================
5
5
  # Included methods
6
6
  #==================================================================
@@ -9,22 +9,22 @@ module MnoEnterprise::Concerns::Controllers::Auth::RegistrationsController
9
9
  included do
10
10
  before_filter :configure_sign_up_params, only: [:create]
11
11
  # before_filter :configure_account_update_params, only: [:update]
12
-
12
+
13
13
  protected
14
14
  def configure_sign_up_params
15
15
  devise_parameter_sanitizer.for(:sign_up) { |u| u.permit(
16
- :email,
17
- :password,
18
- :password_confirmation,
19
- :name,
16
+ :email,
17
+ :password,
18
+ :password_confirmation,
19
+ :name,
20
20
  :surname,
21
21
  :company,
22
22
  :phone,
23
23
  :phone_country_code
24
- )}
24
+ )}
25
25
  end
26
26
  end
27
-
27
+
28
28
  #==================================================================
29
29
  # Class methods
30
30
  #==================================================================
@@ -33,7 +33,7 @@ module MnoEnterprise::Concerns::Controllers::Auth::RegistrationsController
33
33
  # 'some text'
34
34
  # end
35
35
  end
36
-
36
+
37
37
  #==================================================================
38
38
  # Instance methods
39
39
  #==================================================================
@@ -46,10 +46,13 @@ module MnoEnterprise::Concerns::Controllers::Auth::RegistrationsController
46
46
  def create
47
47
  build_resource(sign_up_params)
48
48
  resource.password ||= Devise.friendly_token
49
-
49
+
50
50
  resource_saved = resource.save
51
-
51
+
52
52
  if resource_saved
53
+
54
+ MnoEnterprise::EventLogger.info('user_add', resource_saved.id, 'User Signup', resource_saved)
55
+
53
56
  if resource.active_for_authentication?
54
57
  set_flash_message :notice, :signed_up if is_flashing_format?
55
58
  sign_up(resource_name, resource)
@@ -97,7 +100,7 @@ module MnoEnterprise::Concerns::Controllers::Auth::RegistrationsController
97
100
  # end
98
101
 
99
102
  protected
100
-
103
+
101
104
  # You can put the params you want to permit in the empty array.
102
105
  # def configure_account_update_params
103
106
  # devise_parameter_sanitizer.for(:account_update) << :attribute
@@ -112,12 +115,12 @@ module MnoEnterprise::Concerns::Controllers::Auth::RegistrationsController
112
115
  # def after_inactive_sign_up_path_for(resource)
113
116
  # super(resource)
114
117
  # end
115
-
118
+
116
119
  def sign_up_params
117
120
  attrs = super
118
121
  attrs.merge(orga_on_create: create_orga_on_user_creation(attrs))
119
122
  end
120
-
123
+
121
124
  # Check whether we should create an organization for the user
122
125
  def create_orga_on_user_creation(user_attrs)
123
126
  return false unless user_attrs['email']
@@ -133,4 +136,4 @@ module MnoEnterprise::Concerns::Controllers::Auth::RegistrationsController
133
136
  # Get remaining invites via email address
134
137
  return MnoEnterprise::OrgInvite.where(user_email: user_attrs['email']).empty?
135
138
  end
136
- end
139
+ end
@@ -129,8 +129,11 @@ module MnoEnterprise::Concerns::Models::Ability
129
129
  end
130
130
 
131
131
  can :manage_kpi, MnoEnterprise::Impac::Kpi do |kpi|
132
- dashboard = kpi.dashboard
133
- authorize! :manage_dashboard, dashboard
132
+ if kpi.widget.present?
133
+ authorize! :manage_widget, MnoEnterprise::Impac::Widget.find(kpi.widget.id)
134
+ else
135
+ authorize! :manage_dashboard, kpi.dashboard
136
+ end
134
137
  end
135
138
 
136
139
  can :manage_alert, MnoEnterprise::Impac::Alert do |alert|
@@ -141,7 +144,7 @@ module MnoEnterprise::Concerns::Models::Ability
141
144
 
142
145
  # Abilities for admin user
143
146
  def admin_abilities(user)
144
- if user.admin_role == 'admin'
147
+ if user.admin_role.to_s.casecmp('admin').zero?
145
148
  can :manage_app_instances, MnoEnterprise::Organization
146
149
  end
147
150
  end
@@ -106,9 +106,8 @@ module MnoEnterprise::Concerns::Models::AppInstance
106
106
  id: id,
107
107
  uid: uid,
108
108
  name: name,
109
- app_nid: app ? app.nid : nil
109
+ app_nid: app ? app.nid : nil,
110
+ organization_id: owner_id || owner.id
110
111
  }
111
112
  end
112
-
113
-
114
113
  end
@@ -25,4 +25,26 @@ module MnoEnterprise::Concerns::Models::IntercomUser
25
25
  def intercom_user_hash
26
26
  OpenSSL::HMAC.hexdigest('sha256', MnoEnterprise.intercom_api_secret, (self.id || self.email).to_s) if MnoEnterprise.intercom_api_secret
27
27
  end
28
+
29
+ # Return Intercom user data hash
30
+ def intercom_data(update_last_request_at = true)
31
+ data = {
32
+ user_id: self.id,
33
+ name: [self.name, self.surname].join(' '),
34
+ email: self.email,
35
+ created_at: self.created_at.to_i,
36
+ last_seen_ip: self.last_sign_in_ip,
37
+ custom_attributes: {
38
+ first_name: self.name,
39
+ surname: self.surname,
40
+ confirmed_at: self.confirmed_at,
41
+ admin_role: self.admin_role
42
+ },
43
+ update_last_request_at: update_last_request_at
44
+ }
45
+ data[:custom_attributes][:phone]= self.phone if self.phone
46
+ data[:custom_attributes][:external_id]= self.external_id if self.external_id
47
+
48
+ data
49
+ end
28
50
  end
@@ -41,6 +41,10 @@ module MnoEnterprise::Concerns::Models::Organization
41
41
 
42
42
  scope :in_arrears, -> { where(in_arrears?: true) }
43
43
 
44
+ scope :active, -> { where(account_frozen: false) }
45
+
46
+ default_scope lambda { where(account_frozen: false) }
47
+
44
48
  #================================
45
49
  # Associations
46
50
  #================================
@@ -115,4 +119,8 @@ module MnoEnterprise::Concerns::Models::Organization
115
119
  def payment_restriction
116
120
  meta_data && meta_data['payment_restriction']
117
121
  end
122
+
123
+ def has_credit_card_details?
124
+ credit_card.persisted?
125
+ end
118
126
  end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+ # == Schema Information
3
+ #
4
+ # Endpoint:
5
+ # - /v1/app/:app_id/shared_entities
6
+ #
7
+ # id :integer not null, primary key
8
+ # nid :string
9
+ # name :string
10
+ # created_at :datetime not null
11
+ # updated_at :datetime not null
12
+
13
+ module MnoEnterprise::Concerns::Models::SharedEntity
14
+ extend ActiveSupport::Concern
15
+
16
+ #==================================================================
17
+ # Included methods
18
+ #==================================================================
19
+ included do
20
+ # == Relationships ==============================================
21
+ belongs_to :app
22
+ end
23
+
24
+ #==================================================================
25
+ # Class methods
26
+ #==================================================================
27
+ module ClassMethods
28
+ # def some_class_method
29
+ # 'some text'
30
+ # end
31
+ end
32
+
33
+ #==================================================================
34
+ # Instance methods
35
+ #==================================================================
36
+ end
@@ -64,4 +64,11 @@ module MnoEnterprise::Concerns::Models::Team
64
64
  self.reload
65
65
  self
66
66
  end
67
+
68
+ def to_audit_event
69
+ {
70
+ name: name,
71
+ organization_id: self.organization.id
72
+ }
73
+ end
67
74
  end
@@ -13,7 +13,7 @@ namespace :mno_enterprise do
13
13
  ENV["RAILS_ENV"] = 'test'
14
14
 
15
15
  MnoEnterprise::DummyGenerator.start %W[--quiet --lib_name=#{ENV['LIB_NAME']} --database=#{ENV['DB'].presence || 'sqlite3'}]
16
- MnoEnterprise::Generators::InstallGenerator.start %w[--quiet --skip-rspec --skip-sprite --skip-factory-girl --skip-frontend --skip-admin]
16
+ MnoEnterprise::Generators::InstallGenerator.start %w[--quiet --skip-rspec --skip-sprite --skip-factory-girl --skip-application-config --skip-frontend --skip-admin]
17
17
  end
18
18
  end
19
19
  end
@@ -16,11 +16,15 @@ FactoryGirl.define do
16
16
  tags ['Foo', 'Bar']
17
17
  key_benefits ['Super', 'Hyper', 'Good']
18
18
  key_features ['Super', 'Hyper', 'Good']
19
+ key_workflows ['1st workflow', '2nd workflow']
20
+ known_limitations "No limitations"
19
21
  testimonials [{text: 'Bla', company: 'Doe Pty Ltd', author: 'John'}]
20
22
  worldwide_usage 120000
21
23
  tiny_description "A great app"
22
24
  stack 'cube'
23
25
  terms_url "http://opensource.org/licenses/MIT"
26
+ support_url "http://example.com/su pport"
27
+ getting_started "Let's get started"
24
28
  appinfo { {} }
25
29
  average_rating { rand(1..5) }
26
30
  sequence(:rank) { |n| n }
@@ -29,6 +33,8 @@ FactoryGirl.define do
29
33
  'default' => [{name: 'Monthly Plan', price: '20.0', currency: 'AUD', factor: '/month'}]
30
34
  } }
31
35
 
36
+ shared_entities { [] }
37
+
32
38
  trait :cloud do
33
39
  stack 'cloud'
34
40
  end
@@ -6,6 +6,8 @@ FactoryGirl.define do
6
6
  user_id 1
7
7
  description 'Blabla'
8
8
  details 'Details'
9
+ organization_id 1
10
+ organization { {name: 'Org'} }
9
11
  user { {name: 'John', surname: 'Doe'} }
10
12
 
11
13
  # Properly build the resource with Her
@@ -1,12 +1,12 @@
1
1
  module MnoEnterpriseApiTestHelper
2
-
2
+
3
3
  # Take a resource and transform it into a Hash describing
4
4
  # the resource as if it had been returned by the MnoEnterprise
5
5
  # API server
6
6
  def from_api(res)
7
7
  { data: serialize_type(res), metadata: {pagination: {count: entity_count(res)}} }
8
8
  end
9
-
9
+
10
10
  def serialize_type(res)
11
11
  case
12
12
  when res.kind_of?(Array)
@@ -42,7 +42,7 @@ module MnoEnterpriseApiTestHelper
42
42
  return 1
43
43
  end
44
44
  end
45
-
45
+
46
46
  # Reset all API stubs.
47
47
  # Called before each test (see spec_helper)
48
48
  def api_stub_reset
@@ -74,12 +74,27 @@ module MnoEnterpriseApiTestHelper
74
74
  warn("DEPRECATION WARNING: api_stub_for(MyClass,{ some: 'opts'}) is deprecated. Please use api_stub_for({ some: 'opts' }) from now on")
75
75
  real_opts = opts
76
76
  end
77
-
77
+
78
78
  set_api_stub
79
79
  api_stub_add(real_opts)
80
80
  api_stub_configure(@_api_stub)
81
81
  end
82
-
82
+
83
+ # Remove an API stub added with `api_stub_for`
84
+ # This needs to be called with the same options
85
+ def remove_api_stub(opts = {})
86
+ set_api_stub
87
+ api_stub_remove(opts)
88
+ api_stub_configure(@_api_stub)
89
+ end
90
+
91
+ # Remove all api stubs
92
+ def clear_api_stubs
93
+ set_api_stub
94
+ @_stub_list = {}
95
+ api_stub_configure(@_api_stub)
96
+ end
97
+
83
98
  private
84
99
  # Set a stub api on the provider class
85
100
  def set_api_stub
@@ -99,21 +114,37 @@ module MnoEnterpriseApiTestHelper
99
114
  def api_stub_add(orig_opts)
100
115
  @_stub_list ||= {}
101
116
  opts = orig_opts.dup
102
-
103
- # Expand options so that: { put: '/path' } becomes { path: '/path', method: :put }
117
+
118
+ expand_options(opts)
119
+
120
+ key = opts.to_param
121
+ @_stub_list[key] = opts
122
+ end
123
+
124
+ # Remove an API
125
+ # This need to be called with the exact same options as `api_stub_add` was called with
126
+ def api_stub_remove(orig_opts)
127
+ @_stub_list ||= {}
128
+ opts = orig_opts.dup
129
+
130
+ expand_options(opts)
131
+
132
+ key = opts.to_param
133
+ @_stub_list.delete(key)
134
+ end
135
+
136
+ # Expand options so that: { put: '/path' } becomes { path: '/path', method: :put }
137
+ def expand_options(opts)
104
138
  unless opts[:method] && opts[:path]
105
- [:get,:put,:post,:delete].each do |verb|
139
+ [:get, :put, :post, :delete].each do |verb|
106
140
  if path = opts.delete(verb)
107
141
  opts[:path] = path
108
142
  opts[:method] = verb
109
143
  end
110
144
  end
111
145
  end
112
-
113
- key = opts.to_param
114
- @_stub_list[key] = opts
115
146
  end
116
-
147
+
117
148
  # Configure the api and apply a list of stubs
118
149
  def api_stub_configure(api)
119
150
  # This block should match the her.rb initializer
@@ -157,12 +188,12 @@ module MnoEnterpriseApiTestHelper
157
188
  else
158
189
  resp_code = stub[:code] || 200
159
190
  end
160
-
161
-
162
- [resp_code, {}, resp.to_json]
191
+
192
+
193
+ [resp_code, {}, resp.to_json]
163
194
  }
164
195
  end
165
196
  end
166
197
  end
167
198
  end
168
- end
199
+ end