I've installed the UmbracoCms.RestAPI package from NuGet and have it working to retrieve content.
Given the documented endpoints for content, media, members, and relations I assume that this package will not allow me to list approved Entries for a given Umbraco Form.
Has anyone managed to get Umbraco Form entries out using this or any other REST API?
Hi Tim, I was just going down the same path this afternoon. I think that path is going to lead me to create a WebAPI Controller, but the documentation on the forms product is pretty well non-existent. I took a look around GitHub and it doesn't seem to be open source so we can't even take a look under the hood to see how to use it.
This is the basic idea behind creating a Web Api service for forms.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Umbraco.Web.WebApi;
using Umbraco.Forms.Data.Storage;
using Umbraco.Forms.Core;
namespace UmbracoForms.Controllers
{
public class FormEntriesController : UmbracoApiController
{
// GET: FormEntries
public IEnumerable<Form> GetForms()
{
IEnumerable<Form> forms;
using (var context= new FormStorage()){
forms = context.GetAllForms();
}
return forms;
}
public IEnumerable<Record> GetRecords(Guid id)
{
Form form;
using (var context = new FormStorage())
{
form = context.GetForm(id);
}
IEnumerable<Record> records;
using (var context = new RecordStorage())
{
records = context.GetAllRecords(form);
}
return records;
}
}
}
The main problem I had when looking at getting the forms data out was in getting any Umbraco context into the API which is external to my web project.
Since I'm (currently) only reading the forms data through my API I have resorted to accessing the data directly in the SQL tables, database schema here.
The definition for the forms themselves are stored as Json within the file system, the entries made into them are stored within the SQL tables.
Admittedly it would've been much cleaner to get the data using FormStorage and RecordStorage, but I used the database workaround which has at least got something working!
Glad to hear that you found a solution. I've developed the api option a little further. I'll include it below in case anybody else comes by looking for a solution. With this solution I can connect to our site from our internal network and copy form data into our internal LOBS.
public class FormEntriesController : UmbracoAuthorizedApiController
{
// GET: FormEntries
public IEnumerable<Form> GetForms()
{
IEnumerable<Form> forms;
using (var context= new FormStorage()){
forms = context.GetAllForms();
}
return forms;
}
public Form GetForm(Guid id)
{
using (var context = new FormStorage())
{
return context.GetForm(id);
}
}
public Dictionary<string, object> GetRecord(Guid id)
{
using (var context = new RecordStorage())
{
var record = context.GetRecordByUniqueId(id);
return ConvertRecord(record);
}
}
private Dictionary<string, object> ConvertRecord(Record record)
{
var collection = new Dictionary<string, object>();
//Give our item an Id
collection.Add("Id", record.UniqueId);
//field values accessed via page/fieldset/field are null so we create a lookup to grab the values from the field retrieved from record.FieldValues (which isn't null)
var fieldLookup = record.RecordFields.Values.ToDictionary<RecordField, Guid>(r => r.FieldId);
Regex rgx = new Regex("[^a-zA-Z0-9]");
//Loop through the page/fieldset/fields to generate a decent propertyName
foreach (var page in record.GetForm().Pages)
{
foreach (var fieldset in page.FieldSets)
{
foreach (var field in fieldset.Containers.SelectMany(c => c.Fields))
{
//Geenerate a property name that is based on the page/fieldset/field names
string pageName = rgx.Replace(page.Caption ?? "", "");
string fieldSetName = rgx.Replace(fieldset.Caption ?? "", "");
string fieldName = rgx.Replace(field.Caption ?? "", "");
string propertyName = ((string.IsNullOrWhiteSpace(pageName) ? "" : pageName + "_") + (string.IsNullOrWhiteSpace(fieldSetName) ? "" : fieldSetName + "_") + fieldName);
if (!fieldLookup.ContainsKey(field.Id)) continue;
if (collection.ContainsKey(propertyName))
{
//Well this is gross -- if we are goig to drop in a duplicate key, update the existing record (and make it multivalued if not)
var value = collection[propertyName];
if (!(value is List<object>))
{
value = new List<object>(new object[] { value });
}
(value as List<object>).AddRange(fieldLookup[field.Id].Values);
collection[propertyName] = value;
}
else
{
//Doesn't already exist, let's insert
if (field.FieldType.Name == "Multiple choice" || field.FieldType.Name == "File upload")
{
collection.Add(propertyName, fieldLookup[field.Id].Values);
}
else
{
collection.Add(propertyName, fieldLookup[field.Id].Values.FirstOrDefault());
}
}
}
}
}
return collection;
}
public IEnumerable<Dictionary<string,object>> GetRecords(Guid id)
{
Form form;
using (var context = new FormStorage())
{
form = context.GetForm(id);
}
//IEnumerable<Record> records;
using (var context = new RecordStorage())
{
var records = context.GetAllRecords(form);
var result = context.GetAllRecords(form).Select(record => ConvertRecord(record));
return result;
}
}
public IEnumerable<Record> GetRecordsRaw(Guid id)
{
Form form;
using (var context = new FormStorage())
{
form = context.GetForm(id);
}
using (var context = new RecordStorage())
{
var records = context.GetAllRecords(form);
return records;
}
}
}
It's now inheriting from UmbracoAuthorizedApiController. So to call any of these actions, you first have to hit up /umbraco/oauth/token to get a token, then set the Authorization header to bearer
Just to update the thread with the direct to database approach, this is the main method, using Entity Framework (database first).
private static IEnumerable<FormEntryVM> GetAllEntriesForForm(Guid formId)
{
List<FormEntryVM> entries = new List<FormEntryVM>();
using (var context = new umbracoFormEntities())
{
var recordData = context.UFRecordFields
.Include("UFRecordDataStrings")
.Include("UFRecordDataLongStrings")
.Include("UFRecordDataIntegers")
.Include("UFRecordDataDateTimes")
.Include("UFRecordDataBits");
var records = context.UFRecords.Where(r => r.Form == formId)
.GroupJoin(
recordData,
record => record.Id,
recordField => recordField.Record,
(record, recordField) => new
{ Record = record, RecordFields = recordField });
foreach (var record in records)
{
List<FormEntryPropertyVM> props = new List<FormEntryPropertyVM>();
foreach (var field in record.RecordFields)
{
FormEntryPropertyVM prop = new FormEntryPropertyVM() { Key = field.Alias };
if (field.DataType == "LongString")
prop.Value = field.UFRecordDataLongStrings
.Select(rd => rd.Value).FirstOrDefault();
if (field.DataType == "DateTime")
prop.Value = field.UFRecordDataDateTimes
.Select(rd => rd.Value).FirstOrDefault();
if (field.DataType == "String")
prop.Value = field.UFRecordDataStrings
.Select(rd => rd.Value).FirstOrDefault();
if (field.DataType == "Bit")
prop.Value = field.UFRecordDataBits
.Select(rd => rd.Value).FirstOrDefault();
if (field.DataType == "Integer")
prop.Value = field.UFRecordDataIntegers
.Select(rd => rd.Value).FirstOrDefault();
props.Add(prop);
}
FormState State = (FormState)Enum.Parse(typeof(FormState), record.Record.State);
FormEntryVM entry = new FormEntryVM() {
Id = record.Record.Id,
Created = record.Record.Created,
FormId = record.Record.Form,
State = State,
Updated = record.Record.Updated,
Properties = props };
entries.Add(entry);
}
}
return entries.AsEnumerable();
}
And the classes for the view models being returned are:
[DataContract(Name = "Property")]
public class FormEntryPropertyVM
{
[DataMember]
public string Key { get; set; }
[DataMember]
public object Value { get; set; }
}
[Serializable]
[DataContract(Name = "Entry")]
public class FormEntryVM
{
[DataMember]
public int Id { get; set; }
[DataMember]
public Guid FormId { get; set; }
/// <summary>
/// Workflow state
/// </summary>
[DataMember]
public FormState State { get; set; }
[DataMember]
public DateTime Created { get; set; }
[DataMember]
public DateTime Updated { get; set; }
/// <summary>
/// User defined form fields, and their corresponding values
/// </summary>
[DataMember]
public List<FormEntryPropertyVM> Properties { get; set; }
}
/// <summary>
/// Copy of the Umbraco.Forms.Core.Enums.FormState (do not want to add a
/// reference to Umbraco, and all the dependencies, just for this)
/// </summary>
[DataContract]
public enum FormState
{
[EnumMember(Value = "Opened")]
Opened = 0,
[EnumMember(Value = "Resumed")]
Resumed = 1,
[EnumMember(Value = "Partially Submitted")]
PartiallySubmitted = 2,
[EnumMember(Value = "Submitted")]
Submitted = 3,
[EnumMember(Value = "Approved")]
Approved = 4,
[EnumMember(Value = "Deleted")]
Deleted = 5
}
What is not included in this code sample are the entity framework model classes but these are simple enough to recreate by creating the models from the existing database see this link for a step by step guide.
Umbraco.RestAPI for Form Entries?
Hi,
I've installed the UmbracoCms.RestAPI package from NuGet and have it working to retrieve content.
Given the documented endpoints for content, media, members, and relations I assume that this package will not allow me to list approved Entries for a given Umbraco Form.
Has anyone managed to get Umbraco Form entries out using this or any other REST API?
Thanks,
Tim
Hi Tim, I was just going down the same path this afternoon. I think that path is going to lead me to create a WebAPI Controller, but the documentation on the forms product is pretty well non-existent. I took a look around GitHub and it doesn't seem to be open source so we can't even take a look under the hood to see how to use it.
This is the basic idea behind creating a Web Api service for forms.
You might prefer something more like this which returns more concision objects:
Hi Evan,
Thanks for your reply.
The main problem I had when looking at getting the forms data out was in getting any Umbraco context into the API which is external to my web project.
Since I'm (currently) only reading the forms data through my API I have resorted to accessing the data directly in the SQL tables, database schema here.
The definition for the forms themselves are stored as Json within the file system, the entries made into them are stored within the SQL tables.
Admittedly it would've been much cleaner to get the data using FormStorage and RecordStorage, but I used the database workaround which has at least got something working!
Tim
Glad to hear that you found a solution. I've developed the api option a little further. I'll include it below in case anybody else comes by looking for a solution. With this solution I can connect to our site from our internal network and copy form data into our internal LOBS.
It's now inheriting from UmbracoAuthorizedApiController. So to call any of these actions, you first have to hit up /umbraco/oauth/token to get a token, then set the Authorization header to bearer
Just to update the thread with the direct to database approach, this is the main method, using Entity Framework (database first).
And the classes for the view models being returned are:
What is not included in this code sample are the entity framework model classes but these are simple enough to recreate by creating the models from the existing database see this link for a step by step guide.
Tim
is working on a reply...