How to send alert to user via plugin without using InvalidPluginExecutionException? - c#

I am currently using InvalidPluginExecutionException to send the message to the user, but it turns out that the message is in English "Business Process Error" beyond which the error box appears the button "download log file". This is not an error because the user is trying to duplicate a record, as can be seen in the code. Is there other way without having to use InvalidPluginExecutionException to show an alert?
QueryExpression query1 = new QueryExpression();
query1.ColumnSet = new ColumnSet(true);
query1.EntityName = "new_appraisers";
EntityCollection ec = service.RetrieveMultiple(query1);
if (ec.Entities.Count <= 0)
{
log.Tb_Log_Create("Appraiser created");
}
else
{
foreach (Entity app in ec.Entities)
{
if (app["fcg_appraiser"].ToString() == name)
{
log.Tb_Log_Create("appraiser allready exist");
throw new InvalidPluginExecutionException("The name allready exists");
}
if (app["new_login"].ToString() == login)
{
log.Tb_Log_Create("appraiser allready exist");
throw new InvalidPluginExecutionException("The login allready exists.");
}
}
}

The only method to display a message box to the user from a plugin is using an exception from the validation stage. You could use javascript however, perform a simple OData query on the On_Save event of the form, and display an alert box with whatever information you'd like, and cancel the saving of the form.
This would allow you to display whatever custom message you'd like, and keep the plugin from firing and displaying the download file dialog.

I may be little late, however, in more recent versions of CRM there are several possibilites to achieve what you want. The better ones beeing:
Business Rules
Validation using JS and notifying the user using
Form Notifications or
Control Notifications
I hope Microsoft doesn't read this but...
You can also use a synchronous Plugin and be happy with the Business Process Error Dialog popping off. I just found out that this Dialog is hackable to some degree. Just return HTML in the Exeptions Message like so:
throw new InvalidPluginExecutionException(
#"<img height='16px' src='http://emojione.com/wp-content/uploads/assets/emojis/1f644.svg'> <strong>Oh snap!</strong>
It seems the record can not be saved in its current state.
");
Which results in sth. like this:

Related

Message read by OnEntryWritten are coming with parameters not set

I'm using the OnEntryWritten event in order to get events from the event log when they are fired.
The problem I started to see today is that some messages come with parameters unset.
For example:
The Windows Filtering Platform has permitted a bind to a local port. Application Information: Process ID:9852 Application Name:\device\harddiskvolume7\program files (x86)\google\chrome\application\chrome.exe Network Information: Source Address::: Source Port:51714 Protocol:17 Filter Information: Filter Run-Time ID:0 Layer Name:%%14608 Layer Run-Time ID:38
You can see the %%14608 parameter. This comes with a value if I see the same log in the Event Viewer.
I'm running a windows service as LocalSystem, so I don't know if this is a permission issue or this technology is not useful at all.
I have tried the rest offered on C# and they also don't meet my requirements.
UPDATE: this is the code I'm using to read the events.
1) First I subscribe to the corresponding Event log:
private void Subscribe()
{
_eventLog.EnableRaisingEvents = true;
var callbackFunction = new EntryWrittenEventHandler(OnEntryWritten);
_eventLog.EntryWritten += callbackFunction;
// Save a reference for callbackFunction
_eventHandler = callbackFunction;
}
2) Then on the callback method, I read data from the message:
public void OnEntryWritten(Object source, EntryWrittenEventArgs entryArgs)
{
// When overwrite policy is enabled, this will trigger for all elements when it starts writing new ones
try
{
var entry = entryArgs.Entry;
var timeWritten = entry.TimeWritten;
// This comes with %% values depending on the log
string message = entry.Message;
}
catch(Exception ex)
{
...
}
}
3) The event log variable is simply initialized as:
var eventLog = EventLog.GetEventLogs().FirstOrDefault(el => el.Log.Equals(logName, StringComparison.OrdinalIgnoreCase));
I need some help on this, honestly I don't know what else to try.
UPDATE
I'm adding some images here so everybody can understand the situation a little bit better. To be honest, it looks like there's no solution but to implement a dictionary and replace manually the required values, which appear to be always constants.
This is what I see on the Event Viewer for a given Event ID:
This is what I see on my program when I read that entry:
You can clearly see that the following values:
"Machine key." (Key type)
"Read persisted key from file." (Operation)
Are coming unmapped in the ReplacementStrings and the Message properties as: %%2499 and %%2458
This is the message value I get on the program:
"Key file operation.\r\n\r\nSubject:\r\n\tSecurity ID:\t\tS-1-5-18\r\n\tAccount Name:\t\tMyAccount$\r\n\tAccount Domain:\t\tWORKGROUP\r\n\tLogon ID:\t\t0x3e7\r\n\r\nProcess Information:\r\n\tProcess ID:\t\t6644\r\n\tProcess Creation Time:\t2019-04-03T12:17:24.587994400Z\r\n\r\nCryptographic Parameters:\r\n\tProvider Name:\tMicrosoft Software Key Storage Provider\r\n\tAlgorithm Name:\tUNKNOWN\r\n\tKey Name:\t816339d2-c476-4f1e-bc40-954f0aa0f851\r\n\tKey Type:\t%%2499\r\n\r\nKey File Operation Information:\r\n\tFile Path:\tC:\ProgramData\Microsoft\Crypto\Keys\6d55a45fd69327293e9ed3e7930f4565_5663a8bb-2d1d-4c0d-90c1-624beddabe9c\r\n\tOperation:\t%%2458\r\n\tReturn Code:\t0x0"
What can be done here? There also nothing in entry.Data that might help me out to obtain both values.
No, I believe you're mistaken, sorry that this answer is too late. I found a similar event that was raised by chrome and evaluated if there's anything missed by the event handler. There wasn't anything missed. The message I got in my console output exactly matched what I saw in my Event Viewer.
A better solution would be to
Use entry.Data to get the complete data instead of entry.Message
entry.Data will return a byte[] which you can convert to a string. Here's the link to all properties that an entry will have.

