More in this section
Categories
Bloggers
Blogs RSS feed

Creating Dynamic Module Items Using the Module Builder API

by Josh Morales

We previously looked at how you can retrieve dynamic module items using the Module Builder API. Today we'll take a look at how you can use that same API to also create new items.

Using the sample code from the Code Reference (generated for every dynamic module you create) you can easily develop a new public widget that allows your site visitors to create items. Because dynamic module items support the Content Lifecycle, you can prevent these items from being published before they are reviewed.

For this example, we will once again be using the Showcases module example that Gabe created and has available on the Module Builder Webinar notes.

Submit Showcases Widget

Because Sitefinity widgets are simply user controls, all we really need to do is create a new User Control and add our input markup plus the backing code to write that data to the API. Start with the frontend of the User Control:

<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="SubmitShowcase.ascx.cs" Inherits="SitefinityWebApp.Modules.Showcases.SubmitShowcase" %><%@ Register Assembly="Telerik.Sitefinity" Namespace="Telerik.Sitefinity.Web.UI.Fields" TagPrefix="sf" %><div class="sfShowcaseSubmitForm"> <p>Do you have a website you would like us to feature? Submit it below!</p> <div> <label for="Title" class="txtLbl">Company or Website Name</label> <asp:TextBox ID="Title" ClientIDMode="Static" runat="server" /> </div> <div> <label for="SiteUrl" class="txtLbl">Site Url</label> <asp:TextBox ID="SiteUrl" ClientIDMode="Static" runat="server" /> </div> <div> <label for="LaunchDate" class="txtLbl">Launch Date</label> <telerik:RadDatePicker ID="LaunchDate" runat="server" ClientIDMode="Static" /> <div> <label for="Industries" class="txtLbl">Industry</label> <asp:DropDownList ID="Industries" ClientIDMode="Static" runat="server" /> </div> <div> <label class="txtLbl">Case Study Features</label> <asp:CheckBoxList ID="FeaturesList" ClientIDMode="Static" runat="server" RepeatColumns="4" RepeatDirection="Horizontal" RepeatLayout="Table" /> </div> <div> <label for="Countries" class="txtLbl">Country</label> <asp:DropDownList ID="Countries" ClientIDMode="Static" runat="server" /> </div> <div> <label>Sitefinity Version</label> <asp:RadioButtonList ID="Version" runat="server"> <asp:ListItem Text="Sitefinity 3.x" Value="Sitefinity 3.x" /> <asp:ListItem Text="Sitefinity 4.x" Value="Sitefinity 4.x" /> </asp:RadioButtonList> </div> <div> <label for="Thumbnail" class="txtLbl">Website Screenshot</label> <telerik:RadUpload ID="Thumbnail" ClientIDMode="Static" runat="server" AllowedFileExtensions=".gif,.jpg,.jpeg,.png" MaxFileInputsCount="1" InputSize="45" ControlObjectsVisibility="None" /> <div> <label for="CaseStudy" class="txtLbl">Case Study (PDF)</label> <telerik:RadUpload ID="CaseStudy" ClientIDMode="Static" runat="server" AllowedFileExtensions=".pdf" MaxFileInputsCount="1" InputSize="45" ControlObjectsVisibility="None" /> <div> <label for="Content" class="txtLbl">Case Study</label> <sf:FormManager ID="formManager" runat="server" /> <sf:HtmlField ID="Content" ClientIDMode="Static" runat="server" Width="99%" Height="370px" DisplayMode="Write" FixCursorIssue="True" /> </div> <div> <asp:Button ID="btnSubmit" runat="server" Text="Submit" OnClick="Submit" /> </div> <telerik:RadInputManager ID="RadInput" runat="server"> <telerik:RegExpTextBoxSetting ValidationExpression="http(s)?://([\w-]+\.)+[\w-]+(/[\w- ./?%&amp;=]*)?" ClearValueOnError="false"> <TargetControls> <telerik:TargetInput ControlID="SiteUrl" /> </TargetControls> </telerik:RegExpTextBoxSetting> <telerik:TextBoxSetting Validation-IsRequired="true"> <TargetControls> <telerik:TargetInput ControlID="Title" /> <telerik:TargetInput ControlID="SiteUrl" /> <telerik:TargetInput ControlID="LaunchDate" /> <telerik:TargetInput ControlID="Content" /> </TargetControls> </telerik:TextBoxSetting> </telerik:RadInputManager> </div> </div> </div></div>

