/// <summary> /// Prepares to handle the form submission. /// </summary> /// <param name="context"> /// The form submission context. /// </param> /// <param name="configuration"> /// The handler configuration. /// </param> public override void PrepareHandleForm(FormSubmissionContext context, object configuration) { // Track usage of this feature. UsageTracker.TrackDesignedEmail(); // Boilerplate. base.PrepareHandleForm(context, configuration); }
/// <summary> /// Handles a form submission (sends data to a web API). /// </summary> /// <param name="context"> /// The form submission context. /// </param> /// <param name="configuration"> /// The handler configuration. /// </param> public void HandleForm(FormSubmissionContext context, object configuration) { // Variables. var config = configuration as SendDataConfiguration; var form = context.Form; var data = context.Data; var result = default(SendDataResult); // Convert lists into dictionary. var fieldsById = form.Fields.ToDictionary(x => x.Id, x => x); var valuesById = data.GroupBy(x => x.FieldId).Select(x => new { Id = x.Key, Values = x.SelectMany(y => y.FieldValues).ToList() }).ToDictionary(x => x.Id, x => x.Values); // Attempts to get a field value. Func <Guid, string> tryGetValue = fieldId => { var tempValues = default(List <string>); var tempField = default(IFormField); var hasValues = valuesById.TryGetValue(fieldId, out tempValues); var hasField = fieldsById.TryGetValue(fieldId, out tempField); if (hasField && (hasValues || tempField.IsServerSideOnly)) { tempValues = hasValues ? tempValues : null; return(tempField.FormatValue(tempValues, FieldPresentationFormats.Transmission)); } return(null); }; // Get the data to transmit. var transmissionData = config.Fields .Where(x => fieldsById.ContainsKey(x.FieldId)) .Select(x => new KeyValuePair <string, string>(x.FieldName, tryGetValue(x.FieldId))) .Where(x => x.Value != null) .ToArray(); // Query string format? if ("Query String".InvariantEquals(config.TransmissionFormat)) { result = SendQueryStringRequest(config.Url, transmissionData, config.Method); } // Call function to handle result? if (context != null) { result.Context = context; } config?.ResultHandler?.HandleResult(result); }
/// <summary> /// Handles a form submission (stores it). /// </summary> /// <param name="context"> /// The form submission context. /// </param> /// <param name="configuration"> /// The handler configuration. /// </param> public void HandleForm(FormSubmissionContext context, object configuration) { // Store the submission to the database. if (Submission != null) { var db = context.UmbracoContext.Application.DatabaseContext.Database; db.Insert(Submission); } }
/// <summary> /// Handles a form submission (sends an email). /// </summary> /// <param name="context"> /// The form submission context. /// </param> /// <param name="configuration"> /// The handler configuration. /// </param> public void HandleForm(FormSubmissionContext context, object configuration) { // Variables. var form = context.Form; var data = context.Data; var files = context.Files; var payload = context.Payload; // Create message. var config = configuration as EmailConfiguration; var message = new MailMessage() { IsBodyHtml = false }; message.From = new MailAddress(config.SenderEmail); message.Subject = config.Subject; // Any allowed recipients (if not, abort early)? var allowedRecipients = FilterEmails(config.Recipients); if (!allowedRecipients.Any()) { return; } foreach (var recipient in allowedRecipients) { message.To.Add(recipient); } // Append fields? if (config.AppendFields) { var chosenPayload = config.AppendPayload ? payload : payload.Take(0); message.Body = ConstructMessage(form, data, files, chosenPayload, config); foreach (var file in files) { var dataStream = new MemoryStream(file.FileData); message.Attachments.Add(new Attachment(dataStream, file.FileName)); } } else { message.Body = config.Message; } // Send email. using (var client = new SmtpClient()) { client.Send(message); } }
/// <summary> /// Handles a form submission (sends an email). /// </summary> /// <param name="context"> /// The form submission context. /// </param> /// <param name="configuration"> /// The handler configuration. /// </param> public void HandleForm(FormSubmissionContext context, object configuration) { // Send email. if (Message != null) { using (var client = new SmtpClient()) { client.Send(Message); } } }
/// <summary> /// Returns a dictionary of form field values by field alias. /// </summary> /// <param name="context"> /// The form submission context. /// </param> /// <returns> /// The dictionary of form field values. /// </returns> private Dictionary <string, object[]> GetFormValues(FormSubmissionContext context) { // Variables. var data = context.Data; var files = context.Files; var payload = context.Payload; var fieldsById = context.Form.Fields.ToDictionary(x => x.Id, x => x); var values = new Dictionary <string, object[]>(); // Add the payload items. foreach (var item in payload) { values[item.Name] = new[] { item.Value }; } // Add the field values. foreach (var item in data) { var field = fieldsById[item.FieldId]; var name = string.IsNullOrWhiteSpace(field.Alias) ? field.Name : field.Alias; if (string.IsNullOrWhiteSpace(name)) { continue; } values[name] = item.FieldValues.MakeSafe().ToArray(); } // Add the files. foreach (var item in files) { var field = fieldsById[item.FieldId]; var name = string.IsNullOrWhiteSpace(field.Alias) ? field.Name : field.Alias; if (string.IsNullOrWhiteSpace(name)) { continue; } values[name] = new[] { item }; } // Return the dictionary of values. return(values); }
/// <summary> /// Handles a form submission (sends an email). /// </summary> /// <param name="context"> /// The form submission context. /// </param> /// <param name="configuration"> /// The handler configuration. /// </param> public void HandleForm(FormSubmissionContext context, object configuration) { // Variables. var config = configuration as EmailConfiguration; var form = context.Form; var data = context.Data; var dataForMessage = data; var files = context.Files; var filesForMessage = files; var payload = context.Payload; var extraContext = context.ExtraContext; var extraEmails = (AttemptGetValue(extraContext, ExtraRecipientsKey) as List <string>).MakeSafe(); var extraSubject = AttemptGetValue(extraContext, ExtraSubjectKey) as string ?? string.Empty; var extraMessage = AttemptGetValue(extraContext, ExtraMessageKey) as string ?? string.Empty; var baseMessage = config.Message + extraMessage; // Create message. var message = new MailMessage() { IsBodyHtml = false }; message.From = new MailAddress(config.SenderEmail); message.Subject = config.Subject + extraSubject; // Get recipients from field values. var emailFieldIds = new HashSet <Guid>(config.RecipientFields); var fieldEmails = data .Where(x => emailFieldIds.Contains(x.FieldId)).SelectMany(x => x.FieldValues) .Where(x => !string.IsNullOrWhiteSpace(x)) .Where(x => IsEmailInValidFormat(x)); // Include only specific fields in the email message? if (config.FieldsToInclude.Any()) { // Variables. var fieldIdsToInclude = new HashSet <Guid>(config.FieldsToInclude); // Selected server side fields. var serverSideFields = form.Fields .Where(x => x.IsServerSideOnly) .Where(x => fieldIdsToInclude.Contains(x.Id)) .Select(x => new FieldSubmission() { FieldId = x.Id, // The values don't matter here, as the IFormFieldType.FormatValue is how server // side fields return a field value. FieldValues = Enumerable.Empty <string>() }); // Combine submitted data and server side field data. var dataWithServerSideFields = data.Concat(serverSideFields); // Filter normal fields. dataForMessage = dataWithServerSideFields .Where(x => fieldIdsToInclude.Contains(x.FieldId)).ToArray(); // Filter file fields. filesForMessage = files .Where(x => fieldIdsToInclude.Contains(x.FieldId)).ToArray(); } // Any allowed recipients (if not, abort early)? var rawRecipients = config.Recipients .Concat(fieldEmails) .Concat(extraEmails); var allowedRecipients = FilterEmails(rawRecipients); if (!allowedRecipients.Any()) { return; } foreach (var recipient in allowedRecipients) { // We don't want recipients to see each other, so we use BCC instead of TO. message.Bcc.Add(recipient); } // Append fields? if (config.AppendFields) { var chosenPayload = config.AppendPayload ? payload : payload.Take(0); message.Body = ConstructMessage(form, dataForMessage, filesForMessage, chosenPayload, baseMessage, config.IncludeHiddenFields, config.ExcludeFieldLabels); foreach (var file in filesForMessage) { var dataStream = new MemoryStream(file.FileData); message.Attachments.Add(new Attachment(dataStream, file.FileName)); } } else { message.Body = baseMessage; } // Send email. using (var client = new SmtpClient()) { client.Send(message); } }
/// <summary> /// Submits a form. /// </summary> /// <param name="formId"> /// The ID of the form to submit. /// </param> /// <param name="data"> /// The form data to submit. /// </param> /// <param name="files"> /// The file data to submit. /// </param> /// <param name="payload"> /// Extra data related to the submission. /// </param> /// <param name="options"> /// The options for this submission. /// </param> /// <param name="context"> /// The contextual information for the form request. /// </param> /// <returns> /// The result of the submission. /// </returns> public SubmissionResult SubmitForm(Guid formId, IEnumerable <FieldSubmission> data, IEnumerable <FileFieldSubmission> files, IEnumerable <PayloadSubmission> payload, SubmissionOptions options, FormRequestContext context) { // Is the form ID valid? var form = Forms.Retrieve(formId); if (form == null) { return(new SubmissionResult() { Success = false }); } // Create submission context. var submissionContext = new FormSubmissionContext() { Files = files, Data = data, Form = form, Payload = payload, CurrentPage = context.CurrentPage, HttpContext = context.HttpContext, Services = context.Services, UmbracoContext = context.UmbracoContext, UmbracoHelper = context.UmbracoHelper, SubmissionId = Guid.NewGuid(), ExtraContext = new Dictionary <string, object>(), SubmissionCancelled = false }; // Invoke submitting event (gives listeners a chance to change the submission). Submitting?.Invoke(submissionContext); // Fail the form submission if SubmissionCancelled is true. if (submissionContext.SubmissionCancelled) { return(new SubmissionResult() { Success = false }); } // Validate against native field validations. foreach (var field in form.Fields) { var fieldId = field.Id; var value = data.Where(x => x.FieldId == fieldId) .SelectMany(x => x.FieldValues); if (!field.IsValid(value)) { return(new SubmissionResult() { Success = false }); } } // Validate? if (options.Validate) { var valuesById = data.GroupBy(x => x.FieldId).Select(x => new { Id = x.Key, Values = x.SelectMany(y => y.FieldValues).ToList() }).ToDictionary(x => x.Id, x => x.Values); var filesById = files.GroupBy(x => x.FieldId).Select(x => new { Id = x.Key, Values = x.Select(y => y).ToList() }).ToDictionary(x => x.Id, x => x.Values); foreach (var field in form.Fields) { var validations = field.Validations .Select(x => Validations.Retrieve(x)) .ToList(); if (!validations.Any()) { continue; } var validationContext = new ValidationContext() { Field = field, Form = form }; foreach (var validation in validations) { var dataValues = valuesById.ContainsKey(field.Id) ? valuesById[field.Id] : new List <string>(); var fileValues = filesById.ContainsKey(field.Id) ? filesById[field.Id] : new List <FileFieldSubmission>(); var isValid = validation .IsValueValid(dataValues, fileValues, validationContext); if (!isValid) { return(new SubmissionResult() { Success = false }); } } } } // Prepare the form handlers. // This occurs on the current thread in case the handler needs information // only available in the current thread. var enabledHandlers = form.Handlers .Where(x => x.Enabled) .ToArray(); try { foreach (var handler in enabledHandlers) { handler.PrepareHandleForm(submissionContext); } } catch (Exception ex) { Logger.Error <Submissions_Instance>(ex, PreHandlerError); return(new SubmissionResult() { Success = false }); } // Initiate form handlers on a new thread (they may take some time to complete). var task = new Task(() => { foreach (var handler in enabledHandlers) { handler.HandleForm(submissionContext); } }); task.ContinueWith(FormHandlersExceptionHandler, TaskContinuationOptions.OnlyOnFaulted); task.Start(); // Return success. return(new SubmissionResult() { Success = true }); }
/// <summary> /// Prepares an mail message (e.g., sets the sender and recipients). /// </summary> /// <param name="context"> /// The form submission context. /// </param> /// <param name="config"> /// The configuration for preparing the email message. /// </param> /// <returns> /// The prepared mail message. /// </returns> protected MailMessage PrepareEmailMessage( FormSubmissionContext context, IEmailSenderRecipientConfiguration config) { // Variables. var data = context.Data; var extraContext = context.ExtraContext; var extraEmails = (AttemptGetValue(extraContext, ExtraRecipientsKey) as List <string>).MakeSafe(); // Create message. var message = new MailMessage { From = new MailAddress(config.SenderEmail) }; // Add headers to message. foreach (var header in Config.EmailHeaders) { message.Headers.Add(header.Name, header.Value); } // Get recipients from field values. var emailFieldIds = new HashSet <Guid>(config.RecipientFields); var fieldEmails = data .Where(x => emailFieldIds.Contains(x.FieldId)).SelectMany(x => x.FieldValues) .Where(x => !string.IsNullOrWhiteSpace(x)) .Where(x => IsEmailInValidFormat(x)) .ToArray(); // Any allowed recipients (if not, abort early)? var rawRecipients = config.Recipients .Concat(fieldEmails) .Concat(extraEmails); var allowedRecipients = FilterEmails(rawRecipients); if (!allowedRecipients.Any()) { return(null); } foreach (var recipient in allowedRecipients) { if ("to".InvariantEquals(config.DeliveryType as string)) { message.To.Add(recipient); } else if ("cc".InvariantEquals(config.DeliveryType as string)) { message.CC.Add(recipient); } else { message.Bcc.Add(recipient); } } // Return the mail message. return(message); }
/// <summary> /// Handles a form submission (sends an email). /// </summary> /// <param name="context"> /// The form submission context. /// </param> /// <param name="configuration"> /// The handler configuration. /// </param> public virtual void HandleForm(FormSubmissionContext context, object configuration) { // Prepare the message. var message = PrepareEmailMessage(context, configuration as IEmailSenderRecipientConfiguration); if (message == null) { return; } // Variables. var config = configuration as EmailConfiguration; var form = context.Form; var data = context.Data; var dataForMessage = data; var files = context.Files; var filesForMessage = files; var payload = context.Payload; var extraContext = context.ExtraContext; var extraSubject = AttemptGetValue(extraContext, ExtraSubjectKey) as string ?? string.Empty; var extraMessage = AttemptGetValue(extraContext, ExtraMessageKey) as string ?? string.Empty; var baseMessage = config.Message + extraMessage; var plainTextBody = default(string); var htmlBody = default(string); // Set the email subject. message.Subject = config.Subject + extraSubject; // Include only specific fields in the email message? if (config.FieldsToInclude.Any()) { // Variables. var fieldIdsToInclude = new HashSet <Guid>(config.FieldsToInclude); // Selected server side fields. var serverSideFields = form.Fields .Where(x => x.IsServerSideOnly) .Where(x => fieldIdsToInclude.Contains(x.Id)) .Select(x => new FieldSubmission() { FieldId = x.Id, // The values don't matter here, as the IFormFieldType.FormatValue is how server // side fields return a field value. FieldValues = Enumerable.Empty <string>() }); // Combine submitted data and server side field data. var dataWithServerSideFields = data.Concat(serverSideFields); // Filter normal fields. dataForMessage = dataWithServerSideFields .Where(x => fieldIdsToInclude.Contains(x.FieldId)).ToArray(); // Filter file fields. filesForMessage = files .Where(x => fieldIdsToInclude.Contains(x.FieldId)).ToArray(); } // Append fields? if (config.AppendFields) { var chosenPayload = config.AppendPayload ? payload : payload.Take(0); htmlBody = ConstructMessage(form, dataForMessage, filesForMessage, chosenPayload, baseMessage, config.IncludeHiddenFields, config.ExcludeFieldLabels, true); plainTextBody = ConstructMessage(form, dataForMessage, filesForMessage, chosenPayload, baseMessage, config.IncludeHiddenFields, config.ExcludeFieldLabels, false); foreach (var file in filesForMessage) { var dataStream = new MemoryStream(file.FileData); message.Attachments.Add(new Attachment(dataStream, file.FileName)); } } else { htmlBody = WebUtility.HtmlEncode(baseMessage); plainTextBody = baseMessage; } // Add plain text alternate view. var mimeType = new ContentType(MediaTypeNames.Text.Plain); var emailView = AlternateView.CreateAlternateViewFromString(plainTextBody, mimeType); message.AlternateViews.Add(emailView); // Add HTML alternate view. mimeType = new ContentType(MediaTypeNames.Text.Html); emailView = AlternateView.CreateAlternateViewFromString(htmlBody, mimeType); message.AlternateViews.Add(emailView); // Send email. using (var client = new SmtpClient()) { client.Send(message); } }
/// <summary> /// Prepares to handle to form submission. /// </summary> /// <param name="context"> /// The form submission context. /// </param> /// <param name="configuration"> /// The handler configuration. /// </param> /// <remarks> /// In this case, no preparation is necessary. /// </remarks> public void PrepareHandleForm(FormSubmissionContext context, object configuration) { }
/// <summary> /// Prepares to handle to form submission. /// </summary> /// <param name="context"> /// The form submission context. /// </param> /// <param name="configuration"> /// The handler configuration. /// </param> /// <remarks> /// In this case, no preparation is necessary. /// </remarks> public void PrepareHandleForm(FormSubmissionContext context, object configuration) { //TODO: Should probably prepare the data to be sent here (to avoid threading issues), but then send it in HandleForm. }
/// <summary> /// Handles a form submission (stores it). /// </summary> /// <param name="context"> /// The form submission context. /// </param> /// <param name="configuration"> /// The handler configuration. /// </param> public void HandleForm(FormSubmissionContext context, object configuration) { // Variables. var form = context.Form; var data = context.Data; var files = context.Files; var payload = context.Payload; var fieldsById = form.Fields.ToDictionary(x => x.Id, x => x); var db = context.UmbracoContext.Application.DatabaseContext.Database; // This will store the formatted values. var valueList = new[] { new { FieldId = default(string), // Field name is stored in case the field is deleted from the form // and this stored name is all we have to go on. FieldName = default(string), Value = default(string) } }.Take(0).ToList(); // Group the data values by their field ID. var valuesById = data.GroupBy(x => x.FieldId).Select(x => new { Id = x.Key, Values = x.SelectMany(y => y.FieldValues).ToList() }).ToDictionary(x => x.Id, x => x.Values); // Store the file values by their field ID. var filesById = files.GroupBy(x => x.FieldId).Select(x => new { Id = x.Key, Filename = x.Select(y => y.FileName).FirstOrDefault(), PathSegment = GenerateFilePathSegment(), FileData = x.Select(y => y.FileData).FirstOrDefault() }).ToDictionary(x => x.Id, x => x); // Normal fields. foreach (var key in valuesById.Keys) { var values = valuesById[key]; var formatted = string.Join(", ", values); var field = default(IFormField); var fieldName = default(string); if (fieldsById.TryGetValue(key, out field)) { formatted = field.FormatValue(values, FieldPresentationFormats.Storage); fieldName = field.Name; } valueList.Add(new { FieldId = GuidHelper.GetString(key), FieldName = fieldName, Value = formatted }); } // Store file information for serialization. var fileList = filesById.Values.Select(x => new { FieldId = x.Id, // Field name is stored in case the field is deleted from the form // and this stored name is all we have to go on. FieldName = GetFieldName(x.Id, fieldsById), PathSegment = x.PathSegment, Filename = x.Filename }); // Store the files. if (files.Any()) { // Ensure base path exists. var basePath = HostingEnvironment.MapPath(Config.FileStoreBasePath); if (!Directory.Exists(basePath)) { Directory.CreateDirectory(basePath); } // Create files. foreach(var key in filesById.Keys) { var file = filesById[key]; var fullPath = Path.Combine(basePath, file.PathSegment); var pathOnly = Path.GetDirectoryName(fullPath); Directory.CreateDirectory(pathOnly); File.WriteAllBytes(fullPath, file.FileData); } } // Store data to database. var serializedValues = JsonHelper.Serialize(valueList.ToArray()); var serializedFiles = JsonHelper.Serialize(fileList.ToArray()); db.Insert(new FormulateSubmission() { CreationDate = DateTime.UtcNow, DataValues = serializedValues, FileValues = serializedFiles, FormId = form.Id, GeneratedId = context.SubmissionId, PageId = context?.CurrentPage?.Id, Url = context?.CurrentPage?.Url }); }
/// <summary> /// Handles a form submission (sends an email). /// </summary> /// <param name="context"> /// The form submission context. /// </param> /// <param name="configuration"> /// The handler configuration. /// </param> public void HandleForm(FormSubmissionContext context, object configuration) { // Variables. var config = configuration as EmailConfiguration; var form = context.Form; var data = context.Data; var dataForMessage = data; var files = context.Files; var filesForMessage = files; var payload = context.Payload; var extraContext = context.ExtraContext; var extraEmails = (AttemptGetValue(extraContext, ExtraRecipientsKey) as List <string>).MakeSafe(); var extraSubject = AttemptGetValue(extraContext, ExtraSubjectKey) as string ?? string.Empty; var extraMessage = AttemptGetValue(extraContext, ExtraMessageKey) as string ?? string.Empty; var baseMessage = config.Message + extraMessage; var plainTextBody = default(string); var htmlBody = default(string); // Create message. var message = new MailMessage(); message.From = new MailAddress(config.SenderEmail); message.Subject = config.Subject + extraSubject; // Add headers to message. foreach (var header in Config.EmailHeaders) { message.Headers.Add(header.Name, header.Value); } // Get recipients from field values. var emailFieldIds = new HashSet <Guid>(config.RecipientFields); var fieldEmails = data .Where(x => emailFieldIds.Contains(x.FieldId)).SelectMany(x => x.FieldValues) .Where(x => !string.IsNullOrWhiteSpace(x)) .Where(x => IsEmailInValidFormat(x)); // Include only specific fields in the email message? if (config.FieldsToInclude.Any()) { // Variables. var fieldIdsToInclude = new HashSet <Guid>(config.FieldsToInclude); // Selected server side fields. var serverSideFields = form.Fields .Where(x => x.IsServerSideOnly) .Where(x => fieldIdsToInclude.Contains(x.Id)) .Select(x => new FieldSubmission() { FieldId = x.Id, // The values don't matter here, as the IFormFieldType.FormatValue is how server // side fields return a field value. FieldValues = Enumerable.Empty <string>() }); // Combine submitted data and server side field data. var dataWithServerSideFields = data.Concat(serverSideFields); // Filter normal fields. dataForMessage = dataWithServerSideFields .Where(x => fieldIdsToInclude.Contains(x.FieldId)).ToArray(); // Filter file fields. filesForMessage = files .Where(x => fieldIdsToInclude.Contains(x.FieldId)).ToArray(); } // Any allowed recipients (if not, abort early)? var rawRecipients = config.Recipients .Concat(fieldEmails) .Concat(extraEmails); var allowedRecipients = FilterEmails(rawRecipients); if (!allowedRecipients.Any()) { return; } foreach (var recipient in allowedRecipients) { if ("to".InvariantEquals(config.DeliveryType)) { message.To.Add(recipient); } else if ("cc".InvariantEquals(config.DeliveryType)) { message.CC.Add(recipient); } else { message.Bcc.Add(recipient); } } // Append fields? if (config.AppendFields) { var chosenPayload = config.AppendPayload ? payload : payload.Take(0); htmlBody = ConstructMessage(form, dataForMessage, filesForMessage, chosenPayload, baseMessage, config.IncludeHiddenFields, config.ExcludeFieldLabels, true); plainTextBody = ConstructMessage(form, dataForMessage, filesForMessage, chosenPayload, baseMessage, config.IncludeHiddenFields, config.ExcludeFieldLabels, false); foreach (var file in filesForMessage) { var dataStream = new MemoryStream(file.FileData); message.Attachments.Add(new Attachment(dataStream, file.FileName)); } } else { htmlBody = WebUtility.HtmlEncode(baseMessage); plainTextBody = baseMessage; } // Add plain text alternate view. var mimeType = new ContentType(MediaTypeNames.Text.Plain); var emailView = AlternateView.CreateAlternateViewFromString(plainTextBody, mimeType); message.AlternateViews.Add(emailView); // Add HTML alternate view. mimeType = new ContentType(MediaTypeNames.Text.Html); emailView = AlternateView.CreateAlternateViewFromString(htmlBody, mimeType); message.AlternateViews.Add(emailView); // Send email. using (var client = new SmtpClient()) { client.Send(message); } }
/// <summary> /// Handles a form submission (sends an email). /// </summary> /// <param name="context"> /// The form submission context. /// </param> /// <param name="configuration"> /// The handler configuration. /// </param> public override void HandleForm(FormSubmissionContext context, object configuration) { // Prepare the message. var message = PrepareEmailMessage(context, configuration as IEmailSenderRecipientConfiguration); if (message == null) { return; } // Variables. var config = configuration as DesignedEmailConfiguration; var form = context.Form; var data = context.Data; var dataForMessage = data; var files = context.Files; var filesForMessage = files; var payload = context.Payload; var extraContext = context.ExtraContext; var plainTextBody = default(string); var htmlBody = default(string); // Combine all the email recipients into a single array. var emailDataRecipients = message.To.ToArray() .Concat(message.CC.ToArray()) .Concat(message.Bcc.ToArray()) .Select(x => x.Address).Distinct() .OrderBy(x => x).ToArray(); // Create the email data to be passed to the Razor view. var emailData = new EmailData(GetFormValues(context)) { MailMessage = message, SenderEmail = config.SenderEmail, RecipientEmails = emailDataRecipients, Subject = config.Subject, Message = config.Message, Helper = new EmailDataHelper(message) }; // Generate the email subject. var path = GetViewPath(config.SubjectRazorPath); var contents = path == null ? RazorViewIncorrectMarkup(config.HtmlEmailRazorPath) : File.ReadAllText(path); var templateSource = path == null ? null : new LoadedTemplateSource(contents, path); var key = GetViewKey(path, contents); message.Subject = path == null ? config.Subject : (RazorService.RunCompile(templateSource, key, emailData.GetType(), emailData) ?? "") .Trim() .Replace("\r", string.Empty) .Replace("\n", string.Empty); // Generate the HTML for the message. path = GetViewPath(config.HtmlEmailRazorPath); contents = path == null ? RazorViewIncorrectMarkup(config.HtmlEmailRazorPath) : File.ReadAllText(path); templateSource = new LoadedTemplateSource(contents, path); key = GetViewKey(path, contents); htmlBody = RazorService.RunCompile(templateSource, key, emailData.GetType(), emailData); // Generate the text for the message. path = GetViewPath(config.TextEmailRazorPath); if (path != null) { contents = File.ReadAllText(path); templateSource = new LoadedTemplateSource(contents, path); key = GetViewKey(path, contents); plainTextBody = RazorService.RunCompile(templateSource, key, emailData.GetType(), emailData); } // Add plain text alternate view. var mimeType = new ContentType(MediaTypeNames.Text.Plain); var emailView = AlternateView.CreateAlternateViewFromString(plainTextBody ?? "", mimeType); message.AlternateViews.Add(emailView); // Add HTML alternate view. mimeType = new ContentType(MediaTypeNames.Text.Html); emailView = AlternateView.CreateAlternateViewFromString(htmlBody ?? "", mimeType); message.AlternateViews.Add(emailView); // Send email. using (var client = new SmtpClient()) { client.Send(message); } }
/// <summary> /// Handles a form submission (stores it). /// </summary> /// <param name="context"> /// The form submission context. /// </param> /// <param name="configuration"> /// The handler configuration. /// </param> public void HandleForm(FormSubmissionContext context, object configuration) { // Variables. var form = context.Form; var data = context.Data; var files = context.Files; var payload = context.Payload; var fieldsById = form.Fields.ToDictionary(x => x.Id, x => x); var db = context.UmbracoContext.Application.DatabaseContext.Database; // This will store the formatted values. var valueList = new[] { new { FieldId = default(string), // Field name is stored in case the field is deleted from the form // and this stored name is all we have to go on. FieldName = default(string), Value = default(string) } }.Take(0).ToList(); // Group the data values by their field ID. var valuesById = data.GroupBy(x => x.FieldId).Select(x => new { Id = x.Key, Values = x.SelectMany(y => y.FieldValues).ToList() }).ToDictionary(x => x.Id, x => x.Values); // Store the file values by their field ID. var filesById = files.GroupBy(x => x.FieldId).Select(x => new { Id = x.Key, Filename = x.Select(y => y.FileName).FirstOrDefault(), PathSegment = GenerateFilePathSegment(), FileData = x.Select(y => y.FileData).FirstOrDefault() }).ToDictionary(x => x.Id, x => x); // Normal fields. foreach (var key in valuesById.Keys) { var values = valuesById[key]; var formatted = string.Join(", ", values); var field = default(IFormField); var fieldName = default(string); if (fieldsById.TryGetValue(key, out field)) { formatted = field.FormatValue(values, FieldPresentationFormats.Storage); fieldName = field.Name; } valueList.Add(new { FieldId = GuidHelper.GetString(key), FieldName = fieldName, Value = formatted }); } // Store file information for serialization. var fileList = filesById.Values.Select(x => new { FieldId = GuidHelper.GetString(x.Id), // Field name is stored in case the field is deleted from the form // and this stored name is all we have to go on. FieldName = GetFieldName(x.Id, fieldsById), PathSegment = x.PathSegment, Filename = x.Filename }); // Store the files. if (files.Any()) { // Ensure base path exists. var basePath = HostingEnvironment.MapPath(Config.FileStoreBasePath); if (!Directory.Exists(basePath)) { Directory.CreateDirectory(basePath); } // Create files. foreach (var key in filesById.Keys) { var file = filesById[key]; var fullPath = Path.Combine(basePath, file.PathSegment); var pathOnly = Path.GetDirectoryName(fullPath); Directory.CreateDirectory(pathOnly); File.WriteAllBytes(fullPath, file.FileData); } } // Store data to database. var serializedValues = JsonHelper.Serialize(valueList.ToArray()); var serializedFiles = JsonHelper.Serialize(fileList.ToArray()); db.Insert(new FormulateSubmission() { CreationDate = DateTime.UtcNow, DataValues = serializedValues, FileValues = serializedFiles, FormId = form.Id, GeneratedId = context.SubmissionId, PageId = context?.CurrentPage?.Id, Url = context?.CurrentPage?.Url }); }
/// <summary> /// Submits a form. /// </summary> /// <param name="formId"> /// The ID of the form to submit. /// </param> /// <param name="data"> /// The form data to submit. /// </param> /// <param name="files"> /// The file data to submit. /// </param> /// <param name="payload"> /// Extra data related to the submission. /// </param> /// <param name="options"> /// The options for this submission. /// </param> /// <param name="context"> /// The contextual information for the form request. /// </param> /// <returns> /// The result of the submission. /// </returns> public static SubmissionResult SubmitForm(Guid formId, IEnumerable <FieldSubmission> data, IEnumerable <FileFieldSubmission> files, IEnumerable <PayloadSubmission> payload, SubmissionOptions options, FormRequestContext context) { // Is the form ID valid? var form = Forms.Retrieve(formId); if (form == null) { return(new SubmissionResult() { Success = false }); } // Create submission context. var submissionContext = new FormSubmissionContext() { Files = files, Data = data, Form = form, Payload = payload, CurrentPage = context.CurrentPage, HttpContext = context.HttpContext, Services = context.Services, UmbracoContext = context.UmbracoContext, UmbracoHelper = context.UmbracoHelper, SubmissionId = Guid.NewGuid(), ExtraContext = new Dictionary <string, object>() }; // Invoke submitting event (gives listeners a chance to change the submission). Submitting?.Invoke(submissionContext); // Validate? if (options.Validate) { var valuesById = data.GroupBy(x => x.FieldId).Select(x => new { Id = x.Key, Values = x.SelectMany(y => y.FieldValues).ToList() }).ToDictionary(x => x.Id, x => x.Values); foreach (var field in form.Fields) { var validations = field.Validations.Select(x => Validations.Retrieve(x)).ToList(); foreach (var validation in validations) { //TODO: var validationResult = validation.Validate(form, field, valuesById); } } } // Prepare the form handlers. // This occurs on the current thread in case the handler needs information // only available in the current thread. try { foreach (var handler in form.Handlers) { handler.PrepareHandleForm(submissionContext); } } catch (Exception ex) { LogHelper.Error <Submissions_Instance>(PreHandlerError, ex); return(new SubmissionResult() { Success = false }); } // Initiate form handlers on a new thread (they may take some time to complete). var t = new Thread(() => { try { foreach (var handler in form.Handlers) { handler.HandleForm(submissionContext); } } catch (Exception ex) { LogHelper.Error <Submissions_Instance>(HandlerError, ex); } }); t.IsBackground = true; t.Start(); // Return success. return(new SubmissionResult() { Success = true }); }
/// <summary> /// Sends a web request with the data either in the query string or in the body. /// </summary> /// <param name="config"> /// The configuration for the data to be sent (e.g., contains the URL and request method). /// </param> /// <param name="context"> /// The context for the current form submission. /// </param> /// <param name="data"> /// The data to send. /// </param> /// <param name="sendInBody"> /// Send the data as part of the body (or in the query string)? /// </param> /// <param name="sendJson"> /// Send the data as json /// </param> /// <returns> /// True, if the request was a success; otherwise, false. /// </returns> /// <remarks> /// Parts of this function are from: http://stackoverflow.com/a/9772003/2052963 /// and http://stackoverflow.com/questions/14702902 /// </remarks> private SendDataResult SendData(SendDataConfiguration config, FormSubmissionContext context, IEnumerable <KeyValuePair <string, string> > data, bool sendInBody, bool sendJson) { // Construct a URL, possibly containing the data as query string parameters. var sendInUrl = !sendInBody; var sendDataResult = new SendDataResult(); var uri = new Uri(config.Url); var bareUrl = uri.GetLeftPart(UriPartial.Path); var keyValuePairs = data as KeyValuePair <string, string>[] ?? data.ToArray(); var strQueryString = ConstructQueryString(uri, keyValuePairs); var hasQueryString = !string.IsNullOrWhiteSpace(strQueryString); var requestUrl = hasQueryString && sendInUrl ? $"{bareUrl}?{strQueryString}" : config.Url; var enableLogging = WebConfigurationManager.AppSettings["Formulate:EnableLogging"]; var jsonMode = WebConfigurationManager.AppSettings["Formulate:Send Data JSON Mode"]; var isJsonObjectMode = "JSON Object".InvariantEquals(jsonMode); var isWrappedObjectMode = "Wrap JSON Object in Array".InvariantEquals(jsonMode); // Attempt to send the web request. try { // Construct web request. var request = (HttpWebRequest)WebRequest.Create(requestUrl); request.AllowAutoRedirect = false; request.UserAgent = WebUserAgent; request.Method = config.Method; // Send an event indicating that the data is about to be sent (which allows code // external to Formulate to modify the request). var sendContext = new SendingDataContext() { Configuration = config, Data = keyValuePairs, Request = request, SubmissionContext = context }; SendingData?.Invoke(sendContext); // Update the key/value pairs in case they got changed in the sending data event. // This only updates the methods that send the data in the body (as the URL has // already been set on the request). keyValuePairs = sendContext.Data as KeyValuePair <string, string>[] ?? sendContext.Data.ToArray(); // Send the data in the body (rather than the query string)? if (sendInBody) { if (sendJson) { // Variables. var json = default(string); // Send an event indicating that the data is about to be serialized to // JSON (which allows code external to Formulate to modify the JSON)? if (SerializingJson != null) { json = SerializingJson.Invoke(sendContext.Data); } else { // If sending as JSON, group duplicate keys/value pairs. var grouped = keyValuePairs.GroupBy(x => x.Key).Select(x => new { Key = x.Key, Value = x.Select(y => y.Value) }).ToDictionary(x => x.Key, x => x.Value.Count() > 1 ? x.Value.ToArray() as object : x.Value.FirstOrDefault()); // Convert data to JSON. if (isJsonObjectMode) { json = JsonConvert.SerializeObject(grouped); } else if (isWrappedObjectMode) { json = JsonConvert.SerializeObject(new[] { grouped }); } else { // Ideally, we can remove this "else" branch later. We never really want this // mode to be used, but it's here for legacy support in case anybody managed // to make use of this funky mode. json = JsonConvert.SerializeObject(new[] { grouped }); } } // Log JSON being sent. if (enableLogging == "true") { LogHelper.Info <SendDataHandler>("Sent URL: " + config.Url); LogHelper.Info <SendDataHandler>("Sent Data: " + JsonHelper.FormatJsonForLogging(json)); } // Write JSON data to the request request. var postBytes = Encoding.UTF8.GetBytes(json); request.ContentType = "application/json; charset=utf-8"; request.ContentLength = postBytes.Length; using (var postStream = request.GetRequestStream()) { postStream.Write(postBytes, 0, postBytes.Length); } } else { // Log data being sent. if (enableLogging == "true") { LogHelper.Info <SendDataHandler>("Sent URL: " + config.Url); LogHelper.Info <SendDataHandler>("Sent Data: " + strQueryString); } // Update the data (since the sending data event may have modified it). uri = new Uri(config.Url); strQueryString = ConstructQueryString(uri, keyValuePairs); // Write the data to the request stream. var postBytes = Encoding.UTF8.GetBytes(strQueryString); request.ContentType = "application/x-www-form-urlencoded"; request.ContentLength = postBytes.Length; var postStream = request.GetRequestStream(); postStream.Write(postBytes, 0, postBytes.Length); } } // Get and retain response. var response = (HttpWebResponse)request.GetResponse(); sendDataResult.HttpWebResponse = response; var responseStream = response.GetResponseStream(); var reader = new StreamReader(responseStream); var resultText = reader.ReadToEnd(); sendDataResult.ResponseText = resultText; sendDataResult.Success = true; } catch (Exception ex) { LogHelper.Error <SendDataHandler>(SendDataError, ex); sendDataResult.ResponseError = ex; sendDataResult.Success = false; } // Return the result of the request. return(sendDataResult); }
/// <summary> /// Handles a form submission (sends an email). /// </summary> /// <param name="context"> /// The form submission context. /// </param> /// <param name="configuration"> /// The handler configuration. /// </param> public void HandleForm(FormSubmissionContext context, object configuration) { // Variables. var form = context.Form; var data = context.Data; var files = context.Files; var payload = context.Payload; // Create message. var config = configuration as EmailConfiguration; var message = new MailMessage() { IsBodyHtml = false }; message.From = new MailAddress(config.SenderEmail); message.Subject = config.Subject; // Any allowed recipients (if not, abort early)? var allowedRecipients = FilterEmails(config.Recipients); if (!allowedRecipients.Any()) { return; } foreach (var recipient in allowedRecipients) { message.To.Add(recipient); } // Append fields? if (config.AppendFields) { var chosenPayload = config.AppendPayload ? payload : payload.Take(0); message.Body = ConstructMessage(form, data, files, chosenPayload, config); foreach(var file in files) { var dataStream = new MemoryStream(file.FileData); message.Attachments.Add(new Attachment(dataStream, file.FileName)); } } else { message.Body = config.Message; } // Send email. using (var client = new SmtpClient()) { client.Send(message); } }