Trouble opening QB

I am a long time c# developer but brand new to QBFC. I have downloaded the samples and was actually able to add an invoice to my file with it, but I am a little confused. I have trouble connecting unless QB is up and running. I was trying to follow the code in the sample, but it is difficult. I need this app to add invoices and bills to the file even if QB is not open. They only have one file so there won't be an instance where another file is already open. Also, the environment is simple as everything runs on the same computer.
My basic questions are:
How to select the correct QB file and provide credentials to allow access?
Is there a decent simple example using QBFC? Everything I have found is using XML which seems overly complicated compared to QBFC.
I cannot seem to get QB to open automatically. I have tried the code below and I get an error that states "Could not start QuickBooks".
Any pointers are greatly appreciated.
QBSessionManager qbSession = new QBSessionManager();
qbSession.OpenConnection("", "Lumber Management System");
try
{
qbSession.BeginSession("C:\\Users\\Jerry\\Documents\\QuickBooks\\Company Files\\MRJ Tecnology, LLC", ENOpenMode.omDontCare);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message + '\n' + ex.StackTrace, "Error opening QB");
}
There are a couple of things that you need in order for this to work. The first time that you request access to a company file, QuickBooks must be opened and the Admin must be logged in. The Admin will then be given a dialog to grant permission to your application to access QuickBooks. In the permission dialog, it will ask the Admin if they want to allow the application to read and modify the company file with four options:
No
Yes, prompt each time
Yes, whenever this QuickBooks company file is open
Yes, always; allow access even if QuickBooks is not running
The admin must choose the fourth option to allow your app to launch QuickBooks without running.
I would also suggest that you use OpenConnection2 instead of OpenConnection, and use a unique ID as the first parameter. You will also need to specify the connection type, which should be ENConnectionType.ctLocalQBD.
It also appears that the filename you are passing in the BeginSession call does not include the .qbw extension. Here is a basic sample:
QBSessionManager SessionManager = null;
try
{
SessionManager = new QBSessionManager();
SessionManager.OpenConnection2("UniqueAppID", "Lumber Management System", ENConnectionType.ctLocalQBD);
SessionManager.BeginSession("C:\\Users\\Jerry\\Documents\\QuickBooks\\Company Files\\MRJ Tecnology, LLC.qbw", ENOpenMode.omSingleUser);
// CODE TO SEND TO QB GOES HERE
}
catch(Exception ex)
{
MessageBox.Show("Error opening QB:" + ex.ToString());
}
finally
{
if(SessionManager != null)
{
SessionManager.EndSession();
SessionManager.CloseConnection();
}
}

Sharepoint 2007 - is there a way to determine if first check-in of file?

