Thursday, 8 October 2015

Sitecore SiteMap Part 3






In this post we will talk about the code in details according Advanced Sitecore SiteMap module, I will explain the main areas and at the end I will include links to Github repository contains the latest code.



The main code areas contains the following:
  • Generate SiteMap main function.
  • Send SiteMap to search engine.
  • Add SiteMap file name to Robots.txt
  • Sitecore command and SiteMap manual Generation.
  • HTML SiteMap builder function.


A. Generate SiteMap main function

As you can see from the following the code will loop through the sites already defined under the related configuration item and for each site the code will get the start item for each site, then the code will retrieve the available defined languages under sitecore and loop through them and for each decedents item if it is allowed to be included in sitemap by validating the field "Show In Xml SiteMap " the code will read the SiteMap fields section and create the related xml tags; Finally the xml file will be saved into the root folder with the name exists under related configuration item.

private void GenerateSiteMap()
{
try
{
SiteMapConfig siteMapConfig = new SiteMapConfig();
if (siteMapConfig.definedSites == null || !siteMapConfig.definedSites.Any())
return;
if (siteMapConfig.targetDatabaseName == string.Empty)
return;
foreach (var site in siteMapConfig.definedSites)
{
if (site.Fields[SiteItemFields.SiteName] == null || string.IsNullOrEmpty(site.Fields[SiteItemFields.SiteName].Value))
continue;
Sitecore.Sites.SiteContext _site = Factory.GetSite(site.Fields[SiteItemFields.SiteName].Value);
if (_site == null)
continue;
Item _root = GetTargetDatabase().GetItem(_site.StartPath);
if (_root == null)
continue;
StringBuilder sbSiteMap = new StringBuilder();
sbSiteMap.Append("<urlset xmlns='http://www.sitemaps.org/schemas/sitemap/0.9' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xsi:schemaLocation='http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd'>");
List<Item> siteMapItems = _root.Axes.GetDescendants().Where(P => P.Fields["Show In XML SiteMap"].Value == "1").ToList();
if (siteMapItems != null && siteMapItems.Any())
{
if (_root.Fields["Show In XML SiteMap"].Value == "1")
siteMapItems.Add(_root);
var options = global::Sitecore.Links.LinkManager.GetDefaultUrlOptions();
options.AlwaysIncludeServerUrl = true;
options.LanguageEmbedding = LanguageEmbedding.Always;
options.Site = SiteContext.GetSite(site.Name);
options.SiteResolving = true;
// get langauges
List<Language> _availbleLangauges = null;
if (siteMapConfig.multilingualSiteMapXML)
{
Item _languagesRoot = GetTargetDatabase().GetItem(Guids.Items.LangaugesRootItem);
_availbleLangauges = _languagesRoot.Axes.GetDescendants().Where(P => P.Fields["Iso"].Value != string.Empty).Select(P => Language.Parse(P.Fields["Iso"].Value)).ToList();
}
else
{
_availbleLangauges = new List<Language>() { Language.Parse("en") };
}
XmlDocument doc = new XmlDocument();
foreach (var langauge in _availbleLangauges)
{
options.Language = langauge;
options.EmbedLanguage(langauge);
foreach (var item in siteMapItems)
{
string url = LinkManager.GetItemUrl(item, options);
string hostName = _site.HostName;
url = hostName + "//" + url;
if (!url.Contains("http://"))
{
if (site.Fields[SiteItemFields.ServerURL] != null &&
!string.IsNullOrEmpty(site.Fields[SiteItemFields.ServerURL].Value))
{
url = site.Fields[SiteItemFields.ServerURL].Value + "//" + url;
}
else
{
url = "http://" + url;
}
}
string lastUpdated = DateUtil.IsoDateToDateTime(item.Fields[Sitecore.FieldIDs.Updated].Value).ToString("yyyy-MM-ddTHH:mm:sszzz");
string FrequencyChange = "yearly";
if (item.Fields[SiteMapFields.FrequencyChange] != null)
{
if (!string.IsNullOrEmpty(item.Fields[SiteMapFields.FrequencyChange].Value))
{
Item _FrequencyChange = GetTargetDatabase().GetItem(item.Fields[SiteMapFields.FrequencyChange].Value);
if (_FrequencyChange != null)
FrequencyChange = _FrequencyChange.Name;
}
}
string Priority = "0.0";
if (item.Fields[SiteMapFields.Priority] != null)
{
if (!string.IsNullOrEmpty(item.Fields[SiteMapFields.Priority].Value))
{
Item _priority = GetTargetDatabase().GetItem(item.Fields[SiteMapFields.Priority].Value);
if (_priority != null && _priority.Fields["Value"] != null && !string.IsNullOrEmpty(_priority.Fields["Value"].Value))
Priority = _priority.Fields["Value"].Value;
}
}
sbSiteMap.Append("<url>");
sbSiteMap.Append("<loc>");
sbSiteMap.Append(url);
sbSiteMap.Append("</loc>");
sbSiteMap.Append("<lastmod>");
sbSiteMap.Append(lastUpdated);
sbSiteMap.Append("</lastmod>");
sbSiteMap.Append("<changefreq>");
sbSiteMap.Append(FrequencyChange);
sbSiteMap.Append("</changefreq>");
sbSiteMap.Append("<priority>");
sbSiteMap.Append(Priority);
sbSiteMap.Append("</priority>");
sbSiteMap.Append("</url>");
}
}
sbSiteMap.Append("</urlset>");
string fileName = "SiteMap.xml";
if (site.Fields[SiteItemFields.SitemMapXMLFilename] != null &&
!string.IsNullOrEmpty(site.Fields[SiteItemFields.SitemMapXMLFilename].Value))
fileName = site.Fields[SiteItemFields.SitemMapXMLFilename].Value;
doc.LoadXml(sbSiteMap.ToString());
string xmlFilePath = MainUtil.MapPath("/" + fileName);
doc.Save(xmlFilePath);
if (site.Fields[SiteItemFields.AddToRobotFile] != null)
{
Sitecore.Data.Fields.CheckboxField _AddToRobotFile = site.Fields[SiteItemFields.AddToRobotFile];
if (_AddToRobotFile != null)
{
if (_AddToRobotFile.Checked)
AddSitemapToRobots(fileName);
}
}
Sitecore.Web.UI.Sheer.SheerResponse.Alert("SiteMap has been generated successfully");
}
}
}
catch (Exception exception)
{
Log.Error(exception.Message, "SiteMapBuilder - GenerateSiteMap method");
}
}
view raw GenerateSiteMap hosted with ❤ by GitHub

