Files
Umbraco-CMS/components/editorControls/tinymce/webcontrol/TinyMCE.cs
Shandem 9a46338123 DO NOT DOWNLOAD. DOWNLOAD LATEST STABLE FROM RELEASE TAB
Resolves 23626. ClientDependency work.

[TFS Changeset #57219]
2009-07-26 15:13:15 +00:00

403 lines
17 KiB
C#

using System;
using System.Collections;
using System.Collections.Specialized;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using umbraco.BusinessLogic;
using umbraco.cms.businesslogic.macro;
using umbraco.cms.businesslogic.media;
using umbraco.cms.businesslogic.property;
using Content = umbraco.cms.businesslogic.Content;
namespace umbraco.editorControls.tinymce.webcontrol {
public class TinyMCE : WebControl, IPostBackDataHandler {
private string _javascriptLocation = GlobalSettings.ClientPath + "/tinymce";
private string _coreFile = "/tiny_mce.js";
private int _rows = 13;
private int _cols = 60;
public NameValueCollection config;
private string temp;
private int _nodeId = -1;
private Guid _versionId;
public int NodeId {
set { _nodeId = value; }
}
public Guid VersionId {
set { _versionId = value; }
}
public string CoreFile {
get { return _coreFile; }
}
public TinyMCE() {
config = new NameValueCollection();
config.Add("mode", "exact");
config.Add("theme", "advanced");
//content_css : "/mycontent.css"
temp = string.Empty;
//this._config.SetStringParam(TinyMCEConfig.StringParameter.mode, "exact");
//this._config.SetStringParam(TinyMCEConfig.StringParameter.theme, "advanced");
//this._config.SetStringParam(TinyMCEConfig.StringParameter.plugins, "advlink,noneditable,advimage,flash");
//this._config.SetStringParam(TinyMCEConfig.StringParameter.theme_advanced_buttons3_add, "flash");
}
// <summary>
/// The text of the editor
/// </summary>
public string Text {
get { return (string)ViewState["text"]; }
set { ViewState["text"] = value; }
}
// <summary>
/// The number of rows in the textarea that gets converted to the editor.
/// This affects the size of the editor. Default is 10
/// </summary>
public int Rows {
get { return _rows; }
set { _rows = value; }
}
// <summary>
/// The number of columns in the textarea that gets converted to the editor.
/// This affects the size of the editor. Default is 40.
/// </summary>
public int Cols {
get { return _cols; }
set { _cols = value; }
}
// <summary>
/// Path to the TinyMCE javascript, default is "jscripts/tiny_mce/tiny_mce.js"
/// </summary>
public string JavascriptLocation {
get { return _javascriptLocation; }
set { _javascriptLocation = value; }
}
/// <summary>
/// Draws the editor
/// </summary>
/// <param name="outWriter">The writer to draw the editor to</param>
protected override void Render(HtmlTextWriter writer) {
writer.WriteLine("<!-- tinyMCE -->\n");
writer.WriteLine("<script language=\"javascript\" type=\"text/javascript\">");
writer.WriteLine("tinyMCE.init({");
for (int i = 0; i < config.Count; i++) {
writer.WriteLine(config.GetKey(i) + " : \"" + config.Get(i) + "\",");
}
writer.WriteLine("theme_advanced_resizing : true,");
writer.WriteLine("theme_advanced_resize_horizontal : false,");
writer.WriteLine("apply_source_formatting : true,");
writer.WriteLine("relative_urls : false,");
writer.WriteLine("valid_elements : {0},", tinyMCEConfiguration.ValidElements);
writer.WriteLine("invalid_elements : \"{0}\",", tinyMCEConfiguration.InvalidElements);
writer.WriteLine("elements : \"" + UniqueID + "\",");
writer.WriteLine("umbracoPath: \"" + GlobalSettings.Path + "\"");
writer.WriteLine("});");
// write a message that this is deprecated
writer.WriteLine("alert('This page use an old version of the Richtext Editor (TinyMCE2), which is deprecated and no longer supported in Umbraco 4. \\n\\nPlease upgrade by going to\\n- Developer\\n- Data Types\\n- Richtext editor\\n- And choose \\'TinyMCE3 wysiwyg\\' as the rendercontrol\\n\\nIf you don\\'t have administrative priviledges in umbraco, you should contact your administrator');");
writer.WriteLine("</script>\n");
writer.WriteLine("<!-- /tinyMCE -->\n");
if (Cols > 0)
writer.Write("<textarea class=\"tinymce" + UniqueID + "\" id=\"" + UniqueID + "\" name=\"" + UniqueID + "\" cols=\"" + Cols + "\" rows=\"" +
Rows + "\" style=\"width: 100%\">\n");
else
writer.Write("<textarea class=\"tinymce" + UniqueID + "\" id=\"" + UniqueID + "\" name=\"" + UniqueID + "\">\n");
// if the document exists, parse it for macros
if (_nodeId != -1)
writer.Write(parseMacrosToHtml(formatMedia(Text)));
else
writer.Write(Text);
writer.WriteLine("</textarea>");
}
private string formatMedia(string html) {
// Local media path
string localMediaPath = getLocalMediaPath();
// Find all media images
string pattern = "<img [^>]*src=\"(?<mediaString>/media[^\"]*)\" [^>]*>";
MatchCollection tags =
Regex.Matches(html, pattern, RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace);
foreach (Match tag in tags)
if (tag.Groups.Count > 0) {
// Replace /> to ensure we're in old-school html mode
string tempTag = "<img";
string orgSrc = tag.Groups["mediaString"].Value;
// gather all attributes
// TODO: This should be replaced with a general helper method - but for now we'll wanna leave umbraco.dll alone for this patch
Hashtable ht = new Hashtable();
MatchCollection m =
Regex.Matches(tag.Value.Replace(">", " >"),
"(?<attributeName>\\S*)=\"(?<attributeValue>[^\"]*)\"|(?<attributeName>\\S*)=(?<attributeValue>[^\"|\\s]*)\\s",
RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace);
foreach (Match attributeSet in m) {
if (attributeSet.Groups["attributeName"].Value.ToString().ToLower() != "src")
ht.Add(attributeSet.Groups["attributeName"].Value.ToString(),
attributeSet.Groups["attributeValue"].Value.ToString());
}
// build the element
// Build image tag
IDictionaryEnumerator ide = ht.GetEnumerator();
while (ide.MoveNext())
tempTag += " " + ide.Key.ToString() + "=\"" + ide.Value.ToString() + "\"";
// Find the original filename, by removing the might added width and height
orgSrc =
orgSrc.Replace(
"_" + helper.FindAttribute(ht, "width") + "x" + helper.FindAttribute(ht, "height"), "").
Replace("%20", " ");
// Check for either id or guid from media
string mediaId = getIdFromSource(orgSrc, localMediaPath);
Media imageMedia = null;
try {
int mId = int.Parse(mediaId);
Property p = new Property(mId);
imageMedia = new Media(Content.GetContentFromVersion(p.VersionId).Id);
} catch {
try {
imageMedia = new Media(Content.GetContentFromVersion(new Guid(mediaId)).Id);
} catch {
}
}
// Check with the database if any media matches this url
if (imageMedia != null) {
try {
// Check extention
if (imageMedia.getProperty("umbracoExtension").Value.ToString() != orgSrc.Substring(orgSrc.LastIndexOf(".") + 1, orgSrc.Length - orgSrc.LastIndexOf(".") - 1))
orgSrc = orgSrc.Substring(0, orgSrc.LastIndexOf(".") + 1) +
imageMedia.getProperty("umbracoExtension").Value.ToString();
// Format the tag
tempTag = tempTag + " rel=\"" +
imageMedia.getProperty("umbracoWidth").Value.ToString() + "," +
imageMedia.getProperty("umbracoHeight").Value.ToString() + "\" src=\"" + orgSrc +
"\"";
tempTag += "/>";
// Replace the tag
html = html.Replace(tag.Value, tempTag);
} catch (Exception ee) {
Log.Add(LogTypes.Error, User.GetUser(0), -1,
"Error reading size data from media: " + imageMedia.Id.ToString() + ", " +
ee.ToString());
}
} else
Log.Add(LogTypes.Error, User.GetUser(0), -1,
"Error reading size data from media (not found): " + orgSrc);
}
return html;
}
private string getIdFromSource(string src, string localMediaPath) {
// important - remove out the umbraco path + media!
src = src.Replace(localMediaPath, "");
string _id = "";
// Check for directory id naming
if (src.Length - src.Replace("/", "").Length > 0) {
string[] dirSplit = src.Split("/".ToCharArray());
string tempId = dirSplit[0];
try {
// id
_id = int.Parse(tempId).ToString();
} catch {
// guid
_id = tempId;
}
} else {
string[] fileSplit = src.Replace("/media/", "").Split("-".ToCharArray());
// guid or id
if (fileSplit.Length > 3) {
for (int i = 0; i < 5; i++)
_id += fileSplit[i] + "-";
_id = _id.Substring(0, _id.Length - 1);
} else
_id = fileSplit[0];
}
return _id;
}
private string getLocalMediaPath() {
string[] umbracoPathSplit = GlobalSettings.Path.Split("/".ToCharArray());
string umbracoPath = "";
for (int i = 0; i < umbracoPathSplit.Length - 1; i++)
umbracoPath += umbracoPathSplit[i] + "/";
return umbracoPath + "media/";
}
private string parseMacrosToHtml(string input) {
int nodeId = _nodeId;
Guid versionId = _versionId;
string content = input;
string pattern = @"(<\?UMBRACO_MACRO\W*[^>]*/>)";
MatchCollection tags =
Regex.Matches(content, pattern, RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace);
// Page for macro rendering
page p = new page(nodeId, versionId);
HttpContext.Current.Items["macrosAdded"] = 0;
HttpContext.Current.Items["pageID"] = nodeId.ToString();
foreach (Match tag in tags) {
try {
// Create div
Hashtable attributes = helper.ReturnAttributes(tag.Groups[1].Value);
string div = macro.renderMacroStartTag(attributes, nodeId, versionId).Replace("&quot;", "&amp;quot;");
// Insert macro contents here...
macro m;
if (helper.FindAttribute(attributes, "macroID") != "")
m = new macro(int.Parse(helper.FindAttribute(attributes, "macroID")));
else {
// legacy: Check if the macroAlias is typed in lowercasing
string macroAlias = helper.FindAttribute(attributes, "macroAlias");
if (macroAlias == "") {
macroAlias = helper.FindAttribute(attributes, "macroalias");
attributes.Remove("macroalias");
attributes.Add("macroAlias", macroAlias);
}
if (macroAlias != "")
m = new macro(Macro.GetByAlias(macroAlias).Id);
else
throw new ArgumentException("umbraco is unable to identify the macro. No id or macroalias was provided for the macro in the macro tag.", tag.Groups[1].Value);
}
if (helper.FindAttribute(attributes, "macroAlias") == "")
attributes.Add("macroAlias", m.Alias);
try {
div += macro.MacroContentByHttp(nodeId, versionId, attributes);
} catch {
div += "<span style=\"color: green\">No macro content available for WYSIWYG editing</span>";
}
div += macro.renderMacroEndTag();
content = content.Replace(tag.Groups[1].Value, div);
} catch (Exception ee) {
Log.Add(LogTypes.Error, this._nodeId, "Macro Parsing Error: " + ee.ToString());
string div = "<div class=\"umbMacroHolder mceNonEditable\"><p style=\"color: red\"><strong>umbraco was unable to parse a macro tag, which means that parts of this content might be corrupt.</strong> <br /><br />Best solution is to rollback to a previous version by right clicking the node in the tree and then try to insert the macro again. <br/><br/>Please report this to your system administrator as well - this error has been logged.</p></div>";
content = content.Replace(tag.Groups[1].Value, div);
}
}
return content;
}
private static readonly object TextChangedEvent = new object();
/// <summary>
/// Raises an event when the text in the editor changes.
/// </summary>
public event EventHandler TextChanged {
add { Events.AddHandler(TextChangedEvent, value); }
remove { Events.RemoveHandler(TextChangedEvent, value); }
}
/// <summary>
/// Event for text change.
/// </summary>
/// <param name="e"></param>
protected virtual void OnTextChanged(EventArgs e) {
if (Events != null) {
EventHandler oEventHandler = (EventHandler)Events[TextChangedEvent];
if (oEventHandler != null) {
oEventHandler(this, e);
}
}
}
/// <summary>
/// Called when a postback occurs on the page the control is placed at
/// </summary>
/// <param name="postDataKey">The key of the editor data</param>
/// <param name="postCollection">All the posted data</param>
/// <returns></returns>
bool IPostBackDataHandler.LoadPostData(string postDataKey, NameValueCollection postCollection) {
string newText = postCollection[postDataKey];
if (newText != Text) {
Text = newText;
return true;
}
return false;
}
/// <summary>
/// Raises an event when postback occurs
/// </summary>
void IPostBackDataHandler.RaisePostDataChangedEvent() {
OnTextChanged(EventArgs.Empty);
}
}
public enum TinyMceButtons {
code,
bold,
italic,
underline,
strikethrough,
justifyleft,
justifycenter,
justifyright,
justifyfull,
bullist,
numlist,
outdent,
indent,
cut,
copy,
pasteword,
undo,
redo,
link,
unlink,
image,
table,
hr,
removeformat,
sub,
sup,
charmap,
anchor,
umbracomacro
}
}