mr_common 1.3.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +30 -9
  3. data/app/controllers/mr_common/dashboard_controller.rb +1 -0
  4. data/app/controllers/mr_common/pre_registrations/import_controller.rb +26 -0
  5. data/app/controllers/mr_common/pre_registrations/pre_registrations_controller.rb +48 -0
  6. data/app/controllers/mr_common/registrations/confirmations_controller.rb +54 -0
  7. data/app/controllers/mr_common/registrations/export_controller.rb +35 -4
  8. data/app/controllers/mr_common/registrations/public_controller.rb +24 -1
  9. data/app/controllers/mr_common/registrations/registrations_controller.rb +2 -0
  10. data/app/controllers/mr_common/registrations/resend_notifications_controller.rb +27 -0
  11. data/app/controllers/mr_common/registrations/success_controller.rb +1 -0
  12. data/app/mailers/mr_common/registration_mailer.rb +13 -3
  13. data/app/models/concerns/mr_common/csv_exportable.rb +6 -2
  14. data/app/models/mr_common/country.rb +9 -0
  15. data/app/models/mr_common/csv_renderer.rb +38 -0
  16. data/app/models/mr_common/pattern.rb +10 -0
  17. data/app/models/mr_common/pre_registration.rb +52 -0
  18. data/app/models/mr_common/pre_registration_importer.rb +91 -0
  19. data/app/models/mr_common/registration.rb +11 -2
  20. data/app/models/mr_common/registration_decorator.rb +20 -0
  21. data/app/models/mr_common/reminder.rb +6 -0
  22. data/app/models/mr_common/timezone.rb +5 -0
  23. data/app/views/layouts/mr_common/_navigation.html.erb +7 -0
  24. data/app/views/mr_common/pre_registrations/import/new.html.erb +27 -0
  25. data/app/views/mr_common/pre_registrations/pre_registrations/_form.html.erb +29 -0
  26. data/app/views/mr_common/pre_registrations/pre_registrations/index.html.erb +32 -0
  27. data/{lib/generators/mr_common/views/templates/mr_common/registrations/public → app/views/mr_common/pre_registrations/pre_registrations}/new.html.erb +2 -1
  28. data/{lib/generators/mr_common/views/templates/mr_common/registration_mailer/confirmation.html.erb → app/views/mr_common/registration_mailer/confirmed_registration.html.erb} +3 -0
  29. data/app/views/mr_common/registration_mailer/{confirmation.text.erb → confirmed_registration.text.erb} +2 -0
  30. data/app/views/mr_common/registration_mailer/revoked_registration.html.erb +5 -0
  31. data/app/views/mr_common/registration_mailer/revoked_registration.text.erb +6 -0
  32. data/app/views/mr_common/registration_mailer/unconfirmed_registration.html.erb +3 -0
  33. data/app/views/mr_common/registration_mailer/unconfirmed_registration.text.erb +4 -0
  34. data/app/views/mr_common/registrations/registrations/_form.html.erb +6 -0
  35. data/app/views/mr_common/registrations/registrations/index.html.erb +4 -0
  36. data/app/views/mr_common/registrations/registrations/show.html.erb +34 -3
  37. data/config/routes.rb +13 -1
  38. data/db/migrate/20190522151523_create_pre_registrations.rb +12 -0
  39. data/db/migrate/20190530220614_add_confirmed_to_registrations.rb +5 -0
  40. data/lib/generators/mr_common/controllers/USAGE +9 -0
  41. data/lib/generators/mr_common/controllers/controllers_generator.rb +24 -0
  42. data/lib/generators/mr_common/mailers/USAGE +16 -0
  43. data/lib/generators/mr_common/mailers/mailers_generator.rb +31 -0
  44. data/lib/generators/mr_common/models/USAGE +8 -0
  45. data/lib/generators/mr_common/models/models_generator.rb +23 -0
  46. data/lib/generators/mr_common/views/USAGE +1 -4
  47. data/lib/generators/mr_common/views/views_generator.rb +7 -8
  48. data/lib/mr_common.rb +14 -2
  49. data/lib/mr_common/version.rb +1 -1
  50. data/spec/dummy/config/initializers/mr_common.rb +10 -0
  51. data/spec/dummy/db/schema.rb +14 -4
  52. data/spec/dummy/log/development.log +1969 -50958
  53. data/spec/dummy/log/test.log +647 -14437
  54. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/-G/-gyHBJNssgJVEwk7sD1F9nK_dT1D1R5NjlAvcESJxME.cache +1 -0
  55. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/3d/3DI5adLgOKj6Q76fFxrLy_hKYkrlht0MEWksNkYgoXk.cache +1 -0
  56. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/3n/3NK03OBfUwDPKVYV6_11AOo3IGNUQPed4dqB-Ddv6-Q.cache +0 -0
  57. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/7d/7DbSMErmkAd0cL6q_WyLMfx7gLYQB0h9smKZ0xGjOjY.cache +2 -0
  58. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/9m/9MK1AG2zx0HVFVRULpQQnQQY0HiNroKnYEz2FUBzw6k.cache +1 -0
  59. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/CO/cojbSP1f9tYoCyycggqXgW3OYb3atOkOpf-h-Axn36o.cache +1 -0
  60. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/H6/h6GO-mL-rzWHmoiFw-uuwdny5zj_zSb1Xzun5jkza5k.cache +1 -0
  61. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/LG/LG2fyButWQgZoNvH4qD2dBMQhl35dSysUbxsvw_2PA0.cache +0 -0
  62. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/LZ/lZ_yNthdvXeWexZW9TZuMWs6lZBLMEF5rp7aA5Bl9uU.cache +2 -0
  63. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/Lh/LhBzrZLgp6fzHcgWFt_B-TqHzHaPyKv9Uk67brUycZE.cache +1 -0
  64. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/N-/N-7bXt4Ck0q2VWzHHuRPKL0a0CH_1W_eyAU59tZraxE.cache +1 -0
  65. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/Pi/PI3-Xhs-TBYEqgGeCf3hWGn6vj1qCdqBqgYRY2jNr4Q.cache +1 -0
  66. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/QQ/qqtvJX4CrLCTv4jjIg0LwoNxJUyDpLus0RIbg6D6muk.cache +1 -0
  67. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/Se/setx2SgK-JX4zwjgbekiVnvWJnbp0-2XnO0TQJfCOHU.cache +1 -0
  68. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/Ue/UecY7KKAHkP6oiqpx1zkXAL9WWGqWdhgqhixQyJ82is.cache +1 -0
  69. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/VF/VfH7y0fTeHy8aLBK_bu5OKprS0c1-GCHUzrMzp-O0_I.cache +1 -0
  70. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/WC/WCV-7lpbJsFHZueodYuN5542p56UPNtby1LVPUjvuJU.cache +1 -0
  71. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/WV/wvfl97Gci1mpw00EFHWc0Q9x0DLBar03PTd9BrZ7MF0.cache +1 -0
  72. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/eP/ePjuIgA1bwfnJWATz7q6EjGfNnpvYEaKW_2aozCJFBQ.cache +1 -0
  73. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/ge/GegLYwRNBYKvfYR1-WWnx3ssHerCO6M94VPxwUTYHtM.cache +1 -0
  74. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/h9/H95rpLdUakuAt6lpkUhlZvxsYlSb-VrfpiznZYmWnGo.cache +1 -0
  75. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/p_/P_kWJKPROj6qDVkyzNQgx8wZmba8YaTqtWMQyGHFtuY.cache +1 -0
  76. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/w3/w34qFl6hbbodwCEAr9Px_jzso7svjewL5BaOf6QMIN4.cache +1 -0
  77. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/wi/wi_OyVKzMxj9CcM3e2Bhab5PQSg4wh0nD-7QJQ3SVPw.cache +1 -0
  78. data/spec/dummy/tmp/pids/server.pid +1 -1
  79. data/spec/factories/registrations.rb +5 -5
  80. data/spec/models/registration_spec.rb +40 -0
  81. metadata +80 -11
  82. data/app/helpers/mr_common/form_helper.rb +0 -11
  83. data/app/models/mr_common/csv_exporter.rb +0 -35
  84. data/app/models/mr_common/default_csv_export_adapter.rb +0 -23
  85. data/app/views/mr_common/registration_mailer/confirmation.html.erb +0 -4
  86. data/lib/generators/mr_common/views/templates/mr_common/registration_mailer/confirmation.text.erb +0 -6
  87. data/lib/generators/mr_common/views/templates/mr_common/registrations/success/index.html.erb +0 -2
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "csv"
4
+
5
+ module MrCommon
6
+ # Renders collections of objects to a CSV string.
7
+ #
8
+ # @param collection [Enumerable<Object>] a collection of objects.
9
+ # @param fields [Array<String, Symbol>] fields to use as columns in the CSV.
10
+ # It is expected that each item responds to each field if sent as a message.
11
+ # @param decorator [SimpleDelegator, nil] optional wrapper class used to
12
+ # generate values for CSV rows when items in the collection don't respond to
13
+ # every field provided.
14
+ class CSVRenderer
15
+ attr_reader :collection, :decorator, :fields
16
+
17
+ def initialize(collection: [], fields: [], decorator: nil)
18
+ @collection = collection
19
+ @decorator = decorator
20
+ @fields = fields.map(&:to_sym)
21
+ end
22
+
23
+ # Renders the provided collection to a CSV string.
24
+ #
25
+ # @return [String] the CSV file contents
26
+ def render
27
+ CSV.generate do |csv|
28
+ csv << fields
29
+
30
+ collection.each do |item|
31
+ item = decorator ? decorator.new(item) : item
32
+ row = fields.map { |field_name| item.try(field_name) }
33
+ csv << row
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -1,11 +1,21 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module MrCommon
4
+ # Common regex patterns
4
5
  class Pattern