This is simply a collection of input controls to collect the data from the user that corresponds to the fields for our dynamic content type. For simplicity, we've excluded validation controls.

Choice Fields

In the current version of Sitefinity (4.4 as of today), choice fields used by the module do not yet persist the valid list of items anywhere other than on the backend sitefinity forms that are created by the module. As a result, it is not yet possible to automatically bind input controls such as dropdown menus and radio buttons to those choices, but instead must be manually populated.

This is what we have done in the code above for the Sitefinity Version field, and explicitly defined the valid choices for our module.

Future versions of the module builder will address this so that you can access these choices and bind to them automatically.

Code-Behind: On Control Load

Although we cannot bind to choice fields automatically, there are some controls we can load automatically using the regular Sitefinity API. Specifically we can pre-load the Taxonomy fields for Industry, Features, and Countries.

For this I've defined a helper method that will retrieve the list taxon items for a taxonomy based on the name of the parent taxonomy title.

private IEnumerable<ITaxon> GetTaxonomy<TTaxonomy>(string TaxonomyName) where TTaxonomy : class, ITaxonomy{
    // bind categories selector var taxMgr = TaxonomyManager.GetManager();
    var taxonomy = taxMgr.GetTaxonomies<TTaxonomy>().Where(t => t.Title == TaxonomyName).FirstOrDefault();
    if (taxonomy == null) return null;

    // return taxa items var taxa = taxonomy.Taxa;
    return taxa;
}

Now we can simply call that helper method on the Page_Load event so that our taxonomy controls are loaded and bound.

protected void Page_Load(object sender, EventArgs e)
{
    if (IsPostBack) return;

    // bind Industries menu foreach (var industry in GetTaxonomy<HierarchicalTaxonomy>("Industries"))
    {
        var item = new ListItem(industry.Title, industry.Id.ToString());
        Industries.Items.Add(item);
    }

    // bind features selectors foreach (var feature in GetTaxonomy<FlatTaxonomy>("Features"))
    {
        var chk = new ListItem(feature.Title, feature.Id.ToString());
        FeaturesList.Items.Add(chk);
    }

    // bind countries selectors foreach (var country in GetTaxonomy<FlatTaxonomy>("Countries"))
    {
        var item = new ListItem(country.Title, country.Id.ToString());
        Countries.Items.Add(item);
    }
}

Code-Behind: On Submit

Once the user completes the form and submits it, we want to save that data into a new dynamic module item. Because we have media fields in our content model, we need to first support uploading of media to the Sitefinity Images and Documents modules. This is done with helper methods of each type that write the file contained in a RadUpload control to the database.

protected Telerik.Sitefinity.Libraries.Model.Image UploadImage(RadUpload radUploader, string title)
{
    Telerik.Sitefinity.Libraries.Model.Image img = null;
    if (radUploader.UploadedFiles.Count == 0) return img;

    // select showcases album var imgMgr = LibrariesManager.GetManager();
    var album = imgMgr.GetAlbums().Where(a => a.Title == "Showcase Screenshots").FirstOrDefault();
    
    // prepare transaction using (var sf = App.Prepare().SetTransactionName("UploadTransaction").WorkWith())
    {
        // temporarily disable security checks to allow image creation without login sf.Images().GetManager().Provider.SuppressSecurityChecks = true;

        // save image properties img = imgMgr.CreateImage();
        img.Parent = album;
        img.Title = title;
        img.UrlName = title + "_Screenshot";
        img.Status = Telerik.Sitefinity.GenericContent.Model.ContentLifecycleStatus.Master;

        // upload Image var file = radUploader.UploadedFiles[0];
        imgMgr.Upload(img, file.InputStream, file.GetExtension());
        imgMgr.Publish(img);
        imgMgr.SaveChanges();

        // re-enable security sf.Images().GetManager().Provider.SuppressSecurityChecks = false;
    }

    // return image so it can be associated with module content item return img;
}

