Get parent mail item to read headers when message is forwarded or replied to - c#-4.0

I want to read the mail item headers to look for a specific value when an item is replied to or forwarded. If the value is found in the headers we will then take some action. The code below will trap the Forward event but it does not give me the parent message that contains all the typical header data. Instead it traps a new message based on the parent that has no header data. This makes sense since it has yet to be sent.
I have also attempted do the same thing with the Application.ItemSend event, to trap when the user clicks the send button. Unfortunately, I still can't figure out how to access the parent message.
My question is, is there another event or another process that we need to use to capture the parent when a user hits the reply or forward button or a better wat to go about this entirely?
Thanks in advance for your assistance!
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
_activeExplorer = Application.Explorers[1];
_activeExplorer.SelectionChange += _activeExplorer_SelectionChange;
}
private void _activeExplorer_SelectionChange()
{
Selection selection = Application.ActiveExplorer().Selection;
if (selection != null && selection.Count == 1 && selection[1] is MailItem)
{
MailItem selectedMail = selection[1] as MailItem;
((Outlook.ItemEvents_10_Event)selectedMail).Forward += mailItem_Forward;
}
}
private void mailItem_Forward(object Forward, ref bool Cancel)
{
if (Forward != null)
{
if (Forward is Outlook.MailItem)
{
Outlook.MailItem mailItem = Forward as Outlook.MailItem;
mailItem.Save();
String EmailHeader = mailItem.PropertyAccessor.GetProperty("http://schemas.microsoft.com/mapi/proptag/0x007D001E");
// The string is always null because this is a new message , need to access the parent of the forward...how?
if (EmailHeader.Contains("X-Key-Classification") == true)
{
Debug.WriteLine(EmailHeader);
}
}
}
}

But MailItem.Forward event fires on a particular item, so you do know what that parent message is. If you use the same callback for multiple items, you can create a wrapper for the MailItem object with the object stored in a property and the event handler beign a method on that wrapper object - this way you will know which item raised the event.

Related

Cannot trigger cancel button action after processing results returned