5
6
  class << self
7
+ # Matches valid email addresses. Ensures that the @ symbol is
8
+ # preceded by something resembling a username and followed by something
9
+ # resembling a domain name.
6
10
  def email
7
11
  /\A\S+@\S+\.\S+\z/
8
12
  end
13
+
14
+ # Matches most phone numbers a person could enter. Tests for at least 7 of
15
+ # the characters that can appear in a phone number.
16
+ def phone_number
17
+ /[\d()\\x+\- ]{7,}/
18
+ end
9
19
  end
10
20
  end
11
21
  end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MrCommon
4
+ # When MrCommon.enable_pre_registration is true newly created Registrations
5
+ # will be checked against existing PreRegistrations. If a PreRegistration is
6
+ # found the Registration will be confirmed automatically.
7
+ #
8
+ # A PreRegistration requires a first+last name OR an email to be valid.
9
+ class PreRegistration < ApplicationRecord
10
+ validates :email, presence: true, format: MrCommon::Pattern.email, if: :should_validate_email?
11
+ validates :first_name, presence: true, if: :should_validate_name?
12
+ validates :last_name, presence: true, if: :should_validate_name?
13
+ validates :normalized_name, presence: true, if: :should_validate_name?
14
+
15
+ before_validation :normalize_email
16
+ before_validation :set_normalized_name
17
+
18
+ def self.exists_for?(registration)
19
+ email = registration.email&.downcase
20
+ name = normalized_name_from(registration).presence
21
+
22
+ exists_for_email = email.present? ? exists?(email: email) : false
23
+ exists_for_name = name.present? ? exists?(normalized_name: name) : false
24
+ exists_for_email || exists_for_name
25
+ end
26
+
27
+ private
28
+
29
+ def self.normalized_name_from(registration_or_self)
30
+ first = registration_or_self.first_name.downcase.gsub(/\W+/, "-")
31
+ last = registration_or_self.last_name.downcase.gsub(/\W+/, "-")
32
+ spacer = first.present? && last.present? ? "-" : ""
33
+ "#{first}#{spacer}#{last}"
34
+ end
35
+
36
+ def should_validate_email?
37
+ first_name.blank? && last_name.blank?
38
+ end
39
+
40
+ def should_validate_name?
41
+ email.blank?
42
+ end
43
+
44
+ def set_normalized_name
45
+ self.normalized_name = PreRegistration.normalized_name_from(self)
46
+ end
47
+
48
+ def normalize_email
49
+ self.email = email.downcase
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,91 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MrCommon
4
+ # Imports PreRegistration records from a CSV file that looks like:
5
+ #
6
+ # first_name, last_name, email
7
+ # John, Smith, jsmith@example.com
8
+ # Jane, Doe, jdoe@example.com
9
+ # ,,unknown@example.com
10
+ # Name,Only,
11
+ #
12
+ # @author Corey Smedstad <csmedstad@mreach.com>
13
+ class PreRegistrationImporter
14
+ RowStruct = Struct.new(:first_name, :last_name, :email) do
15
+ # Returns true if the row does not represent a duplicate PreRegistration
16
+ # @return [Boolean]
17
+ def new?
18
+ !PreRegistration.exists_for?(self)
19
+ end
20
+ end
21
+
22
+ ResultStruct = Struct.new(:created, :skipped)
23
+
24
+ attr_reader :csv, :current_row, :result
25
+
26
+ # @param csv [String] a the contents of a CSV file
27
+ def initialize(csv)
28
+ @csv = csv
29
+ @created = 0
30
+ @skipped = 0
31
+ end
32
+
33
+ # Attempts to create PreRegistration records for each row in the CSV
34
+ # recording the number of created and skipped records in the process.
35
+ #
36
+ # The result is memoized to prevent double imports.
37
+ #
38
+ # @returns [ResultStruct] the result of the import
39
+ def import
40
+ @result ||= perform_import
41
+ end
42
+
43
+ private
44
+
45
+ def perform_import
46
+ PreRegistration.transaction do
47
+ rows.each do |data|
48
+ load_row(data)
49
+
50
+ if current_row.new?
51
+ make_pre_registration
52
+ else
53
+ skip_pre_registration
54
+ end
55
+ end
56
+ end
57
+
58
+ ResultStruct.new(@created, @skipped)
59
+ end
60
+
61
+ def rows
62
+ CSV.parse(csv, headers: true)
63
+ end
64
+
65
+ def load_row(data)
66
+ first_name = data[0] || ""
67
+ last_name = data[1] || ""
68
+ email = data[2] || ""
69
+
70
+ @current_row = RowStruct.new(first_name, last_name, email)
71
+ end
72
+
73
+ def make_pre_registration
74
+ pre_registration = PreRegistration.new(
75
+ first_name: current_row.first_name,
76
+ last_name: current_row.last_name,
77
+ email: current_row.email
78
+ )
79
+
80
+ if pre_registration.save
81
+ @created += 1
82
+ else
83
+ skip_pre_registration
84
+ end
85
+ end
86
+
87
+ def skip_pre_registration
88
+ @skipped += 1
89
+ end
90
+ end
91
+ end
@@ -8,11 +8,20 @@ module MrCommon
8
8
 