I thought this would be a lot easier, however I'm unable to find a way to determine in my event handler if it is the FIRST check-in of the file..
You see, I'm breaking role inheritance, and selectively inheriting permissions for files in doc libs, yet I wish to do it only once, during the first check-in of the file.
I've tried adding an entry to 'SPListItem.Properties' in the ItemAdded event in order to indicate if the file is new, however the moment I do 'SPListItem.Update()' it vanishes..
I've played with the ItemCheckingIn and ItemCheckedIn events with no success...
My only hope at the moment is adding a SPField to the ContentType to indicate if new file or not, but I really wish to avoid it..
ANY IDEAS????
PLEASE HELP ME!
I would recommend considering not only whether the system account has access, but also if the checked out date of the file is identical to the file's creation date.
public bool IsFirstCheckIn(SPListItem item)
{
// Item not null!
if (item != null)
{
SPSecurity.RunWithElevatedPrivileges(delegate
{
// Open privileged Site
using (SPSite pSite = new SPSite(site.ID))
{
// Open privileged Web
using (SPWeb pWeb = pSite.OpenWeb(web.ID))
{
// Create privileged SharePoint-Objects
SPList pList = GetList(pWeb, list.ID);
SPListItem pItem = GetListItem(pList, item.UniqueId);
// Check the Item
if (pItem == null)
{
// Can't access
return true;
}
else if (pItem.File != null && pItem.File.CheckedOutByUser != null)
{
// If the Item's File and checked out User is set, check if checked out date is equal creation date
return (pItem.File.CheckedOutDate.ToLocalTime() == pItem.File.TimeCreated.ToLocalTime());
}
}
}
});
}
return false;
}
To use the system account, is definitely a good idea, otherwise authorization settings would cause problems. Use the "local time" instead of the "UTC-Time", SharePoint handled the Time Zone while storing!
Seems like, SharePoint used the UTF-FileTime to store the file's creation time but used the Time Zone defined for the SPWeb or for the SPUser to store the file checked out date based on the "local time".
Fortunately the DateTime value does know what it is and can convert it to the same "local time" while calling ToLocalTime(). Strangely it will be still a File-Time while calling ToUniversalTime();
So I got a solution for this.
I'm sorry I can't post code here, I do not have access to internet on my dev machine.
It should be good enough for everyone.
Solution:
During the CheckingIn event I try to access the file using the SHAREPOINT\system("SPSecurity.RunWithElevatedPrivileges()"). During the first check-in the file is new and not accessible to other users besides the uploader.
So if I fail getting the file with SHAREPOINT\system it means its new, and I save its Guid in a dictionary in my EventHandlers class.
Then in the CheckedIn event I simply check if the dictionary contains the Guid of the current item, if it does - FIRST CHECK-IN, if it does not - NOT FIRST CHECK-IN.
Of course after I'm finished with the file I remove the entry from the dictionary.
Hope it helps, if you got any questions you are welcome to ask :)

Grabbing Currently Selected Folder in Outlook Add-In

