mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2025-07-15 16:41:59 +08:00

* Adds a new CultureSelector shape for front-end * fixed query string culture change * Moving NameValueCollectionExtensions from Orchard.DynamicForms and Orchard.Localization to Orchard.Framework * Code styling * Simplifying UserCultureSelectorController and removing the addition of the culture to the query string * EOF empty lines and code styling * Fixing that the main Orchard.Localization should depend on Orchard.Autoroute * Code styling in LocalizationService * Updating LocalizationService to not have to use IEnumerable.Single * Matching culture name matching in LocalizationService culture- and casing-invariant --------- Co-authored-by: Sergio Navarro <jersio@hotmail.com> Co-authored-by: psp589 <pablosanchez589@gmail.com>
471 lines
20 KiB
C#
471 lines
20 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Collections.Specialized;
|
|
using System.Data;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Web;
|
|
using System.Web.Mvc;
|
|
using DocumentFormat.OpenXml;
|
|
using DocumentFormat.OpenXml.Packaging;
|
|
using DocumentFormat.OpenXml.Spreadsheet;
|
|
using Orchard.Collections;
|
|
using Orchard.ContentManagement;
|
|
using Orchard.ContentManagement.MetaData;
|
|
using Orchard.ContentManagement.Records;
|
|
using Orchard.Core.Contents.Settings;
|
|
using Orchard.Data;
|
|
using Orchard.DynamicForms.Elements;
|
|
using Orchard.DynamicForms.Helpers;
|
|
using Orchard.DynamicForms.Models;
|
|
using Orchard.DynamicForms.Services.Models;
|
|
using Orchard.DynamicForms.ViewModels;
|
|
using Orchard.Layouts.Helpers;
|
|
using Orchard.Layouts.Models;
|
|
using Orchard.Layouts.Services;
|
|
using Orchard.Localization.Services;
|
|
using Orchard.Services;
|
|
using Orchard.Utility.Extensions;
|
|
|
|
namespace Orchard.DynamicForms.Services {
|
|
public class FormService : IFormService {
|
|
private readonly ILayoutSerializer _serializer;
|
|
private readonly IClock _clock;
|
|
private readonly IRepository<Submission> _submissionRepository;
|
|
private readonly IFormElementEventHandler _elementHandlers;
|
|
private readonly IContentManager _contentManager;
|
|
private readonly IContentDefinitionManager _contentDefinitionManager;
|
|
private readonly IBindingManager _bindingManager;
|
|
private readonly IDynamicFormEventHandler _formEventHandler;
|
|
private readonly Lazy<IEnumerable<IElementValidator>> _validators;
|
|
private readonly IDateLocalizationServices _dateLocalizationServices;
|
|
private readonly IOrchardServices _services;
|
|
private readonly ICultureAccessor _cultureAccessor;
|
|
|
|
public FormService(
|
|
ILayoutSerializer serializer,
|
|
IClock clock,
|
|
IRepository<Submission> submissionRepository,
|
|
IFormElementEventHandler elementHandlers,
|
|
IContentDefinitionManager contentDefinitionManager,
|
|
IBindingManager bindingManager,
|
|
IDynamicFormEventHandler formEventHandler,
|
|
Lazy<IEnumerable<IElementValidator>> validators,
|
|
IDateLocalizationServices dateLocalizationServices,
|
|
IOrchardServices services,
|
|
ICultureAccessor cultureAccessor) {
|
|
|
|
_serializer = serializer;
|
|
_clock = clock;
|
|
_submissionRepository = submissionRepository;
|
|
_elementHandlers = elementHandlers;
|
|
_contentManager = services.ContentManager;
|
|
_contentDefinitionManager = contentDefinitionManager;
|
|
_bindingManager = bindingManager;
|
|
_formEventHandler = formEventHandler;
|
|
_validators = validators;
|
|
_dateLocalizationServices = dateLocalizationServices;
|
|
_services = services;
|
|
_cultureAccessor = cultureAccessor;
|
|
}
|
|
|
|
public Form FindForm(LayoutPart layoutPart, string formName = null) {
|
|
var elements = _serializer.Deserialize(layoutPart.LayoutData, new DescribeElementsContext { Content = layoutPart });
|
|
var forms = elements.Flatten().Where(x => x is Form).Cast<Form>();
|
|
return String.IsNullOrWhiteSpace(formName) ? forms.FirstOrDefault() : forms.FirstOrDefault(x => x.Name == formName);
|
|
}
|
|
|
|
public IEnumerable<FormElement> GetFormElements(Form form) {
|
|
return form.Elements.Flatten().Where(x => x is FormElement).Cast<FormElement>();
|
|
}
|
|
|
|
public IEnumerable<string> GetFormElementNames(Form form) {
|
|
return GetFormElements(form).Select(x => x.Name).Where(x => !String.IsNullOrWhiteSpace(x)).Distinct();
|
|
}
|
|
|
|
public NameValueCollection SubmitForm(IContent content, Form form, IValueProvider valueProvider, ModelStateDictionary modelState, IUpdateModel updater) {
|
|
var values = ReadElementValues(form, valueProvider);
|
|
|
|
_formEventHandler.Submitted(new FormSubmittedEventContext {
|
|
Content = content,
|
|
Form = form,
|
|
FormService = this,
|
|
ValueProvider = valueProvider,
|
|
Values = values,
|
|
Updater = updater
|
|
});
|
|
|
|
_formEventHandler.Validating(new FormValidatingEventContext {
|
|
Content = content,
|
|
Form = form,
|
|
FormService = this,
|
|
Values = values,
|
|
ModelState = modelState,
|
|
ValueProvider = valueProvider,
|
|
Updater = updater
|
|
});
|
|
|
|
_formEventHandler.Validated(new FormValidatedEventContext {
|
|
Content = content,
|
|
Form = form,
|
|
FormService = this,
|
|
Values = values,
|
|
ModelState = modelState,
|
|
ValueProvider = valueProvider,
|
|
Updater = updater
|
|
});
|
|
|
|
return values;
|
|
}
|
|
|
|
public Submission CreateSubmission(string formName, NameValueCollection values) {
|
|
var submission = new Submission {
|
|
FormName = formName,
|
|
CreatedUtc = _clock.UtcNow,
|
|
FormData = values.ToQueryString()
|
|
};
|
|
|
|
CreateSubmission(submission);
|
|
return submission;
|
|
}
|
|
|
|
public Submission CreateSubmission(Submission submission) {
|
|
_submissionRepository.Create(submission);
|
|
return submission;
|
|
}
|
|
|
|
public Submission GetSubmission(int id) {
|
|
return _submissionRepository.Get(id);
|
|
}
|
|
|
|
public IPageOfItems<Submission> GetSubmissions(string formName = null, int? skip = null, int? take = null) {
|
|
var query = _submissionRepository.Table;
|
|
|
|
if (!String.IsNullOrWhiteSpace(formName))
|
|
query = query.Where(x => x.FormName == formName);
|
|
|
|
query = new Orderable<Submission>(query).Desc(x => x.CreatedUtc).Queryable;
|
|
|
|
var totalItemCount = query.Count();
|
|
if (skip != null && take.GetValueOrDefault() > 0)
|
|
query = query.Skip(skip.Value).Take(take.GetValueOrDefault());
|
|
|
|
return new PageOfItems<Submission>(query) {
|
|
PageNumber = skip.GetValueOrDefault() * take.GetValueOrDefault(),
|
|
PageSize = take ?? Int32.MaxValue,
|
|
TotalItemCount = totalItemCount
|
|
};
|
|
}
|
|
|
|
public Stream ExportSubmissions(string formName = null) {
|
|
var stream = new MemoryStream();
|
|
|
|
string GetColumnId(int columnNumber) {
|
|
string result = "";
|
|
do {
|
|
result = ((char)((columnNumber - 1) % 26 + (int)'A')).ToString() + result;
|
|
columnNumber = (columnNumber - 1) / 26;
|
|
} while (columnNumber != 0);
|
|
return result;
|
|
}
|
|
|
|
// Create a spreadsheet document.
|
|
var spreadsheetDocument = SpreadsheetDocument.Create(stream, SpreadsheetDocumentType.Workbook);
|
|
|
|
// Add a WorkbookPart to the document.
|
|
var workbookpart = spreadsheetDocument.AddWorkbookPart();
|
|
workbookpart.Workbook = new Workbook();
|
|
|
|
// Add a WorksheetPart to the WorkbookPart.
|
|
var worksheetPart = workbookpart.AddNewPart<WorksheetPart>();
|
|
var sheetData = new SheetData();
|
|
worksheetPart.Worksheet = new Worksheet(sheetData);
|
|
|
|
// Add Sheets to the Workbook.
|
|
var sheets = spreadsheetDocument.WorkbookPart.Workbook.AppendChild(new Sheets());
|
|
|
|
// Fetch submissions
|
|
var query = _submissionRepository.Table;
|
|
|
|
if (!String.IsNullOrWhiteSpace(formName)) {
|
|
query = query.Where(x => x.FormName == formName);
|
|
}
|
|
|
|
var submissions = new Orderable<Submission>(query).Desc(x => x.CreatedUtc).Queryable.ToArray();
|
|
|
|
foreach (var formGroup in submissions.GroupBy(s => s.FormName)) {
|
|
// Append a new worksheet and associate it with the workbook.
|
|
var sheet = new Sheet() {
|
|
Id = spreadsheetDocument.WorkbookPart.GetIdOfPart(worksheetPart),
|
|
SheetId = 1,
|
|
Name = formGroup.Key
|
|
};
|
|
sheets.Append(sheet);
|
|
|
|
var data = GenerateDataTable(formGroup);
|
|
uint rowIndex = 1;
|
|
|
|
var headerRow = new Row { RowIndex = rowIndex };
|
|
sheetData.Append(headerRow);
|
|
|
|
for (int i = 0; i < data.Columns.Count; i++) {
|
|
var title = data.Columns[i].ToString().CamelFriendly();
|
|
headerRow.Append(new Cell {
|
|
CellReference = GetColumnId(i + 1) + rowIndex,
|
|
InlineString = new InlineString { Text = new Text(title) },
|
|
DataType = new EnumValue<CellValues>(CellValues.InlineString),
|
|
});
|
|
}
|
|
|
|
foreach (DataRow dataRow in data.Rows) {
|
|
rowIndex++;
|
|
var row = new Row { RowIndex = rowIndex };
|
|
sheetData.Append(row);
|
|
for (int i = 0; i < data.Columns.Count; i++) {
|
|
var value = dataRow[data.Columns[i]];
|
|
row.Append(new Cell {
|
|
CellReference = GetColumnId(i + 1) + rowIndex,
|
|
InlineString = new InlineString { Text = new Text(value.ToString()) },
|
|
DataType = new EnumValue<CellValues>(CellValues.InlineString),
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
workbookpart.Workbook.Save();
|
|
|
|
// Close the document.
|
|
spreadsheetDocument.Dispose();
|
|
stream.Seek(0, SeekOrigin.Begin);
|
|
|
|
return stream;
|
|
}
|
|
|
|
public void DeleteSubmission(Submission submission) {
|
|
_submissionRepository.Delete(submission);
|
|
}
|
|
|
|
public int DeleteSubmissions(IEnumerable<int> submissionIds) {
|
|
var submissions = _submissionRepository.Table.Where(x => submissionIds.Contains(x.Id)).ToArray();
|
|
|
|
foreach (var submission in submissions) {
|
|
DeleteSubmission(submission);
|
|
}
|
|
|
|
return submissions.Length;
|
|
}
|
|
|
|
public void ReadElementValues(FormElement element, ReadElementValuesContext context) {
|
|
_elementHandlers.GetElementValue(element, context);
|
|
}
|
|
|
|
public NameValueCollection ReadElementValues(Form form, IValueProvider valueProvider) {
|
|
var formElements = GetFormElements(form);
|
|
var values = new NameValueCollection();
|
|
|
|
// Let each element provide its values.
|
|
foreach (var element in formElements) {
|
|
var context = new ReadElementValuesContext { ValueProvider = valueProvider };
|
|
ReadElementValues(element, context);
|
|
|
|
foreach (var key in from string key in context.Output where !String.IsNullOrWhiteSpace(key) && values[key] == null select key) {
|
|
var value = context.Output[key];
|
|
|
|
if (form.HtmlEncode)
|
|
value = HttpUtility.HtmlEncode(value);
|
|
|
|
values.Add(key, value);
|
|
}
|
|
}
|
|
|
|
// Collect any remaining form values not handled by any specific element.
|
|
var requestForm = _services.WorkContext.HttpContext.Request.Form;
|
|
var blackList = new[] { "__RequestVerificationToken", "formName", "contentId" };
|
|
foreach (var key in
|
|
from string key in requestForm
|
|
where !String.IsNullOrWhiteSpace(key) && !blackList.Contains(key) && values[key] == null
|
|
select key) {
|
|
|
|
values.Add(key, requestForm[key]);
|
|
}
|
|
|
|
return values;
|
|
}
|
|
|
|
public DataTable GenerateDataTable(IEnumerable<Submission> submissions) {
|
|
var records = submissions.Select(x => System.Tuple.Create(x, x.ToNameValues())).ToArray();
|
|
var columnNames = new HashSet<string>();
|
|
var dataTable = new DataTable();
|
|
|
|
foreach (var key in
|
|
from record in records
|
|
from string key in record.Item2
|
|
where !columnNames.Contains(key)
|
|
where !String.IsNullOrWhiteSpace(key)
|
|
select key) {
|
|
columnNames.Add(key);
|
|
}
|
|
|
|
dataTable.Columns.Add("Id");
|
|
dataTable.Columns.Add("Created");
|
|
foreach (var columnName in columnNames) {
|
|
dataTable.Columns.Add(columnName);
|
|
}
|
|
|
|
foreach (var record in records) {
|
|
var dataRow = dataTable.NewRow();
|
|
|
|
dataRow["Id"] = record.Item1.Id;
|
|
dataRow["Created"] = _dateLocalizationServices.ConvertToSiteTimeZone(record.Item1.CreatedUtc).ToString(_cultureAccessor.CurrentCulture);
|
|
foreach (var columnName in columnNames) {
|
|
var value = record.Item2[columnName];
|
|
dataRow[columnName] = value;
|
|
}
|
|
|
|
dataTable.Rows.Add(dataRow);
|
|
}
|
|
|
|
return dataTable;
|
|
}
|
|
|
|
public ContentItem CreateContentItem(Form form, IValueProvider valueProvider) {
|
|
var contentTypeDefinition = _contentDefinitionManager.GetTypeDefinition(form.FormBindingContentType);
|
|
|
|
if (contentTypeDefinition == null)
|
|
return null;
|
|
|
|
var contentItem = _contentManager.New(contentTypeDefinition.Name);
|
|
|
|
// Create the version record before updating fields to prevent those field values from being lost when invoking Create.
|
|
// If Create is invoked while VersionRecord is null, a new VersionRecord will be created, wiping out our field values.
|
|
contentItem.VersionRecord = new ContentItemVersionRecord {
|
|
ContentItemRecord = new ContentItemRecord(),
|
|
Number = 1,
|
|
Latest = true,
|
|
Published = true
|
|
};
|
|
|
|
var lookup = _bindingManager.DescribeBindingsFor(contentTypeDefinition);
|
|
var formElements = GetFormElements(form);
|
|
|
|
foreach (var element in formElements) {
|
|
var context = new ReadElementValuesContext { ValueProvider = valueProvider };
|
|
ReadElementValues(element, context);
|
|
|
|
var value = context.Output[element.Name];
|
|
var bindingSettings = element.Data.GetModel<FormBindingSettings>();
|
|
|
|
if (bindingSettings != null) {
|
|
foreach (var partBindingSettings in bindingSettings.Parts) {
|
|
InvokePartBindings(contentItem, lookup, partBindingSettings, value);
|
|
|
|
foreach (var fieldBindingSettings in partBindingSettings.Fields) {
|
|
InvokeFieldBindings(contentItem, lookup, partBindingSettings, fieldBindingSettings, value);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
var contentTypeSettings = contentTypeDefinition.Settings.GetModel<ContentTypeSettings>();
|
|
_contentManager.Create(contentItem, VersionOptions.Draft);
|
|
|
|
if (form.Publication == "Publish" || !contentTypeSettings.Draftable) {
|
|
_contentManager.Publish(contentItem);
|
|
}
|
|
|
|
return contentItem;
|
|
}
|
|
|
|
public IEnumerable<IElementValidator> GetValidators<TElement>() where TElement : FormElement {
|
|
return GetValidators(typeof(TElement));
|
|
}
|
|
|
|
public IEnumerable<IElementValidator> GetValidators(FormElement element) {
|
|
return GetValidators(element.GetType());
|
|
}
|
|
|
|
public IEnumerable<IElementValidator> GetValidators(Type elementType) {
|
|
return _validators.Value.Where(x => IsFormElementType(x, elementType)).ToArray();
|
|
}
|
|
|
|
public IEnumerable<IElementValidator> GetValidators() {
|
|
return _validators.Value.ToArray();
|
|
}
|
|
|
|
public void RegisterClientValidationAttributes(FormElement element, RegisterClientValidationAttributesContext context) {
|
|
_elementHandlers.RegisterClientValidation(element, context);
|
|
}
|
|
|
|
private static void InvokePartBindings(
|
|
ContentItem contentItem,
|
|
IEnumerable<ContentPartBindingDescriptor> lookup,
|
|
PartBindingSettings partBindingSettings,
|
|
string value) {
|
|
|
|
var part = contentItem.Parts.FirstOrDefault(x => x.PartDefinition.Name == partBindingSettings.Name);
|
|
if (part == null)
|
|
return;
|
|
|
|
var partBindingDescriptors = lookup.Where(x => x.Part.PartDefinition.Name == partBindingSettings.Name);
|
|
var partBindingsQuery =
|
|
from descriptor in partBindingDescriptors
|
|
from bindingContext in descriptor.BindingContexts
|
|
where bindingContext.ContextName == part.PartDefinition.Name
|
|
from binding in bindingContext.Bindings
|
|
select binding;
|
|
var partBindings = partBindingsQuery.ToArray();
|
|
|
|
foreach (var binding in partBindingSettings.Bindings.Where(x => x.Enabled)) {
|
|
var localBinding = binding;
|
|
foreach (var partBinding in partBindings.Where(x => x.Name == localBinding.Name)) {
|
|
partBinding.Setter.DynamicInvoke(contentItem, part, value);
|
|
}
|
|
}
|
|
}
|
|
|
|
private static void InvokeFieldBindings(
|
|
ContentItem contentItem,
|
|
IEnumerable<ContentPartBindingDescriptor> lookup,
|
|
PartBindingSettings partBindingSettings,
|
|
FieldBindingSettings fieldBindingSettings,
|
|
string value) {
|
|
|
|
var part = contentItem.Parts.FirstOrDefault(x => x.PartDefinition.Name == partBindingSettings.Name);
|
|
if (part == null)
|
|
return;
|
|
|
|
var field = part.Fields.FirstOrDefault(x => x.Name == fieldBindingSettings.Name);
|
|
if (field == null)
|
|
return;
|
|
|
|
var fieldBindingDescriptorsQuery =
|
|
from partBindingDescriptor in lookup
|
|
where partBindingDescriptor.Part.PartDefinition.Name == partBindingSettings.Name
|
|
from fieldBindingDescriptor in partBindingDescriptor.FieldBindings
|
|
where fieldBindingDescriptor.Field.Name == fieldBindingSettings.Name
|
|
select fieldBindingDescriptor;
|
|
var fieldBindingDescriptors = fieldBindingDescriptorsQuery.ToArray();
|
|
var fieldBindingsQuery =
|
|
from descriptor in fieldBindingDescriptors
|
|
from bindingContext in descriptor.BindingContexts
|
|
where bindingContext.ContextName == field.FieldDefinition.Name
|
|
from binding in bindingContext.Bindings
|
|
select binding;
|
|
var fieldBindings = fieldBindingsQuery.ToArray();
|
|
|
|
foreach (var binding in fieldBindingSettings.Bindings.Where(x => x.Enabled)) {
|
|
var localBinding = binding;
|
|
foreach (var fieldBinding in fieldBindings.Where(x => x.Name == localBinding.Name)) {
|
|
fieldBinding.Setter.DynamicInvoke(contentItem, field, value);
|
|
}
|
|
}
|
|
}
|
|
|
|
private static bool IsFormElementType(IElementValidator validator, Type elementType) {
|
|
var validatorType = validator.GetType();
|
|
var validatorElementType = validatorType.BaseType.GenericTypeArguments[0];
|
|
return validatorElementType == elementType || validatorElementType.IsAssignableFrom(elementType);
|
|
}
|
|
}
|
|
}
|