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.

29 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. Robinjack Aug 24, 2017
    Intriguing post. I Have Been pondering about this issue, so much obliged for posting. Really cool post.It 's truly extremely pleasant and Useful post.Thanks Girl is horny
  5. Robinjack Aug 26, 2017
    A debt of gratitude is in order for imparting this quality data to us. I truly delighted in perusing. Will without a doubt going to impart this URL to my companions.  I Love You Quotes
  6. Robinjack Aug 27, 2017
    i was simply perusing along and happened upon your web journal. simply needed to say great online journal and this article truly helped me.  Maca Root benefits
  7. Robinjack Aug 29, 2017
    Thanks  for every other informative site. The place else may just I get that kind of information written in such an ideal means? I have a venture that I’m just now operating on, and I have been on the look out for such information. Turmeric and Curcumin
  8. Robinjack Aug 30, 2017
    I was perusing your article and thought about whether you had considered making a digital book on this subject. You're composing would offer it quick. You have a great deal of composing ability.  Horny
  9. Robinjack Sep 02, 2017
    This is an incredible moving article.I am essentially satisfied with your great work.You put truly extremely accommodating data...  Relationship ebook
  10. Robinjack Sep 04, 2017
    More often than not I don't make remarks on sites, yet I'd like to say this article truly constrained me to do as such. Truly pleasant post!  The DASH Diet
  11. yahyah Sep 05, 2017
    Thanks for sharing this information. I really like your blog post very much. You have really shared a informative and interesting blog post with people. transformice pirata
  12. Robinjack Sep 10, 2017
    This is a splendid web journal! I'm extremely content with the remarks!..  Falling in Love
  13. Robinjack Sep 11, 2017
    Much obliged to you for setting aside an ideal opportunity to distribute this data extremely valuable!  Mary Courant scam
  14. Rachel Roberson Sep 15, 2017

    Very useful info, thanks

    the impossible game

    bouncing balls

    stickman

  15. Robinjack Sep 18, 2017
    I've been hunting down some better than average stuff on the subject and haven't had any fortunes up until this point, You just got another greatest fan!..  London Accountant Tax Return
  16. Robinjack Sep 19, 2017
    I was taking a gander at some of your posts on this site and I consider this site is truly informational! Keep setting up..  Hero Instinct
  17. Robinjack Sep 20, 2017
    Nice post. I was checking constantly this blog and I’m impressed! Extremely useful info specially the last part   I care for such information a lot. I was seeking this certain info for a long time. Thank you and good luck. Sinus Infection
  18. Robinjack Sep 22, 2017
    A debt of gratitude is in order for sharing the post.. folks are universes best individual in every existences of individual..they need or must succeed to maintain needs of the gang.  Forever Yours
  19. Robinjack Sep 26, 2017
    I read that Post and got it fine and educational. It would be ideal if you share more like that...  Accountants Preparing Limited Company Accounts
  20. peter Sep 29, 2017
    Much thanks to you such a great amount for sharing this incredible blog.Very motivating and accommodating too.Hope you keep on sharing a greater amount of your ideas.I will love to peruse.  i-love-online-casinos
  21. Robinjack Oct 05, 2017
    Best work you have done, this online site is truly cool with incredible certainties.  Accountants in Forest Row
  22. Robinjack Oct 09, 2017
    Goodness, this is truly intriguing perusing. I am happy I discovered this and got the chance to peruse it. Awesome employment on this substance. I like it.  https://h30434.www3.hp.com/t5/user/viewprofilepage/user-id/3026961
  23. Robinjack Oct 10, 2017
    Thanks for posting this info. I just want to let you know that I just check out your site and I find it very interesting and informative. I can't wait to read lots of your posts. https://h30434.www3.hp.com/t5/user/viewprofilepage/user-id/3028633
  24. Robinjack Oct 10, 2017
    This is such a great resource that you are providing and you give it away for free. I love seeing blog that understand the value. Im glad to have found this post as its such an interesting one! I am always on the lookout for quality posts and articles so i suppose im lucky to have found this! I hope you will be adding more in the future... music and audio
  25. Robinjack Oct 11, 2017
    It proved to be Very helpful to me and I am sure to all the commentators here!  Rubiks Kube
  26. Robinjack Oct 11, 2017
    Pleasant to be going to your online journal once more, it has been months for me. Well this article i've been sat tight for so long. I require this article to finish my task in the school, and it has same subject with your article. Much obliged, incredible offer.  best phone companies
  27. Robinjack Oct 12, 2017
    You have made an incredible showing on this article. It's exceptionally comprehensible and exceedingly wise. You have even figured out how to make it justifiable and simple to peruse. You have some genuine composition ability. Much obliged to you.  Cheap Steam Wallet Code
  28. Robinjack Oct 12, 2017
    A debt of gratitude is in order for the post and extraordinary tips..even I additionally surmise that diligent work is the most vital part of getting achievement..  Geoffrey Industry Locksmith
  29. skype login Oct 16, 2017
    The article you have shared here very good. This is really interesting information for me. Thanks for sharing! <a href="http://loginsk.com/">skype login</a>

    Leave a comment