using System; using System.Collections.Generic; using System.Data; using System.Globalization; using System.Xml; using umbraco.cms.businesslogic.cache; using umbraco.DataLayer; using umbraco.BusinessLogic; using System.Linq; using System.Runtime.CompilerServices; [assembly: InternalsVisibleTo("Umbraco.Test")] namespace umbraco.cms.businesslogic.language { /// /// The language class contains methods for creating and modifing installed languages. /// /// A language is used internal in the umbraco console for displaying languagespecific text and /// in the public website for language/country specific representation of ex. date/time, currencies. /// /// Besides by using the built in Dictionary you are able to store language specific bits and pieces of translated text /// for use in templates. /// public class Language { #region Private members private int _id; private string _name = ""; private string _friendlyName; private string _cultureAlias; #endregion #region Constants and static members private static object getLanguageSyncLock = new object(); private static readonly string UmbracoLanguageCacheKey = "UmbracoPropertyTypeCache"; private const int DefaultLanguageId = 1; /// /// Gets the SQL helper. /// /// The SQL helper. protected static ISqlHelper SqlHelper { get { return Application.SqlHelper; } } protected internal const string m_SQLOptimizedGetAll = @"select * from umbracoLanguage"; private static readonly object m_Locker = new object(); #endregion #region Constructors /// /// Initializes a new instance of the class. /// /// The id. public Language(int id) { var lang = GetAllAsList() .Where(x => x.id == id) .SingleOrDefault(); if (lang == null) { throw new ArgumentException("No language found with the specified id"); } _id = lang.id; _cultureAlias = lang.CultureAlias; _friendlyName = lang.FriendlyName; } /// /// Empty constructor used to create a language object manually /// internal Language() { } #endregion #region Static methods /// /// Creates a new language given the culture code - ie. da-dk (denmark) /// /// Culturecode of the language public static void MakeNew(string CultureCode) { if (new CultureInfo(CultureCode) != null) { SqlHelper.ExecuteNonQuery( "insert into umbracoLanguage (languageISOCode) values (@CultureCode)", SqlHelper.CreateParameter("@CultureCode", CultureCode)); InvalidateCache(); NewEventArgs e = new NewEventArgs(); GetByCultureCode(CultureCode).OnNew(e); } } private static void InvalidateCache() { Cache.ClearCacheItem(UmbracoLanguageCacheKey); } /// /// Method for accessing all installed languagess /// [Obsolete("Use the GetAllAsList() method instead")] public static Language[] getAll { get { return GetAllAsList().ToArray(); } } /// /// Returns all installed languages /// /// /// /// This will return a cached set of all languages. if the cache is not found it will create it. /// public static IEnumerable GetAllAsList() { return Cache.GetCacheItem>(UmbracoLanguageCacheKey, getLanguageSyncLock, TimeSpan.FromMinutes(60), delegate { var languages = new List(); using (IRecordsReader dr = SqlHelper.ExecuteReader(m_SQLOptimizedGetAll)) { while (dr.Read()) { //create the ContentType object without setting up Language ct = new Language(); ct.PopulateFromReader(dr); languages.Add(ct); } } return languages; }); } /// /// Gets the language by its culture code, if no language is found, null is returned /// /// The culture code. /// public static Language GetByCultureCode(String cultureCode) { if (new CultureInfo(cultureCode) != null) { return GetAllAsList() .Where(x => x.CultureAlias == cultureCode) .SingleOrDefault(); } return null; } /// /// Imports a language from XML /// /// The XML data. /// public static Language Import(XmlNode xmlData) { string cA = xmlData.Attributes["CultureAlias"].Value; if (Language.GetByCultureCode(cA) == null) { Language.MakeNew(cA); return Language.GetByCultureCode(cA); } else { return null; } } #endregion #region Public Properties /// /// The id used by umbraco to identify the language /// public int id { get { return _id; } } /// /// The culture code of the language: ie. Danish/Denmark da-dk /// public string CultureAlias { get { return _cultureAlias; } set { _cultureAlias = value; SqlHelper.ExecuteNonQuery( "update umbracoLanguage set languageISOCode = @cultureAlias where id = @id", SqlHelper.CreateParameter("@id", id), SqlHelper.CreateParameter("@cultureAlias", _cultureAlias)); updateNames(); } } /// /// The user friendly name of the language/country /// public string FriendlyName { get { return _friendlyName; } } #endregion #region Public methods /// /// Ensures uniqueness by id /// /// /// public override bool Equals(object obj) { var l = obj as Language; if (l != null) { return id.Equals(l.id); } return false; } /// /// Ensures uniqueness by id /// /// public override int GetHashCode() { return id.GetHashCode(); } /// /// Used to persist object changes to the database. In Version3.0 it's just a stub for future compatibility /// public virtual void Save() { SaveEventArgs e = new SaveEventArgs(); FireBeforeSave(e); if (!e.Cancel) { InvalidateCache(); FireAfterSave(e); } } /// /// Deletes the current Language. /// /// Notice: this can have various sideeffects - use with care. /// /// /// You cannot delete the default language: en-US, this is installed by default and is required. /// public void Delete() { if (this.id == DefaultLanguageId) { throw new InvalidOperationException("You cannot delete the default language: en-US"); } if (SqlHelper.ExecuteScalar("SELECT count(id) FROM umbracoDomains where domainDefaultLanguage = @id", SqlHelper.CreateParameter("@id", id)) == 0) { DeleteEventArgs e = new DeleteEventArgs(); FireBeforeDelete(e); if (!e.Cancel) { //remove the dictionary entries first Item.RemoveByLanguage(id); InvalidateCache(); SqlHelper.ExecuteNonQuery("delete from umbracoLanguage where id = @id", SqlHelper.CreateParameter("@id", id)); FireAfterDelete(e); } } else { Log.Add(LogTypes.Error, umbraco.BasePages.UmbracoEnsuredPage.CurrentUser, -1, "Could not remove Language " + _friendlyName + " because it's attached to a node"); throw new DataException("Cannot remove language " + _friendlyName + " because it's attached to a domain on a node"); } } /// /// Converts the instance to XML /// /// The xml document. /// public System.Xml.XmlNode ToXml(XmlDocument xd) { XmlNode language = xd.CreateElement("Language"); language.Attributes.Append(xmlHelper.addAttribute(xd, "Id", this.id.ToString())); language.Attributes.Append(xmlHelper.addAttribute(xd, "CultureAlias", this.CultureAlias)); language.Attributes.Append(xmlHelper.addAttribute(xd, "FriendlyName", this.FriendlyName)); return language; } #endregion #region Protected methods protected void PopulateFromReader(IRecordsReader dr) { _id = Convert.ToInt32(dr.GetShort("id")); _cultureAlias = dr.GetString("languageISOCode"); updateNames(); } #endregion #region Private methods private void updateNames() { try { CultureInfo ci = new CultureInfo(_cultureAlias); _friendlyName = ci.DisplayName; } catch { _friendlyName = _name + "(unknown Culture)"; } } #endregion #region Events /// /// The save event handler /// public delegate void SaveEventHandler(Language sender, SaveEventArgs e); /// /// The new event handler /// public delegate void NewEventHandler(Language sender, NewEventArgs e); /// /// The delete event handler /// public delegate void DeleteEventHandler(Language sender, DeleteEventArgs e); /// /// Occurs when a language is saved. /// public static event SaveEventHandler BeforeSave; protected virtual void FireBeforeSave(SaveEventArgs e) { if (BeforeSave != null) BeforeSave(this, e); } public static event SaveEventHandler AfterSave; protected virtual void FireAfterSave(SaveEventArgs e) { if (AfterSave != null) AfterSave(this, e); } public static event NewEventHandler New; protected virtual void OnNew(NewEventArgs e) { if (New != null) New(this, e); } public static event DeleteEventHandler BeforeDelete; protected virtual void FireBeforeDelete(DeleteEventArgs e) { if (BeforeDelete != null) BeforeDelete(this, e); } public static event DeleteEventHandler AfterDelete; protected virtual void FireAfterDelete(DeleteEventArgs e) { if (AfterDelete != null) AfterDelete(this, e); } #endregion } }