erp_tech_svcs 4.0.0 → 4.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +0 -24
- data/app/controllers/api/v1/audit_log_items_controller.rb +33 -0
- data/app/controllers/api/v1/audit_logs_controller.rb +32 -0
- data/app/controllers/api/v1/capabilities_controller.rb +160 -0
- data/app/controllers/api/v1/file_assets_controller.rb +40 -0
- data/app/controllers/api/v1/groups_controller.rb +236 -0
- data/app/controllers/api/v1/security_roles_controller.rb +276 -0
- data/app/controllers/api/v1/users_controller.rb +262 -0
- data/app/controllers/erp_tech_svcs/session_controller.rb +8 -5
- data/app/controllers/erp_tech_svcs/user_controller.rb +14 -15
- data/app/mailers/user_mailer.rb +8 -5
- data/app/models/audit_log.rb +111 -36
- data/app/models/audit_log_item.rb +30 -0
- data/app/models/audit_log_item_type.rb +1 -0
- data/app/models/audit_log_type.rb +19 -0
- data/app/models/capability.rb +22 -6
- data/app/models/extensions/tracked_status_type.rb +3 -0
- data/app/models/file_asset.rb +245 -20
- data/app/models/file_asset_holder.rb +20 -0
- data/app/models/group.rb +38 -25
- data/app/models/notification.rb +32 -13
- data/app/models/notification_type.rb +13 -0
- data/app/models/security_role.rb +17 -4
- data/app/models/user.rb +116 -29
- data/app/validators/password_strength_validator.rb +1 -1
- data/app/views/user_mailer/activation_needed_email.html.erb +293 -15
- data/app/views/user_mailer/reset_password_email.html.erb +268 -13
- data/config/initializers/logger.rb +19 -0
- data/config/initializers/sorcery.rb +2 -0
- data/config/initializers/wickedpdf.rb +4 -0
- data/config/routes.rb +64 -0
- data/db/data_migrations/20110802200222_schedule_delete_expired_sessions_job.rb +1 -5
- data/db/data_migrations/20150819140550_create_job_tracker_for_notification.rb +14 -0
- data/db/migrate/20080805000010_base_tech_services.rb +99 -39
- data/db/migrate/20150414151421_add_nested_set_columns_to_security_role.rb +13 -0
- data/db/migrate/20150609003216_update_user_for_sorcery.rb +11 -0
- data/db/migrate/20150819135108_add_custom_fields_to_notifications.rb +5 -0
- data/db/migrate/20160122155402_add_description_to_file_asset.rb +13 -0
- data/db/migrate/20160310163060_add_created_by_updated_by_to_erp_tech_svcs.rb +35 -0
- data/db/migrate/20160313161611_add_tenant_id_to_audit_log.rb +16 -0
- data/lib/erp_tech_svcs.rb +6 -10
- data/lib/erp_tech_svcs/config.rb +7 -2
- data/lib/erp_tech_svcs/delayed_jobs/delete_expired_sessions_job.rb +49 -0
- data/lib/erp_tech_svcs/delayed_jobs/notification_job.rb +50 -0
- data/lib/erp_tech_svcs/engine.rb +0 -1
- data/lib/erp_tech_svcs/erp_tech_svcs_audit_log.rb +12 -6
- data/lib/erp_tech_svcs/extensions.rb +0 -1
- data/lib/erp_tech_svcs/extensions/active_record/has_capability_accessors.rb +57 -29
- data/lib/erp_tech_svcs/extensions/active_record/has_file_assets.rb +57 -31
- data/lib/erp_tech_svcs/extensions/active_record/has_security_roles.rb +12 -4
- data/lib/erp_tech_svcs/extensions/active_record/is_json.rb +22 -15
- data/lib/erp_tech_svcs/extensions/active_record/scoped_by.rb +16 -13
- data/lib/erp_tech_svcs/extensions/compass_ae/erp_base_erp_svcs/controllers/api/parties_controller.rb +15 -0
- data/lib/erp_tech_svcs/file_support.rb +1 -0
- data/lib/erp_tech_svcs/file_support/file_system_manager.rb +77 -44
- data/lib/erp_tech_svcs/file_support/manager.rb +12 -3
- data/lib/erp_tech_svcs/file_support/railties/compass_ae_resolver.rb +49 -0
- data/lib/erp_tech_svcs/file_support/s3_manager.rb +73 -51
- data/lib/erp_tech_svcs/utils/compass_access_negotiator.rb +11 -2
- data/lib/erp_tech_svcs/utils/default_nested_set_methods.rb +238 -46
- data/lib/erp_tech_svcs/version.rb +1 -1
- data/lib/tasks/erp_tech_svcs_tasks.rake +43 -5
- metadata +73 -42
- data/app/models/user_defined_data.rb +0 -6
- data/app/models/user_defined_field.rb +0 -8
- data/config/initializers/pdfkit.rb +0 -18
- data/db/data_migrations/20121130212146_note_capabilities.rb +0 -23
- data/db/migrate/20121116151510_create_groups.rb +0 -18
- data/db/migrate/20121126171612_upgrade_security.rb +0 -53
- data/db/migrate/20121126173506_upgrade_security2.rb +0 -274
- data/db/migrate/20130410135419_add_queue_to_delayed_jobs.rb +0 -13
- data/db/migrate/20130610163240_create_notifications.rb +0 -37
- data/db/migrate/20130725212647_add_party_id_idx_to_users.rb +0 -9
- data/db/migrate/20131113213843_add_audit_log_item_old_value.rb +0 -13
- data/db/migrate/20131113213844_add_erp_tech_svcs_missing_indexes.rb +0 -31
- data/db/migrate/20131129203603_add_user_defined_fields.rb +0 -43
- data/db/migrate/20141013060204_add_custom_fields_to_notifications.rb +0 -12
- data/db/migrate/20141108182427_add_scoped_by_to_file_assets.rb +0 -14
- data/lib/erp_tech_svcs/extensions/active_record/has_user_defined_data.rb +0 -147
- data/lib/erp_tech_svcs/sessions/delete_expired_sessions_job.rb +0 -47
- data/lib/erp_tech_svcs/sessions/delete_expired_sessions_service.rb +0 -15
- data/lib/erp_tech_svcs/utils/compass_logger.rb +0 -87
data/app/models/notification.rb
CHANGED
@@ -1,12 +1,27 @@
|
|
1
|
+
# create_table :notifications do |t|
|
2
|
+
# t.string :type
|
3
|
+
# t.references :created_by
|
4
|
+
# t.text :message
|
5
|
+
# t.references :notification_type
|
6
|
+
# t.string :current_state
|
7
|
+
# t.text :custom_fields
|
8
|
+
#
|
9
|
+
# t.timestamps
|
10
|
+
# end
|
11
|
+
#
|
12
|
+
# add_index :notifications, :notification_type_id
|
13
|
+
# add_index :notifications, :created_by_id
|
14
|
+
# add_index :notifications, :type
|
15
|
+
|
1
16
|
class Notification < ActiveRecord::Base
|
2
17
|
attr_protected :created_at, :updated_at
|
3
|
-
|
4
|
-
# serialize custom attributes
|
5
|
-
is_json :custom_fields
|
6
18
|
|
7
19
|
belongs_to :notification_type
|
8
20
|
belongs_to :created_by, :foreign_key => 'created_by_id', :class_name => 'Party'
|
9
|
-
|
21
|
+
|
22
|
+
# serialize custom attributes
|
23
|
+
is_json :custom_fields
|
24
|
+
|
10
25
|
include AASM
|
11
26
|
|
12
27
|
aasm_column :current_state
|
@@ -22,28 +37,32 @@ class Notification < ActiveRecord::Base
|
|
22
37
|
|
23
38
|
class << self
|
24
39
|
|
25
|
-
|
26
|
-
|
40
|
+
# Creates a Notification record with the notification type passed
|
41
|
+
#
|
42
|
+
# @param [NotificationType | String] the notification type to set, can be a NotificationType record or InternalIdentifier
|
43
|
+
# @param [Hash] custom fields to set on the notification
|
44
|
+
# @param [Party] the party that created the notification
|
45
|
+
def create_notification_of_type(notification_type, custom_fields={}, created_by=nil)
|
46
|
+
notification_type = notification_type.class == NotificationType ? notification_type : NotificationType.iid(notification_type)
|
27
47
|
|
28
|
-
notification = self.
|
48
|
+
notification = self.create(
|
29
49
|
created_by: created_by,
|
30
50
|
notification_type: notification_type
|
31
51
|
)
|
32
52
|
|
33
|
-
|
34
|
-
notification.custom_fields[k] = v
|
35
|
-
end
|
53
|
+
notification.custom_fields = custom_fields
|
36
54
|
|
37
|
-
notification.save
|
55
|
+
notification.save!
|
38
56
|
|
39
57
|
notification
|
40
58
|
end
|
41
59
|
|
42
60
|
end
|
43
61
|
|
62
|
+
# Delivers notification, called by the notifications delayed job
|
63
|
+
# this is a template method and should be overridden by sub class
|
64
|
+
#
|
44
65
|
def deliver_notification
|
45
|
-
# template method
|
46
|
-
# should be overridden in sub class
|
47
66
|
end
|
48
67
|
|
49
68
|
end
|
@@ -1,5 +1,18 @@
|
|
1
|
+
# create_table :notification_types do |t|
|
2
|
+
# t.string :internal_identifier
|
3
|
+
# t.string :description
|
4
|
+
#
|
5
|
+
# t.timestamps
|
6
|
+
# end
|
7
|
+
#
|
8
|
+
# add_index :notification_types, :internal_identifier
|
9
|
+
|
1
10
|
class NotificationType < ActiveRecord::Base
|
2
11
|
attr_protected :created_at, :updated_at
|
3
12
|
|
13
|
+
acts_as_erp_type
|
14
|
+
|
4
15
|
has_many :notifications
|
16
|
+
|
17
|
+
validates :internal_identifier, uniqueness: {message: "Internal Identifiers should be unique"}
|
5
18
|
end
|
data/app/models/security_role.rb
CHANGED
@@ -1,4 +1,7 @@
|
|
1
1
|
class SecurityRole < ActiveRecord::Base
|
2
|
+
|
3
|
+
acts_as_nested_set
|
4
|
+
include ErpTechSvcs::Utils::DefaultNestedSetMethods
|
2
5
|
acts_as_erp_type
|
3
6
|
has_capability_accessors
|
4
7
|
has_and_belongs_to_many :parties
|
@@ -10,10 +13,10 @@ class SecurityRole < ActiveRecord::Base
|
|
10
13
|
|
11
14
|
attr_accessible :description, :internal_identifier
|
12
15
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
16
|
+
def to_xml(options = {})
|
17
|
+
default_only = []
|
18
|
+
options[:only] = (options[:only] || []) + default_only
|
19
|
+
super(options)
|
17
20
|
end
|
18
21
|
|
19
22
|
# creating method because we only want a getter, not a setter for iid
|
@@ -45,4 +48,14 @@ class SecurityRole < ActiveRecord::Base
|
|
45
48
|
Group.joins(:party).joins("LEFT JOIN #{join_parties_security_roles}").where("parties_security_roles.security_role_id IS NULL")
|
46
49
|
end
|
47
50
|
|
51
|
+
def to_data_hash
|
52
|
+
hash = to_hash(:only => [:id, :description, :internal_identifier, :created_at, :updated_at])
|
53
|
+
|
54
|
+
if parent
|
55
|
+
hash[:parent] = parent.to_data_hash
|
56
|
+
end
|
57
|
+
|
58
|
+
hash
|
59
|
+
end
|
60
|
+
|
48
61
|
end
|
data/app/models/user.rb
CHANGED
@@ -7,7 +7,11 @@ class User < ActiveRecord::Base
|
|
7
7
|
belongs_to :party
|
8
8
|
|
9
9
|
attr_accessible :email, :password, :password_confirmation
|
10
|
+
|
10
11
|
authenticates_with_sorcery!
|
12
|
+
|
13
|
+
attr_protected :created_at, :updated_at
|
14
|
+
|
11
15
|
has_capability_accessors
|
12
16
|
|
13
17
|
#password validations
|
@@ -22,12 +26,20 @@ class User < ActiveRecord::Base
|
|
22
26
|
validates :username, :presence => {:message => 'cannot be blank'}, :uniqueness => {:case_sensitive => false}
|
23
27
|
|
24
28
|
validate :email_cannot_match_username_of_other_user
|
29
|
+
|
25
30
|
def email_cannot_match_username_of_other_user
|
26
|
-
unless User.where(:username => self.email).where('id != ?',self.id).first.nil?
|
31
|
+
unless User.where(:username => self.email).where('id != ?', self.id).first.nil?
|
27
32
|
errors.add(:email, "In use by another user")
|
28
33
|
end
|
29
34
|
end
|
30
35
|
|
36
|
+
# auth token used for mobile app security
|
37
|
+
def generate_auth_token!
|
38
|
+
self.auth_token = SecureRandom.uuid
|
39
|
+
self.auth_token_expires_at = Time.now + 30.days
|
40
|
+
self.save
|
41
|
+
end
|
42
|
+
|
31
43
|
# This allows the disabling of the activation email sent via the sorcery user_activation submodule
|
32
44
|
def send_activation_needed_email!
|
33
45
|
super unless skip_activation_email
|
@@ -38,7 +50,7 @@ class User < ActiveRecord::Base
|
|
38
50
|
@instance_attrs.nil? ? {} : @instance_attrs
|
39
51
|
end
|
40
52
|
|
41
|
-
def add_instance_attribute(k,v)
|
53
|
+
def add_instance_attribute(k, v)
|
42
54
|
@instance_attrs = {} if @instance_attrs.nil?
|
43
55
|
@instance_attrs[k] = v
|
44
56
|
end
|
@@ -57,7 +69,7 @@ class User < ActiveRecord::Base
|
|
57
69
|
result = false
|
58
70
|
passed_roles.flatten!
|
59
71
|
passed_roles.each do |role|
|
60
|
-
role_iid = role.is_a?(SecurityRole) ?
|
72
|
+
role_iid = role.is_a?(SecurityRole) ? role.internal_identifier : role.to_s
|
61
73
|
all_uniq_roles.each do |this_role|
|
62
74
|
result = true if (this_role.internal_identifier == role_iid)
|
63
75
|
break if result
|
@@ -71,46 +83,45 @@ class User < ActiveRecord::Base
|
|
71
83
|
party.add_role(role)
|
72
84
|
end
|
73
85
|
|
86
|
+
alias :add_security_role :add_role
|
87
|
+
|
74
88
|
def add_roles(*passed_roles)
|
75
89
|
party.add_roles(*passed_roles)
|
76
90
|
end
|
77
91
|
|
92
|
+
alias :add_security_roles :add_roles
|
93
|
+
|
78
94
|
def remove_roles(*passed_roles)
|
79
95
|
party.remove_roles(*passed_roles)
|
80
96
|
end
|
81
97
|
|
98
|
+
alias :remove_security_roles :remove_roles
|
99
|
+
|
82
100
|
def remove_role(role)
|
83
101
|
party.remove_role(role)
|
84
102
|
end
|
85
103
|
|
104
|
+
alias :remove_security_role :remove_role
|
105
|
+
|
86
106
|
def remove_all_roles
|
87
107
|
party.remove_all_roles
|
88
108
|
end
|
89
109
|
|
90
|
-
|
91
|
-
def group_relationships
|
92
|
-
role_type = RoleType.find_by_internal_identifier('group_member')
|
93
|
-
PartyRelationship.where(:party_id_from => self.party.id, :role_type_id_from => role_type.id)
|
94
|
-
end
|
95
|
-
|
96
|
-
def join_party_relationships
|
97
|
-
role_type = RoleType.find_by_internal_identifier('group_member')
|
98
|
-
"party_relationships ON party_id_from = #{self.party.id} AND party_id_to = parties.id AND role_type_id_from=#{role_type.id}"
|
99
|
-
end
|
110
|
+
alias :remove_all_security_roles :remove_all_roles
|
100
111
|
|
101
112
|
# party records for the groups this user belongs to
|
102
113
|
def group_parties
|
103
|
-
Party.joins("JOIN #{
|
114
|
+
Party.joins("JOIN #{group_member_join}")
|
104
115
|
end
|
105
116
|
|
106
117
|
# groups this user belongs to
|
107
118
|
def groups
|
108
|
-
Group.joins(:party).joins("JOIN #{
|
119
|
+
Group.joins(:party).joins("JOIN #{group_member_join}")
|
109
120
|
end
|
110
121
|
|
111
122
|
# groups this user does NOT belong to
|
112
123
|
def groups_not
|
113
|
-
Group.joins(:party).joins("LEFT JOIN #{
|
124
|
+
Group.joins(:party).joins("LEFT JOIN #{group_member_join}").where("party_relationships.id IS NULL")
|
114
125
|
end
|
115
126
|
|
116
127
|
# roles assigned to the groups this user belongs to
|
@@ -120,10 +131,50 @@ class User < ActiveRecord::Base
|
|
120
131
|
where("parties.business_party_id IN (#{groups.select('groups.id').to_sql})")
|
121
132
|
end
|
122
133
|
|
134
|
+
# Add a group to this user
|
135
|
+
#
|
136
|
+
# @param group [Group] Group to add
|
137
|
+
def add_group(group)
|
138
|
+
group.add_user(self)
|
139
|
+
end
|
140
|
+
|
141
|
+
# Add multiple groups to this user
|
142
|
+
#
|
143
|
+
# @param _groups [Array] Groups to add
|
144
|
+
def add_groups(_groups)
|
145
|
+
_groups.each do |group|
|
146
|
+
add_group(group)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
# Remove a group from this user
|
151
|
+
#
|
152
|
+
# @param group [Group] Group to remove
|
153
|
+
def remove_group(group)
|
154
|
+
group.remove_user(self)
|
155
|
+
end
|
156
|
+
|
157
|
+
# Remove multiple groups from this user
|
158
|
+
#
|
159
|
+
# @param _groups [Array] Groups to remove
|
160
|
+
def remove_groups(_groups)
|
161
|
+
_groups.each do |group|
|
162
|
+
remove_group(group)
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
# Remove all current groups from this user
|
167
|
+
#
|
168
|
+
def remove_all_groups
|
169
|
+
groups.each do |group|
|
170
|
+
remove_group(group)
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
123
174
|
# composite roles for this user
|
124
175
|
def all_roles
|
125
176
|
SecurityRole.joins(:parties).joins("LEFT JOIN users ON parties.id=users.party_id").
|
126
|
-
|
177
|
+
where("(parties.business_party_type='Group' AND
|
127
178
|
parties.business_party_id IN (#{groups.select('groups.id').to_sql})) OR
|
128
179
|
(users.id=#{self.id})")
|
129
180
|
end
|
@@ -133,20 +184,20 @@ class User < ActiveRecord::Base
|
|
133
184
|
end
|
134
185
|
|
135
186
|
def group_capabilities
|
136
|
-
Capability.joins(:capability_type).joins(:capability_accessors).
|
137
|
-
|
138
|
-
|
187
|
+
Capability.includes(:capability_type).joins(:capability_type).joins(:capability_accessors).
|
188
|
+
where(:capability_accessors => {:capability_accessor_record_type => "Group"}).
|
189
|
+
where("capability_accessor_record_id IN (#{groups.select('groups.id').to_sql})")
|
139
190
|
end
|
140
191
|
|
141
192
|
def role_capabilities
|
142
|
-
Capability.joins(:capability_type).joins(:capability_accessors).
|
143
|
-
|
144
|
-
|
193
|
+
Capability.includes(:capability_type).joins(:capability_type).joins(:capability_accessors).
|
194
|
+
where(:capability_accessors => {:capability_accessor_record_type => "SecurityRole"}).
|
195
|
+
where("capability_accessor_record_id IN (#{all_roles.select('security_roles.id').to_sql})")
|
145
196
|
end
|
146
197
|
|
147
198
|
def all_capabilities
|
148
|
-
Capability.joins(:capability_type).joins(:capability_accessors).
|
149
|
-
|
199
|
+
Capability.includes(:capability_type).joins(:capability_type).joins(:capability_accessors).
|
200
|
+
where("(capability_accessors.capability_accessor_record_type = 'Group' AND
|
150
201
|
capability_accessor_record_id IN (#{groups.select('groups.id').to_sql})) OR
|
151
202
|
(capability_accessors.capability_accessor_record_type = 'SecurityRole' AND
|
152
203
|
capability_accessor_record_id IN (#{all_roles.select('security_roles.id').to_sql})) OR
|
@@ -178,11 +229,47 @@ class User < ActiveRecord::Base
|
|
178
229
|
end
|
179
230
|
|
180
231
|
def class_capabilities_to_hash
|
181
|
-
all_uniq_class_capabilities.map {|capability|
|
182
|
-
{ :
|
183
|
-
:
|
184
|
-
|
232
|
+
all_uniq_class_capabilities.map { |capability|
|
233
|
+
{ capability_type_iid: capability.capability_type.internal_identifier,
|
234
|
+
capability_type_description: capability.capability_type.description,
|
235
|
+
capability_resource_type: capability.capability_resource_type
|
236
|
+
}
|
185
237
|
}.compact
|
186
238
|
end
|
187
239
|
|
240
|
+
def to_data_hash
|
241
|
+
data = to_hash(only: [
|
242
|
+
:auth_token,
|
243
|
+
:id,
|
244
|
+
:username,
|
245
|
+
:email,
|
246
|
+
:activation_state,
|
247
|
+
:last_login_at,
|
248
|
+
:last_logout_at,
|
249
|
+
:last_activity_at,
|
250
|
+
:failed_logins_count,
|
251
|
+
:created_at,
|
252
|
+
:updated_at
|
253
|
+
],
|
254
|
+
display_name: party.description,
|
255
|
+
is_admin: party.has_security_role?('admin'),
|
256
|
+
party: party.to_data_hash
|
257
|
+
)
|
258
|
+
|
259
|
+
# add first name and last name if this party is an Individual
|
260
|
+
if self.party.business_party.is_a?(Individual)
|
261
|
+
data[:first_name] = self.party.business_party.current_first_name
|
262
|
+
data[:last_name] = self.party.business_party.current_last_name
|
263
|
+
end
|
264
|
+
|
265
|
+
data
|
266
|
+
end
|
267
|
+
|
268
|
+
protected
|
269
|
+
|
270
|
+
def group_member_join
|
271
|
+
role_type = RoleType.find_by_internal_identifier('group_member')
|
272
|
+
"party_relationships ON party_id_from = #{self.party.id} AND party_id_to = parties.id AND role_type_id_from=#{role_type.id}"
|
273
|
+
end
|
274
|
+
|
188
275
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
class PasswordStrengthValidator < ActiveModel::EachValidator
|
2
2
|
# implement the method where the validation logic must reside
|
3
3
|
def validate_each(record, attribute, value)
|
4
|
-
password_validation_hash = record.password_validator || {:error_message => 'must be
|
4
|
+
password_validation_hash = record.password_validator || {:error_message => 'must be between 8 and 20 characters with no spaces', :regex => '^\S{8,20}$'}
|
5
5
|
record.errors[attribute] << password_validation_hash[:error_message] unless Regexp.new(password_validation_hash[:regex]) =~ value
|
6
6
|
end
|
7
7
|
end
|
@@ -1,24 +1,302 @@
|
|
1
|
-
<!DOCTYPE html
|
2
|
-
|
1
|
+
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
2
|
+
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
3
|
+
<html xmlns="http://www.w3.org/1999/xhtml">
|
3
4
|
<head>
|
4
|
-
<meta content="text/html; charset=
|
5
|
+
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1"/>
|
7
|
+
<title>Welcome</title>
|
8
|
+
<style type="text/css">
|
9
|
+
.ReadMsgBody {
|
10
|
+
width: 100%;
|
11
|
+
background-color: #ffffff;
|
12
|
+
}
|
13
|
+
|
14
|
+
.ExternalClass {
|
15
|
+
width: 100%;
|
16
|
+
background-color: #ffffff;
|
17
|
+
}
|
18
|
+
|
19
|
+
html {
|
20
|
+
width: 100%;
|
21
|
+
}
|
22
|
+
|
23
|
+
body {
|
24
|
+
-webkit-text-size-adjust: none;
|
25
|
+
-ms-text-size-adjust: none;
|
26
|
+
margin: 0;
|
27
|
+
padding: 0;
|
28
|
+
}
|
29
|
+
|
30
|
+
table {
|
31
|
+
border-spacing: 0;
|
32
|
+
border-collapse: collapse;
|
33
|
+
}
|
34
|
+
|
35
|
+
img {
|
36
|
+
display: block !important;
|
37
|
+
}
|
38
|
+
|
39
|
+
table td {
|
40
|
+
border-collapse: collapse;
|
41
|
+
}
|
42
|
+
|
43
|
+
.top-margin {
|
44
|
+
height: 50px;
|
45
|
+
}
|
46
|
+
|
47
|
+
.main-header {
|
48
|
+
padding: 50px 100px;
|
49
|
+
background: rgba(0, 0, 0, 0.5);
|
50
|
+
font-weight: 300;
|
51
|
+
}
|
52
|
+
|
53
|
+
.footer {
|
54
|
+
margin-top: 20px;
|
55
|
+
}
|
56
|
+
|
57
|
+
/* ----------- media queries (responsive) ----------- */
|
58
|
+
|
59
|
+
@media only screen and (max-width: 640px) {
|
60
|
+
body .show {
|
61
|
+
display: block !important;
|
62
|
+
}
|
63
|
+
|
64
|
+
body .hide {
|
65
|
+
display: none !important;
|
66
|
+
}
|
67
|
+
|
68
|
+
body .container590 {
|
69
|
+
width: 440px !important;
|
70
|
+
}
|
71
|
+
}
|
72
|
+
|
73
|
+
@media only screen and (max-width: 480px) {
|
74
|
+
body .show {
|
75
|
+
display: block !important;
|
76
|
+
}
|
77
|
+
|
78
|
+
body .hide {
|
79
|
+
display: none !important;
|
80
|
+
}
|
81
|
+
|
82
|
+
body .container590 {
|
83
|
+
width: 280px !important;
|
84
|
+
}
|
85
|
+
}
|
86
|
+
|
87
|
+
</style>
|
5
88
|
</head>
|
89
|
+
|
6
90
|
<body>
|
7
|
-
<h1>Welcome <%= @user.username %></h1>
|
8
|
-
<% if @temp_password %>
|
9
|
-
<p>An account has been created for you, to activate your account follow this link: <a href="<%= @url %>"><%= @url %></a></p>
|
10
91
|
|
11
|
-
|
92
|
+
<div class="top-margin"></div>
|
93
|
+
|
94
|
+
<table border="0" width="100%" cellpadding="0" cellspacing="0">
|
95
|
+
<tr>
|
96
|
+
<td align="center" bgcolor="323232"
|
97
|
+
style="background-image: url('https://truenorthtechnology.com/download/life3.jpg?path=&disposition=inline'); background-size: cover; background-position: top center; background-repeat: repeat;">
|
98
|
+
|
99
|
+
<table border="0" align="center" cellpadding="0" cellspacing="0" class="container590">
|
100
|
+
|
101
|
+
<tr>
|
102
|
+
<td height="25" style="font-size: 25px; line-height: 25px;"> </td>
|
103
|
+
</tr>
|
104
|
+
|
105
|
+
|
106
|
+
<tr>
|
107
|
+
<td height="30" style="font-size: 30px; line-height: 30px;"> </td>
|
108
|
+
</tr>
|
109
|
+
<tr>
|
110
|
+
<td height="50" style="font-size: 50px; line-height: 50px;"> </td>
|
111
|
+
</tr>
|
112
|
+
|
113
|
+
<tr>
|
114
|
+
<td class="main-header" align="center" height="42"
|
115
|
+
style="color: #ffffff; font-size: 32px; font-family: 'Open Sans', Calibri, sans-serif; mso-line-height-rule: exactly; line-height: 40px;">
|
116
|
+
|
117
|
+
<!-- ============ title header ============ -->
|
118
|
+
|
119
|
+
<div style="line-height: 34px;">
|
120
|
+
<span>
|
121
|
+
<multiline>
|
122
|
+
<strong>Welcome <%= @user.username %></strong> <br><br>
|
123
|
+
An account has been created for you!
|
124
|
+
</multiline>
|
125
|
+
</span>
|
126
|
+
</div>
|
127
|
+
</td>
|
128
|
+
</tr>
|
129
|
+
|
130
|
+
<tr>
|
131
|
+
<td height="10" style="font-size: 10px; line-height: 10px;"> </td>
|
132
|
+
</tr>
|
133
|
+
|
134
|
+
|
135
|
+
<tr>
|
136
|
+
<td height="50" style="font-size: 50px; line-height: 50px;"> </td>
|
137
|
+
</tr>
|
138
|
+
|
139
|
+
<tr>
|
140
|
+
<td align="center">
|
141
|
+
|
142
|
+
<table class="cta-button" border="0" align="center" width="163" cellpadding="0" cellspacing="0"
|
143
|
+
style="border: none">
|
144
|
+
<tr>
|
145
|
+
<td height="10" style="font-size: 10px; line-height: 10px;"> </td>
|
146
|
+
</tr>
|
147
|
+
|
148
|
+
<tr>
|
149
|
+
<td>
|
150
|
+
<table border="0" align="center" cellpadding="0" cellspacing="0">
|
151
|
+
<tr>
|
152
|
+
<td align="center"
|
153
|
+
style="color: #ffffff; font-size: 13px; font-family: 'Open Sans', Calibri, sans-serif; font-weight: 600; line-height: 24px;">
|
154
|
+
<!-- ============ headline button ============ -->
|
155
|
+
</td>
|
156
|
+
|
157
|
+
<td width="14" align="right" valign="middle">
|
158
|
+
|
159
|
+
</td>
|
160
|
+
</tr>
|
161
|
+
</table>
|
162
|
+
</td>
|
163
|
+
</tr>
|
164
|
+
|
165
|
+
<tr>
|
166
|
+
<td height="10" style="font-size: 10px; line-height: 10px;"> </td>
|
167
|
+
</tr>
|
168
|
+
|
169
|
+
</table>
|
170
|
+
</td>
|
171
|
+
</tr>
|
172
|
+
|
173
|
+
<tr>
|
174
|
+
<td height="115" style="font-size: 115px; line-height: 115px;"> </td>
|
175
|
+
</tr>
|
176
|
+
|
177
|
+
</table>
|
178
|
+
</td>
|
179
|
+
</tr>
|
180
|
+
|
181
|
+
</table>
|
182
|
+
|
183
|
+
|
184
|
+
<table width="100%" border="0" align="center" cellpadding="0" cellspacing="0" bgcolor="#ffffff">
|
185
|
+
<tr>
|
186
|
+
<td height="50"> </td>
|
187
|
+
</tr>
|
188
|
+
|
189
|
+
<tr>
|
190
|
+
<td>
|
191
|
+
<table class="container590" border="0" align="center" width="590" cellpadding="0" cellspacing="0">
|
192
|
+
<tr>
|
193
|
+
<td>
|
194
|
+
<p class="instructions" style="text-align: center; color: #555; font-size: 24px; font-family: 'Open Sans', Calibri, sans-serif; mso-line-height-rule: exactly; line-height: 42px;">To
|
195
|
+
Activate your new account, please follow this link:</p>
|
196
|
+
|
197
|
+
<p style="text-align: center">
|
198
|
+
|
199
|
+
<table border="0" align="center" width="124" cellpadding="0" cellspacing="0"
|
200
|
+
bgcolor="5ab600" style="border-radius: 3px;">
|
201
|
+
<tr>
|
202
|
+
<td height="6" style="font-size: 6px; line-height: 6px;"> </td>
|
203
|
+
</tr>
|
204
|
+
|
205
|
+
<tr>
|
206
|
+
<td>
|
207
|
+
<table border="0" align="center" cellpadding="0" cellspacing="0">
|
208
|
+
<tr>
|
209
|
+
<td align="center"
|
210
|
+
style="color: #ffffff; font-size: 13px; font-family: Open Sans, Calibri, sans-serif; font-weight: 700;">
|
211
|
+
|
212
|
+
<div style="line-height: 24px;">
|
213
|
+
<span>
|
214
|
+
<a href="<%= @url %>"
|
215
|
+
style="color: #ffffff; text-decoration: none;">
|
216
|
+
<singleline>Confirm</singleline>
|
217
|
+
</a>
|
218
|
+
</span>
|
219
|
+
</div>
|
220
|
+
</td>
|
221
|
+
</tr>
|
222
|
+
</table>
|
223
|
+
</td>
|
224
|
+
</tr>
|
225
|
+
|
226
|
+
<tr>
|
227
|
+
<td height="6" style="font-size: 6px; line-height: 6px;"> </td>
|
228
|
+
</tr>
|
229
|
+
|
230
|
+
</table>
|
231
|
+
|
232
|
+
<p style="text-align: center; color: #555; font-size: 12px; font-family: 'Open Sans', Calibri, sans-serif; mso-line-height-rule: exactly; line-height: 42px;"><%= @url %></p>
|
233
|
+
|
234
|
+
|
235
|
+
<p style="text-align: center; color: #555; font-size: 12px; font-family: 'Open Sans', Calibri, sans-serif; mso-line-height-rule: exactly; line-height: 42px;">Your
|
236
|
+
username is: <strong><%= @user.username %></strong>
|
237
|
+
<% if @temp_password %>
|
238
|
+
Your password is: <strong><%= @temp_password %>
|
239
|
+
<% else %>
|
240
|
+
, your password is what was used during registration.
|
241
|
+
<% end %>
|
242
|
+
</strong></p>
|
243
|
+
|
244
|
+
</td>
|
245
|
+
</tr>
|
246
|
+
</table>
|
247
|
+
|
248
|
+
</td>
|
249
|
+
</tr>
|
250
|
+
|
251
|
+
<tr>
|
252
|
+
<td height="100" style="font-size: 100px; line-height: 100px;"> </td>
|
253
|
+
</tr>
|
254
|
+
|
255
|
+
</table>
|
256
|
+
|
257
|
+
|
258
|
+
<table class="footer" border="0" width="100%" cellpadding="0" cellspacing="0" bgcolor="2d2d2d">
|
259
|
+
|
260
|
+
<tr>
|
261
|
+
<td height="20" style="font-size: 20px; line-height: 20px;"> </td>
|
262
|
+
</tr>
|
263
|
+
|
264
|
+
<tr>
|
265
|
+
<td align="center">
|
266
|
+
|
267
|
+
<table border="0" align="center" width="590" class="container590" cellpadding="0" cellspacing="0">
|
268
|
+
|
269
|
+
<tr>
|
270
|
+
<td align="center">
|
271
|
+
|
272
|
+
<table border="0" class="container590" align="center" cellpadding="0" cellspacing="0">
|
273
|
+
<tr>
|
274
|
+
<td align="center"
|
275
|
+
style="color: #717171; font-size: 14px; font-family: 'Open Sans', Calibri, sans-serif; line-height: 25px;">
|
276
|
+
<div style=" line-height: 25px;">
|
277
|
+
<span>
|
278
|
+
<multiline>
|
279
|
+
© <%= Time.now.year %> Copyright. All Rights Reserved.
|
280
|
+
</multiline>
|
281
|
+
</span>
|
282
|
+
</div>
|
283
|
+
</td>
|
284
|
+
</tr>
|
285
|
+
|
286
|
+
</table>
|
287
|
+
|
288
|
+
</td>
|
289
|
+
</tr>
|
290
|
+
|
291
|
+
</table>
|
292
|
+
</td>
|
293
|
+
</tr>
|
12
294
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
You have successfully registered, to activate your account follow this link: <a href="<%= @url %>"><%= @url %></a>
|
17
|
-
</p>
|
295
|
+
<tr>
|
296
|
+
<td height="20" style="font-size: 20px; line-height: 20px;"> </td>
|
297
|
+
</tr>
|
18
298
|
|
19
|
-
|
299
|
+
</table>
|
20
300
|
|
21
|
-
<p>Thanks for joining and have a great day!!</p>
|
22
|
-
<% end %>
|
23
301
|
</body>
|
24
302
|
</html>
|