Sitecore Context and Service classes and Saving Items

The SitecoreService class represents the Sitecore.Data.Database class and allows you to get items out of Sitecore.

The SitecoreContext class is the equivalent of accessing items via the Sitecore.Context namespace. It has a variety of methods for getting the current item plus the site root. It also inherits from the SitecoreService class so that you can request items from the database defined by Sitecore.Context.Database.

In this tutorial we will use both services to create a comments control that allows a site user to create a comment that is saved to a Sitecore database. First lets define two models, the first model represents the page that the comments will be added to and could represent any item in our solution. The second class is the comment itself that will be saved back to Sitecore:

using System.Collections.Generic;
using Sitecore.Data;

namespace Glass.Mapper.Sites.Sc.Models.Content
{
    public class CommentPage
    {
        public virtual ID Id { get; set; }
        public virtual IEnumerable<Comment> Children { get; set; }
    }
}


using System;
using Glass.Mapper.Sc.Configuration.Attributes;

namespace Glass.Mapper.Sites.Sc.Models.Content
{
    [SitecoreType(TemplateId = "{B6E1D6BA-EE54-4E71-8003-E6A1AF7119EB}", AutoMap = true)]
    public class Comment
    {
        public virtual string Name { get; set; }

        public string FullName { get; set; }
        public virtual string Email { get; set; }

        [SitecoreField("Comment")]
        public virtual string Content { get; set; }

        [SitecoreField("__Created")]
        public virtual DateTime Date { get; set; }
    }
}

Notice that on the Comment class we have  added the SitecoreType attribute and defined a TemplateId, this is essential because Glass.Mapper needs to understand which template to use when create the comment item. Your model must also have a Name property so that Glass.Mapper knows what to call your new item. The actual property that is used for the Name can be controlled via configuration.

Now that we have or model lets layout the page, I have used a mixture of WebControls and inline statements to create the forms and comments rendering:

<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="CommentSublayout.ascx.cs" 
    Inherits="Glass.Mapper.Sites.Sc.layouts.Site.Content.CommentSublayout" %>
<form>
    <fieldset>
        <asp:Label ID="Label1" runat="server" AssociatedControlID="CommentName">Name:</asp:Label>
        <asp:TextBox runat="server" ID="CommentName" CssClass="input-xlarge"></asp:TextBox>
        <asp:Label ID="Label2" runat="server" AssociatedControlID="CommentEmail">Email:</asp:Label>
        <asp:TextBox runat="server" ID="CommentEmail" CssClass="input-xlarge"></asp:TextBox>
        <asp:Label ID="Label3" runat="server" AssociatedControlID="CommentContent">Comment:</asp:Label>
        <asp:TextBox runat="server" ID="CommentContent" TextMode="MultiLine" CssClass="input-xlarge"></asp:TextBox>
        <div>
            <asp:Button runat="server" ID="CommentSubmit" Text="Submit" CssClass="btn" />
        </div>
    </fieldset>
</form>
<div class="alert alert-success" runat="server" id="CommentThankYou" Visible="false">
    <strong>Thank You for commenting!</strong> Please wait while we approve you comment.
</div>

<% if (Model.Children.Any())
   { %>
<h2>Comments</h2>
<ul>
    <% foreach (var comment in Model.Children)
       { %>
    <li>
        <small><%=comment.Date.ToString("dd MMM yy") %></small> <strong><%=comment.FullName %></strong> 
        <p>
            <%=comment.Content %>
        </p>
    </li>
    <% } %>
</ul>
<% } %>

The code behind for our comment sublayout looks like this:

using System;
using Glass.Mapper.Sc;
using Glass.Mapper.Sc.Web.Ui;
using Glass.Mapper.Sites.Sc.Models.Content;
using Sitecore.SecurityModel;

namespace Glass.Mapper.Sites.Sc.layouts.Site.Content
{
    public partial class CommentSublayout : GlassUserControl<CommentPage>
    {
        protected override void OnInit(EventArgs e)
        {
            CommentSubmit.Click += CommentSubmit_Click;
            base.OnInit(e);
        }

        void CommentSubmit_Click(object sender, EventArgs e)
        {
            var contextService = new SitecoreContext();
            var masterService = new SitecoreService("master");

            var page = contextService.GetCurrentItem<CommentPage>();
            //we could also use the model property instead of the line above:
            // var page = this.Model;

            var comment = new Comment();

            //This value will be used for the name of the item
            comment.Name = DateTime.Now.ToString("yy-MM-ddThh-mm-ss");
            comment.Content = CommentContent.Text;
            comment.FullName = CommentName.Text;
            comment.Email = CommentEmail.Text;

            using (new SecurityDisabler())
            {
                masterService.Create(page, comment);
            }

            CommentThankYou.Visible = true;
            comment.Content = string.Empty;
            comment.FullName = string.Empty;
            comment.Email = string.Empty;
        }
    }
}

The UserControl still inherits from the GlassUserControl so we still specify the model type we want loaded, this allows the UserControl to do most of the work.

Within the click event we create an instance of SitecoreContext and SitecoreService. Notice that the SitecoreService class takes the name of the database we want to save items to. The SitecoreContext class allows use to grab the current page as a CommentPage type. We could have used the Model property on the GlassUserControl but I wanted to show how you could use the SitecoreContext class to achieve the same thing.

We then map the data from the form onto the Comment object. It is important to note that the Name property will be used by Glass.Mapper as the Sitecore item name so we need to ensure that name is something Sitecore friendly.

Next we save the comment to the master database, Glass.Mapper doesn't bypass the security settings in Sitecore so you either have to disable the security or ensure you have the correct Create permissions assigned. It is important that the parent item type (the CommentPage type) has a property that represents the items Id, if you want to save the item as a specific language you also need to ensure that the parent has a property to represent the language.