Portal Engine Questions on portal engine and web parts.
Version 7.x > Portal Engine > How do you cancel insert/update and notify user via Custom DocumentEvents? View modes: 
User avatar
Member
Member
matt-awg - 12/18/2012 9:08:45 AM
   
How do you cancel insert/update and notify user via Custom DocumentEvents?
The following code partially works but it does not cancel or notify the user... I do some checks to see if an entry would be a duplicate and attempt to cancel the insert/update but it does not cancel it nor does it raise an error or notify the user. It does however work when it is not a duplicate, it changes the two fields as per the code below, so I know the logic is correct and the code is being called, I just don't know the correct steps to cancel the update/insert.

I searched the devnet site and nothing comes up for this and I can't find anything in the API documentation I downloaded either. Anyone know how to cancel the update and show a message to the user as to why it was canceled?

Or is there an easier way to do the check for duplicates? This is a custom document type and I could not find any way to enforce a primary key via the Site Manager but maybe there is some other way to do it?

Thanks,
Matt
using CMS.DocumentEngine;
using CMS.SettingsProvider;
using System.Data;

[CustomDocumentEvents]
public partial class CMSModuleLoader
{
/// <summary>
/// Attribute class that ensures the loading of custom handlers
/// </summary>
private class CustomDocumentEventsAttribute : CMSLoaderAttribute
{
/// <summary>
/// Called automatically when the application starts
/// </summary>
public override void Init()
{
// Assigns custom handlers to the appropriate events
DocumentEvents.Insert.Before += Document_Insert_Before;
DocumentEvents.Update.Before += Document_Update_Before;
}

private void Document_Insert_Before(object sender, DocumentEventArgs e)
{
// type the document as TreeNode
TreeNode objDoc = (TreeNode)e.Node;
updateHouseListingStateAndTitle(objDoc, "INSERT");
}

private void Document_Update_Before(object sender, DocumentEventArgs e)
{
// type the document as TreeNode
TreeNode objDoc = (TreeNode)e.Node;
updateHouseListingStateAndTitle(objDoc, "UPDATE");
}

private void updateHouseListingStateAndTitle(TreeNode objDoc, string InsertOrUpdate)
{
// handle the event only for house listing items
if (objDoc.NodeClassName.ToLower() == "cms.houselisting")
{
TreeNode parentNode = objDoc.Parent;
string state = parentNode.DocumentName;
string street = objDoc.GetStringValue("Street", "");
string city = objDoc.GetStringValue("City", "");
string zip = objDoc.GetStringValue("Zip", "");
string title = street + ", " + city + ", " + state + " " + zip;
//make sure no other house listing already exists with this title... if so, throw error and cancel update?
QueryDataParameters prms = new QueryDataParameters();
prms.Add("@street", street);
prms.Add("@city", city);
prms.Add("@state", state);
prms.Add("@zip", zip);
DataSet ds = SqlHelperClass.ExecuteQuery("CMS.HouseListing.HouseListingLookupByAddress", prms, null, null);
if (ds.Tables[0].Rows.Count > 0)
{
//cancel insert/update... this one exists! how to raise error to user?
if (InsertOrUpdate.Equals("UPDATE"))
{
DocumentEvents.Update.Allow = false;
DocumentEvents.Update.Continue = false;
}
else
{
DocumentEvents.Insert.Allow = false;
DocumentEvents.Insert.Continue = false;
}
}
else
{
objDoc.SetValue("HouseListingTitle", title);
objDoc.SetValue("State", state);
}
}
}

}
}

User avatar
Member
Member
kentico_edwardh - 12/20/2012 9:40:09 AM
   
RE:How do you cancel insert/update and notify user via Custom DocumentEvents?
Hello Matt,

I assume by a duplicate entry you are referring to a given field in the document type, and not a duplicate document with the same name/content. In this case you could consider using the SelectNodes method with a WHERE condition which checks the content of your field. The example below would check the title field:
public static DataSet SelectNodes(string aliasPath,
bool combineWithDefaultCulture,
string classNames,
string where)

Example:
DataSet ds = null;
string title = "DocumentTitle";
UserInfo ui = UserInfoProvider.GetUserInfo("administrator");
CMS.TreeEngine.TreeProvider tree = new CMS.TreeEngine.TreeProvider(ui);
ds = tree.SelectNodes("CorporateSite", "/%", "en-us", true, "[your_document_type]","Title like '" + title + "'");
if(ds==null)
{
// there is no document with Title = DocumentTitle
}