Within the Acumatica 19.201.0070 framework I have created a custom processing page that utilizes PXFilteredProcessing with the old style processing UI public override bool IsProcessing => false; I have defined a cancel button (below) that will clear the graph and set some values of the processing filter.
public PXCancel<NPMasterSubGeneratorFilter> Cancel;
[PXCancelButton()]
protected virtual IEnumerable cancel(PXAdapter adapter)
{
NPMasterSubGeneratorFilter row = Filter.Current;
if (row != null)
{
this.Clear();
Filter.SetValueExt<NPMasterSubGeneratorFilter.segmentID>(Filter.Current, row.SegmentID);
if (!(row.NewSegment ?? false)) Filter.SetValueExt<NPMasterSubGeneratorFilter.segmentValue>(Filter.Current, row.SegmentValue);
}
return adapter.Get();
}
This works perfectly fine except for a single use case, after processing results are shown if the user then presses the cancel button the corresponding action is never hit. ( My fellow office devs state that core Acumatica processing pages seem to operate the same. )
Setting of the processing delegate is within the filter RowSelected event.
GeneratedSubs.SetProcessDelegate(list => CreateSubaccounts(list, row));
I have implemented a few iterations of my processing method but the current is below.
protected virtual void CreateSubaccounts(List<NPGeneratedSub> subs, NPMasterSubGeneratorFilter filter)
{
if (filter.NewSegment ?? false)
{
try
{
SegmentMaint segGraph = PXGraph.CreateInstance<SegmentMaint>();
segGraph.Segment.Update(segGraph.Segment.Search<Segment.dimensionID, Segment.segmentID>(AADimension.Subaccount, filter.SegmentID.Value));
SegmentValue value = segGraph.Values.Insert(new SegmentValue() { Value = filter.SegmentValue, Descr = filter.Description });
segGraph.Actions.PressSave();
}
catch
{
throw new PXOperationCompletedSingleErrorException(NonProfitPlusMessages.SegmentValueCannotCreate);
}
}
SubAccountMaint subGraph = PXGraph.CreateInstance<SubAccountMaint>();
NPSubAccountMaintExtension subGraphExt = subGraph.GetExtension<NPSubAccountMaintExtension>();
subGraphExt.save.ConfirmSaving = false;
Sub newSub;
bool errored = false;
foreach (NPGeneratedSub sub in subs)
{
PXProcessing<NPGeneratedSub>.SetCurrentItem(sub);
try
{
newSub = subGraph.SubRecords.Insert(new Sub() { SubCD = sub.SubCD, Description = sub.Description });
subGraph.Save.Press();
subGraph.Clear();
PXProcessing<NPGeneratedSub>.SetProcessed();
}
catch (Exception e)
{
PXProcessing<NPGeneratedSub>.SetError(e);
errored = true;
}
}
if (errored)
{
throw new PXOperationCompletedWithErrorException();
}
}
What needs to be adjusted to allow the buttons action to be triggered on press after processing results have been returned?
After stepping through the javascript I discovered that it wasn't sending a request to the server when you click the cancel button on this screen after processing. The reason is because SuppressActions is getting set to true on the Cancel PXToolBarButton. I compared what I was seeing on this screen to what was happening on screens that work correctly and realized that Acumatica is supposed to set SuppressActions to true on the Schedule drop down PXToolBarButton but for some reason, on this screen, it is incorrectly setting it to true on whatever button is after the Schedule drop down button.
I looked through the code in PX.Web.UI and it looks like they set SuppressActions to true when a drop down button is disabled and PXProcessing adds a FieldSelecting event to the Schedule button which disables the button after you click process. However, I didn't notice any obvious issues as to why the code would be setting it on the wrong PXToolBarButton so someone will likely need to debug the code and see what's going on (we are unable to debug code in PX.Web.UI.dll).
I tried commenting out the other grids in the aspx file that aren't related to the PXProcessing view and this resolved the issue. So my guess would be that having multiple grids on the PXProcessing screen somehow causes a bug where it sets SuppressActions on the wrong PXToolBarButton. However, since the multiple grids are a business requirement, removing them is not a solution. Instead, I would suggest moving all buttons that are after the schedule button to be before the schedule button. To do this, just declare the PXActions before the PXFilteredProcessing view in the graph.
Please try this
Override IsDirty property
Use PXAction instead of PXCancel
Add PXUIField attribute with enable rights
action name should start from lowercase letter
delegate name should start from uppercase letter
see code below
public override bool IsDirty => false;
public override bool IsProcessing
{
get { return false;}
set { }
}
public PXAction<NPMasterSubGeneratorFilter> cancel;
[PXUIField(MapEnableRights = PXCacheRights.Select)]
[PXCancelButton]
protected virtual IEnumerable Cancel(PXAdapter adapter)
{
NPMasterSubGeneratorFilter row = Filter.Current;
if (row != null)
{
this.Clear();
Filter.SetValueExt<NPMasterSubGeneratorFilter.segmentID>(Filter.Current, row.SegmentID);
if (!(row.NewSegment ?? false)) Filter.SetValueExt<NPMasterSubGeneratorFilter.segmentValue>(Filter.Current, row.SegmentValue);
}
return adapter.Get();
}

Controlling the event ElementTypeDuplicated

I am controlling the event "application.ControlledApplication.ElementTypeDuplicated" and this event raise after the name of the new type is imputed, but after that I would like to override the result of the dialog box ( ID: "IDD_SYMBOL_ATTRIB") that were raised before the event ElementTypeDuplicated. I already try to get a Object Args and override the result inside the method that is suubscribing the event ElementTypeDuplicated, but is not working. Is there a way of doing this?
Example:
public void OnElementTypeDuplicated(object o, ElementTypeDuplicatedEventArgs args)
{
//doing things
duplicatingTypeArgs.OverrideResult(0);
}
}
}
public void OnDialogDuplicatingELement(object o, DialogBoxShowingEventArgs args)
{
if (args.DialogId=="IDD_SYMBOL_ATTRIB")
{
duplicatingTypeArgs = args;
}
}
Haven't tested this yet, but how about implementing IUpdater with "Element.GetChangeTypeElementAddition" instead subscribing to the duplicating type event
You could subscribe to the DocumentChanged event before duplicating the symbol. That will provide you with the element ids of all newly created elements. An example of using that is provided by the place family instance sample.
After the duplication, unsubscribe again.
You can use the Idling event to be notified when the duplication has terminated.

OnEnterBreakMode does not fire

