diff --git a/app/controllers/OrderApiController.asp b/app/controllers/OrderApiController.asp index 9736d0c..efaa0e6 100644 --- a/app/controllers/OrderApiController.asp +++ b/app/controllers/OrderApiController.asp @@ -78,7 +78,7 @@ Class OrderApiController_Class On Error GoTo 0 Set answers = json().data - Dim validationError : validationError = ValidateAnswers(answers) + Dim validationError : validationError = ValidateOrderDetailsAnswers(answers) If Len(validationError) > 0 Then WriteJsonError "400 Bad Request", validationError Exit Sub @@ -109,426 +109,11 @@ Class OrderApiController_Class ' Best-effort: the order is already safely persisted at this point, so an SMTP hiccup ' should not turn a successful submission into a failed one. - SendOrderDetailsEmail order, model + OrderMailer().SendOrderDetailsEmail order, model Response.Write "{""success"":true}" End Sub - '------------------------------------------------------------------------------------------------------------------- - ' Order-confirmation email - '------------------------------------------------------------------------------------------------------------------- - Private Sub SendOrderDetailsEmail(order, model) - On Error Resume Next - - Dim smtpPort : smtpPort = GetAppSetting("SmtpPort") - If Not IsNumeric(smtpPort) Then smtpPort = 25 - - Dim mail : Set mail = CDOEmail() - mail.SMTPServer = GetAppSetting("SmtpServer") - mail.SMTPPort = CInt(smtpPort) - mail.SMTPUsername = GetAppSetting("SmtpUsername") - mail.SMTPPassword = GetAppSetting("SmtpPassword") - mail.SMTPUseSSL = (LCase(GetAppSetting("SmtpUseSSL")) = "true") - mail.From = GetAppSetting("SmtpFromAddress") - mail.Subject = "Your Purple Envelope order details - Jurisdiction " & order("JurisdictionNumber") - mail.IsBodyHTML = True - mail.Body = BuildOrderDetailsEmailBody(order, model) - mail.AddRecipient "To", order("Email") - mail.Send - - Err.Clear - On Error GoTo 0 - End Sub - - Private Function BuildOrderDetailsEmailBody(order, model) - Dim html - html = "
" & _ - "" & _
- "
|
Click the link below to continue your order:
" & _ - "" & _ - "This link expires in " & H(GetAppSetting("OrderTokenExpirationHours")) & _ - " hours and can only be used once.
" - mail.AddRecipient "To", email - mail.Send - End Sub - End Class Dim RequestOrderController_Class__Singleton diff --git a/app/controllers/autoload_controllers.asp b/app/controllers/autoload_controllers.asp index edb70b3..255211b 100644 --- a/app/controllers/autoload_controllers.asp +++ b/app/controllers/autoload_controllers.asp @@ -2,6 +2,9 @@ + + + diff --git a/app/models/OrderDetailsAnswersMapper.asp b/app/models/OrderDetailsAnswersMapper.asp new file mode 100644 index 0000000..3fb4860 --- /dev/null +++ b/app/models/OrderDetailsAnswersMapper.asp @@ -0,0 +1,91 @@ +<% +'======================================================================================================================= +' Maps a parsed-JSON answers Dictionary (from the SurveyJS order-details form) onto a +' POBO_OrderDetails instance, and validates it first - the client-side SurveyJS +' required/enum rules in continue.asp are a UX convenience, not something the server can trust. +'======================================================================================================================= + +' Returns "" if valid, or a "; "-joined list of validation error messages. +Function ValidateOrderDetailsAnswers(answers) + Dim errors() : ReDim errors(-1) + + RequireNonEmptyString errors, answers, "ContactName", "Contact name is required." + RequireNonEmptyString errors, answers, "Municipality", "Municipality is required." + RequireNonEmptyString errors, answers, "Phone", "Phone number is required." + + RequireEnum errors, answers, "PurpleEnvelopeStock", Array("KCIStock", "OwnStock") + RequireEnum errors, answers, "PurplePrintOption", Array("AddressesAndPermit", "PermitOnly") + RequireEnum errors, answers, "PurpleBlueProvider", Array("ElectionSource", "PSI", "Spectrum", "Other") + RequireEnum errors, answers, "PurplePrintStyle", Array("ColorCoding", "BlackOnly") + RequireEnum errors, answers, "PurpleColorCodingBy", Array("Precinct", "Election") + RequireEnum errors, answers, "BluePermitOwnership", Array("KCIPermit", "OwnPermit") + RequireEnum errors, answers, "MailingPostageOption", Array("FirstClass", "NonprofitRate", "PresortStandard", "No") + RequireEnum errors, answers, "MailingHasNonprofitStatus", Array("Yes", "No", "NotSure") + + RequireBoolean errors, answers, "PurpleWantsQuote" + RequireBoolean errors, answers, "PurpleWantsBlueEnvelopes" + RequireBoolean errors, answers, "PurpleTrackMIBallot" + RequireBoolean errors, answers, "BlueWantsQuote" + RequireBoolean errors, answers, "BluePrintPermit" + RequireBoolean errors, answers, "BlueHasNonprofitStatus" + RequireBoolean errors, answers, "MailingWantsService" + + RequireNonNegativeInteger errors, answers, "PurplePrecinctCount" + RequireNonNegativeInteger errors, answers, "PurpleExtraEnvelopeQty" + RequireNonNegativeInteger errors, answers, "MailingEstimatedQuantity" + RequireNonNegativeInteger errors, answers, "SecrecySleevesQty" + RequireNonNegativeInteger errors, answers, "IVotedStickerRolls" + RequireNonNegativeInteger errors, answers, "FutureVoterStickerRolls" + + RequireValidDate errors, answers, "MailingPickupDate" + + If UBound(errors) >= 0 Then + ValidateOrderDetailsAnswers = Join(errors, "; ") + Else + ValidateOrderDetailsAnswers = "" + End If +End Function + +Sub MapOrderDetailsAnswers(ByRef model, answers) + ' Contact + model.ContactName = DictValue(answers, "ContactName") + model.Municipality = DictValue(answers, "Municipality") + model.Phone = DictValue(answers, "Phone") + + ' Purple envelope + model.PurpleWantsQuote = DictValue(answers, "PurpleWantsQuote") + model.PurpleEnvelopeStock = DictValue(answers, "PurpleEnvelopeStock") + model.PurplePrintOption = DictValue(answers, "PurplePrintOption") + model.PurpleWantsBlueEnvelopes = DictValue(answers, "PurpleWantsBlueEnvelopes") + model.PurpleBlueProvider = DictValue(answers, "PurpleBlueProvider") + model.PurpleBlueProviderOther = DictValue(answers, "PurpleBlueProviderOther") + model.PurplePrintStyle = DictValue(answers, "PurplePrintStyle") + model.PurpleColorCodingBy = DictValue(answers, "PurpleColorCodingBy") + model.PurplePrecinctCount = DictValue(answers, "PurplePrecinctCount") + model.PurpleColorNames = DictValue(answers, "PurpleColorNames") + model.PurpleTrackMIBallot = DictValue(answers, "PurpleTrackMIBallot") + model.PurpleExtraEnvelopeQty = DictValue(answers, "PurpleExtraEnvelopeQty") + + ' Blue envelope + model.BlueWantsQuote = DictValue(answers, "BlueWantsQuote") + model.BluePrintPermit = DictValue(answers, "BluePrintPermit") + model.BluePermitOwnership = DictValue(answers, "BluePermitOwnership") + model.BlueHasNonprofitStatus = DictValue(answers, "BlueHasNonprofitStatus") + model.BlueNonprofitAuthCode = DictValue(answers, "BlueNonprofitAuthCode") + model.BluePermitCity = DictValue(answers, "BluePermitCity") + model.BluePermitNumber = DictValue(answers, "BluePermitNumber") + + ' Mailing service + model.MailingWantsService = DictValue(answers, "MailingWantsService") + model.MailingPostageOption = DictValue(answers, "MailingPostageOption") + model.MailingHasNonprofitStatus = DictValue(answers, "MailingHasNonprofitStatus") + model.MailingEstimatedQuantity = DictValue(answers, "MailingEstimatedQuantity") + model.MailingPickupDate = DictValue(answers, "MailingPickupDate") + + ' Additional items + model.SecrecySleevesQty = DictValue(answers, "SecrecySleevesQty") + model.IVotedStickerRolls = DictValue(answers, "IVotedStickerRolls") + model.FutureVoterStickerRolls = DictValue(answers, "FutureVoterStickerRolls") + model.SpecialRequests = DictValue(answers, "SpecialRequests") +End Sub +%> diff --git a/app/models/OrderDetailsLabels.asp b/app/models/OrderDetailsLabels.asp new file mode 100644 index 0000000..c33be53 --- /dev/null +++ b/app/models/OrderDetailsLabels.asp @@ -0,0 +1,75 @@ +<% +'======================================================================================================================= +' Human-readable labels for the coded choice values written by continue.asp's SurveyJS +' model - falls back to the raw code for anything unrecognized rather than hiding it. +'======================================================================================================================= + +Function LabelPrintOption(code) + Select Case CStr(code & "") + Case "AddressesAndPermit" : LabelPrintOption = "Addresses and state of Michigan permit" + Case "PermitOnly" : LabelPrintOption = "State of Michigan permit only" + Case Else : LabelPrintOption = CStr(code & "") + End Select +End Function + +Function LabelPurpleBlueProvider(code) + Select Case CStr(code & "") + Case "ElectionSource" : LabelPurpleBlueProvider = "ElectionSource" + Case "PSI" : LabelPurpleBlueProvider = "PSI" + Case "Spectrum" : LabelPurpleBlueProvider = "Spectrum" + Case "Other" : LabelPurpleBlueProvider = "Other" + Case Else : LabelPurpleBlueProvider = CStr(code & "") + End Select +End Function + +Function LabelPermitOwnership(code) + Select Case CStr(code & "") + Case "KCIPermit" : LabelPermitOwnership = "KCI permit" + Case "OwnPermit" : LabelPermitOwnership = "My organization's permit" + Case Else : LabelPermitOwnership = CStr(code & "") + End Select +End Function + +Function LabelEnvelopeStock(code) + Select Case CStr(code & "") + Case "KCIStock" : LabelEnvelopeStock = "KCI's stock" + Case "OwnStock" : LabelEnvelopeStock = "My own stock" + Case Else : LabelEnvelopeStock = CStr(code & "") + End Select +End Function + +Function LabelPrintStyle(code) + Select Case CStr(code & "") + Case "ColorCoding" : LabelPrintStyle = "Color Coding" + Case "BlackOnly" : LabelPrintStyle = "Black Ink Only" + Case Else : LabelPrintStyle = CStr(code & "") + End Select +End Function + +Function LabelColorCodingBy(code) + Select Case CStr(code & "") + Case "Precinct" : LabelColorCodingBy = "Precinct" + Case "Election" : LabelColorCodingBy = "Election" + Case Else : LabelColorCodingBy = CStr(code & "") + End Select +End Function + +Function LabelPostageOption(code) + Select Case CStr(code & "") + Case "FirstClass" : LabelPostageOption = "Yes, at First Class Rate ($0.721/pc.)" + Case "NonprofitRate" : LabelPostageOption = "Yes, at Nonprofit Rate ($0.263/pc.)" + Case "PresortStandard" : LabelPostageOption = "Yes, at Presort Standard Rate ($0.473/pc.)" + Case "No" : LabelPostageOption = "No" + Case Else : LabelPostageOption = CStr(code & "") + End Select +End Function + +Function LabelNonprofitStatus(code) + Select Case CStr(code & "") + Case "Yes" : LabelNonprofitStatus = "Yes" + Case "No" : LabelNonprofitStatus = "No" + Case "NotSure" : LabelNonprofitStatus = "I'm not sure" + Case Else : LabelNonprofitStatus = CStr(code & "") + End Select +End Function +%> diff --git a/app/models/OrderMailer.asp b/app/models/OrderMailer.asp new file mode 100644 index 0000000..9c99c24 --- /dev/null +++ b/app/models/OrderMailer.asp @@ -0,0 +1,214 @@ +<% +'======================================================================================================================= +' Order-related outbound email: the "continue your order" link and the order-details +' confirmation sent after SubmitOrderDetails. +'======================================================================================================================= +Class OrderMailer_Class + + Public Sub SendContinuationEmail(email, token) + Dim continueUrl + continueUrl = Routes().AppURL & "order/continue?token=" & Server.URLEncode(token) + + Dim mail : Set mail = NewConfiguredMail() + mail.Subject = "Continue your Purple Envelope order" + mail.IsBodyHTML = True + mail.Body = "Click the link below to continue your order:
" & _ + "" & _ + "This link expires in " & H(GetAppSetting("OrderTokenExpirationHours")) & _ + " hours and can only be used once.
" + mail.AddRecipient "To", email + mail.Send + End Sub + + ' Best-effort: the order is already safely persisted by the time a caller reaches this - + ' an SMTP hiccup here should not turn a successful submission into a failed one. + Public Sub SendOrderDetailsEmail(order, model) + On Error Resume Next + + Dim mail : Set mail = NewConfiguredMail() + mail.Subject = "Your Purple Envelope order details - Jurisdiction " & order("JurisdictionNumber") + mail.IsBodyHTML = True + mail.Body = BuildOrderDetailsEmailBody(order, model) + mail.AddRecipient "To", order("Email") + mail.Send + + Err.Clear + On Error GoTo 0 + End Sub + + '------------------------------------------------------------------------------------------------------------------- + ' Private helpers + '------------------------------------------------------------------------------------------------------------------- + Private Function NewConfiguredMail() + Dim smtpPort : smtpPort = GetAppSetting("SmtpPort") + If Not IsNumeric(smtpPort) Then smtpPort = 25 + + Dim mail : Set mail = CDOEmail() + mail.SMTPServer = GetAppSetting("SmtpServer") + mail.SMTPPort = CInt(smtpPort) + mail.SMTPUsername = GetAppSetting("SmtpUsername") + mail.SMTPPassword = GetAppSetting("SmtpPassword") + mail.SMTPUseSSL = (LCase(GetAppSetting("SmtpUseSSL")) = "true") + mail.From = GetAppSetting("SmtpFromAddress") + Set NewConfiguredMail = mail + End Function + + Private Function BuildOrderDetailsEmailBody(order, model) + Dim html + html = "" & _ + "" & _
+ "
|