You could use this example to match the given fields and if duplicate content is found, you can send an e-mail message using our API: CMS.EmailEngine.EmailSender.SendEmail() method. If duplicate content is not found, you can continue with the document update.

User avatar
Member
Member
matt-awg - 1/3/2013 12:20:31 PM
   
RE:How do you cancel insert/update and notify user via Custom DocumentEvents?
I have been having a discussion with support on this topic and here is the final resolution for those that may want to know....

my code was almost correct except I needed to change this:
DocumentEvents.Insert.Allow = false;
DocumentEvents.Insert.Continue = false;

to this:
DocumentEvents.Insert.SupportsCancel = false;
DocumentEvents.Insert.Continue = false;

BUT there is no way to change the error message returned without a source code license so users will have no idea why the insert was canceled. Also, apparently you have to run this code or no further events will be processed:
DocumentEvents.Insert.SupportsCancel = true;
DocumentEvents.Insert.Continue = true;

Not ideal, especially if your installation has multiple sites, and they all use event handling. Once a person trips this canceling of handling the event, all future events won't be handled until the above code is executed. I am looking into other ways of doing this since this does not work for me, possibly a database trigger to cancel the update or insert instead. For now I just added a unique index on the table for this custom document type which prevents the insert/update from going through but still does not allow you to customize the message returned.

User avatar
Kentico Support
Kentico Support
kentico_jurajo - 1/4/2013 3:48:28 AM
   
RE:How do you cancel insert/update and notify user via Custom DocumentEvents?
Hi,

I think I may have found a solution, there is also property "Continue" that is resolved in current Insert context only. If you do not want to continue with this insertion, you will set the Continue to false, otherwise it will be true. This won't affect the events on a global way - just for that particular insert execution.

Best regards,
Juraj Ondrus

User avatar
Member
Member
matt-awg - 1/4/2013 7:09:06 AM
   
RE:How do you cancel insert/update and notify user via Custom DocumentEvents?
Hi Juraj,
Thanks but look in my original code and I was originally setting the continue property:
DocumentEvents.Insert.Allow = false;
DocumentEvents.Insert.Continue = false;

I just ran another test removing the line above it where I was also setting the "Allow" property but the insert still goes through. I can tell it is going into the section of code that finds it to be a duplicate because the title and state values are not set which means it is not entering this section of code:
objDoc.SetValue("HouseListingTitle", title);
objDoc.SetValue("State", state);

That means it is calling the "Continue=false;" but still doing the insert. Is this a bug or am I missing something else here?
Thanks again,
Matt

User avatar
Kentico Support
Kentico Support
kentico_jurajo - 1/9/2013 7:22:39 AM
   
RE:How do you cancel insert/update and notify user via Custom DocumentEvents?
Hi again,

It is not a bug, it is my mistake. Using the continue property is global again. You shall not use it as it is in your first post at all. Instead, in the "DocumentEventArgs e" object is a property "CancelCurrent" and you need to use and set this one for the current event only.

Best regards,
Juraj Ondrus

User avatar
Member
Member
matt-awg - 1/9/2013 7:47:38 AM
   
RE:How do you cancel insert/update and notify user via Custom DocumentEvents?
Thanks Juraj,
But CancelCurrent is not a property of DocumentEventArgs. I can't even find CancelCurrent in any version 7 API or developer documentation or anywhere on devnet if I search for it. Am I misunderstanding you? I tried this code:
public override void Init()
{
// Assigns custom handlers to the appropriate events
DocumentEvents.Insert.Before += Document_Insert_Before;
DocumentEvents.Update.Before += Document_Update_Before;
DocumentEvents.Insert.After += Document_Insert_After;
DocumentEvents.Update.After += Document_Update_After;
}

private void Document_Insert_Before(object sender, DocumentEventArgs e)
{
// type the document as TreeNode
TreeNode objDoc = (TreeNode)e.Node;

switch (objDoc.NodeClassName.ToLower())
{
case "cms.houselisting":
// handle the event only for house listing items
if (!updateHouseListingStateAndTitle(objDoc))
{
//if this returned false it means there was a problem (duplicate house listing) so cancel update
e.CancelCurrent();
}
break;
}
}

but I get this error:
Error 28 'CMS.DocumentEngine.DocumentEventArgs' does not contain a definition for 'CancelCurrent' and no extension method 'CancelCurrent' accepting a first argument of type 'CMS.DocumentEngine.DocumentEventArgs' could be found (are you missing a using directive or an assembly reference?)

User avatar
Kentico Support
Kentico Support
kentico_jurajo - 1/9/2013 8:21:03 AM
   