Running VS 2012. I created an add-in. I want to handle the OnEnterBreakMode event. The Exec method is called. I tried returning handled = true/false. The handler is never invoked. I tried a few variations of DTE and DTE2. I go to the Tools menu > click "MyAddIn1" and the Exec method is called. I verified the event is bound. I do not know how the life cycle of an add-in works.
StartEvents is not an override and it's not connected to anything. I find that strange...
public void Exec(string commandName, vsCommandExecOption executeOption, ref object varIn, ref object varOut, ref bool handled)
{
handled = false;
if(executeOption == vsCommandExecOption.vsCommandExecOptionDoDefault)
{
if(commandName == "MyAddin1.Connect.MyAddin1")
{
//handled = true;
// Place the following code in the Exec method of the add-in:
EnvDTE.DebuggerEvents debugEvents = _applicationObject.Events.DebuggerEvents;
debugEvents.OnEnterBreakMode += new _dispDebuggerEvents_OnEnterBreakModeEventHandler(Connect.BreakHandler);
return;
}
}
}
private DTE _applicationObject;
private AddIn _addInInstance;
// Place the following Event handler code in the add-in:
// Needed to activate event handlers in Connect.Exec.
public static void StartEvents(DTE dte)
{
Console.WriteLine("Events are attached.");
}
// OnEnterBreakMode Event.
public static void BreakHandler(dbgEventReason reason, ref dbgExecutionAction execAction)
{
Console.WriteLine("Debugger enters break mode. " + "Reason: " + reason.ToString());
}
If you create a local variable for an event, and subscribe to it, then the variable will be freed by GC after scope is left, and your event handler won't fire.
If you make debugEvents a member variable then it should work fine.

Passing args in ExecuteQueryAsync method.

I have Silverlight app, which has some combo boxes, which I want to fill with fields names from SharePoint list. Every ComboBox can have different fields from this list, e.g. ComboBoxA holds user field and ComboBoxB number fields.
Now, I don't want to create different ClientRequestSucceededEventHandler and ClientRequestFailedEventHandler for every ComboBox. I don't want to "simulate" asynchronous processing either.
My idea was to pass some param to these event handlers (e.g. reference to destination combo box & items collection):
void UserControl_Loaded(object sender, RoutedEventArgs e)
{
Context = ClientContext.Current;
//load query for getting user fields
Context.ExecuteQueryAsync(fieldsCallback_Succeeded(cbUserFields, userFields), fieldCallback_Failed);
//load query for getting number fields
Context.ExecuteQueryAsync(fieldsCallback_Succeeded(cbUserFields, numberFields), fieldCallback_Failed);
}
void fieldsCallback_Succeeded(object sender, ClientRequestSucceededEventArgs e)
{
FieldsQueryParams queryParams = sender as FieldsQueryParams;
this.Dispatcher.BeginInvoke(() => queryParams.cbToFill = queryParams.Fields);
}
OR
void fieldsCallback_Succeeded(object sender, ClientRequestSucceededEventArgs e, ComboBox cbToFill, IEnumerable<Field> fields)
{
this.Dispatcher.BeginInvoke(() => cbToFill.ItemsSource = fields);
}
So the question is: how to pass some param to these event handlers (e.g. reference to destination control). Or how to resolve this problem in other way?
Inherit the class, create a specialized instance that takes arguments, either a list, object or specifically typed objects as you like. You should be able to pass it in and then process the result as you're envisioning since it has all the implementation it expects to see and evaluates to the correct type. Since it's a callback, I don't think you'll need to cast it.
It expects to see:
public virtual void ExecuteQueryAsync(
ClientRequestSucceededEventHandler succeededCallback,
ClientRequestFailedEventHandler failedCallback
)

SPItemEvent: abort the event from the EventHandler

How can I abort an item even (in my case, ItemDeleting) so that it doesn'r get executed? I want the deletion not to take place if certain conditions are matched and do it silently for the use (no messages, no exceptions). Thanks
EDIT:
SP 2010
public override void ItemDeleting(SPItemEventProperties properties) {
properties.Cancel = true;
properties.ErrorMessage = "Something went wrong!";
}
If you cancel it though, it will be reported back to the user, nothing you can do about that.
UPDATE
For use the Status property
http://msdn.microsoft.com/en-us/library/microsoft.sharepoint.speventpropertiesbase.status.aspx
public override void ItemDeleting(SPItemEventProperties properties) {
properties.Status = SPEventReceiverStatus.CancelNoError;
}

Resources