Pragmatically sync office 365 exchange online and GMAIL - gmail

So I would want below functionality;
Connect to GMAIL for Business using service account (Already DONE)
Get emails from gmail (Got some API)
Connect to office 365 using oAuth access token (Will be done, I think no issues in it)
Copy the gmail message to office 365 message.
How can I do it?
Here is the code done so far to download message from Google;
Console.WriteLine("Connect to Google API");
Console.WriteLine("=====================");
String serviceAccountEmail = "3512650851-4tpr9073rju4deqtfjp210j07q52hu2j#developer.gserviceaccount.com";
var certificate = new X509Certificate2(#"My Project-d3e5dda28438.p12", "notasecret", X509KeyStorageFlags.Exportable);
ServiceAccountCredential credential = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer(serviceAccountEmail)
{
User = "<UserEmail for which to download message>",
Scopes = new[] { GmailService.Scope.GmailCompose, GmailService.Scope.GmailModify }
}.FromCertificate(certificate));
var gmailservice = new Google.Apis.Gmail.v1.GmailService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "MyNewProject",
});
try
{
ListMessagesResponse messages = gmailservice.Users.Messages.List("<User Email>").Execute();
IList<Google.Apis.Gmail.v1.Data.Thread> threads = gmailservice.Users.Threads.List("<User Email>").Execute().Threads;
List<Message> responsemessages = new List<Message>();
responsemessages.AddRange(messages.Messages);
foreach(Message msg in responsemessages)
{
Google.Apis.Gmail.v1.UsersResource.MessagesResource.GetRequest gr = gmailservice.Users.Messages.Get("<User Email>", msg.Id);
gr.Format = Google.Apis.Gmail.v1.UsersResource.MessagesResource.GetRequest.FormatEnum.Full;
Message m = gr.Execute();
if (gr.Format == Google.Apis.Gmail.v1.UsersResource.MessagesResource.GetRequest.FormatEnum.Raw)
{
byte[] decodedByte = FromBase64ForUrlString(m.Raw);
string base64Encoded = Convert.ToString(decodedByte);
MailMessage msg2 = new MailMessage();
//msg2.LoadMessage(decodedByte);
}
}
}
catch (Exception ex) { }
Note: The code is very rough for now. Will make it more formal later..
So basically the question is, How can I upload the message row format to office 365 or is there any COPY api?

I am not aware of any C# library/API that handles email synchronization, but maybe Google finds you something.
If not you will have to 'roll your own'. We are doing exactly that with calendar synchronization (in Delphi). The steps to take are:
[Note that I am answering for full synchronization as your question title says]
Analyze the email formats for both systems in detail. Set up data/storage structures that can handle all formats and their differences. You may have to resort to using 'extended/user defined/custom' properties to store properties of system Y in system X, when not present there. You will surely have to use custom properties for storing typical synchronization data: date of last synchronization, date of last change, maybe mutual IDs*
Read emails from both systems over a certain 'synchronization period'.
Do your own comparison looking for differences (added, deleted, modified emails)
Your comparison may have to take configuration/settings into account like Do we synchronize both ways?, When an email is modified on both sides, which one takes precedence?. That's not really necessary, you can define sensible defaults for that. Many synchronisation systems do, they don't ask your for any configuration, but then the user sometimes has to figure out Huh, why did it update this way?).
Write modifications to the external systems.
No small task, I can tell you, so I doubt it fits your your requirement pragmatically ;-) So first invest heavily in searching if someone has already done this.
(And note that asking for libraries is off-topic in SO)
* You will even have to store 'my own ID' as a custom property for each email. If you don't do that you can't distinguish emails that were copied in the external system.

Related

Stripe: Getting Credit Card's Last 4 Digits

