erp_tech_svcs 3.0.0 → 3.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +62 -0
- data/app/controllers/erp_tech_svcs/session_controller.rb +12 -7
- data/app/controllers/erp_tech_svcs/user_controller.rb +35 -14
- data/app/mailers/user_mailer.rb +8 -2
- data/app/models/attribute_type.rb +30 -0
- data/app/models/attribute_value.rb +41 -0
- data/app/models/file_asset.rb +58 -12
- data/app/models/secured_model.rb +4 -2
- data/app/models/user.rb +9 -7
- data/app/validators/password_strength_validator.rb +8 -0
- data/app/views/user_mailer/activation_needed_email.html.erb +1 -1
- data/config/initializers/erp_tech_svcs.rb +6 -1
- data/config/initializers/file_support.rb +9 -1
- data/config/initializers/sorcery.rb +3 -3
- data/config/routes.rb +2 -1
- data/db/data_migrations/20120109173616_create_download_capability_type.rb +13 -0
- data/db/migrate/20080805000010_base_tech_services.rb +12 -5
- data/db/migrate/20111117183144_create_has_attribute_tables.rb +38 -0
- data/db/migrate/{20111109161549_add_capabilites.rb → upgrade/20111109161549_add_capabilites.rb} +0 -0
- data/db/migrate/upgrade/20120329161641_add_file_asset_indexes.rb +22 -0
- data/lib/erp_tech_svcs/config.rb +20 -2
- data/lib/erp_tech_svcs/engine.rb +6 -0
- data/lib/erp_tech_svcs/extensions/active_record/has_capabilities.rb +18 -6
- data/lib/erp_tech_svcs/extensions/active_record/has_file_assets.rb +16 -3
- data/lib/erp_tech_svcs/extensions/active_record/has_relational_dynamic_attributes.rb +99 -0
- data/lib/erp_tech_svcs/extensions/active_record/has_roles.rb +3 -0
- data/lib/erp_tech_svcs/extensions/sorcery/user_activation.rb +13 -0
- data/lib/erp_tech_svcs/extensions.rb +5 -1
- data/lib/erp_tech_svcs/file_support/base.rb +1 -3
- data/lib/erp_tech_svcs/file_support/file_system_manager.rb +21 -19
- data/lib/erp_tech_svcs/file_support/manager.rb +29 -9
- data/lib/erp_tech_svcs/file_support/paperclip_patch.rb +13 -9
- data/lib/erp_tech_svcs/file_support/railties/s3_resolver.rb +24 -4
- data/lib/erp_tech_svcs/file_support/s3_manager.rb +133 -86
- data/lib/erp_tech_svcs/file_support.rb +5 -3
- data/lib/erp_tech_svcs/sms_wrapper/clickatell.rb +25 -0
- data/lib/erp_tech_svcs/sms_wrapper.rb +1 -0
- data/lib/erp_tech_svcs/utils/compass_access_negotiator.rb +12 -6
- data/lib/erp_tech_svcs/utils/default_nested_set_methods.rb +14 -1
- data/lib/erp_tech_svcs/version.rb +7 -1
- data/lib/erp_tech_svcs.rb +4 -2
- data/lib/tasks/erp_tech_svcs_tasks.rake +1 -1
- data/spec/controllers/erp_tech_svcs/user_controller_spec.rb +0 -0
- data/spec/lib/file_support/file_system_manager_spec.rb +82 -0
- data/spec/lib/file_support/s3_manager_spec.rb +92 -0
- data/spec/models/file_asset_spec.rb +78 -0
- data/spec/models/secured_model_spec.rb +22 -0
- data/spec/spec_helper.rb +9 -7
- metadata +146 -133
- data/README.rdoc +0 -2
- data/lib/erp_tech_svcs/file_support/aws_s3_patch.rb +0 -3
- data/spec/dummy/db/schema.rb +0 -571
- data/spec/dummy/db/spec.sqlite3 +0 -0
- data/spec/dummy/log/spec.log +0 -2862
data/README.md
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
#ErpTechSvcs
|
2
|
+
|
3
|
+
This engine is implemented with the premise that services like logging, tracing and encryption would likely already exist in many organizations, so they are factored here so they can easily be re-implemented. There are default implementations here, and we track several excellent Rails projects as potential implementations of services like security and content/digital asset mgt.
|
4
|
+
|
5
|
+
##Initializer Options
|
6
|
+
|
7
|
+
- installation\_domain
|
8
|
+
- The domain that your Compass AE instance is installed at.
|
9
|
+
- Default : 'localhost:3000'
|
10
|
+
- login\_url
|
11
|
+
- Path to the login page.
|
12
|
+
- Default : '/erp_app/login'
|
13
|
+
- email\_notifications\_from
|
14
|
+
- From address for email notifications.
|
15
|
+
- Default : 'notifications@noreply.com'
|
16
|
+
- max\_file\_size\_in\_mb
|
17
|
+
- Max allowed file upload size in mega bytes.
|
18
|
+
- Default : 5
|
19
|
+
- file\_assets\_location
|
20
|
+
- Where you want file_assets to be saved to.
|
21
|
+
- Default : file_assets
|
22
|
+
- file\_storage
|
23
|
+
- File storage to use either s3 or filesystem.
|
24
|
+
- Default : :filesystem
|
25
|
+
|
26
|
+
### Override Initializer
|
27
|
+
|
28
|
+
To override these settings simple create a erp_tech_svcs.rb file in your initializers and override the config options you want
|
29
|
+
|
30
|
+
Rails.application.config.erp_tech_svcs.configure do |config|
|
31
|
+
config.installation_domain = 'localhost:3000'
|
32
|
+
config.login_url = '/erp_app/login'
|
33
|
+
config.email_notifications_from = 'notifications@noreply.com'
|
34
|
+
config.max_file_size_in_mb = 5
|
35
|
+
config.file_assets_location = 'file_assets'
|
36
|
+
config.file_storage = :filesystem
|
37
|
+
end
|
38
|
+
Rails.application.config.erp_tech_svcs.configure!
|
39
|
+
|
40
|
+
##Notes
|
41
|
+
|
42
|
+
We use [pdfkit](https://github.com/jdpace/PDFKit) and there is an initializer in erp\_tech\_svcs to set it up with some defaults. You will need to create your
|
43
|
+
own initializer to overwrite this if you have wkhtmltopdf in another location
|
44
|
+
|
45
|
+
# config/initializers/pdfkit.rb
|
46
|
+
PDFKit.configure do |config|
|
47
|
+
if RUBY_PLATFORM =~ /(:?mswin|mingw)/
|
48
|
+
# set path to wkhtmltopdf on windows here
|
49
|
+
config.wkhtmltopdf = '/opt/local/bin/wkhtmltopdf'
|
50
|
+
else
|
51
|
+
config.wkhtmltopdf = '/opt/local/bin/wkhtmltopdf'
|
52
|
+
end
|
53
|
+
|
54
|
+
config.default_options = {
|
55
|
+
:page_size => 'Letter',
|
56
|
+
:print_media_type => true,
|
57
|
+
:disable_smart_shrinking => true,
|
58
|
+
:dpi => 300,
|
59
|
+
:no_background => true
|
60
|
+
# :use_xserver => true
|
61
|
+
}
|
62
|
+
end
|
@@ -1,13 +1,18 @@
|
|
1
1
|
module ErpTechSvcs
|
2
2
|
class SessionController < ActionController::Base
|
3
3
|
def create
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
4
|
+
last_login_at = nil
|
5
|
+
potential_user = User.where('username = ? or email = ?', params[:login], params[:login]).first
|
6
|
+
last_login_at = potential_user.last_login_at unless potential_user.nil?
|
7
|
+
if login(params[:login],params[:password])
|
8
|
+
login_to = last_login_at.nil? ? params[:first_login_to] : params[:login_to]
|
9
|
+
login_to = login_to || params[:login_to]
|
10
|
+
request.xhr? ? (render :json => {:success => true, :login_to => login_to}) : (redirect_to login_to)
|
11
|
+
else
|
12
|
+
message = "Login failed. Try again"
|
13
|
+
flash[:notice] = message
|
14
|
+
request.xhr? ? (render :json => {:success => false, :errors => {:reason => message}}) : (render :text => message)
|
15
|
+
end
|
11
16
|
end
|
12
17
|
|
13
18
|
def destroy
|
@@ -10,31 +10,52 @@ module ErpTechSvcs
|
|
10
10
|
end
|
11
11
|
end
|
12
12
|
|
13
|
-
def
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
new_password = Sorcery::Model::TemporaryToken.generate_random_token
|
18
|
-
user.password_confirmation = new_password
|
19
|
-
if user.change_password!(new_password)
|
20
|
-
user.add_instance_attribute(:login_url,login_url)
|
21
|
-
user.deliver_reset_password_instructions!
|
22
|
-
message = "Password has been reset. An email has been sent with further instructions to #{user.email}."
|
13
|
+
def update_password
|
14
|
+
if user = User.authenticate(current_user.username, params[:old_password])
|
15
|
+
user.password_confirmation = params[:password_confirmation]
|
16
|
+
if user.change_password!(params[:password])
|
23
17
|
success = true
|
24
18
|
else
|
25
|
-
|
19
|
+
#### validation failed ####
|
20
|
+
message = user.errors.full_messages
|
26
21
|
success = false
|
27
22
|
end
|
28
23
|
else
|
29
|
-
message = "Invalid
|
24
|
+
message = "Invalid current password."
|
30
25
|
success = false
|
31
26
|
end
|
32
|
-
|
27
|
+
|
28
|
+
request.xhr? ? (render :json => {:success => success, :message => message}) : (render :text => message)
|
29
|
+
end
|
30
|
+
|
31
|
+
def reset_password
|
32
|
+
begin
|
33
|
+
login_url = params[:login_url].blank? ? ErpTechSvcs::Config.login_url : params[:login_url]
|
34
|
+
if user = (User.find_by_email(params[:login]) || User.find_by_username(params[:login]))
|
35
|
+
new_password = Sorcery::Model::TemporaryToken.generate_random_token
|
36
|
+
user.password_confirmation = new_password
|
37
|
+
if user.change_password!(new_password)
|
38
|
+
user.add_instance_attribute(:login_url,login_url)
|
39
|
+
user.add_instance_attribute(:domain, params[:domain])
|
40
|
+
user.deliver_reset_password_instructions!
|
41
|
+
message = "Password has been reset. An email has been sent with further instructions to #{user.email}."
|
42
|
+
success = true
|
43
|
+
else
|
44
|
+
message = "Error re-setting password."
|
45
|
+
success = false
|
46
|
+
end
|
47
|
+
else
|
48
|
+
message = "Invalid email address."
|
49
|
+
success = false
|
50
|
+
end
|
51
|
+
render :json => {:success => success,:message => message}
|
33
52
|
rescue Exception=>ex
|
34
53
|
logger.error ex.message
|
35
54
|
logger.error ex.backtrace
|
36
55
|
render :json => {:success => false,:message => 'Error sending email.'}
|
37
56
|
end
|
38
57
|
end
|
58
|
+
|
59
|
+
|
39
60
|
end#UserController
|
40
|
-
end#ErpTechSvcs
|
61
|
+
end#ErpTechSvcs
|
data/app/mailers/user_mailer.rb
CHANGED
@@ -3,14 +3,20 @@ class UserMailer < ActionMailer::Base
|
|
3
3
|
|
4
4
|
def activation_needed_email(user)
|
5
5
|
@user = user
|
6
|
-
@url = "#{
|
6
|
+
@url = "#{get_domain(user.instance_attributes[:domain])}/users/activate/#{user.activation_token}"
|
7
7
|
@url << "?login_url=#{@user.instance_attributes[:login_url]}" unless @user.instance_attributes[:login_url].nil?
|
8
8
|
mail(:to => user.email, :subject => "An account has been created and needs activation")
|
9
9
|
end
|
10
10
|
|
11
11
|
def reset_password_email(user)
|
12
12
|
@user = user
|
13
|
-
@url = "#{
|
13
|
+
@url = "#{get_domain(user.instance_attributes[:domain])}#{@user.instance_attributes[:login_url]}"
|
14
14
|
mail(:to => user.email, :subject => "Your password has been reset")
|
15
15
|
end
|
16
|
+
|
17
|
+
def get_domain(domain)
|
18
|
+
domain = domain || ErpTechSvcs::Config.installation_domain
|
19
|
+
domain = "http://#{domain}" if domain.index('http').nil?
|
20
|
+
domain
|
21
|
+
end
|
16
22
|
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
class AttributeType < ActiveRecord::Base
|
2
|
+
has_many :attribute_values, :dependent => :destroy
|
3
|
+
|
4
|
+
validates_uniqueness_of :internal_identifier
|
5
|
+
validates :description, :presence => true
|
6
|
+
|
7
|
+
before_save :update_iid
|
8
|
+
|
9
|
+
def values_by_date_range(start_date, end_date)
|
10
|
+
raise "attribute_type does not have a data_type of Date" unless self.data_type == "Date"
|
11
|
+
|
12
|
+
attribute_values = self.attribute_values
|
13
|
+
attribute_values.each do |attribute_value|
|
14
|
+
unless attribute_value.value_as_date >= start_date and attribute_value.value_as_date <= end_date
|
15
|
+
attribute_values.delete(attribute_value)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
attribute_values
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.find_by_iid_with_description(description)
|
23
|
+
iid = description.strip.underscore.gsub(/\s+/,"_")
|
24
|
+
AttributeType.find_by_internal_identifier iid
|
25
|
+
end
|
26
|
+
|
27
|
+
def update_iid
|
28
|
+
self.internal_identifier = self.description.strip.underscore.gsub(/\s+/,"_")
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
class AttributeValue < ActiveRecord::Base
|
2
|
+
belongs_to :attributed_record, :polymorphic => true
|
3
|
+
belongs_to :attribute_type
|
4
|
+
|
5
|
+
validates_format_of :value, :with => /\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}/, :if => :is_date?
|
6
|
+
before_destroy :destroy_attribute_types_without_values
|
7
|
+
|
8
|
+
def is_date?
|
9
|
+
self.attribute_type.data_type == 'Date' ? true : false
|
10
|
+
end
|
11
|
+
|
12
|
+
def value_as_data_type
|
13
|
+
data_type = self.attribute_type.data_type
|
14
|
+
|
15
|
+
case data_type
|
16
|
+
when "Date"
|
17
|
+
self.value.to_date
|
18
|
+
when "Boolean"
|
19
|
+
self.value == "true" ? true : false
|
20
|
+
when "Int"
|
21
|
+
self.value.to_i
|
22
|
+
when "Float"
|
23
|
+
self.value.to_f
|
24
|
+
else
|
25
|
+
self.value
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def value_as_date
|
30
|
+
if self.is_date?
|
31
|
+
self.value.to_date
|
32
|
+
else
|
33
|
+
raise "value is not a Date or is not properly formated"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def destroy_attribute_types_without_values
|
38
|
+
self.attribute_type.destroy unless self.attribute_type.attribute_values.count > 1
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
data/app/models/file_asset.rb
CHANGED
@@ -1,15 +1,31 @@
|
|
1
1
|
require 'fileutils'
|
2
2
|
|
3
3
|
Paperclip.interpolates(:file_path){|data, style|
|
4
|
-
case
|
4
|
+
case Rails.application.config.erp_tech_svcs.file_storage
|
5
5
|
when :filesystem
|
6
|
-
|
6
|
+
file_support = ErpTechSvcs::FileSupport::Base.new
|
7
|
+
File.join(file_support.root,data.instance.directory,data.instance.name)
|
7
8
|
when :s3
|
8
9
|
File.join(data.instance.directory,data.instance.name)
|
9
10
|
end
|
10
11
|
}
|
11
12
|
|
12
|
-
Paperclip.interpolates(:file_url){|data, style|
|
13
|
+
Paperclip.interpolates(:file_url){|data, style|
|
14
|
+
url = File.join(data.instance.directory, data.instance.name)
|
15
|
+
case Rails.application.config.erp_tech_svcs.file_storage
|
16
|
+
when :filesystem
|
17
|
+
#if public is at the front of this path and we are using file_system remove it
|
18
|
+
dir_pieces = url.split('/')
|
19
|
+
unless dir_pieces[1] == 'public'
|
20
|
+
"/download/#{data.instance.name}?path=#{dir_pieces.delete_if{|name| name == data.instance.name}.join('/')}"
|
21
|
+
else
|
22
|
+
dir_pieces.delete_at(1) if dir_pieces[1] == 'public'
|
23
|
+
dir_pieces.join('/')
|
24
|
+
end
|
25
|
+
when :s3
|
26
|
+
url
|
27
|
+
end
|
28
|
+
}
|
13
29
|
|
14
30
|
class FileAsset < ActiveRecord::Base
|
15
31
|
if respond_to?(:class_attribute)
|
@@ -28,9 +44,13 @@ class FileAsset < ActiveRecord::Base
|
|
28
44
|
belongs_to :file_asset_holder, :polymorphic => true
|
29
45
|
instantiates_with_sti
|
30
46
|
|
47
|
+
has_capabilities
|
48
|
+
|
31
49
|
#paperclip
|
32
50
|
has_attached_file :data,
|
33
|
-
:storage =>
|
51
|
+
:storage => Rails.application.config.erp_tech_svcs.file_storage,
|
52
|
+
:s3_protocol => Rails.application.config.erp_tech_svcs.s3_protocol,
|
53
|
+
:s3_permissions => :public_read,
|
34
54
|
:s3_credentials => "#{Rails.root}/config/s3.yml",
|
35
55
|
:path => ":file_path",
|
36
56
|
:url => ":file_url",
|
@@ -59,11 +79,13 @@ class FileAsset < ActiveRecord::Base
|
|
59
79
|
end
|
60
80
|
|
61
81
|
def type_by_extension(extension)
|
62
|
-
all_subclasses.detect{ |k| k.valid_extensions.include?(extension) }
|
82
|
+
klass = all_subclasses.detect{ |k| k.valid_extensions.include?(extension) }
|
83
|
+
klass = TextFile if klass.nil?
|
84
|
+
klass
|
63
85
|
end
|
64
86
|
|
65
87
|
def validate_extension(data, file)
|
66
|
-
if file.name && !file.class.valid_extensions.include?(
|
88
|
+
if file.name && !file.class.valid_extensions.include?(File.extname(file.name))
|
67
89
|
types = all_valid_extensions.map{ |type| type.gsub(/^\./, '') }.join(', ')
|
68
90
|
"#{file.name} is not a valid file type. Valid file types are #{types}."
|
69
91
|
end
|
@@ -92,9 +114,12 @@ class FileAsset < ActiveRecord::Base
|
|
92
114
|
base_path ||= data.original_filename if data.respond_to?(:original_filename)
|
93
115
|
|
94
116
|
directory, name = FileAsset.split_path(base_path) if base_path and name.blank?
|
95
|
-
directory.gsub!(
|
117
|
+
directory.gsub!(Rails.root.to_s,'')
|
96
118
|
|
97
119
|
@type ||= FileAsset.type_for(name) if name
|
120
|
+
@type = "TextFile" if @type.nil?
|
121
|
+
@name = name
|
122
|
+
|
98
123
|
data = StringIO.new(data) if data.is_a?(String)
|
99
124
|
|
100
125
|
super attributes.merge(:directory => directory, :name => name, :data => data)
|
@@ -115,7 +140,7 @@ class FileAsset < ActiveRecord::Base
|
|
115
140
|
def set_content_type
|
116
141
|
unless @type.nil?
|
117
142
|
klass = @type.constantize
|
118
|
-
content_type = klass == Image ? "image/#{
|
143
|
+
content_type = klass == Image ? "image/#{File.extname(@name).gsub(/^\.+/, '')}" : klass.content_type
|
119
144
|
self.data.instance_write(:content_type, content_type)
|
120
145
|
end
|
121
146
|
end
|
@@ -124,11 +149,20 @@ class FileAsset < ActiveRecord::Base
|
|
124
149
|
update_attribute :data_file_name, name if data_file_name != name
|
125
150
|
end
|
126
151
|
|
152
|
+
def get_contents
|
153
|
+
file_support = ErpTechSvcs::FileSupport::Base.new(:storage => Rails.application.config.erp_tech_svcs.file_storage)
|
154
|
+
file_support.get_contents(File.join(self.directory,self.data_file_name))
|
155
|
+
end
|
156
|
+
|
127
157
|
def move(new_parent_path)
|
128
|
-
file_support = ErpTechSvcs::FileSupport::Base.new(:storage =>
|
129
|
-
file_support.save_move(File.join(self.directory,self.name), new_parent_path)
|
130
|
-
|
131
|
-
|
158
|
+
file_support = ErpTechSvcs::FileSupport::Base.new(:storage => Rails.application.config.erp_tech_svcs.file_storage)
|
159
|
+
result, message = file_support.save_move(File.join(self.directory, self.name), new_parent_path)
|
160
|
+
if result
|
161
|
+
self.directory = new_parent_path.gsub(Regexp.new(Rails.root.to_s), '') # strip rails root from new_parent_path, we want relative path
|
162
|
+
self.save
|
163
|
+
end
|
164
|
+
|
165
|
+
return result, message
|
132
166
|
end
|
133
167
|
|
134
168
|
end
|
@@ -176,3 +210,15 @@ class HtmlFile < TextFile
|
|
176
210
|
self.content_type = 'text/html'
|
177
211
|
self.valid_extensions = %w(.html)
|
178
212
|
end
|
213
|
+
|
214
|
+
class Pdf < TextFile
|
215
|
+
self.file_type = :pdf
|
216
|
+
self.content_type = 'application/pdf'
|
217
|
+
self.valid_extensions = %w(.pdf)
|
218
|
+
end
|
219
|
+
|
220
|
+
class Swf < TextFile
|
221
|
+
self.file_type = :swf
|
222
|
+
self.content_type = 'application/x-shockwave-flash'
|
223
|
+
self.valid_extensions = %w(.swf)
|
224
|
+
end
|
data/app/models/secured_model.rb
CHANGED
@@ -4,9 +4,11 @@ class SecuredModel < ActiveRecord::Base
|
|
4
4
|
|
5
5
|
class << self
|
6
6
|
def find_models_by_klass_and_role(model_klass, role)
|
7
|
-
role
|
7
|
+
role = Role.iid(role) if role.is_a? String
|
8
|
+
model_klass = model_klass.to_s unless model_klass.is_a? String
|
8
9
|
|
9
|
-
SecuredModel.joins(['join roles_secured_models on roles_secured_models.secured_model_id = secured_models.id'])
|
10
|
+
SecuredModel.joins(['join roles_secured_models on roles_secured_models.secured_model_id = secured_models.id'])
|
11
|
+
.where('secured_record_type = ? and role_id = ?', model_klass, role.id).collect(&:secured_record)
|
10
12
|
end
|
11
13
|
end
|
12
14
|
|
data/app/models/user.rb
CHANGED
@@ -1,24 +1,26 @@
|
|
1
1
|
class User < ActiveRecord::Base
|
2
|
+
include ActiveModel::Validations
|
3
|
+
|
4
|
+
attr_accessor :password_validator
|
5
|
+
|
2
6
|
has_roles
|
3
7
|
include ErpTechSvcs::Utils::CompassAccessNegotiator
|
4
8
|
|
5
9
|
belongs_to :party
|
6
|
-
|
10
|
+
|
7
11
|
attr_accessible :email, :password, :password_confirmation
|
8
12
|
authenticates_with_sorcery!
|
9
13
|
|
10
14
|
#password validations
|
11
|
-
validates_length_of :password, :minimum => 3, :message => "password must be at least 3 characters long", :if => :password
|
12
15
|
validates_confirmation_of :password, :message => "should match confirmation", :if => :password
|
16
|
+
validates :password, :presence => true, :password_strength => true, :if => :password
|
13
17
|
|
14
18
|
#email validations
|
15
|
-
validates :email, :presence => {:message => 'Email cannot be blank'}
|
16
|
-
validates_uniqueness_of :email
|
19
|
+
validates :email, :presence => {:message => 'Email cannot be blank'}, :uniqueness => true
|
17
20
|
validates_format_of :email, :with => /\b[A-Z0-9._%a-z\-]+@(?:[A-Z0-9a-z\-]+\.)+[A-Za-z]{2,4}\z/
|
18
21
|
|
19
22
|
#username validations
|
20
|
-
validates :
|
21
|
-
validates_uniqueness_of :username
|
23
|
+
validates :username, :presence => {:message => 'Username cannot be blank'}, :uniqueness => true
|
22
24
|
|
23
25
|
#these two methods allow us to assign instance level attributes that are not persisted. These are used for mailers
|
24
26
|
def instance_attributes
|
@@ -29,5 +31,5 @@ class User < ActiveRecord::Base
|
|
29
31
|
@instance_attrs = {} if @instance_attrs.nil?
|
30
32
|
@instance_attrs[k] = v
|
31
33
|
end
|
32
|
-
|
34
|
+
|
33
35
|
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
class PasswordStrengthValidator < ActiveModel::EachValidator
|
2
|
+
# implement the method where the validation logic must reside
|
3
|
+
def validate_each(record, attribute, value)
|
4
|
+
password_validation_hash = record.password_validator || {:error_message => 'must be at least 8 characters long and start and end with a letter', :regex => '^[A-Za-z]\w{6,}[A-Za-z]$'}
|
5
|
+
record.errors[attribute] << password_validation_hash[:error_message] unless Regexp.new(password_validation_hash[:regex]) =~ value
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
@@ -8,7 +8,7 @@
|
|
8
8
|
<p>
|
9
9
|
You have successfully registered, to activate your account follow this link: <a href="<%= @url %>"><%= @url %></a>.
|
10
10
|
</p>
|
11
|
-
<p>Your username is <%=@user.username
|
11
|
+
<p>Your username is <%=@user.username%>, use the password you set during registration to login.</p>
|
12
12
|
<p>Thanks for joining and have a great day!!</p>
|
13
13
|
</body>
|
14
14
|
</html>
|
@@ -3,5 +3,10 @@ Rails.application.config.erp_tech_svcs.configure do |config|
|
|
3
3
|
config.login_url = '/erp_app/login'
|
4
4
|
config.email_notifications_from = 'notifications@noreply.com'
|
5
5
|
config.max_file_size_in_mb = 5
|
6
|
+
config.file_assets_location = 'file_assets' # relative to Rails.root/
|
7
|
+
config.s3_url_expires_in_seconds = 60
|
8
|
+
config.s3_protocol = 'https' # Can be either 'http' or 'https'
|
9
|
+
config.file_storage = :filesystem # Can be either :s3 or :filesystem
|
10
|
+
config.s3_cache_expires_in_minutes = 60
|
6
11
|
end
|
7
|
-
Rails.application.config.erp_tech_svcs.configure!
|
12
|
+
Rails.application.config.erp_tech_svcs.configure!
|
@@ -1 +1,9 @@
|
|
1
|
-
|
1
|
+
# we need to_prepare so that this is checked multiple times during startup as erp_tech_svcs.file_storage can be overridden.
|
2
|
+
Rails.application.config.to_prepare do
|
3
|
+
# Rescue here, Compass should not die if it cannot connect to S3
|
4
|
+
begin
|
5
|
+
ErpTechSvcs::FileSupport::S3Manager.setup_connection() if Rails.application.config.erp_tech_svcs.file_storage == :s3
|
6
|
+
rescue Exception => e
|
7
|
+
Rails.logger.error "ERROR: Failed to connect to Amazon S3 #{e.inspect}"
|
8
|
+
end
|
9
|
+
end
|
@@ -125,7 +125,7 @@ Rails.application.config.sorcery.configure do |config|
|
|
125
125
|
user.activation_success_email_method_name = nil # activation success email method
|
126
126
|
# on your mailer class.
|
127
127
|
|
128
|
-
|
128
|
+
user.prevent_non_active_users_to_login = true # do you want to prevent or allow
|
129
129
|
# users that did not activate by
|
130
130
|
# email to login?
|
131
131
|
|
@@ -164,9 +164,9 @@ Rails.application.config.sorcery.configure do |config|
|
|
164
164
|
# user is banned and when it will
|
165
165
|
# be active again.
|
166
166
|
|
167
|
-
user.consecutive_login_retries_amount_limit =
|
167
|
+
user.consecutive_login_retries_amount_limit = 3 # how many failed logins allowed.
|
168
168
|
|
169
|
-
|
169
|
+
user.login_lock_time_period = 60 # how long the user should be
|
170
170
|
# banned. in seconds. 0 for
|
171
171
|
# permanent.
|
172
172
|
|
data/config/routes.rb
CHANGED
@@ -6,4 +6,5 @@ Rails.application.routes.draw do
|
|
6
6
|
#handle activation
|
7
7
|
get "/users/activate/:activation_token" => 'erp_tech_svcs/user#activate'
|
8
8
|
post "/users/reset_password" => 'erp_tech_svcs/user#reset_password'
|
9
|
-
|
9
|
+
post "/users/update_password" => 'erp_tech_svcs/user#update_password'
|
10
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class CreateDownloadCapabilityType
|
2
|
+
|
3
|
+
def self.up
|
4
|
+
CapabilityType.create(:internal_identifier => 'download', :description => 'Download')
|
5
|
+
Role.create(:description => 'File Downloader', :internal_identifier => 'file_downloader')
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.down
|
9
|
+
CapabilityType.where("internal_identifier = 'download'").first.destroy unless Role.where("internal_identifier = 'download'").first.nil?
|
10
|
+
Role.where("internal_identifier = 'file_downloader'").first.destroy unless Role.where("internal_identifier = 'file_downloader'").first.nil?
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
@@ -32,15 +32,20 @@ class BaseTechServices < ActiveRecord::Migration
|
|
32
32
|
t.string :activation_state, :default => nil
|
33
33
|
t.string :activation_token, :default => nil
|
34
34
|
t.datetime :activation_token_expires_at, :default => nil
|
35
|
-
|
35
|
+
|
36
|
+
t.string :security_question_1
|
37
|
+
t.string :security_answer_1
|
38
|
+
t.string :security_question_2
|
39
|
+
t.string :security_answer_2
|
40
|
+
|
36
41
|
t.timestamps
|
37
42
|
end
|
38
43
|
add_index :users, :email, :unique => true
|
39
44
|
add_index :users, :username, :unique => true
|
40
|
-
add_index :users, [:last_logout_at, :last_activity_at], :name => 'activity_idx'
|
41
|
-
add_index :users, :remember_me_token
|
42
|
-
add_index :users, :reset_password_token
|
43
|
-
add_index :users, :activation_token
|
45
|
+
add_index :users, [:last_logout_at, :last_activity_at], :name => 'activity_idx'
|
46
|
+
add_index :users, :remember_me_token
|
47
|
+
add_index :users, :reset_password_token
|
48
|
+
add_index :users, :activation_token
|
44
49
|
|
45
50
|
end
|
46
51
|
|
@@ -169,6 +174,8 @@ class BaseTechServices < ActiveRecord::Migration
|
|
169
174
|
end
|
170
175
|
add_index :file_assets, :type
|
171
176
|
add_index :file_assets, [:file_asset_holder_id, :file_asset_holder_type], :name => 'file_asset_holder_idx'
|
177
|
+
add_index :file_assets, :name
|
178
|
+
add_index :file_assets, :directory
|
172
179
|
end
|
173
180
|
|
174
181
|
unless table_exists?(:delayed_jobs)
|
@@ -0,0 +1,38 @@
|
|
1
|
+
class CreateHasAttributeTables < ActiveRecord::Migration
|
2
|
+
def up
|
3
|
+
unless table_exists?(:attribute_types)
|
4
|
+
create_table :attribute_types do |t|
|
5
|
+
t.string :internal_identifier
|
6
|
+
t.string :description
|
7
|
+
t.string :data_type
|
8
|
+
|
9
|
+
t.timestamps
|
10
|
+
end
|
11
|
+
|
12
|
+
add_index :attribute_types, :internal_identifier, :name => ':attribute_types_iid_idx'
|
13
|
+
end
|
14
|
+
unless table_exists?(:attribute_values)
|
15
|
+
create_table :attribute_values do |t|
|
16
|
+
t.integer :attributed_record_id
|
17
|
+
t.string :attributed_record_type
|
18
|
+
t.references :attribute_type
|
19
|
+
t.string :value
|
20
|
+
|
21
|
+
t.timestamps
|
22
|
+
end
|
23
|
+
|
24
|
+
add_index :attribute_values, [:attributed_record_id, :attributed_record_type], :name => 'attribute_values_attributed_record_idx'
|
25
|
+
add_index :attribute_values, :attribute_type_id, :name => 'attribute_values_attributed_type_idx'
|
26
|
+
add_index :attribute_values, :value, :name => 'attribute_values_value_idx'
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def down
|
31
|
+
if table_exists?(:attribute_types)
|
32
|
+
drop_table :attribute_types
|
33
|
+
end
|
34
|
+
if table_exists?(:attribute_values)
|
35
|
+
drop_table :attribute_values
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
data/db/migrate/{20111109161549_add_capabilites.rb → upgrade/20111109161549_add_capabilites.rb}
RENAMED
File without changes
|
@@ -0,0 +1,22 @@
|
|
1
|
+
class AddFileAssetIndexes < ActiveRecord::Migration
|
2
|
+
def self.up
|
3
|
+
unless indexes(:file_assets).collect {|i| i.name}.include?('index_file_assets_on_type')
|
4
|
+
add_index :file_assets, :type
|
5
|
+
end
|
6
|
+
|
7
|
+
unless indexes(:file_assets).collect {|i| i.name}.include?('file_asset_holder_idx')
|
8
|
+
add_index :file_assets, [:file_asset_holder_id, :file_asset_holder_type], :name => 'file_asset_holder_idx'
|
9
|
+
end
|
10
|
+
|
11
|
+
unless indexes(:file_assets).collect {|i| i.name}.include?('index_file_assets_on_name')
|
12
|
+
add_index :file_assets, :name
|
13
|
+
end
|
14
|
+
|
15
|
+
unless indexes(:file_assets).collect {|i| i.name}.include?('index_file_assets_on_directory')
|
16
|
+
add_index :file_assets, :directory
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.down
|
21
|
+
end
|
22
|
+
end
|
data/lib/erp_tech_svcs/config.rb
CHANGED
@@ -1,10 +1,28 @@
|
|
1
1
|
module ErpTechSvcs
|
2
2
|
module Config
|
3
3
|
class << self
|
4
|
-
attr_accessor :max_file_size_in_mb,
|
4
|
+
attr_accessor :max_file_size_in_mb,
|
5
|
+
:installation_domain,
|
6
|
+
:login_url,
|
7
|
+
:email_notifications_from,
|
8
|
+
:file_assets_location,
|
9
|
+
:s3_url_expires_in_seconds,
|
10
|
+
:s3_protocol,
|
11
|
+
:file_storage,
|
12
|
+
:s3_cache_expires_in_minutes
|
5
13
|
|
6
14
|
def init!
|
7
|
-
@defaults = {
|
15
|
+
@defaults = {
|
16
|
+
:@max_file_size_in_mb => 5,
|
17
|
+
:@installation_domain => 'localhost:3000',
|
18
|
+
:@login_url => '/erp_app/login',
|
19
|
+
:@email_notifications_from => 'notifications@noreply.com',
|
20
|
+
:@file_assets_location => 'file_assets', # relative to Rails.root/
|
21
|
+
:@s3_url_expires_in_seconds => 60,
|
22
|
+
:@s3_protocol => 'https', # Can be either 'http' or 'https'
|
23
|
+
:@file_storage => :filesystem, # Can be either :s3 or :filesystem
|
24
|
+
:@s3_cache_expires_in_minutes => 60
|
25
|
+
}
|
8
26
|
end
|
9
27
|
|
10
28
|
def reset!
|