9
9
  validates :first_name, presence: true
10
10
  validates :last_name, presence: true
11
- validates :email, presence: true
11
+ validates :email, presence: true, format: Pattern.email
12
12
  validates :company_name, presence: true
13
- validates :telephone, presence: true
13
+ validates :telephone, presence: true, format: Pattern.phone_number
14
14
  validates :job_title, presence: true
15
15
  validates :contact_via_email, inclusion: [true, false]
16
16
  validates :contact_via_phone, inclusion: [true, false]
17
+
18
+ validate :telephone_length
19
+
20
+ private
21
+
22
+ def telephone_length
23
+ digits = telephone.gsub(/\D+/, "")
24
+ errors.add(:telephone, "must be longer") if digits.length < 7
25
+ end
17
26
  end
18
27
  end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MrCommon
4
+ # Implements custom field values when exporting Registration records as CSV.
5
+ class RegistrationDecorator < SimpleDelegator
6
+ def preregistered?
7
+ __getobj__.preregistered? ? "YES" : "NO"
8
+ end
9
+
10
+ def confirmed?
11
+ __getobj__.confirmed? ? "YES" : "NO"
12
+ end
13
+
14
+ if MrCommon.registration_confirmation_strategy == :pre_register
15
+ def pre_registered?
16
+ PreRegistration.exists_for?(__getobj__) ? "YES" : "NO"
17
+ end
18
+ end
19
+ end
20
+ end
@@ -3,6 +3,10 @@
3
3
  require_dependency "mr_common/application_record"