B. Send SiteMap to Search Engine
If you enabled sending the generated  SiteMap xml file to search engines and you defined search engines under the related configuration item the following code will loop through the defined search engines and loop through defined sites and send the xml into these search engines.

private void SendSiteMapTOSearchEngines()
{
try
{
SiteMapConfig siteMapConfig = new SiteMapConfig();
if (siteMapConfig.enabledSearchEngines == null)
return;
if (!siteMapConfig.enabledSearchEngines.Any())
return;
if (siteMapConfig.definedSites == null)
return;
if (!siteMapConfig.definedSites.Any())
return;
foreach (var engine in siteMapConfig.enabledSearchEngines)
{
foreach (var site in siteMapConfig.definedSites)
{
string SearchEngineRequstUrl = engine.Fields[SiteMapSearchEngineFields.SearchEngineRequestUrl].Value;
string siteMapUrl = HttpUtility.HtmlEncode(site.Fields[SiteItemFields.ServerURL].Value + "/" + site.Fields[SiteItemFields.SitemMapXMLFilename].Value);
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(SearchEngineRequstUrl + siteMapUrl);
try
{
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
if (response.StatusCode != HttpStatusCode.OK)
{
throw new Exception("Submit SsiteMap error. Engine name is : " + engine.Name);
}
}
catch
{
throw new Exception("404 error. Engine name is : " + engine.Name);
}
}
}
}
catch (Exception exception)
{
Log.Error(exception.Message, "SiteMapBuilder - SendSiteMapTOSearchEngines");
}
}


C. Add SiteMap file name to Robots.txt
The following code simply read the Robots.txt file and add the SiteMap xml path into that file:

public void AddSitemapToRobots(string xmlFileName)
{
try
{
string _RobotsFilePath = MainUtil.MapPath("/" + "robots.txt");
StringBuilder sbRobots = new StringBuilder(string.Empty);
if (File.Exists(_RobotsFilePath))
{
StreamReader reader = new StreamReader(_RobotsFilePath);
sbRobots.Append(reader.ReadToEnd());
reader.Close();
}
StreamWriter writer = new StreamWriter(_RobotsFilePath, false);
string _stringToAppend = "Sitemap: " + xmlFileName;
if (!sbRobots.ToString().Contains(_stringToAppend))
{
sbRobots.AppendLine(_stringToAppend);
}
writer.Write(sbRobots.ToString());
writer.Close();
}
catch (Exception ex)
{
Log.Error(ex.Message, "SiteMapBuilder - AddSitemapToRobots method");
}
}

D. Sitecore command and SiteMap manual Generation.
In this section I explain how the manual generation of SiteMap will be done. and to implanted I did two things:
1.       Adding related configuration into sitecore commands.config
And to this I just create new configuration file called "Sitecore.AdvancedSiteMap.Commands.GenerateSiteMap.config" and the following is the content of this file:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <sitecore>
    <commands>
      <command name="contenteditor:GenerateSiteMap"            type="Sitecore.AdvancedSiteMap.ManualSiteMapGeneration, Sitecore.AdvancedSiteMap"/>
    </commands>
  </sitecore>
</configuration>



2.       Creating the command class "ManualSiteMapGeneration.cs"
It is simple class the inherits " Sitecore.Shell.Framework.Commands.command" and has the following code:

