using System;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Collections;
using CMS.CMSHelper;
using CMS.FormControls;
using CMS.FormEngine;
using CMS.GlobalHelper;
using CMS.ResourceManager;
using CMS.SettingsProvider;
using CMS.SiteProvider;
[ValidationPropertyAttribute("Value")]
public partial class CMSFormControls_System_LocalizableTextBox : FormEngineUserControl, ICallbackEventHandler
{
#region "Macros and variables"
///
/// Localization macro starts with '{$' characters.
///
public const string MACRO_START = "{$";
///
/// Localization macro starts with '$}' characters.
///
public const string MACRO_END = "$}";
///
/// In-place localization macro starts with '{$=' characters and should not be localized in localizable textbox!
///
protected const string INPLACE_MACRO_START = "{$=";
///
/// URL of field localization modal dialog.
///
public const string LOCALIZE_FIELD = "~/CMSFormControls/Selectors/LocalizableTextBox/LocalizeField.aspx";
///
/// URL of string localization modal dialog.
///
public const string LOCALIZE_STRING = "~/CMSFormControls/Selectors/LocalizableTextBox/LocalizeString.aspx";
///
/// Default prefix for keys created in development mode.
///
private const string PREFIX = "test.";
///
/// Maximum resource string key length.
///
private const int MAX_KEY_LENGTH = 200;
///
/// Default button style.
///
private const string BUTTON_STYLE = "background-image: url('{0}');";
// Indicates if changes to resource string should be performed immediately after each PostBack.
private bool mAutoSave = true;
// Value returned by callback
private string callbackReturnVal = null;
// Resource key prefix
private string mResourceKeyPrefix = String.Empty;
private bool prefixIsSet = false;
private bool? mLocalizationExists = null;
private bool mSaveAutomatically = false;
#endregion
#region "Properties"
///
/// Gets or sets whether the control is read only
///
public bool ReadOnly
{
get
{
return textbox.ReadOnly;
}
set
{
textbox.ReadOnly = value;
}
}
///
/// Gets or sets the enabled state of the control.
///
public override bool Enabled
{
get
{
return base.Enabled;
}
set
{
base.Enabled = value;
if (textbox != null)
{
textbox.Enabled = value;
btnLocalize.Enabled = value;
btnOtherLanguages.Enabled = value;
btnRemoveLocalization.Enabled = value;
}
}
}
///
/// Contains unresolved resource string.
///
private string OriginalValue
{
get
{
if (!string.IsNullOrEmpty(hdnValue.Value))
{
return hdnValue.Value;
}
return ValidationHelper.GetString(ViewState["OriginalValue"], string.Empty);
}
set
{
hdnValue.Value = value;
ViewState["OriginalValue"] = value;
}
}
///
/// Gets or sets form control value.
///
public override object Value
{
get
{
// Return macro contained in hidden field if text is macro
if (IsLocalizationMacro)
{
if (LocalizationExists)
{
return OriginalValue;
}
else
{
return MACRO_START + textbox.Text + MACRO_END;
}
}
// Return plain text contained in textbox
else
{
return textbox.Text;
}
}
set
{
if (mSaveAutomatically)
{
// Save if configured to save automatically
Save();
mSaveAutomatically = false;
}
string valueStr = OriginalValue = ValidationHelper.GetString(value, null);
// Check if value is localization macro
if (!String.IsNullOrEmpty(valueStr) && !valueStr.StartsWithCSafe(INPLACE_MACRO_START) && valueStr.StartsWithCSafe(MACRO_START) && valueStr.EndsWithCSafe(MACRO_END))
{
if (!RequestHelper.IsPostBack())
{
textbox.Text = ResHelper.LocalizeString(valueStr);
}
}
// Value is plain text
else
{
textbox.Text = valueStr;
if (!string.IsNullOrEmpty(valueStr) && pnlButtons.Visible)
{
// Hide localization buttons if in-place macro is edited
pnlButtons.Visible = !valueStr.StartsWithCSafe(INPLACE_MACRO_START);
}
}
}
}
///
/// Returns client ID of the textbox.
///
public override string ValueElementID
{
get
{
return textbox.ClientID;
}
}
///
/// Gets value indicating if localization of key exists.
///
private bool LocalizationExists
{
get
{
// Determine if translation exists
if (mLocalizationExists == null)
{
bool translationFound = false;
ResHelper.LocalizeString(OriginalValue, out translationFound);
mLocalizationExists = translationFound;
}
if (mLocalizationExists == true)
{
return true;
}
else
{
return false;
}
}
}
///
/// Publicly visible textbox which contains translated string or plain text.
///
public TextBox TextBox
{
get
{
return textbox;
}
}
///
/// TextMode of textbox.
///
public TextBoxMode TextMode
{
get
{
return textbox.TextMode;
}
set
{
textbox.TextMode = value;
}
}
///
/// Number of columns of textbox in multiline mode.
///
public int Columns
{
get
{
return textbox.Columns;
}
set
{
textbox.Columns = value;
}
}
///
/// Number of rows of textbox in multiline mode.
///
public int Rows
{
get
{
return textbox.Rows;
}
set
{
textbox.Rows = value;
}
}
///
/// Gets or sets value indicating if control should save changes to resource string immediately after each PostBack. Default true.
///
public bool AutoSave
{
get
{
return mAutoSave;
}
set
{
mAutoSave = value;
}
}
///
/// Indicates if text contained in textbox is resolved resource string or if it is just plain text.
///
private bool IsLocalizationMacro
{
get
{
return MacroResolver.IsLocalizationMacro(OriginalValue);
}
}
///
/// Modal dialog identifier.
///
private Guid Identifier
{
get
{
// Try to load data from control ViewState
Guid identifier = ValidationHelper.GetGuid(Request.Params[hdnIdentifier.UniqueID], Guid.Empty);
// Create new GUID for identifier
if (identifier == Guid.Empty)
{
identifier = Guid.NewGuid();
}
// Assign identifier to hidden field
hdnIdentifier.Value = identifier.ToString();
return identifier;
}
}
///
/// Maximum length of plain text or resource string key. Validates in IsValid() method.
///
public int MaxLength
{
get
{
return textbox.MaxLength;
}
set
{
textbox.MaxLength = value;
}
}
///
/// Resource key prefix. Default value is empty for DevelopmentMode, 'custom.' value otherwise.
///
public string ResourceKeyPrefix
{
get
{
// If user set prefix
if (prefixIsSet)
{
return mResourceKeyPrefix;
}
// If in DevelopmentMode
else if (SettingsKeyProvider.DevelopmentMode)
{
return "test.";
}
// Otherwise return "custom."
else
{
return "custom.";
}
}
set
{
mResourceKeyPrefix = value;
prefixIsSet = true;
}
}
///
/// If TRUE then resource string key selection is skipped. Instead resource string key is automaticaly created from entered text.
/// Also when removing localization it also deletes resource string key assigned.
///
public bool AutomaticMode
{
get;
set;
}
///
/// Indicates if control is used on live site. Default value is FALSE for localizable text box.
///
public override bool IsLiveSite
{
get;
set;
}
#endregion
#region "Methods"
protected void Page_Load(object sender, EventArgs e)
{
// Set controls
btnOtherLanguages.ToolTip = GetString("localizable.otherlanguages");
btnOtherLanguages.OnClientClick = "LocalizeString('" + hdnValue.ClientID + "', '" + textbox.ClientID + "'); return false;";
btnLocalize.ToolTip = GetString("localizable.localize");
btnRemoveLocalization.Click += btnRemoveLocalization_Click;
btnRemoveLocalization.ToolTip = GetString("localizable.remove");
string buttonScript =
@"var confirmValue = confirm('" + GetString("localizable.removelocalization") + @"');
if (!confirmValue) return false;";
btnRemoveLocalization.OnClientClick = buttonScript;
// In automatic mode resource string key is generated from plain text
if (AutomaticMode)
{
btnLocalize.Click += btnLocalize_Click;
}
// Otherwise user has option to select resource string key
else
{
btnLocalize.OnClientClick = "LocalizationDialog" + ClientID + "('$|' + document.getElementById('" + textbox.ClientID + "').value); return false;";
}
if (textbox.TextMode == TextBoxMode.MultiLine)
{
pnlButtons.CssClass = "LocalizablePanel LocalizableTop";
}
// Show/hide localization controls
pnlButtons.Visible =
CMSContext.CurrentUser.IsAuthorizedPerResource("CMS.Localization", "LocalizeStrings") &&
!IsLiveSite &&
(SettingsKeyProvider.DevelopmentMode || (UICultureInfoProvider.NumberOfUICultures > 1)) &&
!textbox.Text.StartsWithCSafe(INPLACE_MACRO_START);
// Apply CSS style
if (!String.IsNullOrEmpty(CssClass))
{
textbox.CssClass = CssClass;
CssClass = null;
}
// Register event handler for saving data in BasicForm
if (Form != null)
{
Form.OnAfterSave += Form_OnAfterSave;
}
// Save changes after each PostBack if set so
else if (RequestHelper.IsPostBack() && Visible && AutoSave && IsLocalizationMacro && !String.IsNullOrEmpty(textbox.Text.Trim()))
{
mSaveAutomatically = true;
}
SetClientSideMaxLength();
}
protected override void OnPreRender(EventArgs e)
{
if (mSaveAutomatically)
{
Save();
}
// Ensure the text in textbox
if (IsLocalizationMacro && RequestHelper.IsPostBack())
{
if (LocalizationExists)
{
textbox.Text = ResHelper.LocalizeString(OriginalValue);
}
else
{
textbox.Text = OriginalValue.Substring(MACRO_START.Length, OriginalValue.Length - (MACRO_END.Length + MACRO_START.Length));
}
}
Reload();
// Ensure Enabled property so all inner controls are set properly
Enabled = Enabled;
// Register the scripts
if (pnlButtons.Visible)
{
RegisterScripts();
}
base.OnPreRender(e);
}
///
/// Validates control.
///
public override bool IsValid()
{
// Check for maximum length
if (textbox.MaxLength > 0)
{
return (OriginalValue.Length <= MaxLength) && (textbox.Text.Length <= MaxLength);
}
else
{
return true;
}
}
///
/// Reloads control.
///
public void Reload()
{
if (pnlButtons.Visible)
{
string iconLocalize = GetImageUrl("/Objects/CMS_UICulture/add.png");
string iconOther = GetImageUrl("Objects/CMS_UICulture/list.png");
string iconRemove = GetImageUrl("Design/Controls/UniGrid/Actions/Delete.png");
// Textbox contains translated macro
if (IsLocalizationMacro && LocalizationExists)
{
btnLocalize.Attributes.Add("style", string.Format(BUTTON_STYLE, iconLocalize) + "display: none;");
btnOtherLanguages.Attributes.Add("style", string.Format(BUTTON_STYLE, iconOther) + "display: inline;");
btnRemoveLocalization.Attributes.Add("style", string.Format(BUTTON_STYLE, iconRemove) + "display: inline;");
if (!this.textbox.CssClass.EndsWith(" TextBoxLocalized"))
{
this.textbox.CssClass += " TextBoxLocalized";
}
this.textbox.ToolTip = GetString("localizable.localized");
}
// Textbox contains only plain text
else
{
if (this.textbox.CssClass.EndsWith(" TextBoxLocalized"))
{
this.textbox.CssClass = this.textbox.CssClass.Substring(0, this.textbox.CssClass.Length - 17);
}
btnOtherLanguages.Attributes.Add("style", string.Format(BUTTON_STYLE, iconOther) + "display: none;");
btnRemoveLocalization.Attributes.Add("style", string.Format(BUTTON_STYLE, iconRemove) + "display: none;");
btnLocalize.Attributes.Add("style", string.Format(BUTTON_STYLE, iconLocalize) + "display:inline;");
}
}
}
///
/// Registers JS.
///
private void RegisterScripts()
{
// Register function to set translation string key from dialog window
ScriptHelper.RegisterDialogScript(Page);
StringBuilder script = new StringBuilder();
script.Append(
@"
function Get(id) {
return document.getElementById(id);
}
function SetResource(hdnValue, value, textbox, textboxvalue, btnLocalizeField, btnLocalizeString, btnRemoveLocalization) {
SetProperties(hdnValue, value, textbox, textboxvalue, btnLocalizeField, btnLocalizeString, btnRemoveLocalization);
return false;
}
function SetResourceAndOpen(hdnValue, value, textbox, textboxvalue, btnLocalizeField, btnLocalizeString, btnRemoveLocalization) {
SetResource(hdnValue, value, textbox, textboxvalue, btnLocalizeField, btnLocalizeString, btnRemoveLocalization);
OpenLocalize(hdnValue, textbox);
return false;
}
function SetProperties(hdnValue, value, textbox, textboxvalue, btnLocalizeField, btnLocalizeString, btnRemoveLocalization) {
Get(hdnValue).value = '", MACRO_START, @"' + value + '", MACRO_END, @"';
var txt = Get(textbox);
txt.value = textboxvalue;
txt.className += ' TextBoxLocalized';
Get(btnLocalizeField).style.display='none';
Get(btnLocalizeString).style.display='inline';
Get(btnRemoveLocalization).style.display='inline';
return false;
}
function OpenLocalize(hdnValue, textbox) {
window.setTimeout(function() { LocalizeString(hdnValue, textbox); }, 1);
return false
}
function SetTranslation(textbox, textboxvalue, hdnValue, value) {
Get(textbox).value = textboxvalue;
Get(hdnValue).value = '", MACRO_START, @"' + value + '", MACRO_END, @"';
}
function LocalizeFieldReady(rvalue, context) {
modalDialog(context, 'localizableField', 500, 350, null, null, true);
return false;
}
function LocalizeString(hiddenValueControl, textboxControl) {
var stringKey = Get(hiddenValueControl).value;
stringKey = stringKey.substring(", MACRO_START.Length, @",stringKey.length-", MACRO_END.Length, @");
modalDialog('", ResolveUrl(LOCALIZE_STRING), @"?hiddenValueControl=' + hiddenValueControl + '&stringKey=' + escape(stringKey) + '&parentTextbox=' + textboxControl, 'localizableString', 600, 635, null, null, true);
return false;
}
");
ScriptHelper.RegisterClientScriptBlock(this, typeof(string), "LocalizationDialogFunction", ScriptHelper.GetScript(script.ToString()));
// Register callback to send current plain text to modal window for localization
string url = LOCALIZE_FIELD + "?params=" + Identifier;
url += "&hash=" + QueryHelper.GetHash(url, false);
script = new StringBuilder();
script.Append(
@"
function LocalizationDialog", ClientID, @"(values) {
", Page.ClientScript.GetCallbackEventReference(this, "values", "LocalizeFieldReady", "'" + ResolveUrl(url) + "'"), @"
}
"
);
ScriptHelper.RegisterClientScriptBlock(this, typeof(string), "LocalizationDialog" + ClientID, ScriptHelper.GetScript(script.ToString()));
}
///
/// Saves translation for given resource string.
///
/// Returns TRUE if resource string was successfully modified
public bool Save()
{
// Save changes only when macro is edited
if (IsLocalizationMacro)
{
if (!textbox.Text.Trim().StartsWithCSafe(INPLACE_MACRO_START))
{
// Update key
string key = OriginalValue.Substring(MACRO_START.Length, OriginalValue.Length - (MACRO_END.Length + MACRO_START.Length));
var ri = SqlResourceManager.GetResourceStringInfo(key) ?? new ResourceStringInfo { StringKey = key };
ri.TranslationText = textbox.Text.Trim();
if (SqlResourceManager.GetUICultureID(CultureHelper.PreferredUICulture) != 0)
{
ri.UICultureCode = CultureHelper.PreferredUICulture;
}
else
{
ri.UICultureCode = CultureHelper.DefaultUICulture;
}
SqlResourceManager.SetResourceStringInfo(ri);
return true;
}
else
{
// Remove localization if in-place macro was inserted
RemoveLocalization();
}
}
return false;
}
///
/// Removes localization from the textbox.
///
private void RemoveLocalization()
{
// In automatic mode remove resource string key
if (AutomaticMode)
{
string key = OriginalValue.Substring(MACRO_START.Length, OriginalValue.Length - (MACRO_END.Length + MACRO_START.Length));
SqlResourceManager.DeleteResourceStringInfo(key, CultureHelper.DefaultUICulture);
}
OriginalValue = textbox.Text;
}
///
/// Sets the dialog parameters to the context.
///
protected void SetFieldDialog(string textboxValue)
{
Hashtable parameters = new Hashtable();
parameters["TextBoxValue"] = textboxValue;
parameters["HiddenValue"] = hdnValue.ClientID;
parameters["TextBoxID"] = textbox.ClientID;
parameters["ButtonLocalizeField"] = btnLocalize.ClientID;
parameters["ButtonLocalizeString"] = btnOtherLanguages.ClientID;
parameters["ButtonRemoveLocalization"] = btnRemoveLocalization.ClientID;
parameters["ResourceKeyPrefix"] = ResourceKeyPrefix;
WindowHelper.Add(Identifier.ToString(), parameters);
}
///
/// Sets client-side max length of the textbox control.
///
private void SetClientSideMaxLength()
{
if (FieldInfo != null)
{
switch (FieldInfo.DataType)
{
case (FormFieldDataTypeEnum.Text):
textbox.MaxLength = FieldInfo.Size;
break;
case (FormFieldDataTypeEnum.Integer):
case (FormFieldDataTypeEnum.LongInteger):
if (string.IsNullOrEmpty(FieldInfo.MaxValue) || string.IsNullOrEmpty(FieldInfo.MinValue))
{
// One of the limit value is not set => set maxint/maxlong length
textbox.MaxLength = (FieldInfo.DataType == FormFieldDataTypeEnum.Integer) ?
ValidationHelper.MAX_INT_LENGTH :
ValidationHelper.MAX_LONGINT_LENGTH;
}
else
{
// Set maxlength to the bigger one
textbox.MaxLength = Math.Max(FieldInfo.MaxValue.Length, FieldInfo.MinValue.Length);
}
break;
}
}
}
#endregion
#region "Event handlers"
///
/// Button localize click. In AutomaticMode available only.
///
void btnLocalize_Click(object sender, EventArgs e)
{
if (!String.IsNullOrEmpty(textbox.Text.Trim()))
{
// Get maximum length of resource key
int maxKeyLength = MAX_KEY_LENGTH;
string prefix = ResourceKeyPrefix;
if (SettingsKeyProvider.DevelopmentMode && String.IsNullOrEmpty(ResourceKeyPrefix))
{
prefix = PREFIX;
}
if (!String.IsNullOrEmpty(prefix))
{
maxKeyLength -= prefix.Length;
}
// Initialize resource string
string newResource = TextHelper.LimitLength(ValidationHelper.GetCodeName(textbox.Text.Trim()), maxKeyLength, String.Empty, true);
int i = 0;
if (!newResource.StartsWithCSafe(prefix))
{
OriginalValue = prefix + newResource;
}
else
{
OriginalValue = newResource;
}
// If key exists then create new one with number as a suffix
while (SqlResourceManager.GetResourceStringInfo(OriginalValue) != null)
{
// If newly created resource key is longer then allowed length then trim end by one character
if ((prefix.Length + newResource.Length + ++i) > MAX_KEY_LENGTH)
{
newResource = newResource.Substring(0, newResource.Length - 1);
}
if (!newResource.StartsWithCSafe(prefix))
{
OriginalValue = prefix + newResource + i;
}
else
{
OriginalValue = newResource + i;
}
}
// Check if current user's culture exists
UICultureInfo uiCulture = null;
string cultureCode = CultureHelper.PreferredUICulture;
try
{
uiCulture = UICultureInfoProvider.GetUICultureInfo(CultureHelper.PreferredUICulture);
}
// Use default UI culture
catch
{
cultureCode = CultureHelper.DefaultUICulture;
}
// Use default UI culture
if (uiCulture == null)
{
cultureCode = CultureHelper.DefaultUICulture;
}
// Save ResourceString
ResourceStringInfo ri = new ResourceStringInfo();
ri.StringKey = OriginalValue;
ri.UICultureCode = cultureCode;
ri.TranslationText = textbox.Text;
ri.StringIsCustom = !SettingsKeyProvider.DevelopmentMode;
SqlResourceManager.SetResourceStringInfo(ri);
// Open 'localization to other languages' window
ScriptHelper.RegisterStartupScript(this, typeof(string), "OpenLocalization", ScriptHelper.GetScript("modalDialog('" + ResolveUrl(LOCALIZE_STRING) + "?hiddenValueControl=" + hdnValue.ClientID + "&stringKey=" + ri.StringKey + "&parentTextbox=" + textbox.ClientID + "', 'localizableString', 600, 635, null, null, true);"));
// Set macro settings
Value = MACRO_START + OriginalValue + MACRO_END;
Reload();
}
else
{
lblError.Visible = true;
lblError.ResourceString = "localize.entertext";
}
}
///
/// Remove localization button click.
///
private void btnRemoveLocalization_Click(object sender, EventArgs e)
{
RemoveLocalization();
}
///
/// BasicForm saved event handler.
///
private void Form_OnAfterSave(object sender, EventArgs e)
{
Save();
}
///
/// Gets callback result.
///
string ICallbackEventHandler.GetCallbackResult()
{
// Prepare the parameters for dialog
SetFieldDialog(callbackReturnVal);
return string.Empty;
}
///
/// Raise callback event.
///
void ICallbackEventHandler.RaiseCallbackEvent(string eventArgument)
{
// Get value from callback
if ((eventArgument != null) && eventArgument.StartsWithCSafe("$|"))
{
callbackReturnVal = eventArgument.Substring(2);
}
}
#endregion
}