4
4
 
5
5
  module MrCommon
6
+ # Stores information for generating downloadable calendar reminders that
7
+ # appear in some emails.
8
+ #
9
+ # When
6
10
  class Reminder < ApplicationRecord
7
11
  validates :start_time, presence: true
8
12
  validates :end_time, presence: true
@@ -15,6 +19,8 @@ module MrCommon
15
19
 
16
20
  before_validation :parameterize_slug
17
21
 
22
+ scope :for_confirmed_notice, -> { where(include_in_confirmation_mailer: true) }
23
+
18
24
  def to_ical
19
25
  cal = Icalendar::Calendar.new
20
26
 
@@ -1,12 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module MrCommon
4
+ # Helper class for building time zone select options. Pares down the list by
5
+ # grouping locations that share ones or offsets.
4
6
  class Timezone
5
7
  class << self
8
+ # @return [Array<String>] list of time zone names ordered by offset
6
9
  def time_zone_options
7
10
  filtered_time_zones.collect(&:name)
8
11
  end
9
12
 
13
+ # @return [Array<Array<String, String>>] list of label value pairs for
14
+ # building time zone select options ordered by offset.
10
15
  def time_zone_select_options
11
16
  filtered_time_zones.collect do |tz|
12
17
  ["(UTC#{tz_utc_offset(tz)}) #{tz_friendly_name(tz)}", tz.name]
