2009-06-19 07:39:16 +00:00
using System ;
using System.Data ;
using System.Net.Mail ;
using System.Runtime.CompilerServices ;
using System.Text ;
using System.Web ;
2010-06-12 15:01:24 +00:00
using System.Collections.Generic ;
2009-06-19 07:39:16 +00:00
using umbraco.BusinessLogic ;
using umbraco.cms.businesslogic.property ;
using umbraco.cms.businesslogic.web ;
using umbraco.interfaces ;
using umbraco.DataLayer ;
2010-01-28 12:51:46 +00:00
using umbraco.IO ;
2010-06-12 15:01:24 +00:00
using System.Collections ;
2009-06-19 07:39:16 +00:00
namespace umbraco.cms.businesslogic.workflow
{
/// <summary>
/// Notifications are a part of the umbraco workflow.
/// A notification is created every time an action on a node occurs and a umbraco user has subscribed to this specific action on this specific node.
/// Notifications generates an email, which is send to the subscribing users.
/// </summary>
public class Notification
{
2010-06-12 15:01:24 +00:00
public int NodeId { get ; private set ; }
public int UserId { get ; private set ; }
public char ActionId { get ; private set ; }
/// <summary>
/// Private constructor as this object should not be allowed to be created currently
/// </summary>
private Notification () { }
2009-06-19 07:39:16 +00:00
/// <summary>
/// Gets the SQL helper.
/// </summary>
/// <value>The SQL helper.</value>
protected static ISqlHelper SqlHelper
{
get { return Application . SqlHelper ; }
}
/// <summary>
2010-06-12 15:01:24 +00:00
/// Sends the notifications for the specified user regarding the specified node and action.
2009-06-19 07:39:16 +00:00
/// </summary>
/// <param name="Node">The node.</param>
/// <param name="user">The user.</param>
/// <param name="Action">The action.</param>
public static void GetNotifications ( CMSNode Node , User user , IAction Action )
{
User [] allUsers = User . getAll ();
foreach ( User u in allUsers )
{
try
{
if (! u . Disabled && u . GetNotifications ( Node . Path ). IndexOf ( Action . Letter . ToString ()) > - 1 )
{
Log . Add ( LogTypes . Notify , User . GetUser ( 0 ), Node . Id ,
"Notification about " + ui . Text ( Action . Alias , u ) + " sent to " + u . Name + " (" + u . Email +
")" );
sendNotification ( user , u , ( Document ) Node , Action );
}
}
catch ( Exception notifyExp )
{
Log . Add ( LogTypes . Error , u , Node . Id , "Error in notification: " + notifyExp );
}
}
}
///TODO: Include update with html mail notification and document contents
private static void sendNotification ( User performingUser , User mailingUser , Document documentObject ,
IAction Action )
{
// retrieve previous version of the document
DocumentVersionList [] versions = documentObject . GetVersions ();
int versionCount = ( versions . Length > 1 ) ? ( versions . Length - 2 ) : ( versions . Length - 1 );
Document oldDoc = new Document ( documentObject . Id , versions [ versionCount ]. Version );
// build summary
StringBuilder summary = new StringBuilder ();
2009-10-13 15:00:49 +00:00
var props = documentObject . getProperties ;
foreach ( Property p in props )
2009-06-19 07:39:16 +00:00
{
// check if something was changed and display the changes otherwise display the fields
Property oldProperty = oldDoc . getProperty ( p . PropertyType . Alias );
string oldText = oldProperty . Value . ToString ();
string newText = p . Value . ToString ();
// replace html with char equivalent
ReplaceHTMLSymbols ( ref oldText );
ReplaceHTMLSymbols ( ref newText );
// make sure to only highlight changes done using TinyMCE editor... other changes will be displayed using default summary
///TODO PPH: Had to change this, as a reference to the editorcontrols is not allowed, so a string comparison is the only way, this should be a DIFF or something instead..
if ( p . PropertyType . DataTypeDefinition . DataType . ToString () == "umbraco.editorControls.tinymce.TinyMCEDataType" &&
string . Compare ( oldText , newText ) != 0 )
{
summary . Append ( "<tr>" );
summary . Append ( "<th style='text-align: left; vertical-align: top; width: 25%;'> Note: </th>" );
summary . Append ( "<td style='text-align: left; vertical-align: top;'> <span style='background-color:red;'>Red for deleted characters</span> <span style='background-color:yellow;'>Yellow for inserted characters</span></td>" );
summary . Append ( "</tr>" );
summary . Append ( "<tr>" );
summary . Append ( "<th style='text-align: left; vertical-align: top; width: 25%;'> New " + p . PropertyType . Name + "</th>" );
summary . Append ( "<td style='text-align: left; vertical-align: top;'>" + replaceLinks ( CompareText ( oldText , newText , true , false , "<span style='background-color:yellow;'>" , string . Empty )) + "</td>" );
summary . Append ( "</tr>" );
summary . Append ( "<tr>" );
summary . Append ( "<th style='text-align: left; vertical-align: top; width: 25%;'> Old " + oldProperty . PropertyType . Name + "</th>" );
summary . Append ( "<td style='text-align: left; vertical-align: top;'>" + replaceLinks ( CompareText ( newText , oldText , true , false , "<span style='background-color:red;'>" , string . Empty )) + "</td>" );
summary . Append ( "</tr>" );
}
else
{
summary . Append ( "<tr>" );
summary . Append ( "<th style='text-align: left; vertical-align: top; width: 25%;'>" + p . PropertyType . Name + "</th>" );
summary . Append ( "<style='text-align: left; vertical-align: top;'>" + p . Value . ToString () + "</td>" );
summary . Append ( "</tr>" );
}
summary . Append ( "<tr><td colspan=\"2\" style=\"border-bottom: 1px solid #CCC; font-size: 2px;\"> </td></tr>" );
}
string [] subjectVars = {
2010-07-20 09:47:27 +00:00
HttpContext . Current . Request . ServerVariables [ "SERVER_NAME" ] + ":" + HttpContext . Current . Request . Url . Port . ToString () + IOHelper . ResolveUrl ( SystemDirectories . Umbraco ), ui . Text ( Action . Alias )
2009-06-19 07:39:16 +00:00
,
documentObject . Text
};
string [] bodyVars = {
mailingUser . Name , ui . Text ( Action . Alias ), documentObject . Text , performingUser . Name ,
2010-07-20 09:47:27 +00:00
HttpContext . Current . Request . ServerVariables [ "SERVER_NAME" ] + ":" + HttpContext . Current . Request . Url . Port . ToString () + IOHelper . ResolveUrl ( SystemDirectories . Umbraco ),
2009-06-19 07:39:16 +00:00
documentObject . Id . ToString (), summary . ToString (),
String . Format ( "http://{0}/{1}" ,
HttpContext . Current . Request . ServerVariables [ "SERVER_NAME" ] + ":" + HttpContext . Current . Request . Url . Port . ToString (),
/*umbraco.library.NiceUrl(documentObject.Id))*/ documentObject . Id . ToString () + ".aspx" )
///TODO: PPH removed the niceURL reference... cms.dll cannot reference the presentation project...
///TODO: This should be moved somewhere else..
};
// create the mail message
MailMessage mail = new MailMessage ( UmbracoSettings . NotificationEmailSender , mailingUser . Email );
// populate the message
mail . Subject = ui . Text ( "notifications" , "mailSubject" , subjectVars , mailingUser );
if ( UmbracoSettings . NotificationDisableHtmlEmail )
{
mail . IsBodyHtml = false ;
mail . Body = ui . Text ( "notifications" , "mailBody" , bodyVars , mailingUser );
} else
{
mail . IsBodyHtml = true ;
mail . Body =
@"<html><head>
</head>
<body style='font-family: Trebuchet MS, arial, sans-serif; font-color: black;'>
" +
ui . Text ( "notifications" , "mailBodyHtml" , bodyVars , mailingUser ) + "</body></html>" ;
}
// send it
SmtpClient sender = new SmtpClient ();
sender . Send ( mail );
}
private static string replaceLinks ( string text ) {
string domain = "http://" + HttpContext . Current . Request . ServerVariables [ "SERVER_NAME" ] + ":" + HttpContext . Current . Request . Url . Port . ToString () + "/" ;
text = text . Replace ( "href=\"/" , "href=\"" + domain );
text = text . Replace ( "src=\"/" , "src=\"" + domain );
return text ;
}
2010-06-12 15:01:24 +00:00
/// <summary>
/// Returns the notifications for a user
/// </summary>
/// <param name="user"></param>
/// <returns></returns>
public static IEnumerable < Notification > GetUserNotifications ( User user )
{
var items = new List < Notification >();
using ( IRecordsReader dr = SqlHelper . ExecuteReader ( "select * from umbracoUser2NodeNotify where userId = @userId order by nodeId" , SqlHelper . CreateParameter ( "@userId" , user . Id )))
{
while ( dr . Read ())
{
items . Add ( new Notification ()
{
NodeId = dr . GetInt ( "nodeId" ),
ActionId = Convert . ToChar ( dr . GetString ( "action" )),
UserId = dr . GetInt ( "userId" )
});
}
}
return items ;
}
/// <summary>
/// Returns the notifications for a node
/// </summary>
/// <param name="user"></param>
/// <returns></returns>
public static IEnumerable < Notification > GetNodeNotifications ( CMSNode node )
{
var items = new List < Notification >();
using ( IRecordsReader dr = SqlHelper . ExecuteReader ( "select * from umbracoUser2NodeNotify where nodeId = @nodeId order by nodeId" , SqlHelper . CreateParameter ( "@nodeId" , node . Id )))
{
while ( dr . Read ())
{
items . Add ( new Notification ()
{
NodeId = dr . GetInt ( "nodeId" ),
ActionId = Convert . ToChar ( dr . GetString ( "action" )),
UserId = dr . GetInt ( "userId" )
});
}
}
return items ;
}
/// <summary>
/// Deletes notifications by node
/// </summary>
/// <param name="nodeId"></param>
public static void DeleteNotifications ( CMSNode node )
{
// delete all settings on the node for this node id
SqlHelper . ExecuteNonQuery ( "delete from umbracoUser2NodeNotify where nodeId = @nodeId" ,
SqlHelper . CreateParameter ( "@nodeId" , node . Id ));
}
/// <summary>
/// Delete notifications by user
/// </summary>
/// <param name="user"></param>
public static void DeleteNotifications ( User user )
{
// delete all settings on the node for this node id
SqlHelper . ExecuteNonQuery ( "delete from umbracoUser2NodeNotify where userId = @userId" ,
SqlHelper . CreateParameter ( "@userId" , user . Id ));
}
/// <summary>
/// Delete notifications by user and node
/// </summary>
/// <param name="user"></param>
/// <param name="node"></param>
public static void DeleteNotifications ( User user , CMSNode node )
{
// delete all settings on the node for this user
SqlHelper . ExecuteNonQuery ( "delete from umbracoUser2NodeNotify where userId = @userId and nodeId = @nodeId" ,
SqlHelper . CreateParameter ( "@userId" , user . Id ), SqlHelper . CreateParameter ( "@nodeId" , node . Id ));
}
2009-06-19 07:39:16 +00:00
/// <summary>
/// Creates a new notification
/// </summary>
/// <param name="User">The user.</param>
/// <param name="Node">The node.</param>
/// <param name="ActionLetter">The action letter.</param>
[MethodImpl(MethodImplOptions.Synchronized)]
public static void MakeNew ( User User , CMSNode Node , char ActionLetter )
{
IParameter [] parameters = new IParameter [] {
SqlHelper . CreateParameter ( "@userId" , User . Id ),
SqlHelper . CreateParameter ( "@nodeId" , Node . Id ),
SqlHelper . CreateParameter ( "@action" , ActionLetter . ToString ()) };
// Method is synchronized so exists remains consistent (avoiding race condition)
bool exists = SqlHelper . ExecuteScalar < int >( "SELECT COUNT(userId) FROM umbracoUser2nodeNotify WHERE userId = @userId AND nodeId = @nodeId AND action = @action" ,
parameters ) > 0 ;
if (! exists )
SqlHelper . ExecuteNonQuery ( "INSERT INTO umbracoUser2nodeNotify (userId, nodeId, action) VALUES (@userId, @nodeId, @action)" ,
parameters );
}
/// <summary>
/// Updates the notifications.
/// </summary>
/// <param name="User">The user.</param>
/// <param name="Node">The node.</param>
/// <param name="Notifications">The notifications.</param>
2010-06-12 15:01:24 +00:00
[MethodImpl(MethodImplOptions.Synchronized)]
2009-06-19 07:39:16 +00:00
public static void UpdateNotifications ( User User , CMSNode Node , string Notifications )
{
// delete all settings on the node for this user
2010-06-12 15:01:24 +00:00
DeleteNotifications ( User , Node );
2009-06-19 07:39:16 +00:00
// Loop through the permissions and create them
foreach ( char c in Notifications . ToCharArray ())
MakeNew ( User , Node , c );
}
/// <summary>
/// Replaces the HTML symbols with the character equivalent.
/// </summary>
/// <param name="oldString">The old string.</param>
private static void ReplaceHTMLSymbols ( ref string oldString )
{
oldString = oldString . Replace ( " " , " " );
oldString = oldString . Replace ( "’" , "'" );
oldString = oldString . Replace ( "&" , "&" );
oldString = oldString . Replace ( "“" , "“" );
oldString = oldString . Replace ( "”" , "”" );
oldString = oldString . Replace ( """ , "\"" );
}
/// <summary>
/// Compares the text.
/// </summary>
/// <param name="oldText">The old text.</param>
/// <param name="newText">The new text.</param>
/// <returns></returns>
private static string CompareText ( string oldText , string newText )
{
return CompareText ( oldText , newText , true , true );
}
/// <summary>
/// Compares the text.
/// </summary>
/// <param name="oldText">The old text.</param>
/// <param name="newText">The new text.</param>
/// <param name="displayInsertedText">if set to <c>true</c> [display inserted text].</param>
/// <param name="displayDeletedText">if set to <c>true</c> [display deleted text].</param>
/// <returns></returns>
private static string CompareText ( string oldText , string newText , bool displayInsertedText , bool displayDeletedText )
{
return CompareText ( oldText , newText , displayInsertedText , displayDeletedText , "<span style='background-color:red;'>" , "<span style='background-color:yellow;'>" );
}
/// <summary>
/// Compares the text.
/// </summary>
/// <param name="oldText">The old text.</param>
/// <param name="newText">The new text.</param>
/// <param name="displayInsertedText">if set to <c>true</c> [display inserted text].</param>
/// <param name="displayDeletedText">if set to <c>true</c> [display deleted text].</param>
/// <param name="insertedStyle">The inserted style.</param>
/// <param name="deletedStyle">The deleted style.</param>
/// <returns></returns>
private static string CompareText ( string oldText , string newText , bool displayInsertedText , bool displayDeletedText , string insertedStyle , string deletedStyle )
{
StringBuilder sb = new StringBuilder ();
Diff . Item [] diffs = Diff . DiffText1 ( oldText , newText );
int pos = 0 ;
for ( int n = 0 ; n < diffs . Length ; n ++)
{
Diff . Item it = diffs [ n ];
// write unchanged chars
while (( pos < it . StartB ) && ( pos < newText . Length ))
{
sb . Append ( newText [ pos ]);
pos ++;
} // while
// write deleted chars
if ( displayDeletedText && it . deletedA > 0 )
{
sb . Append ( deletedStyle );
for ( int m = 0 ; m < it . deletedA ; m ++)
{
sb . Append ( oldText [ it . StartA + m ]);
} // for
sb . Append ( "</span>" );
}
// write inserted chars
if ( displayInsertedText && pos < it . StartB + it . insertedB )
{
sb . Append ( insertedStyle );
while ( pos < it . StartB + it . insertedB )
{
sb . Append ( newText [ pos ]);
pos ++;
} // while
sb . Append ( "</span>" );
} // if
} // while
// write rest of unchanged chars
while ( pos < newText . Length )
{
sb . Append ( newText [ pos ]);
pos ++;
} // while
return sb . ToString ();
}
}
}