using System;
using System.Data;
using System.Data.SqlClient;
using System.Configuration;
using System.Collections;
using System.Xml;
using System.IO;
namespace Blend.Ektron.Context
{
///
/// Contains information about a folder's position in the Ektron folder tree. This the only class with which you'll interact. All other classes contained in this file are used internally by the EktronContext class.
///
public class EktronContext
{
///
/// The folder ID of the source folder -- the folder from which you want to retrieve context information
///
public int FolderId;
///
/// An instance of the utility EktronDatabase class. This class just provides a simple entrance point to the Ektron database.
///
private EktronDatabase Database;
///
/// An XML writer used to create the XML document
///
private XmlWriter Xml;
///
/// This is the name of the settings folders. It can be changed to whatever you like.
///
public string SettingsFolderName = "_settings";
///
/// Contains an internal list of EktronFolders from the source folder back to the root folder
///
public ArrayList SettingsFolders = new ArrayList();
///
/// The XML document which will contain the context information
///
public XmlDocument XmlDoc = new XmlDocument();
///
/// Constructor. Passing a folder ID into the constructor retieves all the context information for that folder.
///
///
public EktronContext(int FolderId)
{
//Store the passed in FolderId
this.FolderId = FolderId;
//Create an instance of the Ektron database. Use the connection string from the Web.config
this.Database = new EktronDatabase(ConfigurationManager.ConnectionStrings["Ektron.DbConnection"].ToString());
//Intialize the list of folders. This will contain the EktronFolder objects from our source folder back to root.
this.SettingsFolders = new ArrayList();
//Add this folder
EktronFolder ThisFolder = new EktronFolder(FolderId, SettingsFolderName, Database);
SettingsFolders.Add(ThisFolder);
//Keep adding folders until you hit root
while (ThisFolder.IsRootFolder() == false)
{
ThisFolder = new EktronFolder(ThisFolder.ParentId, SettingsFolderName, Database);
SettingsFolders.Add(ThisFolder);
}
//Now that we have our list of folders, create the XML documenbt
this.FillXml();
}
///
/// Creates the XML context information from the list of EktronFolder objects
///
private void FillXml()
{
//Initialize the Xml
StringWriter XmlString = new StringWriter();
this.Xml = new XmlTextWriter(XmlString);
//Write the root element
this.Xml.WriteStartElement("EktronContext");
//Spin through the list of EktronFolder objects
foreach (EktronFolder ThisFolder in this.SettingsFolders)
{
//Start the element and write all the folder meta
this.Xml.WriteStartElement("Folder");
this.Xml.WriteAttributeString("Id", ThisFolder.Id.ToString());
this.Xml.WriteAttributeString("ObjectCount", ThisFolder.SettingsObjects.Count.ToString());
this.Xml.WriteStartElement("Name");
this.Xml.WriteString(ThisFolder.Name.ToString());
this.Xml.WriteEndElement();
this.Xml.WriteStartElement("Description");
this.Xml.WriteString(ThisFolder.Description.ToString());
this.Xml.WriteEndElement();
//Sping through the settings objects in this folder
foreach (EktronSettingsObject ThisObject in ThisFolder.SettingsObjects)
{
//Write the Object tag
this.Xml.WriteStartElement("Object");
this.Xml.WriteAttributeString("Id", ThisObject.Id.ToString());
this.Xml.WriteAttributeString("SmartFormId", ThisObject.SmartFormId.ToString());
//Have the object write its own XML
this.Xml.WriteRaw(ThisObject.Content);
this.Xml.WriteEndElement();
}
this.Xml.WriteEndElement();
}
this.Xml.WriteEndElement();
//Close out the XML and load it into the XmlDocument
this.Xml.Close();
this.XmlDoc.LoadXml(XmlString.ToString());
}
///
/// A convenience method to get the raw XML as a string
///
///
public string GetXml()
{
return this.XmlDoc.OuterXml;
}
///
/// This is the workhorse of the entire system. This will return the requested information nearest the source folder.
///
/// An abbreviatwed XPath string. This string will have "/EktronContext/Folder/Object/" prepending to it, before being used to search the XML of the object
/// A string containing the nearest information it finds. If it finds nothing, it returns an empty string.
public string GetNearest(string XPath)
{
//Prepend the standard XPath
string FullXPath = "/EktronContext/Folder/Object/" + XPath;
//Get all nodes that match
XmlNodeList MatchingNodes = this.XmlDoc.SelectNodes(FullXPath);
//Spin through those, and return the first one that has a value
//TODO: There has to be a more intelligent way of doing this...
string ReturnValue = "";
if (MatchingNodes.Count > 0)
{
foreach (XmlNode MatchingNode in MatchingNodes)
{
if (MatchingNode.InnerXml.Length > 0)
{
ReturnValue = MatchingNode.InnerXml;
break;
}
}
}
return ReturnValue;
}
///
/// Does the same thing as GetNearest, except it returns the entire XML node, rather than its contents as a string
///
/// An abbreviatwed XPath string. This string will have "/EktronContext/Folder/Object/" prepending to it, before being used to search the XML of the object
/// An XML node containing the nearest information it finds. If it finds nothing, it returns an empty node.
public XmlNode GetNearestNode(string XPath)
{
XmlNodeList ThisNodeList = this.XmlDoc.SelectNodes("/EktronContext/Folder/Object/" + XPath);
if (ThisNodeList.Count > 0)
{
return ThisNodeList[0];
}
else
{
return this.XmlDoc.CreateNode(XmlNodeType.Element, "Empty", "");
}
}
///
/// Wrapper for DistanceFromFolderByXPath
///
/// The folder ID of the target folder
/// The number of "hops" fro the target folder
public int DistanceFromFolder(int FolderId)
{
string XPath = "//Folder[@Id='" + FolderId.ToString() + "']";
return this.DistanceFromFolderByXPath(XPath);
}
///
/// Wrapper for DistanceFromFolderByXPath
///
/// The folder name of the target folder
/// The number of "hops" fro the target folder
public int DistanceFromFolder(string FolderName)
{
string XPath = "//Folder[Name='" + FolderName + "']";
return this.DistanceFromFolderByXPath(XPath);
}
///
/// This is the internal method that the two "DistanceFromFolder" methods use
///
/// The XPath of the target folder
/// An integer representing the nunber of "folder hops" from source to target. -1 means the source folder is not under the target folder. 0 means that the source folder *is* the target folder. 1 means the source folder ia a direct subfolder of the target folder. And so on.
private int DistanceFromFolderByXPath(string XPath)
{
//Does this folder exist at all?
if (this.XmlDoc.SelectSingleNode(XPath) == null)
{
return -1;
}
else
{
//Find it, and return all the folder nodes preceding it
XmlNodeList PrecedingNodes = this.XmlDoc.SelectNodes(XPath + "/preceding-sibling::*");
return PrecedingNodes.Count;
}
}
///
/// Used to determine if the source folder is anywhere under the target folder.
///
/// The ID of the target folder.
/// True if the source folder is under the target folder, false otherwise. (Note that this method returns false if the source folder *is* the target folder.)
public bool IsUnderFolder(int FolderId)
{
//If the distance from folder is 0 or -1, then we're not under it
if (this.DistanceFromFolder(FolderId) < 1)
{
return false;
}
else
{
return true;
}
}
///
/// Used to determine if the source folder is anywhere under the target folder.
///
/// The Iname of the target folder.
/// True if the source folder is under the target folder, false otherwise. (Note that this method returns false if the source folder *is* the target folder.)
public bool IsUnderFolder(string FolderName)
{
//If the distance from folder is 0 or -1, then we're not under it
if (this.DistanceFromFolder(FolderName) < 1)
{
return false;
}
else
{
return true;
}
}
}
///
/// Represents a single Ektron folder.
///
public class EktronFolder
{
///
/// The ID of the folder
///
public int Id;
///
/// The name of the folder
///
public string Name;
///
/// The description of the folder
///
public string Description;
///
/// The ID of the folder's parent
///
public int ParentId;
///
/// A list of objects inside this folder's settings folder
///
public ArrayList SettingsObjects = new ArrayList();
///
/// Constuctor. Pass in the folder's ID to create an object representing this folder.
///
/// The ID of the desired folder
/// The name of the settings folder (this is set in the EktronContext class.
/// An EktronDatabase object
public EktronFolder(int FolderId, string SettingsFolderName, EktronDatabase Database)
{
//Get data on this folder
SqlDataReader ThisFolder = Database.GetDataReader("SELECT * FROM content_folder_tbl WHERE folder_id = " + FolderId.ToString());
ThisFolder.Read();
//Set the available properties
this.Id = FolderId;
this.Name = ThisFolder["folder_name"].ToString();
this.Description = ThisFolder["folder_description"].ToString();
this.ParentId = int.Parse(ThisFolder["parent_id"].ToString());
ThisFolder.Close();
//Look for a settings folder in this folder
SqlDataReader SettingsFolder = Database.GetDataReader("SELECT * FROM content_folder_tbl f INNER JOIN content c ON f.folder_id = c.folder_id WHERE f.folder_name = '" + SettingsFolderName + "' AND f.parent_id = " + FolderId.ToString());
//Do we have one?
if (SettingsFolder.HasRows)
{
//Yes...
while (SettingsFolder.Read())
{
//Create an EktronSettingsObject from the data and store in SettingsObjects
EktronSettingsObject ThisObject = new EktronSettingsObject(SettingsFolder);
this.SettingsObjects.Add(ThisObject);
}
}
SettingsFolder.Close();
}
///
/// A handy was of determining whether or not this is Ektron's root folder. Used to deteremine when to stop recursing up the tree.
///
/// True if this is the root folder, false otherwise.
public bool IsRootFolder()
{
if (this.Id == 0)
{
return true;
}
else
{
return false;
}
}
}
///
/// Represents a single settings object -- a single content object contained in a settings folder.
///
public class EktronSettingsObject
{
///
/// The Ektron ID of this content object
///
public int Id;
///
/// The name of this content object
/// Note: I don't think this is used anywhere...
///
public string Name;
///
/// An abbreviation of the XML contained in this content object. It consists of the InnerXML of the EktronXML (so, everything inside the "root" element)
///
public string Content;
///
/// The ID of the SmartForm on which the content is based
///
public string SmartFormId;
///
/// Constructor. Takes in a SqlDataReader, set to the first (and only) row, and uses columns in it to populate the properties
///
///
public EktronSettingsObject(SqlDataReader Data)
{
this.Id = int.Parse(Data["content_id"].ToString());
this.Name = Data["content_title"].ToString();
this.SmartFormId = Data["xml_config_id"].ToString();
//Note: I don't know why I don't use the InnerXml of the DocumentElement here...but there must be a reason so I'm leaving it for now (I'm documenting this some time after I wrote it...)
Content = Data["content_html"].ToString().Replace("", "").Replace("", "");
}
}
///
/// A simple interface to the Ektron database
///
public class EktronDatabase
{
///
/// The database connection object
///
SqlConnection Connection;
///
/// Constructor. Opens a database connection
///
/// The database connection string
public EktronDatabase(string ConnectionString)
{
//Open a database connection...
SqlConnection ThisConnection = new SqlConnection(ConnectionString);
ThisConnection.Open();
//...and set it as an object property.
this.Connection = ThisConnection;
}
///
/// Returns a SqlDataReader from specified SQL
///
/// A SQL string
/// The results of the SQL string run against the database
public SqlDataReader GetDataReader(string Sql)
{
SqlCommand Command = new SqlCommand(Sql, this.Connection);
return Command.ExecuteReader();
}
}
}