@@ -13,6 +13,13 @@
13
13
  <li>
14
14
  <%= link_to 'Reminders', reminders_path %>
15
15
  </li>
16
+
17
+ <% if MrCommon.registration_confirmation_strategy == :pre_register %>
18
+ <li>
19
+ <%= link_to 'PreRegistrations', pre_registrations_path %>
20
+ </li>
21
+ <% end %>
22
+
16
23
  <% if MrCommon.authentication_method_name.present? %>
17
24
  <li>
18
25
  <%= link_to 'Log Out', main_app.send(MrCommon.logout_path_helper), method: MrCommon.logout_path_method %>
@@ -0,0 +1,27 @@
1
+ <div class="row">
2
+ <div class="col">
3
+ <h1>Import Pre Registrations</h1>
4
+ <%= form_with scope: :import, url: pre_registrations_import_index_url, local: true do |f| %>
5
+ <fieldset>
6
+ <legend>Import</legend>
7
+ <div class="form-group">
8
+ <%= f.label :csv_file, "CSV File" %>
9
+ <%= f.file_field :csv_file, required: true, accept: "text/csv" %>
10
+ </div>
11
+ <p>The CSV file should be formatted like this example:</p>
12
+ <pre>
13
+ first_name, last_name, email
14
+ John, Smith, jsmith@example.com
15
+ Jane, Doe, jdoe@example.com
16
+ ,,unknown@example.com
17
+ Name,Only,
18
+ </pre>
19
+ <p>If you have a spreadsheet with these columns, "Save as..." and choose the CSV as the file type.</p>
20
+ <p>It is assumed that your CSV has column headings. The first non-empty row will be ignored.</p>
21
+ </fieldset>
22
+
23
+ <%= f.submit "Import" %>
24
+ <%= link_to "Cancel", pre_registrations_path, class: 'button-secondary' %>
25
+ <% end %>
26
+ </div>
27
+ </div>
@@ -0,0 +1,29 @@
1
+ <%= form_with model: @pre_registration, local: true do |f| %>
2
+ <fieldset>
3
+ <legend>Pre-registration details</legend>
4
+ <%= f.form_group :email do %>
5
+ <%= f.label :email %>
6
+ <%= f.text_field :email %>
7
+ <%= f.errors :email %>
8
+ <% end %>
9
+
10
+ <%= f.form_group :first_name do %>
11
+ <%= f.label :first_name %>
12
+ <%= f.text_field :first_name %>
13
+ <%= f.errors :first_name %>
14
+ <% end %>
15
+
16
+ <%= f.form_group :last_name do %>
17
+ <%= f.label :last_name %>
18
+ <%= f.text_field :last_name %>
19
+ <%= f.errors :last_name %>
20
+ <% end %>
21
+ </fieldset>
22
+
23
+ <%= f.submit "Save" %>
24
+ <% if @pre_registration.new_record? %>
25
+ <%= link_to "Cancel", pre_registrations_path, class: 'button-secondary' %>
26
+ <% else %>
27
+ <%= link_to "Cancel", pre_registration_path(@pre_registration), class: 'button-secondary' %>
28
+ <% end %>
29
+ <% end %>
@@ -0,0 +1,32 @@
1
+ <div class="row">
2
+ <div class="col">
3
+ <h1>PreRegistrations</h1>
4
+ <p>
5
+ <%= link_to 'New', new_pre_registration_path, class: 'button' %>
6
+ </p>
7
+ </div>
8
+ </div>
9
+ <div class="row">
10
+ <div class="col">
11
+ <table>
12
+ <thead>
13
+ <tr>
14
+ <th>Email</th>
15
+ <th>First</th>
16
+ <th>Last</th>
17
+ <th>Actions</th>
18
+ </tr>
19
+ </thead>
20
+ <tbody>
21
+ <% @pre_registration.each do |r| %>
22
+ <tr>
23
+ <td><%= r.email %></td>
24
+ <td><%= r.first_name %></td>
25
+ <td><%= r.last_name %></td>
26
+ <td><%= link_to 'Delete', r, method: :delete, data: { confirm: 'Are you sure?' } %></td>
27
+ </tr>
28
+ <% end %>
29
+ </tbody>
30
+ </table>
31
+ </div>
32
+ </div>
@@ -1,6 +1,7 @@
1
1
  <div class="row">
