403 lines
17 KiB
C#
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 = "/umbraco_client/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\" src=\"" + JavascriptLocation + "\"/>\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;");
|
|
|
|
// 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
|
|
}
|
|
} |