blythedunham-sms_on_rails 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (102) hide show
  1. data/MIT-LICENSE +20 -0
  2. data/Manifest +101 -0
  3. data/README +163 -0
  4. data/README.rdoc +211 -0
  5. data/Rakefile +76 -0
  6. data/app/controllers/admin/sms_on_rails/base_controller.rb +11 -0
  7. data/app/controllers/admin/sms_on_rails/drafts_controller.rb +75 -0
  8. data/app/controllers/admin/sms_on_rails/outbounds_controller.rb +117 -0
  9. data/app/controllers/admin/sms_on_rails/phone_carriers_controller.rb +85 -0
  10. data/app/controllers/admin/sms_on_rails/phone_numbers_controller.rb +101 -0
  11. data/app/controllers/sms_on_rails/creation_support.rb +99 -0
  12. data/app/controllers/sms_on_rails_controller.rb +14 -0
  13. data/app/helpers/admin/sms_on_rails/drafts_helper.rb +2 -0
  14. data/app/helpers/admin/sms_on_rails/phone_carriers_helper.rb +2 -0
  15. data/app/helpers/sms_on_rails/phone_numbers_helper.rb +9 -0
  16. data/app/helpers/sms_on_rails/sms_helper.rb +44 -0
  17. data/app/models/sms_on_rails/draft.rb +9 -0
  18. data/app/models/sms_on_rails/outbound.rb +17 -0
  19. data/app/models/sms_on_rails/phone_carrier.rb +14 -0
  20. data/app/models/sms_on_rails/phone_number.rb +8 -0
  21. data/app/views/admin/sms_on_rails/base/index.html.erb +5 -0
  22. data/app/views/admin/sms_on_rails/drafts/_show.html.erb +34 -0
  23. data/app/views/admin/sms_on_rails/drafts/edit.html.erb +36 -0
  24. data/app/views/admin/sms_on_rails/drafts/index.html.erb +32 -0
  25. data/app/views/admin/sms_on_rails/drafts/new.html.erb +34 -0
  26. data/app/views/admin/sms_on_rails/drafts/send_sms.html.erb +3 -0
  27. data/app/views/admin/sms_on_rails/drafts/show.html.erb +4 -0
  28. data/app/views/admin/sms_on_rails/outbounds/edit.html.erb +68 -0
  29. data/app/views/admin/sms_on_rails/outbounds/index.html.erb +37 -0
  30. data/app/views/admin/sms_on_rails/outbounds/new.html.erb +54 -0
  31. data/app/views/admin/sms_on_rails/outbounds/show.html.erb +69 -0
  32. data/app/views/admin/sms_on_rails/phone_carriers/edit.html.erb +24 -0
  33. data/app/views/admin/sms_on_rails/phone_carriers/index.html.erb +24 -0
  34. data/app/views/admin/sms_on_rails/phone_carriers/new.html.erb +22 -0
  35. data/app/views/admin/sms_on_rails/phone_carriers/show.html.erb +24 -0
  36. data/app/views/admin/sms_on_rails/phone_numbers/edit.html.erb +33 -0
  37. data/app/views/admin/sms_on_rails/phone_numbers/index.html.erb +28 -0
  38. data/app/views/admin/sms_on_rails/phone_numbers/new.html.erb +31 -0
  39. data/app/views/admin/sms_on_rails/phone_numbers/show.html.erb +32 -0
  40. data/app/views/layouts/sms_on_rails/basic.html.erb +26 -0
  41. data/app/views/sms_on_rails/_phone_carrier_form_item.html.erb +6 -0
  42. data/app/views/sms_on_rails/_send_sms.html.erb +33 -0
  43. data/app/views/sms_on_rails/index.html.erb +8 -0
  44. data/app/views/sms_on_rails/send_sms.html.erb +3 -0
  45. data/app/views/sms_on_rails/show.html.erb +29 -0
  46. data/config/routes.rb +19 -0
  47. data/db/data/fixtures/sms_phone_carriers.yml +110 -0
  48. data/db/migrate/sms_on_rails_carrier_tables.rb +9 -0
  49. data/db/migrate/sms_on_rails_model_tables.rb +48 -0
  50. data/db/migrate/sms_on_rails_phone_number_tables.rb +11 -0
  51. data/db/seed_data.rb +16 -0
  52. data/generators/sms_on_rails/USAGE +31 -0
  53. data/generators/sms_on_rails/commands/inserts.rb +63 -0
  54. data/generators/sms_on_rails/commands/timestamps.rb +33 -0
  55. data/generators/sms_on_rails/runners/add_all_models.rb +6 -0
  56. data/generators/sms_on_rails/runners/dependencies.rb +1 -0
  57. data/generators/sms_on_rails/runners/remove_all_models.rb +5 -0
  58. data/generators/sms_on_rails/runners/sms_on_rails_routes.rb +14 -0
  59. data/generators/sms_on_rails/sms_on_rails_generator.rb +255 -0
  60. data/generators/sms_on_rails/templates/configuration/clickatell.rb +6 -0
  61. data/generators/sms_on_rails/templates/configuration/email_gateway.rb +7 -0
  62. data/generators/sms_on_rails/templates/migrate/schema_migration.rb +15 -0
  63. data/generators/sms_on_rails/templates/migrate/sms_on_rails_update_phone_numbers.rb +40 -0
  64. data/generators/sms_on_rails/templates/phone_number_collision.rb +2 -0
  65. data/init.rb +3 -0
  66. data/install.rb +1 -0
  67. data/lib/sms_on_rails.rb +8 -0
  68. data/lib/sms_on_rails/activerecord_extensions/acts_as_deliverable.rb +92 -0
  69. data/lib/sms_on_rails/activerecord_extensions/acts_as_substitutable.rb +80 -0
  70. data/lib/sms_on_rails/activerecord_extensions/has_a_sms_service_provider.rb +101 -0
  71. data/lib/sms_on_rails/activerecord_extensions/lockable_record.rb +186 -0
  72. data/lib/sms_on_rails/all_models.rb +3 -0
  73. data/lib/sms_on_rails/model_support/draft.rb +178 -0
  74. data/lib/sms_on_rails/model_support/outbound.rb +136 -0
  75. data/lib/sms_on_rails/model_support/phone_carrier.rb +77 -0
  76. data/lib/sms_on_rails/model_support/phone_number.rb +248 -0
  77. data/lib/sms_on_rails/model_support/phone_number_associations.rb +13 -0
  78. data/lib/sms_on_rails/schema_helper.rb +51 -0
  79. data/lib/sms_on_rails/service_providers/base.rb +222 -0
  80. data/lib/sms_on_rails/service_providers/clickatell.rb +52 -0
  81. data/lib/sms_on_rails/service_providers/dummy.rb +19 -0
  82. data/lib/sms_on_rails/service_providers/email_gateway.rb +68 -0
  83. data/lib/sms_on_rails/service_providers/email_gateway_support/errors.rb +20 -0
  84. data/lib/sms_on_rails/service_providers/email_gateway_support/sms_mailer.rb +21 -0
  85. data/lib/sms_on_rails/service_providers/email_gateway_support/sms_mailer/sms_through_gateway.erb +6 -0
  86. data/lib/sms_on_rails/util/sms_error.rb +12 -0
  87. data/lib/smsonrails.rb +1 -0
  88. data/public/images/sms_on_rails/railsYoDawg.jpg +0 -0
  89. data/public/stylesheets/sms_on_rails.css +137 -0
  90. data/sms_on_rails.gemspec +32 -0
  91. data/tasks/sms_on_rails_tasks.rake +67 -0
  92. data/test/active_record_extensions/delivery_and_locking_test.rb +84 -0
  93. data/test/models/draft_test.rb +72 -0
  94. data/test/models/outbound_test.rb +89 -0
  95. data/test/models/phone_number_test.rb +131 -0
  96. data/test/run.rb +18 -0
  97. data/test/service_providers/abstract_test_support.rb +104 -0
  98. data/test/service_providers/clickatell_test.rb +39 -0
  99. data/test/service_providers/email_gateway_test.rb +30 -0
  100. data/test/test_helper.rb +24 -0
  101. data/uninstall.rb +1 -0
  102. metadata +187 -0