I'm working on an Outlook Add-In that can work in one of two ways, depending on a user's choice - it can process selected emails, or alternatively, process all emails in the selected folder. I've gotten the first part working, but the second part is giving me trouble, possibly because I'm just adapting the code from the first part incorrectly. I believe the trouble comes down to grabbing the currently selected folder properly in a C# Outlook add-in. I'm using .NET 3.5 and Outlook 2007, by the way.
First, the email code - if a user selects one or more emails in their inbox, and runs my add-in with the "selected emails" option, the following code is run (and works fine!):
public static void processSelectedEmails(Outlook.Explorer explorer)
{
//Run through every selected email
for (int i = 1; i <= explorer.Selection.Count; i++)
//alternatively, foreach (Object selectedObject in explorer.Selection)
{
Object selectedObject = explorer.Selection[i];
if (!(selectedObject is Outlook.Folder))
{
string errorMessage = "At least one of the items you have selected is not an email.";
//Code for displaying the error
return;
}
else
Outlook.MailItem email = (selectedObject as Outlook.MailItem);
//Do something with current email
}
}
I've tried to adapt this code to do something else if a user goes to the Navigation Pane (on the left by default) in Outlook, selects a folder or subfolder (perhaps Inbox, Sent Items, or another folder they've created). The user can then choose the "process selected folder" option in my Add-In, which will do essentially the same thing as the code above, but process all of the email inside the selected folder. I have set it to only work if the user has selected a single folder.
public static void processFolder(Outlook.Explorer explorer)
{
//Assuming they have selected only one item
if (explorer.Selection.Count == 1)
{
//Make sure that that selected item is a folder
Object selectedObject = explorer.Selection[1];
if (!(selectedObject is Outlook.Folder))
{
string errorMessage = "The item you have selected is not a folder.";
//Code for displaying the error
return;
}
//Code for running through every email in that folder
}
}
I have not yet written the code to actually run through all of the emails in the selected folder, because my code never gets past the if (!(selectedObject is Outlook.Folder)). Even if the most recently selected item is your Inbox, I receive the error I have programmed in at that point. Perhaps I am misusing the explorer.Selection thing? Any help would be much appreciated.
This may be important to answering my question - the add-in has a field called 'explorer', which is generated on startup: explorer = this.Application.ActiveExplorer. This is the 'explorer' that is passed to my functions so that they can know what is selected. As I said, this works fine for selected emails, but does not work for selected folders. Any insight would be greatly appreciated!
Edit 1: It appears that this question is basically a duplicate of Get all mails in outlook from a specific folder, but it has no answers.
Edit 2: I've been doing further research, it appears that I can get virtually the same functionality (but with an additional step unfortunately) by creating a popup to select a folder using the Application.Session.PickFolder() method. Is there any way to do it based on the currently selected folder, instead of forcing the user to pick a new folder?
Edit 3: I have modified the code found here: http://msdn.microsoft.com/en-us/library/ms268994(v=vs.80).aspx to further show what is not working properly for me:
public static void processFolder(Outlook.Explorer explorer)
{
string message;
if (explorer.Selection.Count > 0)
{
Object selObject = explorer.Selection[1];
if (selObject is Outlook.MailItem)
{
message = "The item is an e-mail";
}
else if (selObject is Outlook.Folder)
{
message = "The item is a folder";
}
else
{
message = "No idea what the item is!";
}
Console.WriteLine(Message);
return;
}
}
Whether I select a message, or go to the Navigation Pane and select a folder, I receive the message "This item is an e-mail".
Explorer.Selection is for Items only (MailItem, AppointmentItem, etc.) - not Folders. To get access to the currently selected Folder you would need Explorer.CurrentFolder.
Folder.Items would provide you access to all the Items in a given Folder.

How to write to a custom event log?

I'm trying to get my .Net Windows Service to right to a custom event log. I'm using EventLogInstaller to create the event log and source when the application is installed. I read here that it takes a while for Windows to register the source so they reccomend you restart the application before trying to write to the log.
As this is a Windows Service I didn't want to have to force a computer restart or get the user to manually start the service up, so I use this code to wait for the log to exist and then start the service automatically.
while (!(EventLog.Exists("ManageIT") || EventLog.SourceExists("ManageIT Client Service")))
{
Thread.Sleep(1000);
}
System.ServiceProcess.ServiceController controller = new System.ServiceProcess.ServiceController("ManageIT.Client.Service");
controller.Start();
My problem is that events from the service are still written to the Application Log and although I can see my custom log in the Registry Editor it does not show up in the Windows 7 Event Viewer.
Any help will be much appreciated.
By default when a service is installed, the source gets associated with the Application Log.
If we change this association at a later point, the system needs a restart.
We can however prevent the association of the service with the application log, by setting autolog property to false in the service class (class which inherits from servicebase) constructor.
http://msdn.microsoft.com/en-us/library/system.serviceprocess.servicebase.autolog.aspx
Try this snippet:
edit - caveat: if the user running the code does not have administrator rights, this will throw an exception. Since this is the case (and if the user will not have these rights) best practices should be to assume the log exists, and simply write to it. see: The source was not found, but some or all event logs could not be searched
if (!EventLog.SourceExists("MyApplicationEventLog"))
{
EventSourceCreationData eventSourceData = new EventSourceCreationData("MyApplicationEventLog", "MyApplicationEventLog");
EventLog.CreateEventSource(eventSourceData);
}
using (EventLog myLogger = new EventLog("MyApplicationEventLog", ".", "MyApplicationEventLog"))
{
myLogger.WriteEntry("Error message", EventLogEntryType.Error);
myLogger.WriteEntry("Info message", EventLogEntryType.Information);
}
It sounds like you are writing to the event log like this:
EventLog.WriteEntry("Source", "Message");
This will write to the application log.
If you use the code in simons post with the creation of myLogger, you can specify the name of the Log.
I did something like this:
var logName = EventLog.LogNameFromSourceName("MyApp", Environment.MachineName);
//delete the source if it associated with the wrong Log
if (!string.IsNullOrEmpty(logName) & logName != "MyLog")
{
EventLog.DeleteEventSource("MyApp", Environment.MachineName);
}
if (!EventLog.SourceExists("MyApp"))
{
EventLog.CreateEventSource("MyApp", "MyLog");
}

Categories

Resources