I have upgraded the Stripe.net to the latest version which is 20.3.0 and now I don't seem to find the .Last4 for the credit card. I had the following method:
public void CreateLocalCustomer(Stripe.Customer stipeCustomer)
{
var newCustomer = new Data.Models.Customer
{
Email = stipeCustomer.Email,
StripeCustomerId = stipeCustomer.Id,
CardLast4 = stipeCustomer.Sources.Data[0].Card.Last4
};
_dbService.Add(newCustomer);
_dbService.Save();
}
But now the stipeCustomer.Sources.Data[0].Card.Last4 says 'IPaymentSource' does not contain a definition for 'Card'. Does anyone know how I can get the card details now? The flow is that I create the customer by passing the Stripe token to Stripe, then I get the above stripeCustomer. So I expect it to be somewhere in that object. But I can't find it. The release notes can be found here.
Thank you.
In the old world of Stripe, there only used to be one type of payment method you could attach to a Customer; specifically, Card-objects. You would create a Card-object by using Stripe.js/v2 or the Create Token API Endpoint to first create a Token-object and then attach that token to a Customer-object with the Create Card API Endpoint.
Once Stripe expanded to support a number of other payment methods though, Stripe built support for a new object type that encapsulated a number of payment methods (including credit cards) called Source-objects. A Source-object is created either by using Stripe.js/v3 or the Create Source API Endpoint. It can also be attached to a Customer-object in much the same way as the Card-objects mentioned above, except they retain their object type. They're still a Source. You use the Attach Source API Endpoint to do this (that is notably identical to the Create Card API Endpoint mentioned above).
What I'm getting at here, is there are now two different object types (or more) that you can expect to see returned in the sources-array (or Sources in .NET). All of these methods though inherit from the IPaymentSource-interface. So if you know you have a Card-object getting returned, you can simply cast the returned object to the Card-class.
Something like this should get you going:
CardLast4 = ((Card) stipeCustomer.Sources.Data[0]).Last4
You can see what I mean by inheritance by looking at this line in the Card-class file:
https://github.com/stripe/stripe-dotnet/blob/master/src/Stripe.net/Entities/Cards/Card.cs#L7
Good luck!
As of Stripe.net.21.4.1, this is what works:
var chargeService = new ChargeService();
var charge = chargeService.Get(id);
CardLast4 = ((Card)charge.Source).Last4;
It's getting hard not to panic when code breaks because of all the micro-changes Stripe makes.
So after debugging, it looks like the Data[0] needs to be cast as Card to get the card.
So it will be CardLast4 = ((Card)stipeCustomer.Sources.Data[0]).Last4.

How to update a deployed plugin in Dynamics CRM Online