protected Telerik.Sitefinity.Libraries.Model.Document UploadDocument(RadUpload radUploader, string title)
{
    Telerik.Sitefinity.Libraries.Model.Document doc = null;
    if (CaseStudy.UploadedFiles.Count == 0) return doc;

    // select showcases album var docMgr = LibrariesManager.GetManager();
    var album = docMgr.GetDocumentLibraries().Where(a => a.Title == "Showcase Case Studies").FirstOrDefault();

    // prepare transaction using (var sf = App.Prepare().SetTransactionName("UploadTransaction").WorkWith())
    {
        // temporarily disable security checks to allow image creation without login sf.Documents().GetManager().Provider.SuppressSecurityChecks = true;

        // save image properties doc = docMgr.CreateDocument();
        doc.Parent = album;
        doc.Title = title;
        doc.UrlName = title;
        doc.Status = Telerik.Sitefinity.GenericContent.Model.ContentLifecycleStatus.Master;

        // upload Image var file = radUploader.UploadedFiles[0];
        docMgr.Upload(doc, file.InputStream, file.GetExtension());
        docMgr.Publish(doc);
        docMgr.SaveChanges();

        // re-enable security sf.Documents().GetManager().Provider.SuppressSecurityChecks = false;
    }

    // return image so it can be associated with module content item return doc;
}

Finally, we simply go through each field and map the simple properties (text, datetime, boolean, etc) using the SetValue() extension method.

NOTE: be sure to include the using Telerik.Sitefinity.Model namespace to use this extension method.

