1-888-365-2779
+1-888-365-2779
Try Now
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.

23 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 Jul 12, 2017
    Excellent information on your blog, thank you for taking the time to share with us. Amazing insight you have on this, it's nice to find a website that details so much information about different artists. ielts reading tips
  5. Robinjack Jul 12, 2017
    Thank you for taking the time to publish this information very useful!  ielts general reading
  6. chelsieanayas Jul 17, 2017
    Just a smile and the rain is gone Can hardly believe it, yeah. There's an angel standing next to me. Reaching for my heart Just a smile and there's no way back .Can hardly believe it, yeah But there's an angel calling me. Reaching for my heart I know that I'll be okay now. This time, it's real I lay my love on you It's all I wanna do Every time I breathe I feel brand new You open up my heart Show me all your love and walk right through As I lay my love on you. dumb ways to die fireboy and watergirl 4
  7. Robinjack Jul 18, 2017
    This is a smart blog. I mean it. You have so much knowledge about this issue, and so much passion. You also know how to make people rally behind it, obviously from the responses. 20 Questions
  8. Robinjack Jul 23, 2017
    A debt of gratitude is in order for each other instructive site. The spot else might just I understand that sort of data written in such a perfect means? I have an endeavor that I'm a few seconds ago working on, and I have been watchful for such data.  13 Questions
  9. Robinjack Jul 24, 2017
    Im no expert, but I believe you just made an excellent point. You certainly fully understand what youre speaking about, and I can truly get behind that.  voyance par
  10. Carl Jul 25, 2017
    Really great post. I just unearthed your online journal and needed to say that I have truly delighted in perusing your blog entries. Any way I'll be subscribing to your food and I trust you post again soon. Huge a debt of gratitude is in order for the valuable information.  Relationship
  11. Robinjack Jul 25, 2017
    If you don't mind proceed with this extraordinary work and I anticipate a greater amount of your marvelous blog entries.  Sex
  12. Robinjack Jul 28, 2017
    Much obliged for the site stacked with such a variety of data. Ceasing by your web journal assisted me with getting what I was searching for.  Spinach
  13. SEO Jul 28, 2017
    I can see that you are an expert at your field! I am launching a website soon, and your information will be very useful for me.. Thanks for all your help and wishing you all the success in your business. kush clothing
  14. SEO Jul 28, 2017
    I love the way you write and share your niche! Very interesting and different! Keep it coming! kush clothing
  15. Robinjack Aug 09, 2017
    Your blog provided us with valuable information to work with. Each &  every tips of your post are awesome. Thanks a lot for sharing. Keep blogging, 100 Questions
  16. Robinjack Aug 10, 2017
    I am continually amazed by the amount of information available on this subject.  What you presented was well researched and well worded in order to get your stand on this across to all your readers. 20 Questions
  17. Carl Aug 11, 2017
    Your site is truly cool and this is an incredible rousing article. Much thanks to you to such an extent.  BonsaiTreeGardener
  18. SEO Aug 15, 2017
    New site is solid. A debt of gratitude is in order for the colossal exertion.  voyance en ligne
  19. Robinjack Aug 16, 2017
    This is a shrewd online journal. I would not joke about this. You have such a great amount of information about this issue, thus much energy. You likewise know how to make individuals rally behind it, clearly from the reactions.  How Do I Get Him Back
  20. seo Aug 16, 2017
    A debt of gratitude is in order for the blog entry amigo! Keep them coming...  voyance pas cher
  21. Robinjack Aug 17, 2017
    Truly an extraordinary expansion. I have perused this brilliant post. A debt of gratitude is in order for sharing data about it. I truly like that. Much obliged so parcel for your assemble.  How to ride a guy
  22. Robinjack Aug 21, 2017
    A debt of gratitude is in order for your data, it was truly exceptionally helpfull..  Clickhowto
  23. Robinjack Aug 22, 2017
    This sort of message continually moving and I like to peruse quality substance, so upbeat to discover great spot to numerous here in the post, the written work is simply extraordinary, much obliged for the post.  Questions to ask

    Leave a comment