[Serializable]
public class ManualSiteMapGeneration : Command
{
public override void Execute(CommandContext context)
{
Sitecore.Context.ClientPage.Start(this, "Run", context.Parameters);
}
protected static void Run(ClientPipelineArgs args)
{
var SyunObj = new SiteMapBuilder();
SyunObj.BuildSiteMap();
}
}



E. HTML SiteMap builder function.
In the following code you will see how HTML SiteMap is being generated using Sitecore advanced SiteMap module

protected void Page_Load(object sender, EventArgs e)
{
AdvancedSiteMap.HTMLSiteMapBuilder hTMLSiteMapBuilder = new AdvancedSiteMap.HTMLSiteMapBuilder();
liSiteMap.Text = hTMLSiteMapBuilder.BuildSitemapHTML();
}
public class HTMLSiteMapBuilder
{
static SiteMapConfig siteMapConfig = new SiteMapConfig();
StringBuilder sbSiteMapHTML = new StringBuilder();
private Database GetTargetDatabase()
{
return Factory.GetDatabase(siteMapConfig.targetDatabaseName);
}
public string BuildSitemapHTML()
{
Item _currentItem = Sitecore.Context.Item;
if (_currentItem == null)
return string.Empty;
Item _root = _currentItem.Parent;
if (_root == null)
return string.Empty;
string displayText = string.Empty;
if (_root.Fields[SiteMapFields.HTMLSiteMapTitle] != null)
displayText = _root.Fields[SiteMapFields.HTMLSiteMapTitle].Value;
if (displayText == string.Empty && _root.Name != null)
displayText = _root.Name;
var options = global::Sitecore.Links.LinkManager.GetDefaultUrlOptions();
options.AlwaysIncludeServerUrl = true;
options.LanguageEmbedding = LanguageEmbedding.Always;
options.Language = Sitecore.Context.Language;
options.EmbedLanguage(Sitecore.Context.Language);
string itemURL = Sitecore.Links.LinkManager.GetItemUrl(_root, options);
bool ShowInHTMLSiteMap = false;
CheckboxField _ShowInHTMLSiteMap = _root.Fields[SiteMapFields.ShowInHTMLSiteMap];
if (_ShowInHTMLSiteMap != null)
ShowInHTMLSiteMap = _ShowInHTMLSiteMap.Checked;
if (ShowInHTMLSiteMap)
{
sbSiteMapHTML.Append("<ul>");
sbSiteMapHTML.Append("<li>");
sbSiteMapHTML.Append("<a href='" + itemURL + "'>");
sbSiteMapHTML.Append(displayText);
sbSiteMapHTML.Append("</a>");
sbSiteMapHTML.Append("</li>");
RecursiveChildBuilder(_root);
sbSiteMapHTML.Append("</ul>");
}
else
{
RecursiveChildBuilder(_root);
}
return sbSiteMapHTML.ToString();
}
private void RecursiveChildBuilder(Item Root)
{
if (Root == null)
return;
Item _currentLoopItem = Root;
if (_currentLoopItem == null)
return;
var options = global::Sitecore.Links.LinkManager.GetDefaultUrlOptions();
options.AlwaysIncludeServerUrl = true;
options.LanguageEmbedding = LanguageEmbedding.Always;
options.Language = Sitecore.Context.Language;
options.EmbedLanguage(Sitecore.Context.Language);
List<Item> children = _currentLoopItem.Children.Where(x => x.Name != "*").ToList();
if (children != null && children.Any())
{
sbSiteMapHTML.Append("<ul>");
foreach (var child in children)
{
string displayText = string.Empty;
if (child.Fields[SiteMapFields.HTMLSiteMapTitle] != null)
displayText = child.Fields[SiteMapFields.HTMLSiteMapTitle].Value;
if (displayText == string.Empty && child.Name != null)
displayText = child.Name;
string itemURL = Sitecore.Links.LinkManager.GetItemUrl(child, options);
bool ShowInHTMLSiteMap = false;
CheckboxField _ShowInHTMLSiteMap = child.Fields[SiteMapFields.ShowInHTMLSiteMap];
if (_ShowInHTMLSiteMap != null)
ShowInHTMLSiteMap = _ShowInHTMLSiteMap.Checked;
if (ShowInHTMLSiteMap)
{
sbSiteMapHTML.Append("<li>");
sbSiteMapHTML.Append("<a href='" + itemURL + "'>");
sbSiteMapHTML.Append(displayText);
sbSiteMapHTML.Append("</a>");
sbSiteMapHTML.Append("</li>");
}
if (child.HasChildren)
RecursiveChildBuilder(child);
}
sbSiteMapHTML.Append("</ul>");
}
}

At the end I believe I just provide  a detailed description of all Sitecore Advanced SiteMap module, Also you download the code on the following link:

Sitecore Advanced SiteMap Module 

No comments:

Post a Comment