erp_tech_svcs 4.0.0 → 4.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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>
|