Nibble

Archive for the 'ui-o-matic' Category


Adding a contact form to your Umbraco site 0

UI-O-Matic is great for quickly adding a CRUD section to your Umbraco backoffice but what if you also want to add a form to your frontend based on the same poco…? Meet MCFly:

UI-O-Matic V2 and custom repositories 0

One of the most thrilling new features in UI-O-Matic v2 is the ability to plug in your own reposities to handle the crud (thanks to Matt for the idea and implementation). So by default UI-O-Matic works agains the database using PetaPoco but with this feature we can pretty much plug in any third party API.

Example

Let’s say we want to be able to create Github issues from inside the Umbraco UI, well that can be done in a couple of easy steps.

Define the Model

1 using System.ComponentModel.DataAnnotations; 2 using UIOMatic.Attributes; 3 using UIOMatic.Example.GitHubIssues.Repo; 4 using Umbraco.Core.Persistence.DatabaseAnnotations; 5 6 namespace UIOMatic.Example.GitHubIssues.Models 7 { 8 [UIOMatic(issues, Issues, Issue, 9 RepositoryType = typeof(GitHubIssuesRepo), 10 ShowOnSummaryDashboard = true)] 11 public class Issue 12 { 13 [PrimaryKeyColumn] 14 public int Id { get; set; } 15 16 [Required] 17 [UIOMaticField(IsNameField = true)] 18 [UIOMaticListViewField] 19 public string Title { get; set; } 20 21 [UIOMaticField(View = UIOMatic.Constants.FieldEditors.Textarea)] 22 public string Body { get; set; } 23 24 public override string ToString() 25 { 26 return Title; 27 } 28 } 29 }

The model consists of a couple of properties of course if we wish that UI-O-Matic picks these up we need to mark them with the correct attributes. One thing that we also need to do is mark 1 property with the primarykeycolumn attribute , this is the property used to identify the object.

As you see on the UIOMatic attribute we setup the RepositoryType to be something custom instead of just the default.

Create the Repository

Next we need to create that custom repo type. We can inherit from the Abstract class class AbstractUIOMaticRepository<TEntity, TId> and override it’s methods, so that’s where we’ll plug in the third party api…