public void Submit(object sender, EventArgs e)
{
    // initialize the dynamic module content manager DynamicModuleManager mgr = DynamicModuleManager.GetManager();
    

    // initialize a new [ModuleItem] Type showcaseType = TypeResolutionService.ResolveType("Telerik.Sitefinity.DynamicTypes.Model.Showcases.Showcase");
    DynamicContent showcaseItem = mgr.CreateDataItem(showcaseType);

    // save simple fields to the item var title = Title.Text.Trim();
    showcaseItem.SetValue("Title", title);
    showcaseItem.SetValue("SiteUrl", SiteUrl.Text);    
    showcaseItem.SetValue("LaunchDate", LaunchDate.SelectedDate);
    showcaseItem.SetValue("UrlName", new Lstring(title.Replace(" ", "-"))); // TODO: use regex to strip characters showcaseItem.SetValue("Content", Content.Value.ToString());
    showcaseItem.SetValue("Platform", new string[] { Version.SelectedValue });

    // save selected country showcaseItem.Organizer.AddTaxa("countries", new Guid(Countries.SelectedValue));

    // save selected category showcaseItem.Organizer.AddTaxa("industries", new Guid(Industries.SelectedValue));

    // save selected tags foreach (ListItem tag in FeaturesList.Items)
        if (tag.Selected) showcaseItem.Organizer.AddTaxa("features", new Guid(tag.Value));

    // save thumbnail if present var img = UploadImage(Thumbnail, title);
    if (img != null)
    {
        var contentLink = new ContentLink(img.Parent.Id, img.Id);
        var screenshots = new List<ContentLink>() { contentLink };
        showcaseItem.SetValue("Screenshot", screenshots.ToArray());
    }

    // save document if present var doc = UploadDocument(CaseStudy, title);
    if (doc != null)
    {
        var contentLink = new ContentLink(doc.Parent.Id, doc.Id);
        var screenshots = new List<ContentLink>() { contentLink };
        showcaseItem.SetValue("CaseStudy", screenshots.ToArray());
    }

    // You need to call SaveChanges() in order for the items to be actually persisted to data store showcaseItem.SetValue("Owner", SecurityManager.GetCurrentUserId());
    showcaseItem.SetValue("PublicationDate", DateTime.Now);
    
    // uncomment this line to publish the item instead of saving as draft //mgr.Lifecycle.Publish(showcaseItem); mgr.SaveChanges();
    
    //// TODO: enable workflow //var contextBag = new Dictionary<string, string>(); //contextBag.Add("ContentType", showcaseType.FullName); //WorkflowManager.MessageWorkflow( // showcaseItem.Id, // showcaseType, // null, // "SendForApproval", // false, // contextBag);}

By default, items created will remain in Draft status. To publish an item, simply uncomment the line in the code above the SaveChanges() method.

mgr.Lifecycle.Publish(showcaseItem);

Wrapping Up

With just a few lines of code on a single user control, we were able to quickly extend our module to allow public users to add content items. The Code Reference is full of additional helpful examples to help you customize and make the most of your new dynamic modules.

If you haven't already, take some time to try out the new Module Builder, and as always, be sure to share your experiences with us in the Sitefinity Discussion Forums.

40 comments

Leave a comment
  1. ian Jun 14, 2012
    Hi

    For simplicity, you've excluded validation controls, but if you were to include them how would you do this? Are they normal ASP.NET validation controls or can you link back to your module items to reference the data type etc?

    Thanks        
  2. ian Jun 14, 2012
    Hi

    For simplicity, you've excluded validation controls, but if you were to include them how would you do this? Are they normal ASP.NET validation controls or can you link back to your module items to reference the data type etc?

    Thanks        
  3. Josh Jun 20, 2012
    Because this is a simple user control (ascx) you could certainly use the normal asp.net validation controls (or the Telerik RadInput control) to validate input.

    You can also validate again on the server side with custom logic inside the Submit method. It's entirely up to you!
  4. arnold Nov 02, 2017
    This content material is written very well. Your use of formatting while making your factors makes your observations very clean and easy to recognize. thanks. Anna University Results
  5. arnold Nov 04, 2017
    This is a decent post. This post gives genuinely quality data. I'm unquestionably going to investigate it. Truly exceptionally helpful tips are given here. Much thanks to you to such an extent. Keep up the acts of kindness. Unlock Your Hip Flexors Review
  6. arnold Nov 06, 2017
    A debt of gratitude is in order for posting this information. I simply need to tell you that I simply look at your site and I discover it extremely fascinating and useful. I can hardly wait to peruse loads of your posts. Top 10 free traffic sources
  7. arnold Nov 06, 2017
    This is the kind of data I've long been attempting to discover. Much obliged to you for composing this data.  http://snaphacktool.club
  8. arnold Nov 08, 2017
    Much obliged for setting aside an ideal opportunity to talk about that, I feel unequivocally about this thus truly like becoming acquainted with additional on this sort of field. Do you psyche overhauling your blog entry with extra understanding? It ought to be truly helpful for every one of us.  play store download for pc
  9. arnold Nov 09, 2017
    This is only the data I am discovering all over. A debt of gratitude is in order for your website, I simply subscribe your online journal. This is a decent blog.. Primary Care Doctors Irving 
  10. arnold Nov 09, 2017
    I truly delighted in perusing this post, huge fan. Keep doing awesome me when would you be able to distribute more articles or where would I be able to peruse more on the subject?  top lawn mower brands
  11. arnold Nov 09, 2017
    valuable data on points that bounty are intrigued on for this magnificent post.Admiring the time and exertion you put into your b!..  lottery dominator review
  12. arnold Nov 10, 2017
    Much obliged such a great amount for this data. I need to tell you I agree on a few of the focuses you make here and others may require some further survey, yet I can see your perspective.  BitCoin Code Scam
  13. arnold Nov 10, 2017
    Your article has provoked a considerable measure of positive hobby. I can see why since you have made such a decent showing of making it fascinating.  kenvinreviews.com
  14. arnold Nov 12, 2017
    Your online journal furnished us with profitable data to work with. Each and every tips of your post are great. Much obliged for sharing. Continue blogging, GeekSites.info
  15. arnold Nov 12, 2017
    Great site! I genuinely adore how it is simple on my eyes it is. I am considering how I may be informed at whatever point another post has been made. I have subscribed to your RSS which may do the trap? Have an extraordinary day!  https://discoup.enable-javascript.com/
  16. asd Nov 14, 2017
  17. arnold Nov 16, 2017
    A debt of gratitude is in order for setting aside an ideal opportunity to examine this, I feel unequivocally about it and adoration adapting more on this point. In the event that conceivable, as you pick up aptitude, would you psyche upgrading your online journal with additional data? It is to a great degree supportive for me.  panic disorder treatment
  18. Arnold Nov 25, 2017
    Extraordinary Article it its truly useful and inventive update us as often as possible with new upgrades. its was truly important. much appreciated.  Lottery Dominator
  19. Arnold Nov 26, 2017
    Thanks a lot for sharing us about this update. Hope you will not get tired on making posts as informative as this.  united
  20. Arnold Nov 28, 2017
    i really like this article please keep it up. quadcopter
  21. Arnold Nov 29, 2017
    Thanks for this article very helpful. thanks. Robert Kiyosaki
  22. Arnold Nov 29, 2017
    I found your this post while searching for information about blog-related research ... It's a good post .. keep posting and updating information. trump news
  23. Arnold Nov 29, 2017
    you understand your tasks stand out of the herd. there may be some thing unique approximately them. It appears to me all of them are clearly wonderful! craigslist search engine
  24. Arnold Dec 02, 2017
    Your article has provoked a great deal of positive hobby. I can see why since you have made such a decent showing of making it fascinating. business ideas
  25. Arnold Dec 04, 2017
    A debt of gratitude is in order for your data, it was truly exceptionally helpfull..  read more
  26. Arnold Dec 04, 2017
    A debt of gratitude is in order for setting aside an ideal opportunity to talk about this, I feel emphatically about it and adoration adapting more on this point. In the event that conceivable, as you pick up skill, would you psyche upgrading your online journal with more data? It is greatly useful for me.  massage parlour in jaipur
  27. Arnold Dec 05, 2017
    first-class blog and genuinely awesome. you may do something lots higher however i still say this ideal.preserve trying for the first-class. auto glass vaughan
  28. Arnold Dec 06, 2017
    hi!! Extremely fascinating examination happy that I ran over such instructive post. Keep doing awesome. Happy to be a piece of your net group.  phobia counselor
  29. Arnold Dec 06, 2017
    I have been looking at a couple of your stories and i can state really well done. I will bookmark your website  intercom installation
  30. Arnold Dec 07, 2017
    This is an awesome rousing article.I am essentially satisfied with your great work.You put truly extremely accommodating data. Keep it up. Continue blogging. Hoping to perusing your next post.  live tv channels
  31. Arnold Dec 08, 2017
    A debt of gratitude is in order for the site stacked with such a variety of data. Halting by your website assisted me with getting what I was searching for.  summit Organisers in KSA
  32. Arnold Dec 09, 2017
    excellent post but i used to be questioning if you may write a litte greater in this challenge? I’d be very thankful if you could difficult a bit bit similarly. recognize it! holm law group san diego
  33. Arnold Dec 11, 2017
    Much obliged to you for some other enlightening site. The spot else might just I understand that sort of data written in such a flawless system? I have an endeavor that I am essentially now running on, and I've been at the look out for such data.  activador para Windows 10
  34. Arnold Dec 11, 2017
    This is an awesome motivating article.I am practically satisfied with your great work.You put truly exceptionally accommodating data. Keep it up. Continue blogging. Hoping to perusing your next post.  What I really think about Clickfunnels
  35. Ash0022 Dec 13, 2017
    Well it would really help to sort my code at least. I was trying to find solution from long time truly an admirable post. RPF vacancy 2018
  36. Arnold Dec 13, 2017
    I can't read articles online all the time, yet I'm happy I did today. This is exceptionally elegantly composed and your focuses are very much communicated. Kindly, absolutely never quit composing.  best stationary bike for home
  37. Arnold Dec 13, 2017
    I adore this online journal!! The glimmer up the top is wonderful!! how to start an online business 2018
  38. Arnold Dec 13, 2017
    Thank you so much for sharing this great blog.Very inspiring and helpful too.Hope you continue to share more of your ideas.I will definitely love to read. Water Damage
  39. Arnold Dec 13, 2017
    A debt of gratitude is in order for imparting pleasant data to us. i like your post and all you impart to us is uptodate and very useful, i might want to bookmark the page so i can come here again to peruse you, as you have made a superb showing.  this is pet
  40. Arnold Dec 13, 2017
    Its an incredible joy perusing your post.Its brimming with data I am searching for and I want to post a remark that "The substance of your post is marvelous" Great work.  https://youtu.be/IuhFa3-LBoc

    Leave a comment