@@ -0,0 +1,99 @@
1
+ module SmsOnRails::CreationSupport
2
+
3
+ def send_sms
4
+ setup_new_draft
5
+
6
+ respond_to do |format|
7
+ format.html { render_send_sms_template }
8
+ format.xml { render :xml => @draft }
9
+ end
10
+ end
11
+
12
+ # GET /sms/new
13
+ # GET /sms/new.xml
14
+ def new; send_sms; end
15
+
16
+
17
+ # GET /sms/1
18
+ # GET /sms/1
19
+ def show
20
+ @draft = SmsOnRails::Draft.find(params[:id])
21
+
22
+ respond_to do |format|
23
+ format.html # show.html.erb
24
+ format.xml { render :xml => @draft }
25
+ end
26
+ end
27
+
28
+ # GET /sms/1/edit
29
+ def edit
30
+ @draft = SmsOnRails::Draft.find(params[:id])
31
+ end
32
+
33
+ # POST /admin/sms/drafts
34
+ # POST /admin/sms/drafts.xml
35
+ def create_sms_draft
36
+
37
+ # Create a draft, send it immediately if the send_immediately param is set
38
+ # and do not throw fatal delivery exception errors
39
+ @draft = SmsOnRails::Draft.create_sms(params[:draft], nil,
40
+ :send_immediately => params[:send_immediately],
41
+ :deliver => {:fatal_exception => nil })
42
+
43
+ respond_to do |format|
44
+ unless @draft.errors.any?
45
+ flash[:notice] = 'Draft was successfully created and sent.'
46
+ format.html { render_sms_creation_success }
47
+ format.xml { render :xml => @draft, :status => :created, :location => @draft }
48
+ else
49
+ unless @draft.outbounds.any?
50
+ setup_new_draft
51
+ else
52
+ #evil hack as when :id is set, the nested attributes updates don't work
53
+ @draft.outbounds.each{|o| o.phone_number.id = nil if o.phone_number}
54
+ end
55
+ sanitize_draft_errors(@draft)
56
+ format.html { render_send_sms_template }
57
+ format.xml { render :xml => @draft.errors, :status => :unprocessable_entity }
58
+ end
59
+ end
60
+ end
61
+
62
+ def create; create_sms_draft; end
63
+
64
+
65
+ protected
66
+
67
+ def render_send_sms_template
68
+ render :action => :send_sms
69
+ end
70
+
71
+ def render_sms_creation_success
72
+ redirect_to(:overwrite_params => {:id => @draft, :action => :show } )
73
+ end
74
+
75
+ def setup_new_draft
76
+ @phone_number = SmsOnRails::PhoneNumber.new
77
+ @outbound = SmsOnRails::Outbound.new
78
+ @draft ||= SmsOnRails::Draft.new
79
+ @outbound.phone_number = @phone_number
80
+ @draft.outbounds << @outbound
81
+ end
82
+
83
+ # Clean up the error messages on drafts a little
84
+ def sanitize_draft_errors(draft)
85
+ if draft.errors.any?
86
+ errors = draft.errors.dup
87
+ draft.errors.clear
88
+ errors.each{|attr, message|
89
+ if attr == 'outbounds_phone_number_phone_number_digits'
90
+ draft.errors.add(:outbounds_phone_number, message)
91
+ elsif !(attr =~ /^outbounds_phone_number/) && !(attr == 'outbounds' && message == 'is invalid')
92
+ draft.errors.add(attr, message)
93
+ end
94
+ }
95
+ end
96
+ end
97
+
98
+
99
+ end
@@ -0,0 +1,14 @@
1
+ class SmsOnRailsController < ApplicationController
2
+ helper SmsOnRails::SmsHelper
3
+ helper SmsOnRails::PhoneNumbersHelper
4
+
5
+ include SmsOnRails::CreationSupport
6
+ layout 'sms_on_rails/basic.html'
7
+
8
+ def index
9
+ respond_to do |format|
10
+ format.html { render :action => 'index.html.erb' }
11
+ end
12
+ end
13
+
14
+ end
@@ -0,0 +1,2 @@
1
+ module Admin::SmsOnRails::DraftsHelper
2
+ end
@@ -0,0 +1,2 @@
1
+ module Admin::SmsOnRails::PhoneCarriersHelper
2
+ end
@@ -0,0 +1,9 @@
1
+ module SmsOnRails::PhoneNumbersHelper
2
+ def phone_carriers_collection_select(form)
3
+ form.collection_select('carrier_id',SmsOnRails::PhoneCarrier.find(:all), :id, :name)
4
+ end
5
+
6
+ def phone_number_carrier_link(phone_number)
7
+ link_to(phone_number.carrier.name, sms_phone_carrier_path(phone_number.carrier_id)) if phone_number.carrier
8
+ end
9
+ end
@@ -0,0 +1,44 @@
1
+ # To change this template, choose Tools | Templates
2
+ # and open the template in the editor.
3
+
4
+ module SmsOnRails::SmsHelper
5
+ def link_to_sms_phone_number(phone_number)
6
+ return '' unless phone_number
7
+ link_to phone_number.human_display, sms_phone_number_url(:id => phone_number)
8
+ end
9
+
10
+ def link_to_sms_draft(draft, message=nil)
11
+ return '' unless draft
12
+ link_to((message||draft.message), sms_draft_url(:id => draft))
13
+ end
14
+
15
+ def service_providers_collection_select(form)
16
+ @@spc_select||= form.select(:sms_service_provider_id,
17
+ SmsOnRails::ServiceProviders::Base.provider_list.collect{|x| [ x.human_name, x.provider_id ]},
18
+ :include_blank => true)
19
+ end
20
+
21
+ def sms_on_rails_status_select(form, options={})
22
+ form.select(:status, form.object.class.locrec_status.values.collect{|x| [x.titleize, x]},
23
+ options)
24
+ end
25
+
26
+ def requires_phone_carrier?
27
+ SmsOnRails::ServiceProviders::Base.default_service_provider.requires_carrier?
28
+ end
29
+
30
+ def send_sms_form_url
31
+ {:controller => controller_name, :action => :create,
32
+ :send_immediately => true,
33
+ :previous_action => (params[:previous_action]|| params[:action])}
34
+ end
35
+
36
+ def staggered_table_rows(objs, &block)
37
+ objs.each_with_index do |obj, idx|
38
+ concat("<tr class=\"#{idx % 2 == 0 ? 'even' : 'odd'}\"")
39
+ yield(obj)
40
+ concat("</tr>")
41
+ end
42
+ end
43
+
44
+ end
@@ -0,0 +1,9 @@
1
+ module SmsOnRails
2
+ class Draft < ActiveRecord::Base
3
+ set_table_name 'sms_drafts'
4
+
5
+ has_many :outbounds, :class_name => 'SmsOnRails::Outbound', :foreign_key => :sms_draft_id, :dependent => :nullify
6
+
7
+ include SmsOnRails::ModelSupport::Draft
8
+ end
9
+ end
@@ -0,0 +1,17 @@
1
+ module SmsOnRails
2
+ class Outbound < ActiveRecord::Base
3
+ set_table_name 'sms_outbounds'
4
+ belongs_to :draft, :class_name => 'SmsOnRails::Draft', :foreign_key => :sms_draft_id
5
+ belongs_to :phone_number, :class_name => 'SmsOnRails::PhoneNumber', :foreign_key => :sms_phone_number_id
6
+
7
+ #adding this here instead of support since might not want it
8
+ #for performance reasons
9
+ validates_uniqueness_of :sms_phone_number_id,
10
+ :scope => :sms_draft_id,
11
+ :message => 'is already included as an outbound for this draft',
12
+ :if => Proc.new { |outbound| outbound.sms_draft_id }
13
+
14
+ include SmsOnRails::ModelSupport::Outbound
15
+
16
+ end
17
+ end
@@ -0,0 +1,14 @@
1
+ module SmsOnRails
2
+ class PhoneCarrier < ActiveRecord::Base
3
+
4
+ set_table_name 'sms_phone_carriers'
5
+
6
+ acts_as_static_record :key => :name
7
+
8
+ has_many :phone_numbers, :class_name => 'SmsOnRails::PhoneNumber',
9
+ :foreign_key => 'carrier_id', :dependent => :nullify
10
+
11
+ include SmsOnRails::ModelSupport::PhoneCarrier
12
+
13
+ end
14
+ end
@@ -0,0 +1,8 @@
1
+ module SmsOnRails
2
+ class PhoneNumber < ActiveRecord::Base
3
+ set_table_name 'phone_numbers'
4
+
5
+ include SmsOnRails::ModelSupport::PhoneNumberAssociations
6
+ include SmsOnRails::ModelSupport::PhoneNumber
7
+ end#end PhoneNumber
8
+ end
@@ -0,0 +1,5 @@
1
+ <h1>SMS On Rails Admin</h1>
2
+
3
+ <%= link_to "Sms Drafts and Outbound Messages", sms_drafts_path %><br/>
4
+ <%= link_to "Phone Numbers", sms_phone_numbers_path %><br/>
5
+ <%= link_to "Phone Carriers", sms_phone_carriers_path %><br/>
@@ -0,0 +1,34 @@
1
+ <p>
2
+ <b>Id:</b>
3
+ <%= h @draft.id %>
4
+ </p>
5
+ <p>
6
+ <b>Message:</b>
7
+ <%=h @draft.message %>
8
+ </p>
9
+
10
+ <p>
11
+ <b>Header:</b>
12
+ <%=h @draft.header %>
13
+ </p>
14
+
15
+ <p>
16
+ <b>Footer:</b>
17
+ <%=h @draft.footer %>
18
+ </p>
19
+
20
+ <p>
21
+ <b>Status:</b>
22
+ <%=h @draft.status %>
23
+ </p>
24
+
25
+ <p>
26
+ <b>Deliver after:</b>
27
+ <%=h @draft.deliver_after %>
28
+ </p>
29
+
30
+ <p>
31
+ <b>Delivery date:</b>
32
+ <%=h @draft.delivery_date %>
33
+ </p>
34
+
@@ -0,0 +1,36 @@
1
+ <h1>Editing draft</h1>
2
+
3
+ <% form_for(:draft, @draft, :url => sms_draft_path, :html => {:method => :put}) do |f| %>
4
+ <%= f.error_messages %>
5
+
6
+ <p>
7
+ <%= f.label :message %><br />
8
+ <%= f.text_field :message %>
9
+ </p>
10
+ <p>
11
+ <%= f.label :header %><br />
12
+ <%= f.text_field :header %>
13
+ </p>
14
+ <p>
15
+ <%= f.label :footer %><br />
16
+ <%= f.text_field :footer %>
17
+ </p>
18
+ <p>
19
+ <%= f.label :status %><br />
20
+ <%= sms_on_rails_status_select f %>
21
+ </p>
22
+ <p>
23
+ <%= f.label :deliver_after %><br />
24
+ <%= f.datetime_select :deliver_after %>
25
+ </p>
26
+ <p>
27
+ <%= f.label :delivery_date %><br />
28
+ <%= f.datetime_select :delivery_date %>
29
+ </p>
30
+ <p>
31
+ <%= f.submit 'Update' %>
32
+ </p>
33
+ <% end %>
34
+
35
+ <%= link_to 'Show', sms_draft_path(@draft) %> |
36
+ <%= link_to 'Back', sms_drafts_path %>
@@ -0,0 +1,32 @@
1
+ <h1>SMS Drafts</h1>
2
+
3
+ <table class="stagger">
4
+ <tr>
5
+ <th class="short">Id</th>
6
+ <th class="long">Message</th>
7
+ <th>Header</th>
8
+ <th>Footer</th>
9
+ <th>Status</th>
10
+ <th>Deliver after</th>
11
+ <th>Delivery date</th>
12
+ </tr>
13
+
14
+ <% staggered_table_rows(@drafts) do |draft| %>
15
+ <td class="short"><%=h draft.id %></td>
16
+ <td><%=h draft.message %></td>
17
+ <td><%=h draft.header %></td>
18
+ <td><%=h draft.footer %></td>
19
+ <td><%=h draft.status %></td>
20
+ <td><%=h draft.deliver_after %></td>
21
+ <td><%=h draft.delivery_date %></td>
22
+ <td class="links"><%= link_to 'Show', sms_draft_outbounds_path(draft)%></td>
23
+ <td class="links"><%= link_to 'Edit', edit_sms_draft_path(draft) %></td>
24
+ <td class="links"><%= link_to 'Destroy', sms_draft_path(draft), :confirm => 'Are you sure?', :method => :delete %></td>
25
+ <% end %>
26
+ </table>
27
+
28
+
29
+ <br />
30
+
31
+ <%= link_to 'New draft', new_sms_draft_path %> |
32
+ <%= link_to 'Send SMS', send_sms_new_sms_draft_path %>
@@ -0,0 +1,34 @@
1
+ <h1>New draft</h1>
2
+
3
+ <% form_for(:draft, @draft, :url => sms_drafts_path) do |f| %>
4
+ <%= f.error_messages %>
5
+ <p>
6
+ <%= f.label :message %><br />
7
+ <%= f.text_field :message %>
8
+ </p>
9
+ <p>
10
+ <%= f.label :header %><br />
11
+ <%= f.text_field :header %>
12
+ </p>
13
+ <p>
14
+ <%= f.label :footer %><br />
15
+ <%= f.text_field :footer %>
16
+ </p>
17
+ <p>
18
+ <%= f.label :status %><br />
19
+ <%= sms_on_rails_status_select f %>
20
+ </p>
21
+ <p>
22
+ <%= f.label :deliver_after %><br />
23
+ <%= f.datetime_select :deliver_after, :include_blank => true %>
24
+ </p>
25
+ <p>
26
+ <%= f.label :delivery_date %><br />
27
+ <%= f.datetime_select :delivery_date %>
28
+ </p>
29
+ <p>
30
+ <%= f.submit 'Create' %>
31
+ </p>
32
+ <% end %>
33
+
34
+ <%= link_to 'Back',sms_drafts_path %>
@@ -0,0 +1,3 @@
1
+ <h1> Create SMS Draft </h1>
2
+ <%= render :partial => '/sms_on_rails/send_sms.html' %>
3
+ <%= link_to 'Back', sms_drafts_path %>
@@ -0,0 +1,4 @@
1
+ <h1>Draft</h1>
2
+ <%= render :partial => 'admin/sms_on_rails/drafts/show.html.erb' %>
3
+ <%= link_to 'Edit', edit_sms_draft_path(@draft) %> |
4
+ <%= link_to 'Back', sms_drafts_path %>
@@ -0,0 +1,68 @@
1
+ <h1>Editing outbound</h1>
2
+
3
+ <% form_for(:outbound, @outbound, :url => sms_draft_outbound_path(@outbound.draft, @outbound), :html => {:method => :put}) do |f| %>
4
+ <%= f.error_messages %>
5
+
6
+ <p>
7
+ <%= f.label :status %><br />
8
+ <%= sms_on_rails_status_select(f) %>
9
+ </p>
10
+ <p>
11
+ <%= f.label :notes %><br />
12
+ <%= f.text_field :notes %>
13
+ </p>
14
+ <p>
15
+ <%= f.label :processed_on %><br />
16
+ <%= f.datetime_select :processed_on %>
17
+ </p>
18
+ <p>
19
+ <%= f.label :sms_service_provider_id, 'Sms Service Provider' %><br />
20
+ <%= service_providers_collection_select(f) %>
21
+ </p>
22
+ <p>
23
+ <%= f.label :created_at %><br />
24
+ <%= f.datetime_select :created_at %>
25
+ </p>
26
+ <p>
27
+ <%= f.label :retry_count %><br />
28
+ <%= f.text_field :retry_count %>
29
+ </p>
30
+ <p>
31
+ <%= f.label :sub_status %><br />
32
+ <%= f.text_field :sub_status %>
33
+ </p>
34
+ <p>
35
+ <%= f.label :options %><br />
36
+ <%= f.text_field :options %>
37
+ </p>
38
+ <p>
39
+ <%= f.label :send_priority %><br />
40
+ <%= f.text_field :send_priority %>
41
+ </p>
42
+
43
+ <p>
44
+ <% f.fields_for :phone_number, @outbound.phone_number do |phone_form| %>
45
+ <%= phone_form.label :name, 'Phone Number'%><br />
46
+ <%= link_to_sms_phone_number(phone_form.object) %>
47
+ <% end %>
48
+ </p>
49
+
50
+ <p>
51
+ <% f.fields_for :draft, @outbound.draft do |draft_form| %>
52
+ <%= draft_form.label :message, 'Draft' %><br />
53
+ <%= link_to_sms_draft(draft_form.object) %>
54
+ <% end %>
55
+ </p>
56
+ <% unless @outbound.delivered? %>
57
+ <p>
58
+ <%= f.label :send_immediately %><br />
59
+ <%= check_box_tag :outbound_send_immediately %>
60
+ </p>
61
+ <% end %>
62
+ <p>
63
+ <%= f.submit 'Update' %>
64
+ </p>
65
+ <% end %>
66
+
67
+ <%= link_to 'Show', sms_draft_outbound_path(@outbound.draft, @outbound) %> |
68
+ <%= link_to 'Back', sms_draft_outbounds_path(@outbound.draft) %>