I'm new to programming in Dynamics CRM Online and I'm having a problem updating a deployed plugin. I'm using Visual Studio 2012 as my IDE. I deployed a plugin that I need to modify and when I re-deploy it thru VS the modified date in CRM is correct but the changes are not there. Here is my code..
if (context.InputParameters.Contains("Target")
&& context.InputParameters["Target"] is Entity)
{
Entity entity = (Entity)context.InputParameters["Target"];
if (entity.LogicalName == "lead")
{
if (entity.Attributes.Contains("companyname") == true)
{
if (entity["firstname"].ToString() != "null")
firstName = entity["firstname"].ToString();
else
firstName = "";
if (entity["lastname"].ToString() != "null")
lastName = entity["lastName"].ToString();
else
lastName = "";
entity["companyName"] = "This is a test";
//entity["companyname"] = firstName + " " + lastName;
}
else
throw new InvalidPluginExecutionException(
"The company name can only be set by the system.");
}
}
When I create a lead the company name is not "This is a test". I'm not sure what I'm doing wrong.
Thanks for the help!
You detect whether the field with company name exists by:
if (entity.Attributes.Contains("companyname") == true)
but you write to an other, namely:
entity["companyName"] = "This is a test";
The value is put in the entity but since it has no counterpart in the metadata, it's not being stored. Set the field name to it's to schema name, i.e. lower cased.
A few other things to consider in case you get additional errors.
You need to call Update method on the service after you've set the field's value.
The field should have some kind of prefix (e.g. new_something, beep_something).
The camel casing is not applicable here (schema name is), so go alltolowercase.
What do you get as the company name? Do you get the exception being thrown?
Also, some pointers on the code quality. I've rebuilt the logic to eliminate unnecessary complexity of scopes. I removed the superfluous else statement and comparisons to true. I'd also recommend that you split the process into different methods but I'm sure you've got that covered already. And you might want to use an auxiliary method to obtain the values from the fields. See in this post at my suggestion.
if (!context.InputParameters.Contains("Target") ||
context.InputParameters["Target"] is Entity)
return;
Entity entity = context.InputParameters["Target"] as Entity;
if (entity.LogicalName != "lead")
return;
if (!entity.Attributes.Contains("companyname"))
throw new InvalidPluginExecutionException(
"The company name can only be set by the system.");
String firstName = String.Empty;
if (entity.Contains("firstname"))
firstName = entity["firstname"] as String;
String lastName = String.Empty;
if (entity.Contains("lastname"))
lastName = entity["lastname"] as String;
entity["companyname"] = "This is a test";
//entity["companyname"] = firstName + " " + lastName;
EDIT:
If you still don't get the requested behavior, try the following. (I'm not sure on what level of expertise you are so olease accept my apology if you feel insulted by me mentioning some very basic stuff you've already tried a gazillion times.)
Technical tricks.
Publish all customizations (I do it very often, just in case).
Hit F5 to reload.
Login/logout.
Reboot IIS (if on-premise).
Unregister the plug in, see if the behavior remains. Then re-register it.
Check for forgotten workflows running.
There might be some delays and lags. Once, I actually had both the old old and the new version of a plugin being fired, depending on whether I created a record from Settings or Workplace. That was weird but resolved itself after a few hours. Seriously. That was weird!
Programmatic tricks.
Check if there are other plugins you might have forgotten to deactivate.
Remove all the leads and make sure that the plugin fires upon the new one being created.
Change the text to e.g. I'm a giant moose, just to make sure that the change goes through.
Slash away all code (or place return in the beginning of Execute. Then, move it down step by step to detect when the weirdness commences.
Of what you've shown, it should work so either you didn't mention something relevant (we do appreciated that you didn't post 100000 lines of code, of course) or it's CRM that's weirding off (which is equally annoying and confusing). So, let's trouble-shoot this thing. What happens when you try the tricks above?
As for the code stub, yeah - I'm not too proud of MS effort there. Try to post that code under the tag C# on Programmers for code review. Be prepared for an angry discussion. :)

Creating record in other databases from CRM 2011

Is there any solution to create record in other DBs from the CRM 2011 records? When a record such as "cost" was created in CRM 2011, we want a record would be created in out Oracle DB. Could it be done through a plugin? Or a service should be created for this?
Could you please provide me references or solutions for this.
Any helps would be greatly appreciated.
We had a similar request from a customer a while ago. They claimed that CRM's database wasn't to be trusted and wanted to securely store a copy of the records created in - guess what - SQL Server too. (Yes, we do understand the irony. They didn't.)
The way we've resolved it was to create a plugin. However, bear in mind that simply reacting to the message of Create won't really do. You need to set up a listener for three of the CRUD operations (retrieval doesn't affect the external database so it's rather C_UD operations, then).
Here's the skeleton of the main Execute method.
public void Execute(IServiceProvider serviceProvider)
{
Context = GetContextFromProvider(serviceProvider);
Service = GetServiceFromProvider(serviceProvider);
switch (Context.MessageName)
{
case "Create": ExecuteCreate(); break;
case "Update": ExecuteUpdate(); break;
case "Delete": ExecuteDelete(); break;
}
}
After this dispatcher, you can implement the actual calls to the other database. There are three gotchas I'd like to give you head-up on.
Remember to provide a suitable value to the outer DB when CRM doesn't offer you one.
Register the plugin as asynchronous since you'll be talking to an external resource.
Consider the problem with entity references, whether to store them recursively as well.
Walk-through for plugin construction
Link to CRM SDK if you haven't got that
Information on registering the plugin
And besides that, I've got a walk-through (including code and structure) on the subject in my blog. The URL to it, you'll have to figure out yourself - I'm not going to self-promote but it's got to do with my name and WP. Google is your friend. :)
You could use a plugin to create a record in another system, although you would need to think about syncing and ensure you don't get duplicates, but it certainly can be done.
Tutorial on plugins can be found here.
You need to write a plugin that runs on Create and uses the information on the created Cost entity to create a record in your Oracle DB.
As an example:
public void Execute(IServiceProvider serviceProvider)
{
var context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
//get the created entity from CRM
var theCreatedEntity = context.InputParameters["Target"] as Entity;
//build up a stored procedure call
using (OracleConnection objConn = new OracleConnection("connection string"))
{
var cmd = new OracleCommand();
cmd.Connection = objConn;
cmd.CommandText = "stored procedure name";
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("param1", OracleType.Number).Value = theCreatedEntity.GetAttributeValue<int>("Attribute1");
cmd.Parameters.Add("param2", OracleType.Number).Value = theCreatedEntity.GetAttributeValue<int>("Attribute2");
//etc
cmd.ExecuteNonQuery();
}
}
That should give you enough to get going

Sharepoint task list and Outlook sync

I am trying to sync Sharepoint task list with Outlook. When the users connect the task list to outlook, the task for all users are visible in outlook. Rather than applying filtering in Outlook, can I provide a filtering at the source itself? There are considerable number of users for my application, it wouldn't be good to ask all users to apply filters on their own.
Any other suggestions?
Thanks.
I`v asked the same question: Sync list with outlook only with items in current view.. In this case it was possible to use stssync protocol to do whatever you want. It takes much effort (luckily someone already wrote an implementation)
But there was another solution i ended up using - implementing a wrapper for Lists.asmx webservice and rewriting outlook requests (by using custom Global.asax file) to use this new webservice instead of Lists.asmx, that only queries specific view in a list.
if (ctx.Request.UserAgent.Contains("Microsoft Office Outlook") && path.ToLower().IndexOf("_vti_bin/lists.asmx") >= 0)
{
ctx.RewritePath("/_layouts/OutlookLists.asmx");
}
I'm not sure you would want a solution like this. If you do, you may ask and i may publish the solution source for the webservice, however i'm not using this webservice myself anymore. And you could use it as a draft, not a production ready code.
The source has been published on CodePlex.
Regarding to the script problem
I don't know why list id isn't being replaced by view id. I tried to run the function within script console (F12 for IE8/9)
>> var menuItems = document.getElementsByTagName('ie:menuitem');
for (var i = 0; i < menuItems.length; i++) {
itm = menuItems(i);
if (itm.id.match('OfflineButton') != null) {
console.log('listName:' + ctx.listName.toLowerCase() + 'viewName:' + ctx.view.toLowerCase());
if (ctx != null && ctx.listName != null && ctx.view != null) {
console.log('Inside if block');
//Replace listId to viewId being used so outlook will query only items in current view.
//Must have custom web service in place to handle that request, because it iwll not work OOTB.
console.log("Before: " + itm.onMenuClick);
itm.onMenuClick = itm.onMenuClick.replace(ctx.listName.toLowerCase(), ctx.view.toLowerCase());
console.log("After: " + itm.onMenuClick);
break;
}
}
}
LOG: listName:{fe89e809-7de4-4f43-9bc2-7e8ce6624ed0}viewName:{7364a843-c7f2-47d8-b4a3-5dc7381b6248}
LOG: Inside if block
LOG: Before: javaScript:ExportHailStorm('tasks','https:\u002f\u002fserver\u002fsapulces\u002fdarbu_parskata','{fe89e809-7de4-4f43-9bc2-7e8ce6624ed0}','Uz\u0146\u0113muma darbu p\u0101rskata sapulce','Uzdevumi','\u002fsapulces\u002fdarbu_parskata\u002fLists\u002fUzdevumi','','\u002fsapulces\u002fdarbu_parskata\u002fLists\u002fUzdevumi');
LOG: After: javaScript:ExportHailStorm('tasks','https:\u002f\u002fserver\u002fsapulces\u002fdarbu_parskata','{7364a843-c7f2-47d8-b4a3-5dc7381b6248}','Uz\u0146\u0113muma darbu p\u0101rskata sapulce','Uzdevumi','\u002fsapulces\u002fdarbu_parskata\u002fLists\u002fUzdevumi','','\u002fsapulces\u002fdarbu_parskata\u002fLists\u002fUzdevumi');
As you can see, the function argument (third one) has been replaced with a view id instead of list id.
Don't forget to remove console.log statements before deploying, because if IE doesn't have web developer tools, javascript will crash there.
Were these tasks created from a workflow? this is a known issue with SharePoint 2007.
http://social.technet.microsoft.com/Forums/en/sharepointadmin/thread/64b3b124-085c-4d8e-8e85-8bd20736e0e7
http://blah.winsmarts.com/2007-4-SharePoint_2007__Fine_grained_permission_control.aspx
You could try setting the read/edit permissions to "only their own", but i think that breaks approval/alerts from working
I believe the problem is fixed in SharePoint 2010, i think tasks get created with fine-grained permissions per task.

Returning a string from LINQ

For a school project we have to create an evaluation website that requires a login.
For the database connection I chose LINQ, because it's new and is supposed to be easier/better in use.
I managed to create a login check with the following:
public static Boolean Controle(int id, string wachtwoord)
{
DataClassesDataContext context = new DataClassesDataContext();
var loginGebruiker =
from p in dc.Gebruikers
where p.GebruikerID == id
where p.GebruikerWachtwoord == wachtwoord
select p;
return true;
}
Now I'm trying to create a "forgot password" option, where you enter your id and the password gets returned (later it would be emailed to you, don't know how I would do this either, suggestions?)
I tried with the following code:
public static string Forgot(int id)
{
var context = new DataClassesDataContext();
var wachtwoordLogin = (
from p in dc.Gebruikers
where p.GebruikerID == id
select p.GebruikerWachtwoord);
return wachtwoordLogin.ToString();
}
Code behind the button on the page:
lbl1.Text = Class1.Forgot(Convert.ToInt32(txt1.Text));
Now when I enter the an id of the first user (1), lbl1 becomes this:
SELECT [t0].[GebruikerWachtwoord] FROM
[dbo].[Gebruiker] AS [t0] WHERE
[t0].[GebruikerID] = #p0
I don't know how to solve this and I have been looking everywhere, I hope somebody can help me.
Thanks,
Thomas
LINQ uses delayed execution, so your 'wachtwoordLogin' is really just "how to get your data." Its not until you apply an operator that LINQ will actually attempt to retrieve your data.
Your first statement:
var loginGebruiker = (
from p in dc.Gebruikers
where p.GebruikerID == id
where p.GebruikerWachtwoord == wachtwoord
select p).FirstOrDefault()
if (loginGeruiker != null) {
//Valid login
} else {
// invalid
}
FirstOrDefault means, take the first item in the list, or return none.
In you other case you need the same thing:
user = wachtwoordLogin.FirstOrDefault();
Further reading: MSDN 101 LINQ Samples
For your question about emailing a forgotten password, have you ever thought about implementing the golden questions algorithm instead? Its simplified, and does the same thing.
Basically, at the time of registering just get them to answer some questions, and if they can verify them, allow them to reset the password.
you enter your id and the password gets returned
What, then, is the point of having a password if anybody who knows a username can see it? I know this isn't what you're asking, but for someone getting started in programming I feel a duty to point this out. What you're creating here is essentially a completely broken login model. Nobody should ever use a system like this.
You should never ever display a password. Not on the screen, not in an email, never.
Passwords, if they even need to be stored at all (CodingHorror has had a couple of good posts on this lately, advocating things like OpenID), should be stored in hashed form and essentially unable to be retrieved. When a user logs in, similarly hash the password they provide (immediately upon reaching the application code, before transporting it anywhere else in the system) and compare that to the stored hashed version.
If the user asks for his password, you don't have it. You can't give it to him. This is for his protection. Instead of providing the user with his password, if it's forgotten then you provide the user with a means to reset his password (sending an email to the address on file with a temporarily available URL, a set of "security questions" to verify his identity, etc.) so that he can enter a new one to overwrite the old one. But you shouldn't be able to "show" the user his password because even you as the administrator of the system shouldn't be able to see it in any usable form.
wachwoordLogin will be an IQueryable so you can get this by using FirstOrDefault() which will return null if not found:
(from p in dc.Gebruikers
where p.GebruikerID == id
select p.GebruikerWachtwoord).FirstOrDefault();

Resources