incline 0.1.5
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 +7 -0
- data/.gitignore +13 -0
- data/Gemfile +17 -0
- data/Gemfile.lock +186 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +208 -0
- data/Rakefile +37 -0
- data/app/assets/fonts/incline/.keep +0 -0
- data/app/assets/images/incline/.keep +0 -0
- data/app/assets/images/incline/barcode-B.svg +181 -0
- data/app/assets/javascripts/incline/activate_classed_items.js +11 -0
- data/app/assets/javascripts/incline/application.js +30 -0
- data/app/assets/javascripts/incline/bootstrap-datepicker.js +1800 -0
- data/app/assets/javascripts/incline/datatables.js +22193 -0
- data/app/assets/javascripts/incline/escapeHtml.js +10 -0
- data/app/assets/javascripts/incline/inline_actions.js +479 -0
- data/app/assets/javascripts/incline/jquery.doubleScroll.js +112 -0
- data/app/assets/javascripts/incline/jquery.number.js +764 -0
- data/app/assets/javascripts/incline/regexMask.js +27 -0
- data/app/assets/javascripts/incline/select2/i18n/en.js +3 -0
- data/app/assets/javascripts/incline/select2/select2.full.js +6436 -0
- data/app/assets/stylesheets/incline/application.css +18 -0
- data/app/assets/stylesheets/incline/bootstrap-theme.min.css +5 -0
- data/app/assets/stylesheets/incline/custom.scss +279 -0
- data/app/assets/stylesheets/incline/datatables.css +494 -0
- data/app/assets/stylesheets/incline/datepicker3.css +790 -0
- data/app/assets/stylesheets/incline/select2.css +484 -0
- data/app/controllers/incline/access_groups_controller.rb +127 -0
- data/app/controllers/incline/access_test_controller.rb +30 -0
- data/app/controllers/incline/account_activations_controller.rb +28 -0
- data/app/controllers/incline/application_controller.rb +11 -0
- data/app/controllers/incline/contact_controller.rb +34 -0
- data/app/controllers/incline/password_resets_controller.rb +113 -0
- data/app/controllers/incline/security_controller.rb +100 -0
- data/app/controllers/incline/sessions_controller.rb +50 -0
- data/app/controllers/incline/users_controller.rb +304 -0
- data/app/controllers/incline/welcome_controller.rb +19 -0
- data/app/helpers/incline/.keep +0 -0
- data/app/mailers/incline/application_mailer_base.rb +11 -0
- data/app/mailers/incline/contact_form.rb +19 -0
- data/app/mailers/incline/user_mailer.rb +45 -0
- data/app/models/incline/access_group.rb +121 -0
- data/app/models/incline/access_group_group_member.rb +12 -0
- data/app/models/incline/access_group_user_member.rb +10 -0
- data/app/models/incline/action_group.rb +12 -0
- data/app/models/incline/action_security.rb +222 -0
- data/app/models/incline/contact_message.rb +37 -0
- data/app/models/incline/disable_info.rb +20 -0
- data/app/models/incline/password_reset.rb +14 -0
- data/app/models/incline/password_reset_request.rb +14 -0
- data/app/models/incline/user.rb +437 -0
- data/app/models/incline/user_login_history.rb +30 -0
- data/app/views/incline/access_groups/_details.json.jbuilder +10 -0
- data/app/views/incline/access_groups/_form.html.erb +19 -0
- data/app/views/incline/access_groups/_list.html.erb +60 -0
- data/app/views/incline/access_groups/_messages.json.jbuilder +6 -0
- data/app/views/incline/access_groups/edit.html.erb +2 -0
- data/app/views/incline/access_groups/index.html.erb +6 -0
- data/app/views/incline/access_groups/index.json.jbuilder +16 -0
- data/app/views/incline/access_groups/new.html.erb +2 -0
- data/app/views/incline/access_groups/show.html.erb +9 -0
- data/app/views/incline/access_groups/show.json.jbuilder +11 -0
- data/app/views/incline/contact/new.html.erb +22 -0
- data/app/views/incline/contact_form/contact.html.erb +16 -0
- data/app/views/incline/contact_form/contact.text.erb +13 -0
- data/app/views/incline/password_resets/edit.html.erb +16 -0
- data/app/views/incline/password_resets/new.html.erb +12 -0
- data/app/views/incline/security/_details.json.jbuilder +7 -0
- data/app/views/incline/security/_form.html.erb +20 -0
- data/app/views/incline/security/_list.html.erb +89 -0
- data/app/views/incline/security/_messages.json.jbuilder +6 -0
- data/app/views/incline/security/edit.html.erb +2 -0
- data/app/views/incline/security/index.html.erb +6 -0
- data/app/views/incline/security/index.json.jbuilder +16 -0
- data/app/views/incline/security/show.html.erb +31 -0
- data/app/views/incline/security/show.json.jbuilder +11 -0
- data/app/views/incline/sessions/new.html.erb +26 -0
- data/app/views/incline/user_mailer/account_activation.html.erb +7 -0
- data/app/views/incline/user_mailer/account_activation.text.erb +6 -0
- data/app/views/incline/user_mailer/invalid_password_reset.html.erb +3 -0
- data/app/views/incline/user_mailer/invalid_password_reset.text.erb +5 -0
- data/app/views/incline/user_mailer/password_reset.html.erb +8 -0
- data/app/views/incline/user_mailer/password_reset.text.erb +7 -0
- data/app/views/incline/users/_details.json.jbuilder +32 -0
- data/app/views/incline/users/_form.html.erb +21 -0
- data/app/views/incline/users/_list.html.erb +102 -0
- data/app/views/incline/users/_messages.json.jbuilder +6 -0
- data/app/views/incline/users/disable_confirm.html.erb +19 -0
- data/app/views/incline/users/edit.html.erb +5 -0
- data/app/views/incline/users/index.html.erb +6 -0
- data/app/views/incline/users/index.json.jbuilder +16 -0
- data/app/views/incline/users/new.html.erb +5 -0
- data/app/views/incline/users/show.html.erb +12 -0
- data/app/views/incline/users/show.json.jbuilder +11 -0
- data/app/views/incline/welcome/home.html.erb +5 -0
- data/app/views/layouts/application.html.erb +1 -0
- data/app/views/layouts/incline/_account_menu.html.erb +18 -0
- data/app/views/layouts/incline/_app_menu_anon.html.erb +1 -0
- data/app/views/layouts/incline/_app_menu_authenticated.html.erb +1 -0
- data/app/views/layouts/incline/_footer.html.erb +13 -0
- data/app/views/layouts/incline/_header.html.erb +21 -0
- data/app/views/layouts/incline/_html_mailer.html.erb +5 -0
- data/app/views/layouts/incline/_incline_app.html.erb +25 -0
- data/app/views/layouts/incline/_messages.html.erb +3 -0
- data/app/views/layouts/incline/_shim.html.erb +3 -0
- data/app/views/layouts/incline/_text_mailer.text.erb +1 -0
- data/app/views/layouts/incline/application.html.erb +1 -0
- data/app/views/layouts/mailer.html.erb +2 -0
- data/app/views/layouts/mailer.text.erb +2 -0
- data/bin/rails +12 -0
- data/bin/test_scaffold.sh +10 -0
- data/config/routes.rb +61 -0
- data/db/migrate/20170511230126_create_incline_users.rb +26 -0
- data/db/migrate/20170515003052_create_incline_access_groups.rb +10 -0
- data/db/migrate/20170515003221_create_incline_user_login_histories.rb +12 -0
- data/db/migrate/20170515150908_create_incline_access_group_user_members.rb +11 -0
- data/db/migrate/20170515151058_create_incline_access_group_group_members.rb +11 -0
- data/db/migrate/20170517193432_add_comments_to_incline_user.rb +5 -0
- data/db/migrate/20170622132700_create_incline_action_securities.rb +16 -0
- data/db/migrate/20170622172712_create_incline_action_groups.rb +11 -0
- data/db/migrate/20170622195742_add_non_standard_to_action_security.rb +5 -0
- data/db/migrate/20170622230422_add_visible_to_action_security.rb +5 -0
- data/db/seeds.rb +81 -0
- data/exe/new_incline_app +42 -0
- data/lib/generators/incline/install_generator.rb +259 -0
- data/lib/generators/incline/templates/_app_menu_anon.html.erb +1 -0
- data/lib/generators/incline/templates/_app_menu_authenticated.html.erb +1 -0
- data/lib/generators/incline/templates/incline_application.css +17 -0
- data/lib/generators/incline/templates/incline_application.html.erb +1 -0
- data/lib/generators/incline/templates/incline_application.js +12 -0
- data/lib/generators/incline/templates/incline_database.yml +25 -0
- data/lib/generators/incline/templates/incline_email.yml +20 -0
- data/lib/generators/incline/templates/incline_mailer.html.erb +2 -0
- data/lib/generators/incline/templates/incline_mailer.text.erb +2 -0
- data/lib/generators/incline/templates/incline_users.yml +64 -0
- data/lib/generators/incline/templates/incline_version.rb +3 -0
- data/lib/incline/auth_engine_base.rb +52 -0
- data/lib/incline/data_tables_request.rb +336 -0
- data/lib/incline/date_time_formats.rb +6 -0
- data/lib/incline/engine.rb +212 -0
- data/lib/incline/errors.rb +15 -0
- data/lib/incline/extensions/action_controller_base.rb +526 -0
- data/lib/incline/extensions/action_mailer_base.rb +66 -0
- data/lib/incline/extensions/action_view_base.rb +489 -0
- data/lib/incline/extensions/active_record_base.rb +308 -0
- data/lib/incline/extensions/application.rb +137 -0
- data/lib/incline/extensions/application_configuration.rb +50 -0
- data/lib/incline/extensions/connection_adapter.rb +55 -0
- data/lib/incline/extensions/date_time_value.rb +123 -0
- data/lib/incline/extensions/date_value.rb +77 -0
- data/lib/incline/extensions/decimal_value.rb +55 -0
- data/lib/incline/extensions/erb_scaffold_generator.rb +31 -0
- data/lib/incline/extensions/float_value.rb +59 -0
- data/lib/incline/extensions/form_builder.rb +617 -0
- data/lib/incline/extensions/integer_value.rb +54 -0
- data/lib/incline/extensions/jbuilder_generator.rb +38 -0
- data/lib/incline/extensions/jbuilder_template.rb +39 -0
- data/lib/incline/extensions/main_app.rb +40 -0
- data/lib/incline/extensions/numeric.rb +63 -0
- data/lib/incline/extensions/object.rb +31 -0
- data/lib/incline/extensions/resource_route_generator.rb +53 -0
- data/lib/incline/extensions/session.rb +113 -0
- data/lib/incline/extensions/string.rb +50 -0
- data/lib/incline/extensions/test_case.rb +764 -0
- data/lib/incline/extensions/time_zone_converter.rb +40 -0
- data/lib/incline/global_status.rb +236 -0
- data/lib/incline/helpers/route_hash_formatter.rb +46 -0
- data/lib/incline/json_log_formatter.rb +96 -0
- data/lib/incline/json_logger.rb +17 -0
- data/lib/incline/log.rb +153 -0
- data/lib/incline/number_formats.rb +17 -0
- data/lib/incline/recaptcha.rb +346 -0
- data/lib/incline/user_manager.rb +212 -0
- data/lib/incline/validators/email_validator.rb +45 -0
- data/lib/incline/validators/ip_address_validator.rb +32 -0
- data/lib/incline/validators/recaptcha_validator.rb +37 -0
- data/lib/incline/validators/safe_name_validator.rb +31 -0
- data/lib/incline/version.rb +3 -0
- data/lib/incline/work_path.rb +75 -0
- data/lib/incline.rb +197 -0
- data/lib/tasks/incline_tasks.rake +4 -0
- data/lib/templates/erb/scaffold/_form.html.erb +43 -0
- data/lib/templates/erb/scaffold/_list.html.erb +81 -0
- data/lib/templates/erb/scaffold/edit.html.erb +1 -0
- data/lib/templates/erb/scaffold/index.html.erb +6 -0
- data/lib/templates/erb/scaffold/new.html.erb +1 -0
- data/lib/templates/erb/scaffold/show.html.erb +34 -0
- data/lib/templates/jbuilder/scaffold/_details.json.jbuilder +20 -0
- data/lib/templates/jbuilder/scaffold/index.json.jbuilder +16 -0
- data/lib/templates/jbuilder/scaffold/show.json.jbuilder +16 -0
- data/lib/templates/rails/scaffold_controller/controller.rb +128 -0
- data/test/controllers/incline/access_groups_controller_test.rb +65 -0
- data/test/controllers/incline/access_test_controller_test.rb +53 -0
- data/test/controllers/incline/contact_controller_test.rb +32 -0
- data/test/controllers/incline/security_controller_test.rb +39 -0
- data/test/controllers/incline/welcome_controller_test.rb +16 -0
- data/test/dummy/README.rdoc +28 -0
- data/test/dummy/Rakefile +6 -0
- data/test/dummy/app/assets/images/.keep +0 -0
- data/test/dummy/app/assets/javascripts/application.js +12 -0
- data/test/dummy/app/assets/stylesheets/application.css +17 -0
- data/test/dummy/app/controllers/application_controller.rb +5 -0
- data/test/dummy/app/controllers/concerns/.keep +0 -0
- data/test/dummy/app/helpers/application_helper.rb +2 -0
- data/test/dummy/app/mailers/.keep +0 -0
- data/test/dummy/app/models/.keep +0 -0
- data/test/dummy/app/models/concerns/.keep +0 -0
- data/test/dummy/app/views/layouts/application.html.erb +1 -0
- data/test/dummy/app/views/layouts/incline/_app_menu_anon.html.erb +1 -0
- data/test/dummy/app/views/layouts/incline/_app_menu_authenticated.html.erb +1 -0
- data/test/dummy/app/views/layouts/mailer.html.erb +2 -0
- data/test/dummy/app/views/layouts/mailer.text.erb +2 -0
- data/test/dummy/bin/bundle +3 -0
- data/test/dummy/bin/rails +4 -0
- data/test/dummy/bin/rake +4 -0
- data/test/dummy/bin/setup +29 -0
- data/test/dummy/config/application.rb +38 -0
- data/test/dummy/config/boot.rb +5 -0
- data/test/dummy/config/database.yml +34 -0
- data/test/dummy/config/email.yml +24 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/development.rb +45 -0
- data/test/dummy/config/environments/production.rb +85 -0
- data/test/dummy/config/environments/test.rb +44 -0
- data/test/dummy/config/initializers/assets.rb +11 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy/config/initializers/cookies_serializer.rb +3 -0
- data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/test/dummy/config/initializers/inflections.rb +16 -0
- data/test/dummy/config/initializers/mime_types.rb +4 -0
- data/test/dummy/config/initializers/session_store.rb +3 -0
- data/test/dummy/config/initializers/to_time_preserves_timezone.rb +10 -0
- data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/test/dummy/config/locales/en.yml +23 -0
- data/test/dummy/config/routes.rb +6 -0
- data/test/dummy/config.ru +4 -0
- data/test/dummy/db/schema.rb +108 -0
- data/test/dummy/lib/assets/.keep +0 -0
- data/test/dummy/log/.keep +0 -0
- data/test/dummy/public/404.html +67 -0
- data/test/dummy/public/422.html +67 -0
- data/test/dummy/public/500.html +66 -0
- data/test/dummy/public/favicon.ico +0 -0
- data/test/extensions/action_controller_base_extensions_test.rb +21 -0
- data/test/extensions/action_mailer_base_extensions_test.rb +20 -0
- data/test/extensions/action_view_base_extensions_test.rb +267 -0
- data/test/extensions/active_record_extensions_test.rb +173 -0
- data/test/extensions/application_configuration_extensions_test.rb +46 -0
- data/test/extensions/application_extensions_test.rb +23 -0
- data/test/extensions/connection_adapter_extensions_test.rb +54 -0
- data/test/extensions/date_time_value_extensions_test.rb +104 -0
- data/test/extensions/date_value_extensions_test.rb +102 -0
- data/test/extensions/decimal_value_extensions_test.rb +85 -0
- data/test/extensions/erb_scaffold_generator_extensions_test.rb +17 -0
- data/test/extensions/float_value_extensions_test.rb +78 -0
- data/test/extensions/form_builder_extensions_test.rb +28 -0
- data/test/extensions/integer_value_extensions_test.rb +78 -0
- data/test/extensions/jbuilder_generator_extensions_test.rb +21 -0
- data/test/extensions/jbuilder_template_extensions_test.rb +47 -0
- data/test/extensions/main_app_extensions_test.rb +55 -0
- data/test/extensions/numeric_extensions_test.rb +76 -0
- data/test/extensions/object_extensions_test.rb +104 -0
- data/test/extensions/session_extensions_test.rb +69 -0
- data/test/extensions/string_extensions_test.rb +32 -0
- data/test/extensions/test_case_extensions_test.rb +538 -0
- data/test/extensions/time_zone_converter_extensions_test.rb +10 -0
- data/test/fixtures/incline/access_group_group_members.yml +1 -0
- data/test/fixtures/incline/access_group_user_members.yml +1 -0
- data/test/fixtures/incline/access_groups.yml +13 -0
- data/test/fixtures/incline/action_groups.yml +6 -0
- data/test/fixtures/incline/action_securities.yml +18 -0
- data/test/fixtures/incline/user_login_histories.yml +1 -0
- data/test/fixtures/incline/users.yml +64 -0
- data/test/incline_test.rb +63 -0
- data/test/integration/incline/users_edit_test.rb +180 -0
- data/test/integration/incline/users_login_test.rb +105 -0
- data/test/integration/incline/users_signup_test.rb +147 -0
- data/test/integration/navigation_test.rb +11 -0
- data/test/lib/data_tables_request_test.rb +245 -0
- data/test/lib/date_time_formats_test.rb +111 -0
- data/test/lib/global_status_test.rb +89 -0
- data/test/lib/json_log_formatter_test.rb +43 -0
- data/test/lib/log_test.rb +36 -0
- data/test/lib/recaptcha_test.rb +75 -0
- data/test/lib/user_manager_test.rb +47 -0
- data/test/lib/work_path_test.rb +18 -0
- data/test/models/incline/access_group_group_member_test.rb +30 -0
- data/test/models/incline/access_group_test.rb +60 -0
- data/test/models/incline/access_group_user_member_test.rb +29 -0
- data/test/models/incline/action_group_test.rb +27 -0
- data/test/models/incline/action_security_test.rb +176 -0
- data/test/models/incline/contact_message_test.rb +66 -0
- data/test/models/incline/disable_info_test.rb +29 -0
- data/test/models/incline/password_reset_request_test.rb +35 -0
- data/test/models/incline/password_reset_test.rb +51 -0
- data/test/models/incline/user_login_history_test.rb +31 -0
- data/test/models/incline/user_test.rb +91 -0
- data/test/test_helper.rb +42 -0
- data/test/validators/email_validator_test.rb +102 -0
- data/test/validators/ip_address_validator_test.rb +107 -0
- data/test/validators/recaptcha_validator_test.rb +57 -0
- data/test/validators/safe_name_validator_test.rb +101 -0
- metadata +584 -0
|
@@ -0,0 +1,437 @@
|
|
|
1
|
+
require_relative './access_group'
|
|
2
|
+
require_relative './user_login_history'
|
|
3
|
+
|
|
4
|
+
module Incline
|
|
5
|
+
|
|
6
|
+
##
|
|
7
|
+
# This class represents an application user.
|
|
8
|
+
class User < ActiveRecord::Base
|
|
9
|
+
|
|
10
|
+
ANONYMOUS_EMAIL = 'anonymous@server.local'
|
|
11
|
+
|
|
12
|
+
has_many :login_histories, class_name: 'Incline::UserLoginHistory'
|
|
13
|
+
|
|
14
|
+
has_many :access_group_user_members, class_name: 'Incline::AccessGroupUserMember', foreign_key: 'member_id'
|
|
15
|
+
private :access_group_user_members, :access_group_user_members=
|
|
16
|
+
|
|
17
|
+
has_many :groups, class_name: 'Incline::AccessGroup', through: :access_group_user_members
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
before_save :downcase_email
|
|
21
|
+
before_create :create_activation_digest
|
|
22
|
+
after_save :refresh_comments
|
|
23
|
+
|
|
24
|
+
attr_accessor :recaptcha
|
|
25
|
+
|
|
26
|
+
attr_accessor :remember_token
|
|
27
|
+
attr_accessor :activation_token
|
|
28
|
+
attr_accessor :reset_token
|
|
29
|
+
|
|
30
|
+
search_attribute :email
|
|
31
|
+
|
|
32
|
+
has_secure_password
|
|
33
|
+
|
|
34
|
+
validates :name,
|
|
35
|
+
presence: true,
|
|
36
|
+
length: { maximum: 100 }
|
|
37
|
+
|
|
38
|
+
validates :email,
|
|
39
|
+
presence: true,
|
|
40
|
+
length: { maximum: 250 },
|
|
41
|
+
uniqueness: { case_sensitive: false },
|
|
42
|
+
'incline/email' => true
|
|
43
|
+
|
|
44
|
+
validates :password,
|
|
45
|
+
presence: true,
|
|
46
|
+
length: { minimum: 8 },
|
|
47
|
+
allow_nil: true
|
|
48
|
+
|
|
49
|
+
validates :disabled_by,
|
|
50
|
+
length: { maximum: 250 }
|
|
51
|
+
|
|
52
|
+
validates :disabled_reason,
|
|
53
|
+
length: { maximum: 200 }
|
|
54
|
+
|
|
55
|
+
validates :last_login_ip,
|
|
56
|
+
length: { maximum: 64 },
|
|
57
|
+
'incline/ip_address' => { no_mask: true }
|
|
58
|
+
|
|
59
|
+
validates :password_digest,
|
|
60
|
+
:activation_digest,
|
|
61
|
+
:remember_digest,
|
|
62
|
+
:reset_digest,
|
|
63
|
+
length: { maximum: 100 }
|
|
64
|
+
|
|
65
|
+
# recaptcha is only required when creating a new record.
|
|
66
|
+
validates :recaptcha,
|
|
67
|
+
presence: true,
|
|
68
|
+
'incline/recaptcha' => true,
|
|
69
|
+
on: :create
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
##
|
|
73
|
+
# Gets all known users.
|
|
74
|
+
scope :known, ->{ where.not(email: ANONYMOUS_EMAIL) }
|
|
75
|
+
|
|
76
|
+
##
|
|
77
|
+
# Gets all of the currently enabled users.
|
|
78
|
+
scope :enabled, ->{ where(enabled: true, activated: true) }
|
|
79
|
+
|
|
80
|
+
##
|
|
81
|
+
# Sorts the users by name.
|
|
82
|
+
scope :sorted, ->{ order(name: :asc) }
|
|
83
|
+
|
|
84
|
+
##
|
|
85
|
+
# Gets the email address in a partially obfuscated fashion.
|
|
86
|
+
def partial_email
|
|
87
|
+
@partial_email ||=
|
|
88
|
+
begin
|
|
89
|
+
uid,_,domain = email.partition('@')
|
|
90
|
+
if uid.length < 4
|
|
91
|
+
uid = '*' * uid.length
|
|
92
|
+
elsif uid.length < 8
|
|
93
|
+
uid = uid[0..2] + ('*' * (uid.length - 3))
|
|
94
|
+
else
|
|
95
|
+
uid = uid[0..2] + ('*' * (uid.length - 6)) + uid[-3..-1]
|
|
96
|
+
end
|
|
97
|
+
"#{uid}@#{domain}"
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
##
|
|
102
|
+
# Gets the email formatted with the name.
|
|
103
|
+
def formatted_email
|
|
104
|
+
"#{name} <#{email}>"
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
##
|
|
108
|
+
# Gets the IDs for the groups that the user explicitly belongs to.
|
|
109
|
+
def group_ids
|
|
110
|
+
groups.map{|g| g.id}
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
##
|
|
114
|
+
# Sets the IDs for the groups that the user explicitly belongs to.
|
|
115
|
+
def group_ids=(values)
|
|
116
|
+
values ||= []
|
|
117
|
+
values = [ values ] unless values.is_a?(::Array)
|
|
118
|
+
values = values.reject{|v| v.blank?}.map{|v| v.to_i}
|
|
119
|
+
self.groups = Incline::AccessGroup.where(id: values).to_a
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
##
|
|
123
|
+
# Gets the effective group membership of this user.
|
|
124
|
+
def effective_groups(refresh = false)
|
|
125
|
+
@effective_groups = nil if refresh
|
|
126
|
+
@effective_groups ||= if system_admin?
|
|
127
|
+
AccessGroup.all.map{ |g| g.to_s.upcase }
|
|
128
|
+
else
|
|
129
|
+
groups
|
|
130
|
+
.collect{ |g| g.effective_groups }
|
|
131
|
+
.flatten
|
|
132
|
+
end
|
|
133
|
+
.map{ |g| g.to_s.upcase }
|
|
134
|
+
.uniq
|
|
135
|
+
.sort
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
##
|
|
139
|
+
# Does this user have the equivalent of one or more of these groups?
|
|
140
|
+
def has_any_group?(*group_list)
|
|
141
|
+
return :system_admin if system_admin?
|
|
142
|
+
return false if anonymous?
|
|
143
|
+
|
|
144
|
+
r = group_list.select{|g| effective_groups.include?(g.upcase)}
|
|
145
|
+
|
|
146
|
+
r.blank? ? false : r
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
##
|
|
150
|
+
# Generates a remember token and saves the digest to the user model.
|
|
151
|
+
def remember
|
|
152
|
+
self.remember_token = Incline::User::new_token
|
|
153
|
+
update_attribute(:remember_digest, Incline::User::digest(self.remember_token))
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
##
|
|
157
|
+
# Removes the remember digest from the user model.
|
|
158
|
+
def forget
|
|
159
|
+
update_attribute(:remember_digest, nil)
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
##
|
|
163
|
+
# Determines if the supplied token digests to the stored digest in the user model.
|
|
164
|
+
def authenticated?(attribute, token)
|
|
165
|
+
return false unless respond_to?("#{attribute}_digest")
|
|
166
|
+
digest = send("#{attribute}_digest")
|
|
167
|
+
return false if digest.blank?
|
|
168
|
+
BCrypt::Password.new(digest).is_password?(token)
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
##
|
|
172
|
+
# Disables the user.
|
|
173
|
+
#
|
|
174
|
+
# The +other_user+ is required, cannot be the current user, and must be a system administrator.
|
|
175
|
+
# The +reason+ is technically optional, but should be provided.
|
|
176
|
+
def disable(other_user, reason)
|
|
177
|
+
return false unless other_user&.system_admin?
|
|
178
|
+
return false if other_user == self
|
|
179
|
+
|
|
180
|
+
update_columns(
|
|
181
|
+
disabled_by: other_user.email,
|
|
182
|
+
disabled_at: Time.now,
|
|
183
|
+
disabled_reason: reason,
|
|
184
|
+
enabled: false
|
|
185
|
+
) && refresh_comments
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
##
|
|
189
|
+
# Enables the user and removes any previous disable information.
|
|
190
|
+
def enable
|
|
191
|
+
update_columns(
|
|
192
|
+
disabled_by: nil,
|
|
193
|
+
disabled_at: nil,
|
|
194
|
+
disabled_reason: nil,
|
|
195
|
+
enabled: true
|
|
196
|
+
) && refresh_comments
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
##
|
|
200
|
+
# Marks the user as activated and removes the activation digest from the user model.
|
|
201
|
+
def activate
|
|
202
|
+
update_columns(
|
|
203
|
+
activated: true,
|
|
204
|
+
activated_at: Time.now,
|
|
205
|
+
activation_digest: nil
|
|
206
|
+
) && refresh_comments
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
##
|
|
210
|
+
# Sends the activation email to the user.
|
|
211
|
+
def send_activation_email(client_ip = '0.0.0.0')
|
|
212
|
+
Incline::UserMailer.account_activation(user: self, client_ip: client_ip).deliver_now
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
##
|
|
216
|
+
# Creates a reset token and stores the digest to the user model.
|
|
217
|
+
def create_reset_digest
|
|
218
|
+
self.reset_token = Incline::User::new_token
|
|
219
|
+
update_columns(
|
|
220
|
+
reset_digest: Incline::User::digest(reset_token),
|
|
221
|
+
reset_sent_at: Time.now
|
|
222
|
+
)
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
##
|
|
226
|
+
# Was the password reset requested more than 2 hours ago?
|
|
227
|
+
def password_reset_expired?
|
|
228
|
+
reset_sent_at.nil? || reset_sent_at < 2.hours.ago
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
##
|
|
232
|
+
# Is this the anonymous user?
|
|
233
|
+
def anonymous?
|
|
234
|
+
email == ANONYMOUS_EMAIL
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
##
|
|
238
|
+
# Gets the last successful login for this user.
|
|
239
|
+
def last_successful_login
|
|
240
|
+
@last_successful_login ||= login_histories.where(successful: true).order(created_at: :desc).first
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
##
|
|
244
|
+
# Gets the last failed login for this user.
|
|
245
|
+
def last_failed_login
|
|
246
|
+
@last_failed_login ||= login_histories.where.not(successful: true).order(created_at: :desc).first
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
##
|
|
250
|
+
# Gets the failed logins for a user since the last successful login.
|
|
251
|
+
def failed_login_streak
|
|
252
|
+
@failed_login_streak ||=
|
|
253
|
+
begin
|
|
254
|
+
results = login_histories.where.not(successful: true)
|
|
255
|
+
if last_successful_login
|
|
256
|
+
results = results.where('created_at > ?', last_successful_login.created_at)
|
|
257
|
+
end
|
|
258
|
+
results.order(created_at: :desc)
|
|
259
|
+
end
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
##
|
|
263
|
+
# Generates some brief comments about the user account and stores them in the comments attribute.
|
|
264
|
+
#
|
|
265
|
+
# This gets updated automatically on every login attempt.
|
|
266
|
+
def refresh_comments
|
|
267
|
+
update_columns :comments => generate_comments
|
|
268
|
+
comments
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
##
|
|
272
|
+
# Sends the password reset email to the user.
|
|
273
|
+
def send_password_reset_email(client_ip = '0.0.0.0')
|
|
274
|
+
Incline::UserMailer.password_reset(user: self, client_ip: client_ip).deliver_now
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
##
|
|
278
|
+
# Sends a missing account message when a user requests a password reset.
|
|
279
|
+
def self.send_missing_reset_email(email, client_ip = '0.0.0.0')
|
|
280
|
+
Incline::UserMailer::invalid_password_reset(email: email, client_ip: client_ip).deliver_now
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
##
|
|
284
|
+
# Sends a disabled account message when a user requests a password reset.
|
|
285
|
+
def self.send_disabled_reset_email(email, client_ip = '0.0.0.0')
|
|
286
|
+
Incline::UserMailer::invalid_password_reset(email: email, message: 'The account attached to this email address has been disabled.', client_ip: client_ip).deliver_now
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
##
|
|
290
|
+
# Sends a non-activated account message when a user requests a password reset.
|
|
291
|
+
def self.send_inactive_reset_email(email, client_ip = '0.0.0.0')
|
|
292
|
+
Incline::UserMailer::invalid_password_reset(email: email, message: 'The account attached to this email has not yet been activated.', client_ip: client_ip).deliver_now
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
##
|
|
296
|
+
# Returns a hash digest of the given string.
|
|
297
|
+
def self.digest(string)
|
|
298
|
+
cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST : BCrypt::Engine.cost
|
|
299
|
+
BCrypt::Password.create(string, cost: cost)
|
|
300
|
+
end
|
|
301
|
+
|
|
302
|
+
##
|
|
303
|
+
# Generates a new random token in (url safe) base64.
|
|
304
|
+
def self.new_token
|
|
305
|
+
SecureRandom.urlsafe_base64(32)
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
##
|
|
309
|
+
# Generates the necessary system administrator account.
|
|
310
|
+
#
|
|
311
|
+
# When the database is initially seeded, the only user is the system administrator.
|
|
312
|
+
#
|
|
313
|
+
# The absolute default is **admin@barkerest.com** with a password of **Password1**.
|
|
314
|
+
# These values will be used if they are not overridden for the current environment.
|
|
315
|
+
#
|
|
316
|
+
# You can override this by setting the +default_admin+ property in "config/secrets.yml".
|
|
317
|
+
#
|
|
318
|
+
# # config/secrets.yml
|
|
319
|
+
# development:
|
|
320
|
+
# default_admin:
|
|
321
|
+
# email: admin@barkerest.com
|
|
322
|
+
# password: Password1
|
|
323
|
+
#
|
|
324
|
+
# Regardless of whether you use the absolute defaults or create your own, you will want
|
|
325
|
+
# to change the password on first login.
|
|
326
|
+
#
|
|
327
|
+
def self.ensure_admin_exists!
|
|
328
|
+
unless where(system_admin: true, enabled: true).count > 0
|
|
329
|
+
|
|
330
|
+
msg = "Creating/reactivating default administrator...\n"
|
|
331
|
+
if Rails.application.running?
|
|
332
|
+
Rails.logger.info msg
|
|
333
|
+
else
|
|
334
|
+
print msg
|
|
335
|
+
end
|
|
336
|
+
|
|
337
|
+
def_adm = (Rails.application.secrets[:default_admin] || {}).symbolize_keys
|
|
338
|
+
|
|
339
|
+
def_adm_email = def_adm[:email] || 'admin@barkerest.com'
|
|
340
|
+
def_adm_pass = def_adm[:password] || 'Password1'
|
|
341
|
+
|
|
342
|
+
user = Incline::Recaptcha::pause_for do
|
|
343
|
+
User
|
|
344
|
+
.where(
|
|
345
|
+
email: def_adm_email
|
|
346
|
+
)
|
|
347
|
+
.first_or_create!(
|
|
348
|
+
name: 'Default Administrator',
|
|
349
|
+
email: def_adm_email,
|
|
350
|
+
password: def_adm_pass,
|
|
351
|
+
password_confirmation: def_adm_pass,
|
|
352
|
+
enabled: true,
|
|
353
|
+
system_admin: true,
|
|
354
|
+
activated: true,
|
|
355
|
+
activated_at: Time.now,
|
|
356
|
+
recaptcha: 'na'
|
|
357
|
+
)
|
|
358
|
+
end
|
|
359
|
+
|
|
360
|
+
|
|
361
|
+
unless user.activated? && user.enabled? && user.system_admin?
|
|
362
|
+
user.password = def_adm_pass
|
|
363
|
+
user.password_confirmation = def_adm_pass
|
|
364
|
+
user.enabled = true
|
|
365
|
+
user.system_admin = true
|
|
366
|
+
user.activated = true
|
|
367
|
+
user.activated_at = Time.now
|
|
368
|
+
user.save!
|
|
369
|
+
end
|
|
370
|
+
end
|
|
371
|
+
end
|
|
372
|
+
|
|
373
|
+
##
|
|
374
|
+
# Gets a generic anonymous user.
|
|
375
|
+
def self.anonymous
|
|
376
|
+
@anonymous = nil if Rails.env.test? # always start fresh in test environment.
|
|
377
|
+
@anonymous ||=
|
|
378
|
+
Incline::Recaptcha::pause_for do
|
|
379
|
+
pwd = new_token
|
|
380
|
+
User
|
|
381
|
+
.where(email: ANONYMOUS_EMAIL)
|
|
382
|
+
.first_or_create!(
|
|
383
|
+
email: ANONYMOUS_EMAIL,
|
|
384
|
+
name: 'Anonymous',
|
|
385
|
+
enabled: false,
|
|
386
|
+
activated: true,
|
|
387
|
+
activated_at: Time.now,
|
|
388
|
+
password: pwd,
|
|
389
|
+
password_confirmation: pwd,
|
|
390
|
+
recaptcha: 'na'
|
|
391
|
+
)
|
|
392
|
+
end
|
|
393
|
+
end
|
|
394
|
+
|
|
395
|
+
##
|
|
396
|
+
# Gets the formatted email for this user.
|
|
397
|
+
def to_s
|
|
398
|
+
formatted_email
|
|
399
|
+
end
|
|
400
|
+
|
|
401
|
+
private
|
|
402
|
+
|
|
403
|
+
def generate_comments
|
|
404
|
+
(system_admin? ? "{ADMIN}\n" : '') +
|
|
405
|
+
if enabled?
|
|
406
|
+
if activated?
|
|
407
|
+
if failed_login_streak.count > 1
|
|
408
|
+
"Failed Login Streak: #{failed_login_streak.count}\nMost Recent Attempt: #{last_failed_login.date_and_ip}\n"
|
|
409
|
+
elsif failed_login_streak.count == 1
|
|
410
|
+
"Failed Login Attempt: #{last_failed_login.date_and_ip}\n"
|
|
411
|
+
else
|
|
412
|
+
''
|
|
413
|
+
end +
|
|
414
|
+
if last_successful_login
|
|
415
|
+
"Most Recent Login: #{last_successful_login.date_and_ip}"
|
|
416
|
+
else
|
|
417
|
+
'Most Recent Login: Never'
|
|
418
|
+
end
|
|
419
|
+
else
|
|
420
|
+
'Not Activated'
|
|
421
|
+
end
|
|
422
|
+
else
|
|
423
|
+
"Disabled #{disabled_at ? disabled_at.in_time_zone.strftime('%m/%d/%Y') : 'some time in the past'} by #{disabled_by.blank? ? 'somebody' : disabled_by}.\n#{disabled_reason}"
|
|
424
|
+
end
|
|
425
|
+
end
|
|
426
|
+
|
|
427
|
+
def downcase_email
|
|
428
|
+
email.downcase!
|
|
429
|
+
end
|
|
430
|
+
|
|
431
|
+
def create_activation_digest
|
|
432
|
+
self.activation_token = Incline::User::new_token
|
|
433
|
+
self.activation_digest = Incline::User::digest(activation_token)
|
|
434
|
+
end
|
|
435
|
+
|
|
436
|
+
end
|
|
437
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
module Incline
|
|
2
|
+
class UserLoginHistory < ActiveRecord::Base
|
|
3
|
+
|
|
4
|
+
belongs_to :user
|
|
5
|
+
after_save :update_user_comments
|
|
6
|
+
|
|
7
|
+
validates :user, presence: true
|
|
8
|
+
validates :ip_address, presence: true, length: { maximum: 64 }, 'incline/ip_address' => { no_mask: true }
|
|
9
|
+
validates :message, length: { maximum: 200 }
|
|
10
|
+
|
|
11
|
+
def time_and_ip
|
|
12
|
+
"#{created_at.in_time_zone.strftime('%m/%d/%Y %H:%M')} from #{ip_address}"
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def date_and_ip
|
|
16
|
+
"#{created_at.in_time_zone.strftime('%m/%d/%Y')} from #{ip_address}"
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def to_s
|
|
20
|
+
"Login #{successful ? 'succeeded' : 'failed'} on #{time_and_ip}"
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
def update_user_comments
|
|
26
|
+
user.refresh_comments
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
unless access_group.new_record?
|
|
2
|
+
json.set! 'DT_RowId', "access_group_#{access_group.id}"
|
|
3
|
+
json.set! 'DT_Path', access_group_path(access_group)
|
|
4
|
+
if access_group.destroyed?
|
|
5
|
+
json.set! 'DT_RowAction', 'remove'
|
|
6
|
+
end
|
|
7
|
+
end
|
|
8
|
+
json.set! 'name', h(access_group.name)
|
|
9
|
+
json.set! 'created_at', access_group.created_at
|
|
10
|
+
json.set! 'updated_at', access_group.updated_at
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<%= error_summary(@access_group) %>
|
|
2
|
+
<div class="col-md-4 col-md-offset-4">
|
|
3
|
+
<div class="panel panel-primary">
|
|
4
|
+
<div class="panel-heading">
|
|
5
|
+
<h4 class="panel-title"><%= @access_group.new_record? ? 'Create' : 'Update' %> Access Group</h4>
|
|
6
|
+
</div>
|
|
7
|
+
<div class="panel-body">
|
|
8
|
+
<%= form_for(@access_group) do |f| %>
|
|
9
|
+
<%= f.text_form_group :name %>
|
|
10
|
+
<%= f.select_form_group :user_ids, Incline::User.where.not(id: current_user.id).sorted, :id, :to_s, label_text: 'Users belonging to this group', field_class: 'form-control select2', field_multiple: true %>
|
|
11
|
+
<%= f.select_form_group :group_ids, Incline::AccessGroup.where.not(id: @access_group.id).sorted, :id, :to_s, label_text: 'Groups belonging to this group', field_class: 'form-control select2', field_multiple: true %>
|
|
12
|
+
|
|
13
|
+
<%= f.submit class: 'btn btn-primary' %>
|
|
14
|
+
<%= link_to 'Cancel', access_groups_url, class: 'btn btn-default' %>
|
|
15
|
+
<% end %>
|
|
16
|
+
|
|
17
|
+
</div>
|
|
18
|
+
</div>
|
|
19
|
+
</div>
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
<table id="dt-access_groups" class="table" style="width: 100%;">
|
|
2
|
+
<thead>
|
|
3
|
+
<tr>
|
|
4
|
+
<th>Name</th>
|
|
5
|
+
<th class="text-right"><%= link_to 'New', new_access_group_path, class: 'btn btn-success btn-xs inline_form' %></th>
|
|
6
|
+
</tr>
|
|
7
|
+
</thead>
|
|
8
|
+
</table>
|
|
9
|
+
|
|
10
|
+
<% provide :scripts do %>
|
|
11
|
+
<script type="text/javascript">
|
|
12
|
+
//<![CDATA[
|
|
13
|
+
$(function() {
|
|
14
|
+
$('#dt-access_groups').DataTable({
|
|
15
|
+
dom: '<"panel-body"<"col-sm-6 col-sm-offset-6"fr>>t<"panel-body"<"col-sm-6"i><"col-sm-6"p>>',
|
|
16
|
+
ajax: {
|
|
17
|
+
url: '<%= api_path %>',
|
|
18
|
+
type: 'POST'
|
|
19
|
+
},
|
|
20
|
+
columns: [
|
|
21
|
+
{
|
|
22
|
+
// the data to display.
|
|
23
|
+
data: 'name',
|
|
24
|
+
// can this column be used for sorting?
|
|
25
|
+
orderable: true,
|
|
26
|
+
// can this column be used for searching?
|
|
27
|
+
searchable: true
|
|
28
|
+
},
|
|
29
|
+
|
|
30
|
+
{
|
|
31
|
+
orderable: false,
|
|
32
|
+
searchable: false,
|
|
33
|
+
data: function (row, type, set, meta) {
|
|
34
|
+
if (type === 'display') {
|
|
35
|
+
var ret = '<div class="text-right">';
|
|
36
|
+
|
|
37
|
+
// the show icon.
|
|
38
|
+
ret += '<a href="javascript:inlineForm(\'' + row.DT_Path + '\')" title="Details" class="btn btn-default btn-xs"><i class="glyphicon glyphicon-zoom-in"></i></a>';
|
|
39
|
+
|
|
40
|
+
// the edit icon.
|
|
41
|
+
ret += '<a href="javascript:inlineForm(\'' + row.DT_Path + '/edit\')" title="Edit" class="btn btn-default btn-xs"><i class="glyphicon glyphicon-pencil"></i></a>';
|
|
42
|
+
|
|
43
|
+
// the delete icon.
|
|
44
|
+
ret += '<a href="javascript:inlineAction(\'' + row.DT_Path + '\',\'delete\')" title="Remove" class="btn btn-danger btn-xs" data-confirm="Are you sure you want to remove this access group?"><i class="glyphicon glyphicon-trash"></i></a>';
|
|
45
|
+
|
|
46
|
+
ret += '</div>';
|
|
47
|
+
return ret;
|
|
48
|
+
} else {
|
|
49
|
+
return row.DT_Path;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
],
|
|
54
|
+
responsive: true,
|
|
55
|
+
serverSide: true
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
//]]>
|
|
59
|
+
</script>
|
|
60
|
+
<% end %>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
if @dt_request&.provided?
|
|
2
|
+
json.set! 'draw', @dt_request.draw
|
|
3
|
+
json.set! 'recordsTotal', @dt_request.records_total
|
|
4
|
+
json.set! 'recordsFiltered', @dt_request.records_filtered
|
|
5
|
+
json.data do
|
|
6
|
+
json.array!(@dt_request.records) do |access_group|
|
|
7
|
+
json.partial! 'details', locals: { access_group: access_group }
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
if @dt_request.error?
|
|
11
|
+
json.set! 'error', @dt_request.error
|
|
12
|
+
end
|
|
13
|
+
else
|
|
14
|
+
json.set! 'error', 'No data tables request received.'
|
|
15
|
+
end
|
|
16
|
+
json.set! 'appInfo', h(Rails.application.app_info)
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
<h1><%= @access_group.name %></h1>
|
|
2
|
+
|
|
3
|
+
<p>
|
|
4
|
+
<h4>Direct and indirect members:</h4>
|
|
5
|
+
<%= @access_group.members.any? ? @access_group.members.map{|u| h(u.to_s)}.join('<br>').html_safe : 'None' %>
|
|
6
|
+
</p>
|
|
7
|
+
<br>
|
|
8
|
+
|
|
9
|
+
<%= link_to 'Cancel', access_groups_path, class: 'btn btn-default' %>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
json.partial! 'messages'
|
|
2
|
+
|
|
3
|
+
if @access_group.errors.any?
|
|
4
|
+
json.api_errors! 'access_group', @access_group.errors
|
|
5
|
+
else
|
|
6
|
+
json.data do
|
|
7
|
+
json.array! [ @access_group ] do |access_group|
|
|
8
|
+
json.partial! 'details', locals: { access_group: access_group }
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
<% provide :title, 'Contact Form' %>
|
|
2
|
+
|
|
3
|
+
<%= error_summary(@msg) %>
|
|
4
|
+
<div class="col-md-6 col-md-offset-3">
|
|
5
|
+
<div class="panel panel-primary">
|
|
6
|
+
<div class="panel-heading">
|
|
7
|
+
<h4 class="panel-title">Contact Form</h4>
|
|
8
|
+
</div>
|
|
9
|
+
<div class="panel-body">
|
|
10
|
+
<%= form_for @msg, url: contact_url, method: :post do |f| %>
|
|
11
|
+
<%= f.text_form_group :your_name %>
|
|
12
|
+
<%= f.text_form_group :your_email, field_type: 'email' %>
|
|
13
|
+
<%= f.select_form_group :related_to, [ 'Program Error', 'Broken Link', 'Feature Request', 'Other' ] %>
|
|
14
|
+
<%= f.text_form_group :subject %>
|
|
15
|
+
<%= f.textarea_form_group :body %>
|
|
16
|
+
<%= f.recaptcha :recaptcha %>
|
|
17
|
+
<%= f.submit 'Send', class: 'btn btn-success' %>
|
|
18
|
+
<%= link_to 'Cancel', root_url, class: 'btn btn-default' %>
|
|
19
|
+
<% end %>
|
|
20
|
+
</div>
|
|
21
|
+
</div>
|
|
22
|
+
</div>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<pre>
|
|
2
|
+
From: <%= @data[:msg].your_name %> <<%= @data[:msg].your_email %>>
|
|
3
|
+
IP Address: <%= @data[:msg].remote_ip %>
|
|
4
|
+
Date/time: <%= Time.zone.now %>
|
|
5
|
+
|
|
6
|
+
Application:
|
|
7
|
+
<%= Rails.application.app_name %> (<%= Rails.application.app_version %>)
|
|
8
|
+
Key Gems:
|
|
9
|
+
<% @data[:gems].each do |name,ver| %>
|
|
10
|
+
<%= name %> (<%= ver %>)
|
|
11
|
+
<% end %>
|
|
12
|
+
|
|
13
|
+
Message:
|
|
14
|
+
<%= @data[:msg].body %>
|
|
15
|
+
|
|
16
|
+
</pre>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
From: <%= @data[:msg].your_name %> <<%= @data[:msg].your_email %>>
|
|
2
|
+
IP Address: <%= @data[:msg].remote_ip %>
|
|
3
|
+
Date/time: <%= Time.zone.now %>
|
|
4
|
+
|
|
5
|
+
Application:
|
|
6
|
+
<%= Rails.application.app_info %>
|
|
7
|
+
Key Gems:
|
|
8
|
+
<% @data[:gems].each do |name,ver| %>
|
|
9
|
+
<%= name %> (<%= ver %>)
|
|
10
|
+
<% end %>
|
|
11
|
+
|
|
12
|
+
Message:
|
|
13
|
+
<%= @data[:msg].body %>
|