📜 ⬆️ ⬇️

Sharepoint 2010 / customization of search alerts

I want to talk about my own successful experience in customizing search alerts in Sharepoint 2010 using IAlertNotifyHandler.

At first, the task seemed trivial. It seems like everything is simple. The MSDN documentation has an article describing the process itself, there are examples on other resources. But, as it turned out, customization of all types of alerts, except search engines , is described everywhere. Since I haven’t found a solution in the whole Internet, I’m presenting my own (maybe not the most optimal, but working).

Condition


There is a SQL database, which Sharepoint search is implemented using Business Connectivity Services (BCS) and Enterprise Search. The output of search results to a Sharepoint user is customized using the appropriate XSLT transforms. Thus, when searching, the user gets the result approximately in this form (the base is working - hereinafter the names and all that are crossed out).


')
By means of Sharepoint, users can assign alerts for each search query that are triggered when changes are made to the database. When triggered, the user receives an email of the following form.



That is, the standard alert does not take into account the XSLT transformations and gives the user unreadable information. Therefore, the problem arises to convert the text of the email into a readable one, preferably the same as in the search.

Standard alert customization solution



First, I briefly describe the standard solution, which is not suitable for search alerts , but parts of which (paragraphs 2, 5-11) will be needed later to launch my version.

1. Create a dll AlertHandler with class Class1 that implements the IAlertNotifyHandler interface, in the OnNotification method of which we can receive the current alert text and modify it accordingly.

2. Put dll in GAC

3. Make a copy of the alertTemplates.xml file which is located: C: \ Program Files \ Common Files \ Microsoft Shared \ Web Server Extensions \ 14 \ TEMPLATE \ XML. In this case, always modify the copy of the file and not the original (this is important so that you can later return to the standard handler and not nakosyachit).

4. Name the new file (copy alertTemplates.xml) of CustomAlertTemplates and save it. Modify the file as follows. Find the properties block and add the following lines to this block:

< NotificationHandlerAssembly > AlertHandler, Version=1.0.0.0, Culture=neutral, PublicKeyToken=d59ecf2a3bd66904 </ NotificationHandlerAssembly >
< NotificationHandlerClassName > AlertHandler.Class1 </ NotificationHandlerClassName >
< NotificationHandlerProperties ></ NotificationHandlerProperties >


* This source code was highlighted with Source Code Highlighter .


Now the whole block will look like this:

< Properties >
< ImmediateNotificationExcludedFields > ID;Author;Editor;Modified_x0020_By;Created_x0020_By;_UIVersionString;ContentType;TaskGroup;IsCurrent;Attachments;NumComments; </ ImmediateNotificationExcludedFields >
< DigestNotificationExcludedFields > ID;Author;Editor;Modified_x0020_By;Created_x0020_By;_UIVersionString;ContentType;TaskGroup;IsCurrent;Attachments;NumComments; </ DigestNotificationExcludedFields >
< NotificationHandlerAssembly > AlertHandler, Version=1.0.0.0, Culture=neutral, PublicKeyToken=d59ecf2a3bd66904 </ NotificationHandlerAssembly >
< NotificationHandlerClassName > AlertHandler.Class1 </ NotificationHandlerClassName >
< NotificationHandlerProperties ></ NotificationHandlerProperties >
</ Properties >


* This source code was highlighted with Source Code Highlighter .


Note: PublicKeyToken can be viewed in the dll properties by finding it in C: \ windows \ assembly.

5. Run the command from C: \ Program Files \ Common Files \ Microsoft Shared \ web server extensions \ 14 \ BIN: stsadm -o updatealerttemplates -filename "C: \ Program Files \ Common Files \ Microsoft Shared \ Web Server Extensions \ 14 \ TEMPLATE \ XML \ customalerttemplates.xml »-url 6. Run the command: stsadm -o setproperty -pn job-immediate-alerts -pv" every 1 minutes "this is needed only for testing. Return back after testing.

7. Make sure that SharePoint is configured to send outgoing emails.

8. Make sure that the alert is enabled for example a document library if you are testing on a document library.

9. Run the command: iisreset

10. Run the command: services.msc

11. Restart Windows SharePoint Services Timer.

Standard solution problem



Everything is great, everything works, but not for the case of search alerts. For search alerts in the OnNotification method (SPAlertHandlerParams ahp), the values ​​of the ahp.eventData and ahp.body fields do not come in, which are actually needed for customization.

In response to my questions on the social.technet.microsoft.com forums , I received the following information.

First, you can customize the SharePoint alert using IAlertNotifyHandler as the following:
blogs.msdn.com/b/sharepointdeveloperdocs/archive/2007/12/14/how-to-customizing-alert-emails-using-ialertnotificationhandler.aspx
Second, there is no search alert.
All the Alert template: msdn.microsoft.com/en-us/library/bb802738.aspx