RE:How do you cancel insert/update and notify user via Custom DocumentEvents?
Grrrrr! I knew I would noticed it before but I thought I was just blind (as it has happened in the past) - I mixed the versions today and the solution I opened was the v8. I am sorry for the confusion - but the good news is that it will be available in v8.

At this point I have no further ideas and you need to use with the previous solution.

Best regards,
Juraj Ondrus

User avatar
Member
Member
@davey_lad - 4/23/2013 9:37:30 AM
   
RE:How do you cancel insert/update and notify user via Custom DocumentEvents?
Hi Matt,

I'm interested if you ever came up with a definitive solution to this problem that you were happy with. And if you ever got around the problem of displaying a suitable error message to the user ?

Cheers
Dave

User avatar
Member
Member
matt-awg - 4/24/2013 8:00:33 AM
   
RE:How do you cancel insert/update and notify user via Custom DocumentEvents?
Hi Dave,

Sorry, but no I did not. I ended up creating my own unique index directly on the SQL table via: CREATE UNIQUE NONCLUSTERED INDEX sql statement. This cancels the update but I could not capture the error and explain to the user why their insert/update failed. That apparently requires a source code license. It is a very ugly solution but it is as close as I could get.

Thanks,
Matt

User avatar
Member
Member
Accepted solutionAccepted solution
lokendra.jain-anktech.co - 5/15/2013 5:38:08 AM
   
RE:How do you cancel insert/update and notify user via Custom DocumentEvents?
Hi Matt,

I have a custom table i want to stop duplicate insertion of records for any column.
could you please suggest me.

Thanks
Lokendra Jain

User avatar
Member
Member
matt-awg - 5/15/2013 6:47:27 AM
   
RE:How do you cancel insert/update and notify user via Custom DocumentEvents?
Hi Lokendra ,
If you read through the thread above, in the last post from me I said:
I ended up creating my own unique index directly on the SQL table via: CREATE UNIQUE NONCLUSTERED INDEX sql statement. This cancels the update but I could not capture the error and explain to the user why their insert/update failed. That apparently requires a source code license. It is a very ugly solution but it is as close as I could get.
That was the best solution I could come up with. If you want to check multiple fields, you will need to create a unique index for each field that you want to be unique (or combination of fields). You will need access to the kentico sql database to create the index(es), you can't do this through the CMS Desk or Site Manager. This will stop duplicates but you can't output a custom error message to the user, it will just have the generic Kentico message that you cannot modify without the source code license. I hope that helps.
Thanks,
Matt

User avatar
Kentico Legend
Kentico Legend
Brenden Kehren - 5/21/2013 1:04:02 PM
   
RE:How do you cancel insert/update and notify user via Custom DocumentEvents?
Matt, are you doing this within the CMSDesk or CMSSiteManager? If so, if you "throw new Exception()" it will be displayed in the UI via the message indicator below the save button.

I've been working through the same issues with custom tables. I need to check if a "primary" user already exists for an account. If so, I set a local variable to false and throw an exception. Here's some code

Initilization:
public override void Init()
{
// -- This line provides the ability to register the classes via web.config cms.extensibility section from App_Code
ClassHelper.OnGetCustomClass += GetCustomClass;
KwikTrip.CustomMacroMethods.RegisterMethods();
ObjectEvents.Insert.SupportsCancel = true;
ObjectEvents.Update.SupportsCancel = true;
ObjectEvents.Insert.Before += Insert_Before;
ObjectEvents.Insert.After += Insert_After;
ObjectEvents.Update.Before += Update_Before;
SecurityEvents.Authenticate.Execute += Authenticate_Execute;
}
Event:
private void Insert_Before(object sender, ObjectEventArgs e)
{
bool cont = true;
// checking to see if there is a an account with the same "primary type"
if (e.Object.ObjectType.ToLower() == "customtableitem.kc.userkcaccount")
{
int accountType = e.Object.GetIntegerValue("UserAccountType", 0);
int kcAccount = e.Object.GetIntegerValue("KcAccount", 0);

if (accountType == 1)
{
if (HasPrimaryAccountHolder(kcAccount))
{
cont = false;
throw new Exception("This account (" + kcAccount.ToString() + ") already has a primary account holder assigned to it.");
}
}
}
ObjectEvents.Insert.Continue = cont;
}
Setting ObjectEvents.Insert.Continue = cont allows me to have the same event fire again. The key is to always set it to "true" unless you find a reason not to, then throw the exception and set the value to false.