Outlook draft message changes immutable ID when sent - email

Context I am using: office-js (retrieve rest ID of message item), java backend (using GraphClient to get the immutable ID, subscription webhook endpoint)
When I get the rest itemId of the draft item via office-js like this:
Office.context.mailbox.item.saveAsync((asyncResult) => {
if (asyncResult.error) {
//hadle
} else {
resolve(
Office.context.mailbox.convertToRestId
(
asyncResult.value,
Office.MailboxEnums.RestVersion.v1_0
)
);
}
});
I send it to the backend where I translate it to Immutable ID, via GraphClient, that I save.
Once I get a notification on my subscription endpoint (I change and save the subject of the message draft
in outlook), it is successfully paired.
Problem is when I send the draft from outlook. I get notification to the subscription enpoint, but it has a different immutable ID. I create subscriptions with Prefer header like this:
Subscription subscription = new Subscription();
subscription.changeType = "updated";
subscription.notificationUrl = notificationUrl;
subscription.resource = resource;
subscription.expirationDateTime = OffsetDateTime.now().plusDays(2);
subscription.clientState = secret;
subscription.latestSupportedTlsVersion = "v1_2";
SubscriptionCollectionRequest request = graphServiceClient.subscriptions().buildRequest();
if(request != null) {
request.addHeader("Prefer", "IdType=\"ImmutableId\"");
request.post(subscription);
} else {
Is there anything I am doing wrong? Draft is move to the "Sent items" folder, which should not change immutable ID (https://learn.microsoft.com/en-us/graph/outlook-immutable-id).
Ids looks like this AAkALgAAA.........yACqAC-EWg0AC.......7B4s_RdwAA....TwAA I suppose they are correct. Just last section after underscore changes on draft sent.

Not surprising at all - it is a physically different message. Just the way Exchange works - sent/unsent flag cannot be flipped after the message is saved, so a new message is created in the Sent Items folder.

Related

Send Money to Paypal Account ASP.Net Server Side Code

I am having a difficult time finding halfway descent documentation or examples on how to send money to another Paypal account.
I have installed the Nuget package PaypalSDK version 1.0.4. I have read the documentation at https://developer.paypal.com/home. I have browsed and tried to implement the sample code at https://github.com/paypal/Checkout-NET-SDK.
The problem I am having is that I am having is that I am not seeing notifications of payments sent or received in my sandbox account. I can successfully execute a checkout with the Javascript button in my shopping cart view. But eventually I want to add the capability to send money from my Paypal business account to another Paypal business account, without the other Paypal Business Account owner having to be logged in to my website.
Does the money recipient have to authorize the money I send, or should it just get deposited into their account once I send it?
Here is my code:
namespace MyShoppingCart.Helpers.Paypal
{
public class CaptureOrderSample
{
static string PayPalClientID = Startup.StaticConfig.GetValue<string>("Paypal:ClientID");
static string PayPalClientSecret = Startup.StaticConfig.GetValue<string>("Paypal:ClientSecret");
public static HttpClient client()
{
// Creating a sandbox environment
PayPalEnvironment environment = new SandboxEnvironment(PayPalClientID, PayPalClientSecret);
// Creating a client for the environment
PayPalHttpClient client = new PayPalHttpClient(environment);
return client;
}
public async static Task<HttpResponse> createOrder(string Email)
{
HttpResponse response;
// Construct a request object and set desired parameters
// Here, OrdersCreateRequest() creates a POST request to /v2/checkout/orders
var order = new OrderRequest()
{
CheckoutPaymentIntent = "CAPTURE",
PurchaseUnits = new List<PurchaseUnitRequest>()
{
new PurchaseUnitRequest()
{
AmountWithBreakdown = new AmountWithBreakdown()
{
CurrencyCode = "USD",
Value = "100.00"
},
Payee = new Payee
{
Email = Email // "payee#email.com"
}
}
}
//,
//ApplicationContext = new ApplicationContext()
//{
// ReturnUrl = "https://www.example.com",
// CancelUrl = "https://www.example.com"
//}
};
// Call API with your client and get a response for your call
var request = new OrdersCreateRequest();
request.Prefer("return=representation");
request.RequestBody(order);
response = await client().Execute(request);
var statusCode = response.StatusCode;
Order result = response.Result<Order>();
Debug.WriteLine($"Status: {result.Status}");
Debug.WriteLine($"Order Id: {result.Id}");
Debug.WriteLine($"Intent: {result.CheckoutPaymentIntent}");
Debug.WriteLine("Links:");
foreach (LinkDescription link in result.Links)
{
Debug.WriteLine($"\t{link.Rel}: {link.Href}\tCall Type: { link.Method}");
}
return response;
}
}
}
And this is currently called from my Orders controller when an order is completed. This is just for testing purposes.
[Authorize]
public async Task<IActionResult> CompleteOrder()
{
var items = _shoppingCart.GetShoppingCartItems();
Models.Order order = await _ordersService.StoreOrderAsync(items);
PrepareSellerEmail(items, order, "You Have a New Order!");
PrepareBuyerEmail(items, order, "Thank You for Your Order!");
await _shoppingCart.ClearShoppingCartAsync(_serviceProvider);
DeleteCartIDCookie();
//OrderRequest request = Helpers.CreateOrderSample.BuildRequestBody("USD", "100.00", "sb-r43z1e9186231#business.example.com");
//var client = Helpers.Paypal.CaptureOrderSample.client();
var result = Helpers.Paypal.CaptureOrderSample.createOrder("sb-r43z1e9186231#business.example.com");
//var response = await PayPalClient.client().execute.(request);
return View("OrderCompleted");
}
The output of the result is:
Status: CREATED
Order Id: 51577255GE4475222
Intent: CAPTURE
Links:
self: https://api.sandbox.paypal.com/v2/checkout/orders/51577255GE4475222 Call Type: GET
approve: https://www.sandbox.paypal.com/checkoutnow?token=51577255GE4475222 Call Type: GET
update: https://api.sandbox.paypal.com/v2/checkout/orders/51577255GE4475222 Call Type: PATCH
capture: https://api.sandbox.paypal.com/v2/checkout/orders/51577255GE4475222/capture Call Type: POST
This is a screen capture from my sandbox account:
Am I supposed to do something else to actually execute the transfer?
Edit: I figured out how to use the Paypal Payouts API.
First I installed the Nuget Package. It's simply called PayoutsSdk. I'm using version 1.1.1.
For the payout to execute, you need the client() method that is listed above in this post, and this CreatePayout() method listed below.
public async static Task<HttpResponse> CreatePayout()
{
var body = new CreatePayoutRequest()
{
SenderBatchHeader = new SenderBatchHeader()
{
EmailMessage = "Congrats on recieving 1$",
EmailSubject = "You recieved a payout!!"
},
Items = new List<PayoutItem>()
{
new PayoutItem()
{
RecipientType="EMAIL",
Amount=new Currency()
{
CurrencyCode="USD",
Value="1",
},
Receiver="sb-r43z1e9186231#business.example.com",
}
}
};
PayoutsPostRequest request = new PayoutsPostRequest();
request.RequestBody(body);
var response = await client().Execute(request);
var result = response.Result<CreatePayoutResponse>();
Debug.WriteLine($"Status: {result.BatchHeader.BatchStatus}");
Debug.WriteLine($"Batch Id: {result.BatchHeader.PayoutBatchId}");
Debug.WriteLine("Links:");
foreach (PayoutsSdk.Payouts.LinkDescription link in result.Links)
{
Debug.WriteLine($"\t{link.Rel}: {link.Href}\tCall Type: {link.Method}");
}
return response;
}
Of course I'll add parameters to the method for email, amount, currency code, email message, and subject.
Right now, I am calling this method from the controller method like this: var result = Helpers.Paypal.CaptureOrderSample.CreatePayout(); where Helpers.Paypal are folders that contain a class called CaptureOrderSample, which I will probably rename.
To send money from your account to another account, there are several different options:
Automate the sending with the Payouts API or Payouts Web (spreadsheet upload). For live, payouts can only be used if the live account sending the payment is approved for payouts.
Log into the account that is going to send the money in https://www.paypal.com or https://www.sandbox.paypal.com and click on the menu for Pay & Get Paid -> Send Money .
Use a PayPal Checkout integration, with or without the Orders API, and specify a payee that is to receive the money. You must log in with the paying (sending) account to approve the sending, and finally the order must be captured (via API or client side actions.order.capture()) which is what results in a PayPal transaction. If the final capture step is not performed, no money will be sent and the order will merely remain created or approved and eventually expire (72 hours after creation or 3 hours after approval)
In the sandbox, no actual emails are sent with notifications. Instead, the developer.paypal.com dashboard has a "Notifications" tab on the left, and of course activity will also be visible in each sandbox account by logging into the account. Only captured activity is likely to be visible.

SignalR sending notification to a specific client it's not working

When sending a notification with SignalR to a specific user it's not working.
I'm storing connection IDs in a table and when the notification should be sent I get the connection ID for the receiver from the DB but he doesn't get anything. What is wrong with my code?
// get the connectionId of the receiver
if (_db.UserConnectionid != null)
{
var userConn = await _db.UserConnectionid.Where(x => x.UserId == receiver).Select(x => x.ConnectionId).FirstOrDefaultAsync();
//if the receiver is online
if (userConn != null)
{
await Clients.Client(userConn).SendAsync("RecieveMessage", message);
}
}
I'm storing connection IDs in a table and when the notification should be sent I get the connection ID for the receiver from the DB but he doesn't get anything. What is wrong with my code?
Firstly, please note that a user could have more than one connection id, to troubleshoot the issue, you can try to debug the code and make sure the connection id you retrieved from db is same as the one of current connecting user.
Besides, to send message to a specific user, you can try to get all stored connection id(s) of a specific user/receiver, then send message by specify connectionIds, like below.
var ConnectionIds = _db.UserConnectionid.Where(x => x.UserId == receiver).Select(x => x.ConnectionId).ToList();
if (ConnectionIds.Count > 0)
{
await Clients.Clients(ConnectionIds).SendAsync("RecieveMessage", message);
}
If you are using default user claims mechanism for authorization, you probably can take a look into this mechanism:
https://learn.microsoft.com/en-us/aspnet/core/signalr/authn-and-authz?view=aspnetcore-5.0#use-claims-to-customize-identity-handling
If you will provide IUserIdProvider service or map userId to ClaimTypes.NameIdentifier claim, you will be able to filter your SignalR clients by stringified user id using this method:
https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.signalr.ihubclients-1.user?view=aspnetcore-5.0#Microsoft_AspNetCore_SignalR_IHubClients_1_User_System_String_
Like this:
await Clients.User(receiver.ToString()).SendAsync("RecieveMessage", message);
As recommended in the article, don’t store Id’s.
https://consultwithgriff.com/signalr-connection-ids/

Leverage Groups for Workflow Approvals

We're implementing a new workflow (combined with staging task sync) on an existing website where we would like to notify all members that "own" that particular section/content to approve changes.
One of the options is to have multiple roles and their corresponding workflows configured for their role and scope, but this seems like overkill - at least for us, as currently one single role is set for approvals (and another for editors)
However I've recently come across this new page property:
And have a couple of questions:
Can regular CMS users (without membership) be part of a group?
Would we be able to leverage this group for the workflow's email notifications instead of the roles? E.g. email to everyone in the owner group when a page was sent for approval.
Is this option by default inherited from the parent page when a new one is created or does it need to be set individually for each page?
We have a Kentico 11 EMS license and working on an advanced workflow, therefore custom code is possible.
Can regular CMS users (without membership) be part of a group?
- why don't you use roles here?
Would we be able to leverage this group for the workflow's email
notifications instead of the roles? E.g. email to everyone in the
owner group when a page was sent for approval.
- you'll need to customize workflow manager class, but in general yes, it is possible. You could find an inspiration in this post
Is this option by default inherited from the parent page when a new
one is created or does it need to be set individually for each page?
- Use a macro to default the field. If you populate it with anything else then the new values will be saved.
Sample code snippet for Custom Global Event Handler for Workflow steps i.e., Reject and Approve steps.
using CMS;
using CMS.Base;
using CMS.DataEngine;
using CMS.DocumentEngine;
using CMS.EmailEngine;
using CMS.EventLog;
using CMS.Helpers;
using CMS.MacroEngine;
using CMS.SiteProvider;
using CMS.WorkflowEngine;
using System;
// Registers the custom module into the system
[assembly: RegisterModule(typeof(CustomWorkflowEvent))]
public class CustomWorkflowEvent : CMSModuleLoader
{
// Module class constructor, the system registers the module under the name "CustomInit"
public CustomWorkflowEvent()
: base("CustomInit")
{
}
// Contains initialization code that is executed when the application starts
protected override void OnInit()
{
base.OnInit();
// Assigns custom handlers to events
// WorkflowEvents.Approve.After += WorkFlow_Event_After();
WorkflowEvents.Reject.After += WorkFlow_Event_After;
WorkflowEvents.Approve.After += Approve_After;
// WorkflowEvents.Action.After += WorkFlowAction_Event_After;
}
private void Approve_After(object sender, WorkflowEventArgs e)
{
try
{
WorkflowStepInfo wsi = e.PreviousStep;
if (wsi != null)
{
CMS.WorkflowEngine.Definitions.SourcePoint s = wsi.GetSourcePoint(Guid.NewGuid());
//Make sure it was an approval (standard) step
var approvers = WorkflowStepInfoProvider.GetUsersWhoCanApprove(wsi, null, SiteContext.CurrentSiteID, "UserID = " + CMSActionContext.CurrentUser.UserID, "UserID", 0, "Email, FullName, Username");
EventLogProvider.LogInformation("Approvers Data", "Approvers Data", approvers.ToString());
if (approvers != null)
{
//Loop through the approvers
string siteName = null;
SiteInfo si = SiteInfoProvider.GetSiteInfo(SiteContext.CurrentSiteID);
if (si != null)
{
siteName = si.SiteName;
}
EmailTemplateInfo eti = EmailTemplateProvider.GetEmailTemplate("Workflow.Rejected", SiteContext.CurrentSiteName);
MacroResolver mcr = MacroResolver.GetInstance();
EmailMessage message = new EmailMessage();
// Get sender from settings
message.EmailFormat = EmailFormatEnum.Both;
message.From = eti.TemplateFrom;
// Do not send the e-mail if there is no sender specified
if (message.From != "")
{
// Initialize message
// message.Recipients = strRecipientEmail;
message.Subject = eti.TemplateSubject;
// Send email via Email engine API
// EmailSender.SendEmailWithTemplateText(SiteContext.CurrentSiteName, message, eti, mcr, true);
}
}
}
}
catch (Exception ex)
{
throw;
}
}
private void WorkFlow_Event_After(object sender, WorkflowEventArgs e)
{
try
{
WorkflowStepInfo wsi = e.PreviousStep;
if (wsi != null)
{
CMS.WorkflowEngine.Definitions.SourcePoint s = wsi.GetSourcePoint(Guid.NewGuid());
//Make sure it was an approval (standard) step
var approvers = WorkflowStepInfoProvider.GetUsersWhoCanApprove(wsi, null, SiteContext.CurrentSiteID, "UserID = " + CMSActionContext.CurrentUser.UserID, "UserID", 0, "Email, FullName, Username");
EventLogProvider.LogInformation("Approvers Data", "Approvers Data", approvers.ToString());
if (approvers != null)
{
//Loop through the approvers
string siteName = null;
SiteInfo si = SiteInfoProvider.GetSiteInfo(SiteContext.CurrentSiteID);
if (si != null)
{
siteName = si.SiteName;
}
EmailTemplateInfo eti = EmailTemplateProvider.GetEmailTemplate("Workflow.Rejected", SiteContext.CurrentSiteName);
MacroResolver mcr = MacroResolver.GetInstance();
EmailMessage message = new EmailMessage();
// Get sender from settings
message.EmailFormat = EmailFormatEnum.Both;
message.From = eti.TemplateFrom;
// Do not send the e-mail if there is no sender specified
if (message.From != "")
{
// Initialize message
// message.Recipients = strRecipientEmail;
message.Subject = eti.TemplateSubject;
// Send email via Email engine API
// EmailSender.SendEmailWithTemplateText(SiteContext.CurrentSiteName, message, eti, mcr, true);
}
}
}
}
catch (Exception ex)
{
throw;
}
}
}
Hope Helps you.
Can regular CMS users (without membership) be part of a group?
It is not part of CMS users. Groups are coming from Groups Application.
GROUP: Allows you to manage user groups. Groups are a social networking
feature enabling users to find information and communicate according
to shared interests.
Would we be able to leverage this group for the workflow's email notifications instead of the roles? E.g. email to everyone in the owner group when a page was sent for approval.
No
Is this option by default inherited from the parent page when a new one is created or does it need to be set individually for each page?
No

how to update an subscription without id in mail chimp rest api

I really like the new Mail Chimp REST API - it is easy to create subscriptions by PUT and those can be updated using the subscription id.
But I would like to update a subscription simply using the email address, because I do not want to save any new Mail Chimp Id in my Middle-ware application, as long as the email should be sufficient as identifier?
To update a List Member the API is:
/lists/{list_id}/members/{id}
but I would prefer a simpler way:
/lists/{list_id}/members/{email}
is something like this possible?
The subscriber's ID is the MD5 hash of their email address. Since you would have to make a function call to URL Encode the email address for your second way, using the first way is just as easy.
See this help document on managing subscribers for more details.
More specifics on updating a subscriber via MailChimp's REST API.
// node/javascript specific, but pretty basic PUT request to MailChimp API endpoint
// dependencies (npm)
var request = require('request'),
url = require('url'),
crypto = require('crypto');
// variables
var datacenter = "yourMailChimpDatacenter", // something like 'us11' (after '-' in api key)
listId = "yourMailChimpListId",
email = "subscriberEmailAddress",
apiKey = "yourMailChimpApiKey";
// mailchimp options
var options = {
url: url.parse('https://'+datacenter+'.api.mailchimp.com/3.0/lists/'+listId+'/members/'+crypto.createHash('md5').update(email).digest('hex')),
headers: {
'Authorization': 'authId '+apiKey // any string works for auth id
},
json: true,
body: {
email_address: email,
status_if_new: 'pending', // pending if new subscriber -> sends 'confirm your subscription' email
status: 'subscribed',
merge_fields: {
FNAME: "subscriberFirstName",
LNAME: "subscriberLastName"
},
interests: {
MailChimpListGroupId: true // if you're using groups within your list
}
}
};
// perform update
request.put(options, function(err, response, body) {
if (err) {
// handle error
} else {
console.log('subscriber added to mailchimp list');
}
});

Updating mail draft using Domino REST API causes message to appear in Send folder

I am using the Domino Mail REST API and am able to create a new draft mail which appears in the Drafts folder.
When I update the draft mail it appears in the Sent folder and is no longer visible in the Drafts folder.
This is unexpected. The message was not sent. I have also tried setting the From and To fields to null and the sresult is always the same.
Partial code:
Gson gson = new Gson();
String json = gson.toJson(message);
// if message has an id then do update
if (href != null && href.trim().length() > 0) {
url = createFullQualifiedRequestUrl(href);
HttpPut request = new HttpPut(url);
request.setHeader("Content-Type", "application/json");
request.setEntity(new StringEntity(json, "utf-8"));
response = this.executeRequest(request, username);
} else {
MailboxFolder folder = getFolder("drafts", username);
url = this.createFullQualifiedRequestUrl(folder.getLink()
.getHref());
HttpPost request = new HttpPost(url);
request.setHeader("Content-Type", "application/json");
request.setEntity(new StringEntity(json, "utf-8"));
response = this.executeRequest(request, username);
}
if (response != null) {
SendMessageResult result = parseResponse(response);
if(href != null)
result.setLocation(href);
return result;
}
That's a bug in the REST mail API and Richard is correct about the root cause. The bug will be fixed in the next release of the extension library (901v00_12). I can't say exactly when release 12 will be available, but it should be soon.
Most likely, your problem is being caused by something that is setting the PostedDate item to a non-empty value.
The Sent "folder" is not a folder. It is a view. The same is true for the Drafts "folder". It is also a view. If you go into Domino Designer and look at the views, you can see their selection formula. You will see that they look something like this
Sent
SELECT DeliveredDate = "" & PostedDate != "" & !(#IsMember("S"; ExcludeFromView))
Drafts
SELECT PostedDate = "" & $MessageType = "" & #IsNotMember("D" : "A"; ExcludeFromView) & ISMAILSTATIONERY != 1 & Form != "Group" & Form != "Person"
Note that these are taken from a rather old version of the mail template, so what you actually see may be different, but AFAIK the idea has not changed. Documents appear in Sent if they contain a non-empty PostedDate item and the DeliveredDate item is either empty or missing and they are not marked for exclusion. Documents appear in Drafts if they do not contain either of those two date items, are not marked for exclusion, are not stationery, and are not Group or Person docs. The one thing that is in common here is the dependence on the PostedDate item.