Files
Umbraco-CMS/umbraco/cms/businesslogic/Dictionary.cs
2010-08-20 11:17:12 +00:00

513 lines
19 KiB
C#

using System;
using System.Collections;
using System.Data;
using System.Xml;
using System.Linq;
using umbraco.cms.businesslogic.language;
using umbraco.DataLayer;
using umbraco.BusinessLogic;
using System.Runtime.CompilerServices;
namespace umbraco.cms.businesslogic
{
/// <summary>
/// The Dictionary is used for storing and retrieving language translated textpieces in Umbraco. It uses
/// umbraco.cms.businesslogic.language.Item class as storage and can be used from the public website of umbraco
/// all text are cached in memory.
/// </summary>
public class Dictionary
{
private static volatile bool cacheIsEnsured = false;
private static readonly object m_Locker = new object();
private static Hashtable DictionaryItems = Hashtable.Synchronized(new Hashtable());
private static Guid topLevelParent = new Guid("41c7638d-f529-4bff-853e-59a0c2fb1bde");
protected static ISqlHelper SqlHelper
{
get { return Application.SqlHelper; }
}
/// <summary>
/// Reads all items from the database and stores in local cache
/// </summary>
private static void ensureCache()
{
if (!cacheIsEnsured)
{
lock (m_Locker)
{
if (!cacheIsEnsured)
{
using (IRecordsReader dr = SqlHelper.ExecuteReader("Select pk, id, [key], parent from cmsDictionary"))
{
while (dr.Read())
{
//create new dictionaryitem object and put in cache
var item = new DictionaryItem(dr.GetInt("pk"),
dr.GetString("key"),
dr.GetGuid("id"),
dr.GetGuid("parent"));
DictionaryItems.Add(item.key, item);
}
}
cacheIsEnsured = true;
}
}
}
}
/// <summary>
/// Retrieve a list of toplevel DictionaryItems
/// </summary>
public static DictionaryItem[] getTopMostItems
{
get
{
ensureCache();
return DictionaryItems.Values.Cast<DictionaryItem>()
.Where(x => x.ParentId == topLevelParent).OrderBy(item => item.key)
.ToArray();
}
}
/// <summary>
/// A DictionaryItem is basically a key/value pair (key/language key/value) which holds the data
/// associated to a key in various language translations
/// </summary>
public class DictionaryItem
{
private string _key;
internal Guid UniqueId { get; private set; }
internal Guid ParentId { get; private set; }
/// <summary>
/// Used internally to construct a new item object and store in cache
/// </summary>
/// <param name="id"></param>
/// <param name="key"></param>
/// <param name="uniqueKey"></param>
/// <param name="parentId"></param>
internal DictionaryItem(int id, string key, Guid uniqueKey, Guid parentId)
{
this.id = id;
this._key = key;
this.UniqueId = uniqueKey;
this.ParentId = parentId;
}
public DictionaryItem(string key)
{
ensureCache();
var item = DictionaryItems.Values.Cast<DictionaryItem>()
.Where(x => x.key == key)
.SingleOrDefault();
if (item == null)
{
throw new ArgumentException("No key " + key + " exists in dictionary");
}
this.id = item.id;
this._key = item.key;
this.ParentId = item.ParentId;
this.UniqueId = item.UniqueId;
}
public DictionaryItem(Guid id)
{
ensureCache();
var item = DictionaryItems.Values.Cast<DictionaryItem>()
.Where(x => x.UniqueId == id)
.SingleOrDefault();
if (item == null)
{
throw new ArgumentException("No unique id " + id.ToString() + " exists in dictionary");
}
this.id = item.id;
this._key = item.key;
this.ParentId = item.ParentId;
this.UniqueId = item.UniqueId;
}
public DictionaryItem(int id)
{
ensureCache();
var item = DictionaryItems.Values.Cast<DictionaryItem>()
.Where(x => x.id == id)
.SingleOrDefault();
if (item == null)
{
throw new ArgumentException("No id " + id + " exists in dictionary");
}
this.id = item.id;
this._key = item.key;
this.ParentId = item.ParentId;
this.UniqueId = item.UniqueId;
}
private DictionaryItem _parent;
/// <summary>
/// Returns if the dictionaryItem is the root item.
/// </summary>
public bool IsTopMostItem()
{
return DictionaryItems.Values.Cast<DictionaryItem>()
.Where(x => x.id == id)
.Select(x => x.ParentId)
.SingleOrDefault() == topLevelParent;
}
/// <summary>
/// Returns the parent.
/// </summary>
public DictionaryItem Parent
{
get
{
if (_parent == null)
{
var p = DictionaryItems.Values.Cast<DictionaryItem>()
.Where(x => x.UniqueId == this.ParentId)
.SingleOrDefault();
if (p == null)
{
throw new ArgumentException("Top most dictionary items doesn't have a parent");
}
else
{
_parent = p;
}
}
return _parent;
}
}
/// <summary>
/// The primary key in the database
/// </summary>
public int id
{
get;
private set;
}
public DictionaryItem[] Children
{
get
{
return DictionaryItems.Values.Cast<DictionaryItem>()
.Where(x => x.ParentId == this.UniqueId).OrderBy(item => item.key)
.ToArray();
}
}
public static bool hasKey(string key)
{
ensureCache();
return DictionaryItems.ContainsKey(key);
}
public bool hasChildren
{
get
{
return (SqlHelper.ExecuteScalar<int>("select count([key]) as tmp from cmsDictionary where parent=@uniqueId", SqlHelper.CreateParameter("@uniqueId", UniqueId)) > 0);
}
}
/// <summary>
/// Returns or sets the key.
/// </summary>
public string key
{
get { return _key; }
set
{
if (!hasKey(value))
{
lock (m_Locker)
{
SqlHelper.ExecuteNonQuery("Update cmsDictionary set [key] = @key WHERE pk = @Id", SqlHelper.CreateParameter("@key", value),
SqlHelper.CreateParameter("@Id", id));
//remove the cached item since the key is different
DictionaryItems.Remove(key);
using (IRecordsReader dr =
SqlHelper.ExecuteReader("Select pk, id, [key], parent from cmsDictionary where id=@id",
SqlHelper.CreateParameter("@id", this.UniqueId)))
{
if (dr.Read())
{
//create new dictionaryitem object and put in cache
var item = new DictionaryItem(dr.GetInt("pk"),
dr.GetString("key"),
dr.GetGuid("id"),
dr.GetGuid("parent"));
DictionaryItems.Add(item.key, item);
}
else
{
throw new DataException("Could not load updated created dictionary item with id " + id.ToString());
}
}
//finally update this objects value
this._key = value;
}
}
else
throw new ArgumentException("New value of key already exists (is key)");
}
}
public string Value(int languageId)
{
if (languageId == 0)
return Value();
if (Item.hasText(UniqueId, languageId))
return Item.Text(UniqueId, languageId);
return "";
}
public void setValue(int languageId, string value)
{
if (Item.hasText(UniqueId, languageId))
Item.setText(languageId, UniqueId, value);
else
Item.addText(languageId, UniqueId, value);
}
public string Value()
{
return Item.Text(UniqueId, 1);
}
/// <summary>
/// This sets the value for the placeholder language (id = 0), not for a language with an ID
/// </summary>
/// <param name="value"></param>
public void setValue(string value)
{
if (Item.hasText(UniqueId, 0))
Item.setText(0, UniqueId, value);
else
Item.addText(0, UniqueId, value);
}
public static int addKey(string key, string defaultValue, string parentKey)
{
ensureCache();
if (hasKey(parentKey))
{
int retval = createKey(key, new DictionaryItem(parentKey).UniqueId, defaultValue);
return retval;
}
else
throw new ArgumentException("Parentkey doesnt exist");
}
public static int addKey(string key, string defaultValue)
{
ensureCache();
int retval = createKey(key, topLevelParent, defaultValue);
return retval;
}
public void delete()
{
OnDeleting(EventArgs.Empty);
// delete recursive
foreach (DictionaryItem dd in Children)
dd.delete();
// remove all language values from key
Item.removeText(UniqueId);
// remove key from database
SqlHelper.ExecuteNonQuery("delete from cmsDictionary where [key] ='" + key + "'");
// Remove key from cache
DictionaryItems.Remove(key);
}
public void Save()
{
OnSaving(EventArgs.Empty);
}
public System.Xml.XmlNode ToXml(XmlDocument xd)
{
XmlNode dictionaryItem = xd.CreateElement("DictionaryItem");
dictionaryItem.Attributes.Append(xmlHelper.addAttribute(xd, "Key", this.key));
foreach (Language lang in Language.GetAllAsList())
{
XmlNode itemValue = xmlHelper.addCDataNode(xd, "Value", this.Value(lang.id));
itemValue.Attributes.Append(xmlHelper.addAttribute(xd, "LanguageId", lang.id.ToString()));
itemValue.Attributes.Append(xmlHelper.addAttribute(xd, "LanguageCultureAlias", lang.CultureAlias));
dictionaryItem.AppendChild(itemValue);
}
if (this.hasChildren)
{
foreach (DictionaryItem di in this.Children)
{
dictionaryItem.AppendChild(di.ToXml(xd));
}
}
return dictionaryItem;
}
public static DictionaryItem Import(XmlNode xmlData)
{
return Import(xmlData, null);
}
public static DictionaryItem Import(XmlNode xmlData, DictionaryItem parent)
{
string key = xmlData.Attributes["Key"].Value;
XmlNodeList values = xmlData.SelectNodes("./Value");
XmlNodeList childItems = xmlData.SelectNodes("./DictionaryItem");
DictionaryItem newItem;
bool retVal = false;
if (!DictionaryItem.hasKey(key))
{
if (parent != null)
DictionaryItem.addKey(key, " ", parent.key);
else
DictionaryItem.addKey(key, " ");
if (values.Count > 0)
{
//Set language values on the dictionary item
newItem = new DictionaryItem(key);
foreach (XmlNode xn in values)
{
string cA = xn.Attributes["LanguageCultureAlias"].Value;
string keyValue = xmlHelper.GetNodeValue(xn);
Language valueLang = Language.GetByCultureCode(cA);
if (valueLang != null)
{
newItem.setValue(valueLang.id, keyValue);
}
}
}
if (parent == null)
retVal = true;
}
newItem = new DictionaryItem(key);
foreach (XmlNode childItem in childItems)
{
Import(childItem, newItem);
}
if (retVal)
return newItem;
else
return null;
}
[MethodImpl(MethodImplOptions.Synchronized)]
private static int createKey(string key, Guid parentId, string defaultValue)
{
if (!hasKey(key))
{
Guid newId = Guid.NewGuid();
SqlHelper.ExecuteNonQuery("Insert into cmsDictionary (id,parent,[key]) values (@id, @parentId, @dictionaryKey)",
SqlHelper.CreateParameter("@id", newId),
SqlHelper.CreateParameter("@parentId", parentId),
SqlHelper.CreateParameter("@dictionaryKey", key));
using (IRecordsReader dr =
SqlHelper.ExecuteReader("Select pk, id, [key], parent from cmsDictionary where id=@id",
SqlHelper.CreateParameter("@id", newId)))
{
if (dr.Read())
{
//create new dictionaryitem object and put in cache
var item = new DictionaryItem(dr.GetInt("pk"),
dr.GetString("key"),
dr.GetGuid("id"),
dr.GetGuid("parent"));
DictionaryItems.Add(item.key, item);
item.setValue(defaultValue);
item.OnNew(EventArgs.Empty);
return item.id;
}
else
{
throw new DataException("Could not load newly created dictionary item with id " + newId.ToString());
}
}
}
else
{
throw new ArgumentException("Key being added already exists!");
}
}
#region Events
public delegate void SaveEventHandler(DictionaryItem sender, EventArgs e);
public delegate void NewEventHandler(DictionaryItem sender, EventArgs e);
public delegate void DeleteEventHandler(DictionaryItem sender, EventArgs e);
public static event SaveEventHandler Saving;
protected virtual void OnSaving(EventArgs e)
{
if (Saving != null)
Saving(this, e);
}
public static event NewEventHandler New;
protected virtual void OnNew(EventArgs e)
{
if (New != null)
New(this, e);
}
public static event DeleteEventHandler Deleting;
protected virtual void OnDeleting(EventArgs e)
{
if (Deleting != null)
Deleting(this, e);
}
#endregion
}
}
}