1 using Octokit; 2 using System; 3 using System.Collections.Generic; 4 using System.Linq; 5 using UIOMatic.Data; 6 using UIOMatic.Models; 7 8 namespace UIOMatic.Example.GitHubIssues.Repo 9 { 10 public class GitHubIssuesRepo : AbstractUIOMaticRepository<Models.Issue, int> 11 { 12 private const string RepoOwner = TimGeyssens; 13 private const string RepoName = UIOMatic; 14 15 private GitHubClient Client 16 { 17 get 18 { 19 20 var client = new GitHubClient(new ProductHeaderValue(my-cool-app)); 21 var basicAuth = new Credentials(USERNAME, PASSWORD); 22 client.Credentials = basicAuth; 23 return client; 24 } 25 } 26 27 public override Models.Issue Create(Models.Issue entity) 28 { 29 var createIssue = new NewIssue(entity.Title); 30 createIssue.Body = entity.Body; 31 32 var issue = Client.Issue.Create(RepoOwner, RepoName, createIssue).Result; 33 34 entity.Id = issue.Number; 35 36 return entity; 37 } 38 39 40 public override void Delete(int[] ids) 41 { 42 43 foreach (var id in ids) 44 { 45 var issue = Client.Issue.Get(RepoOwner, RepoName, id).Result; 46 47 var update = issue.ToUpdate(); 48 49 update.State = ItemState.Closed; 50 51 var updatedIssue = Client.Issue.Update(RepoOwner, RepoName, issue.Number, update).Result; 52 } 53 } 54 55 56 public override Models.Issue Get(int id) 57 { 58 59 var issue = Client.Issue.Get(RepoOwner, RepoName, id).Result; 60 61 return new Models.Issue 62 { 63 64 Id = issue.Number, 65 Title = issue.Title, 66 Body = issue.Body 67 }; 68 } 69 70 71 public override IEnumerable<Models.Issue> GetAll(string sortColumn = “”, string sortOrder = “”) 72 { 73 var retVal = new List<Models.Issue>(); 74 75 var issues = Client.Issue.GetAllForRepository(RepoOwner, RepoName).Result; 76 77 foreach(var issue in issues) 78 { 79 80 retVal.Add(new Models.Issue 81 { 82 Id = issue.Number, 83 Title = issue.Title 84 }); 85 86 } 87 return retVal; 88 } 89 90 public override UIOMaticPagedResult<Models.Issue> GetPaged(int pageNumber, int itemsPerPage, string searchTerm = “”, IDictionary<string, string> filters = null, string sortColumn = “”, string sortOrder = “”) 91 { 92 //used in the ListView but we aren’t using that one currently 93 throw new NotImplementedException(); 94 } 95 96 public override long GetTotalRecordCount() 97 { 98 return GetAll().Count(); 99 } 100 101 public override Models.Issue Update(Models.Issue entity) 102 { 103 104 var issue = Client.Issue.Get(RepoOwner, RepoName, entity.Id).Result.ToUpdate(); 105 106 issue.Title = entity.Title; 107 issue.Body = entity.Body; 108 109 var updatedIssue = Client.Issue.Update(RepoOwner, RepoName, entity.Id, issue).Result; 110 111 return entity; 112 } 113 114 115 } 116 }

So as you see nothing groundbreaking in here, we are just using octokit.net to do the heavy lifting and using its API to do the CRUD operations.

Result

We are now able to perform crud operations against the Github repo from within the Umbraco backoffice.

Screen Shot 2016-11-02 at 21.28.11

Screen Shot 2016-11-02 at 21.28.23

Video

Conclusion

So as you can see the sky is the limit with UI-O-Matic v2!

Extending UI-O-Matic V2 With custom List View filters 0

In the contact entries example you’ll see that we use a relative date filter to output the created date in the format of … minutes ago, hours ago,…days ago

With the following config on the standard label list view field

1 [UIOMaticListViewField(Config = {’format’ : ‘{{value|relativeDate}}’})] 2 [UIOMaticField(View = UIOMatic.Constants.FieldEditors.DateTime)] 3 public DateTime Created { get; set; }

But of course it’s also possible to plug in custom filters

Show me

So let me extend the contact entries example and truncate the message if it’s to large.

I’ve downloaded this truncate filter from github and added that to my project, also added a package manifest and a script where I do the include, so I get the following structure in my AppPlugins folder

Screen Shot 2016-10-28 at 17.49.08

Import looks like

1 var app = angular.module(umbraco); 2 3 //These are my Angular modules that I want to inject/require 4 5 app.requires.push(truncate);

So with that in place I can now use the new filter

1 [SpecialDbType(SpecialDbTypes.NTEXT)] 2 [Required] 3 [UIOMaticListViewField(Config = {’format’ : ‘{{value|characters:25}}’})] 4 [UIOMaticField(View = UIOMatic.Constants.FieldEditors.Textarea)] 5 public string Message { get; set; }

And now we get a truncated message (tailed with …)

Screen Shot 2016-10-28 at 17.53.32

btw RC is out! https://www.nuget.org/packages/Nibble.Umbraco.UIOMatic/2.0.0-rc (last chance to test before final hits the shelves)

Extending UI-O-Matic v2 with custom List Actions 1

Something that we’ve just added to UI-O-Matic v2 is the ability to plug in additional actions. So by default you get CRUD but now you will also be able to plug in third party ones!

Example

Let’s say you want to provide export functionality to your clients, here is how do do that (this is currently run on the latest source so still subject to change)

Defining the action

Step 1 is that we’ll need to define our action, each action consist of a name, alias and a view it will open

1 [UIOMaticAction("export","Export", "~/App_Plugins/UIOMaticAddons/export.html")] 2 public class ExportAction { }

Next we add the our model (if we take the previous example we can just add the new Action by adding it to the ListActions parameter of our UIOMatic attribute)

1 [TableName("ContactEntries")] 2 [PrimaryKey("Id", autoIncrement = true)] 3 [UIOMatic("contactentries", "Contact Entries", "Contact Entry", 4 FolderIcon = "icon-users", 5 SortColumn = "Created", SortOrder = "desc", 6 RenderType = UIOMaticRenderType.List, 7 ListActions = new[]{ typeof(ExportAction)})] 8 public class ContactEntry

The Angular View and Controller

As you see we pointed the new action to ~/App_Plugins/UIOMaticAddons/export.html so we now have to build that dialog

1 <div ng-controller="uioMaticAddons.ExportController"> 2 <p ng-show="loading">Generating Export…</p> 3 <a href="{{file}}" ng-hide="loading" target="_blank">Download export here</a> 4 </div>

 

1 angular.module("umbraco").controller("uioMaticAddons.ExportController", 2 function ($scope, uioMaticExportResource) { 3 $scope.loading = true; 4 uioMaticExportResource.getExport($scope.dialogData.typeAlias).then(function (response) { 5 $scope.file = response.data; 6 $scope.loading = false; 7 }); 8 });

 

As you can see here we just call a resource and provide it with the typeAlias (that is provided to us in the dialog data).

The resource will return the file name of the csv export.

AngularJS Resource

1 angular.module("umbraco.resources") 2 .factory("uioMaticExportResource", function ($http, umbRequestHelper) { 3 return { 4 getExport: function (typeAlias) { 5 return umbRequestHelper.resourcePromise( 6 $http.get(Umbraco.Sys.ServerVariables.uioMaticAddons.ecBaseUrl + "GetExport?typeAlias="+typeAlias), 7 Failed to generate export 8 ); 9 } 10 } 11 });

this one basicly calls our .net controller

.NET Controller

1 using CsvHelper; 2 using System; 3 using System.IO; 4 using Umbraco.Core.IO; 5 using Umbraco.Web.Editors; 6 7 namespace TestUIOMatic2Take2.Controllers 8 { 9 public class ExportController: UmbracoAuthorizedJsonController 10 { 11 public object GetExport(string typeAlias) 12 { 13 var guid = Guid.NewGuid(); 14 15 using (var textWriter = File.CreateText(IOHelper.MapPath(@"~\App_Plugins\UIOMaticAddons\Exports\" + guid + ".csv"))) 16 { 17 using (var csv = new CsvWriter(textWriter)) 18 { 19 var os = new UIOMatic.Services.PetaPocoObjectService(); 20 21 var data = os.GetAll(UIOMatic.Helper.GetUIOMaticTypeByAlias(typeAlias)); 22 23 csv.Configuration.Delimiter = ";"; 24 csv.Configuration.HasHeaderRecord = true; 25 26 csv.WriteRecords(data); 27 } 28 } 29 30 return new { data = "../App_Plugins/UIOMaticAddons/Exports/" + guid.ToString() + ".csv" }; 31 } 32 } 33 }

 

This uses the great and simple CsvHelper package to easily generate a csv file out of our data and then stores it on disk and returns the file location.

Result

I now got an additional export action I can apply to any model I wish!

Screen Shot 2016-10-27 at 22.18.45

Screen Shot 2016-10-27 at 22.18.58

Future

Once v2 final is out, I’ll make a nuget package out of this (and probably make some tweaks since this is just a proof of concept at the moment) so you can easiliy install it and extend your UI-O-Matic models

Using UI-O-Matic V2 as an entries viewer for your custom forms 0

As promised I would share some real world examples of using UI-O-Matic. One thing I tend to use if for is as a quick entries viewer for custom made forms on your Umbraco site.

Prerequisites

So of course we first need to setup a form on our frontend, for that we’ll need a model, view and (surface)controller. If you arent’ familiar with how to set that up check out the chapter on Umbraco.tv http://umbraco.tv/videos/umbraco-v7/developer/fundamentals/surface-controllers/introduction/ or I’ve also seen this video recently https://www.youtube.com/watch?v=3V0A1AYJbys explaining the process.

Model

1 [TableName("ContactEntries")] 2 [PrimaryKey("Id", autoIncrement = true)] 3 public class ContactEntry 4 { 5 [PrimaryKeyColumn(AutoIncrement = true)] 6 public int Id { get; set; } 7 8 [Required] 9 public string Name { get; set; } 10 11 [Required] 12 [EmailAddress] 13 public string Email { get; set; } 14 15 [SpecialDbType(SpecialDbTypes.NTEXT)] 16 [Required] 17 public string Message { get; set; } 18 19 public DateTime Created { get; set; } 20 }


View

1 @if (TempData["success"] != null) 2 { 3 <p>Thank you for your message!</p> 4 } 5 else 6 { 7 using (Html.BeginUmbracoForm<ContactController>("HandlePost")) 8 { 9 <div> 10 @Html.LabelFor(m => m.Name) 11 @Html.TextBoxFor(x => x.Name) 12 @Html.ValidationMessageFor(m => m.Name) 13 </div> 14 15 <div> 16 @Html.LabelFor(m => m.Email) 17 @Html.TextBoxFor(x => x.Email) 18 @Html.ValidationMessageFor(m => m.Email) 19 </div> 20 21 <div> 22 @Html.LabelFor(m => m.Message) 23 @Html.TextAreaFor(x => x.Message) 24 @Html.ValidationMessageFor(m => m.Message) 25 </div> 26 27 <div> 28 <button type="submit">Submit</button> 29 </div> 30 } 31 }


Controller

1 public class ContactController : SurfaceController 2 { 3 [ChildActionOnly] 4 public ActionResult Render() 5 { 6 return PartialView("ContactEntry",new Models.ContactEntry()); 7 } 8 9 [HttpPost] 10 public ActionResult HandlePost(Models.ContactEntry model) 11 { 12 if (!ModelState.IsValid) 13 return CurrentUmbracoPage(); 14 15 //add to db 16 model.Created = DateTime.Now; 17 var db = ApplicationContext.DatabaseContext.Database; 18 db.Insert(model); 19 20 //send email, do other things… 21 22 TempData["Success"] = true; 23 return RedirectToCurrentUmbracoPage(); 24 } 25 }

 

Of course you’ll also need to make sure your db table exist, that can be done with this piece of code

1 public class App: ApplicationEventHandler 2 { 3 protected override void ApplicationStarted(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext) 4 { 5 var ctx = applicationContext.DatabaseContext; 6 var db = new DatabaseSchemaHelper(ctx.Database, applicationContext.ProfilingLogger.Logger, ctx.SqlSyntax); 7 8 //Check if the DB table does NOT exist 9 if (!db.TableExist("ContactEntries")) 10 { 11 //Create DB table - and set overwrite to false 12 db.CreateTable(false, typeof(ContactEntry)); 13 } 14 15 } 16 }

So far this has nothing to do with UI-O-Matic it’s just standard Umbraco, MVC and PetaPoco (to insert a db entry).

But of course we now want to show these entries to the client…

Adding UI-O-Matic V2 into the mix

The only thing we have to do now is install UI-O-Matic v2 (at this moment it’s in beta and you can get it from Nuget or build the latest source from github). In this example I’ve build the latest source and installed that but all these features will make it into the final release.

1 [TableName("ContactEntries")] 2 [PrimaryKey("Id", autoIncrement = true)] 3 [UIOMatic("contactentries", "Contact Entries", "Contact Entry", 4 FolderIcon = "icon-users", ItemIcon = "icon-user", 5 SortColumn = "Created", SortOrder = "desc", 6 RenderType = UIOMaticRenderType.List)] 7 public class ContactEntry 8 { 9 [PrimaryKeyColumn(AutoIncrement = true)] 10 public int Id { get; set; } 11 12 [Required] 13 [UIOMaticListViewField] 14 [UIOMaticField] 15 public string Name { get; set; } 16 17 [Required] 18 [EmailAddress] 19 [UIOMaticListViewField] 20 [UIOMaticField] 21 public string Email { get; set; } 22 23 [SpecialDbType(SpecialDbTypes.NTEXT)] 24 [Required] 25 [UIOMaticListViewField] 26 [UIOMaticField(View = UIOMatic.Constants.FieldEditors.Textarea)] 27 public string Message { get; set; } 28 29 [UIOMaticListViewField(Config = "{’format’ : ‘{{value|relativeDate}}’}")] 30 [UIOMaticField(View = UIOMatic.Constants.FieldEditors.DateTime)] 31 public DateTime Created { get; set; } 32 }

 

So first we simply decorate the class with the UIOMatic attribute, providing a unique alias, a singular name and a plural name for our object and some optional parameters like which icon to use and that we would like to render in a list instead of the tree

Then we can mark the items we want in the actual editor (marked with UIOMaticField, can also specify a different editor then textstring as you can see in the example)

And mark the ones we want in the list view (marked with UIOMaticListViewField)

Result

Screen Shot 2016-10-27 at 13.02.11

Screen Shot 2016-10-27 at 13.40.54

As you see simply by decorating our model with some additional attributes we now get an entries viewer/editor! Kapow!