2
2
  <div class="col">
3
- <h1>New Registration</h1>
3
+
4
+ <h1>New PreRegistration</h1>
4
5
  <%= render 'form' %>
5
6
  </div>
6
7
  </div>
@@ -1,4 +1,7 @@
1
1
  <h1>Thanks for registering</h1>
2
+
3
+ <p>Your registration is confirmed.</p>
4
+
2
5
  <p>Don't forget to download the reminder and add it to your calendar.</p>
3
6
 
4
7
  <%= render 'mr_common/registration_mailer/html_reminders' %>
@@ -1,6 +1,8 @@
1
1
  Thanks for registering
2
2
  ---
3
3
 
4
+ Your registration is confirmed.
5
+
4
6
  Don't forget to download the reminder and add it to your calendar.
5
7
 
6
8
  <%= render 'mr_common/registration_mailer/text_reminders' %>
@@ -0,0 +1,5 @@
1
+ <h1>Your registration is no longer confirmed</h1>
2
+
3
+ <p>Thank you for your interest in our invitation-only event.</p>
4
+
5
+ <p>Unfortunately, we aren’t able to accommodate your registration at this time. If we are able to confirm a spot for you, we will notify you before the event.</p>
@@ -0,0 +1,6 @@
1
+ Your registration is no longer confirmed
2
+ ---
3
+
4
+ Thank you for your interest in our invitation-only event.
5
+
6
+ Unfortunately, we aren’t able to accommodate your registration at this time. If we are able to confirm a spot for you, we will notify you before the event.