That is, it turns out that there is no solution?! .. But since I was not used to giving up without a fight, I continued to collect information from the Internet bit by bit. And that's what came of it.

My version



1. Reading the forums showed that to process search alerts, first of all, you need to add a new type of AlertTemplate - “OSS.Search”. Add to the CustomAlertTemplates file.

< AlertTemplate Type ="Custom" Name ="OSS.Search" AlwaysNotify ="True" DefaultTitle ="Search" >
< EventTypes IsVisible ="True" >
< EventType Mask ="0x1" Selected ="true" > $Resources:Microsoft.Office.Server.Search,SearchResults_ATEventDiscovered; </ EventType >
< EventType Mask ="0x2" > $Resources:Microsoft.Office.Server.Search,SearchResults_ATEventModified; </ EventType >
< EventType Mask ="0x3" > $Resources:Microsoft.Office.Server.Search,SearchResults_ATEventAll; </ EventType >
</ EventTypes >
< Frequency IsVisible ="true" ShowImmediate ="false" ShowDaily ="true" ShowWeekly ="true" ShowTime ="false" DefaultFrequency ="Daily" />
< Filters IsVisible ="false" />
< Properties >
< NotificationHandlerAssembly > mySearchAlert, Version=1.0.0.0, Culture=neutral, PublicKeyToken=aa1e89f3cc0ef56b </ NotificationHandlerAssembly >
< NotificationHandlerClassName > mySearchAlert.MySearchAlertHandler </ NotificationHandlerClassName >
< NotificationHandlerProperties ></ NotificationHandlerProperties >
< UpdateHandlerAssembly > mySearchAlert, Version=1.0.0.0, Culture=neutral, PublicKeyToken=aa1e89f3cc0ef56b </ UpdateHandlerAssembly >
< UpdateHandlerClassName > mySearchAlert.MySearchUpdateAlertHandler </ UpdateHandlerClassName >
< UpdateHandlerProperties ></ UpdateHandlerProperties >
</ Properties >
</ AlertTemplate >


* This source code was highlighted with Source Code Highlighter .


It is important to note that in order to return back to the standard handler, the previously saved standard file is no longer enough, so we copy the standard alertTemplates.xml into a file, for example, into alertTemplates_forRestore.xml and add the following section

< AlertTemplate Type ="Custom" Name ="OSS.Search" AlwaysNotify ="True" DefaultTitle ="Search" >
< EventTypes IsVisible ="True" >
< EventType Mask ="0x1" Selected ="true" > $Resources:Microsoft.Office.Server.Search,SearchResults_ATEventDiscovered; </ EventType >
< EventType Mask ="0x2" > $Resources:Microsoft.Office.Server.Search,SearchResults_ATEventModified; </ EventType >
< EventType Mask ="0x3" > $Resources:Microsoft.Office.Server.Search,SearchResults_ATEventAll; </ EventType >
</ EventTypes >
< Frequency IsVisible ="true" ShowImmediate ="false" ShowDaily ="true" ShowWeekly ="true" ShowTime ="false" DefaultFrequency ="Daily" />
< Filters IsVisible ="false" />
< Properties >
< NotificationHandlerAssembly > Microsoft.Office.Server.Search, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c </ NotificationHandlerAssembly >
< NotificationHandlerClassName > Microsoft.Office.Server.Search.Query.SearchAlertHandler </ NotificationHandlerClassName >
< NotificationHandlerProperties ></ NotificationHandlerProperties >
< UpdateHandlerAssembly > Microsoft.Office.Server.Search, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c </ UpdateHandlerAssembly >
< UpdateHandlerClassName > Microsoft.Office.Server.Search.Query.SearchAlertHandler </ UpdateHandlerClassName >
< UpdateHandlerProperties ></ UpdateHandlerProperties >
</ Properties >
</ AlertTemplate >


* This source code was highlighted with Source Code Highlighter .


Carefully store the alertTemplates_forRestore.xml file - rollback to the standard handler is impossible without it.

2. Having poked my head, I thought that, once using a standard solution, some part of the information still comes to us, then we can start from it. And I can find not the most elegant, but working solution.


I will not fully include all the code (it is long) - only highlights.

public class MySearchAlertHandler : IAlertNotifyHandler
//
public bool OnNotification(SPAlertHandlerParams alertHandler)
{
...
return CustomAlertNotification(alertHandler);
...
}


