2009-06-19 07:39:16 +00:00
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 {
2009-07-26 15:13:15 +00:00
private string _javascriptLocation = GlobalSettings . ClientPath + "/tinymce" ;
2009-06-19 07:39:16 +00:00
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" ) ;
2009-07-26 15:13:15 +00:00
2009-06-19 07:39:16 +00:00
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
}
}