public bool CustomAlertNotification(SPAlertHandlerParams alertHandlerParams)
{
string myBody = "" ;
SPSite site = null ;
int searchAlertNotificationQuota = 200;
TimeSpan span;
SPAlert a = alertHandlerParams.a;


site = new SPSite(alertHandlerParams.siteUrl+ alertHandlerParams.webUrl;);


// alertTime span

DateTime alertTime = a.AlertTime;
if (a.AlertFrequency == SPAlertFrequency.Weekly)
{
span = TimeSpan .FromDays(7.0);
}
else
{
span = TimeSpan .FromDays(1.0);
}

if ((alertTime + span) <= DateTime .Now)
{
return true ;
}

using (SPWeb web = site.OpenWeb())
{


// ( ) queryText

string queryText = Utils.GetValueFromXML(a.Properties[ "p_query" ], "QueryText" );

SPSite s1 = new SPSite (alertHandlerParams.siteUrl+alertHandlerParams.webUrl );
Query q1 = new KeywordQuery(s1);
q1.QueryText = queryText;

// alert2,

SearchAlert alert2 = new SearchAlert(s1,q1);
alert2.ChangeType = AlertChangeType.DiscoveredOrModified;
alert2.InnerAlert.AlertFrequency = alertHandlerParams.a.AlertFrequency ;
alert2.InnerAlert.Title = "Temp#1" ;
alert2.InnerAlert.EventType = alertHandlerParams.a.EventType;
alert2.InnerAlert.User = s1.OpenWeb().CurrentUser ;
alert2.InnerAlert.AlertType = alertHandlerParams.a.AlertType;
alert2.Update();


// query alert2 queryText

using (Microsoft.Office.Server.Search.Query.Query query = alert2.CreateSearchQuery ())
{
ResultTableCollection tables;

query.QueryText = queryText;
query.RowLimit = searchAlertNotificationQuota;
query.TrimDuplicates = false ;

// , , , .
, Reflection internal AlertInfo query, LastUpdateTime

AlertInfo ai = new AlertInfo();
ai.ChangeType = alert2.ChangeType;
ai.LastUpdateTime = alertTime - span;

Type t1 = query.GetType();
if (t1.GetProperty( "AlertInfo" , System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic |
System.Reflection.BindingFlags.Instance) == null )
throw new ArgumentOutOfRangeException( "propName" , string .Format( "Property {0} was not found in Type {1}" ,
"AlertInfo" , query.GetType().FullName));
t1.InvokeMember( "AlertInfo" , System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.SetProperty |
System.Reflection.BindingFlags.Instance, null , query, new object [] { ai });


// query

ResultTable result = null ;
try
{
tables = query.Execute();
}
catch
{
alert2.Delete();
return false ;
}

result = tables[ResultType.RelevantResults];

// result - alert2 -


alert2.Delete();

// DataTable . - WorkId, Rank, Title, Author, Size, Path, Description, Write, SiteName, CollapsingStatus, HitHighlightedSummary, HitHighlightedProperties, ContentClass, IsDocument, PictureThumbnailURL

// DiscoveredTime

System.Data.DataTable myTable = new System.Data.DataTable();
myTable.Load(result2, System.Data.LoadOption.OverwriteChanges);

foreach (System.Data.DataRow myrow in myTable.Rows)
{
if ( Convert .ToDateTime(myrow[ "DiscoveredTime" ]) > alertTime - span)
{
//
}
else
{
//
}
}

// , SQL, FullTextSqlQuery

FullTextSqlQuery fts = new FullTextSqlQuery(site2);
fts.QueryText = "SELECT WorkId, Rank, Title, Author, Size, Path, Description, Write, SiteName, CollapsingStatus, HitHighlightedSummary, HitHighlightedProperties, ContentClass, IsDocument, PictureThumbnailURL, PopularSocialTags, PictureWidth, PictureHeight, DatePictureTaken, ServerRedirectedURL, ErgebnisKenntnis, LetzterKontakt, Kandidatenmail FROM SCOPE() WHERE Path='" + myrow[ "Path" ].ToString() + "'" ;
fts.ResultTypes = ResultType.RelevantResults;
fts.RowLimit = 300;
ResultTableCollection rtc = fts.Execute();


* This source code was highlighted with Source Code Highlighter .


Where ErgebnisKenntnis, LetzterKontakt, Kandidatenmail are the SQL database fields (for example purposes only). To be able to search by them, it is important that these fields be registered in the Metadata property of the Sharepoint 2010 server.

Now we can form the HTML body of the myBody letter using any fields of the SQL database, Sharepoint and at the end do not forget to send it to the user.

SPUtility.SendEmail(web, false , false , string .Format( "{0}" , alertHandlerParams.headers[ "To" ]),
string .Format( "{0}" , alertHandlerParams.headers[ "Subject" ]), myBody);


* This source code was highlighted with Source Code Highlighter .


Now, after performing points 2, 5-11 of the standard solution, users will receive search alerts, for example, in this form.



Everything, the problem is solved. I did not find any other ways on the Internet, so I will be glad if my decision will help someone.

Source: https://habr.com/ru/post/123863/


All Articles