EXM subscribe to list C# - sitecore

I'm working on converting my old Sitecore (< 8) code to work with Sitecore EXM. I'm having a hard time adding users to Recipient Lists from code. The answers in this post: Sitecore 8 EXM add a contact to list from listmanager don't answer my questions completely, and since I cannot comment, I've decided to start a new topic.
My first problem is that my EcmFactory.GetDefaultFactory().Bl.RecipientCollectionRepository.GetEditableRecipientCollection(recipientListId) gives a compilation error on the RecipientCollectionRepository, it says it does not exist. So I've used slightly different code. My code now, is as follows:
var contactRepository = new ContactRepository();
var contactName = this.Email.Text;
var contact = contactRepository.LoadContactReadOnly(contactName);
contact = contactRepository.CreateContact(Sitecore.Data.ID.NewID);
contact.Identifiers.AuthenticationLevel = Sitecore.Analytics.Model.AuthenticationLevel.None;
contact.System.Classification = 0;
contact.ContactSaveMode = ContactSaveMode.AlwaysSave;
contact.Identifiers.Identifier = contactName;
contact.System.OverrideClassification = 0;
contact.System.Value = 0;
contact.System.VisitCount = 0;
var contactPreferences = contact.GetFacet<IContactPreferences>("Preferences");
contactPreferences.Language = "nl-NL";
var contactEmailAddresses = contact.GetFacet<IContactEmailAddresses>("Emails");
contactEmailAddresses.Entries.Create("test").SmtpAddress = this.Email.Text;
contactEmailAddresses.Preferred = "test";
var contactPersonalInfo = contact.GetFacet<IContactPersonalInfo>("Personal");
contactPersonalInfo.FirstName = contactName;
contactPersonalInfo.Surname = "recipient";
if (recipientList != null)
{
var xdbContact = new XdbContactId(contact.ContactId);
if (!recipientList.Contains(xdbContact, true).Value)
{
recipientList.AddRecipient(xdbContact);
}
contactRepository.SaveContact(contact, new ContactSaveOptions(true, null));
}
So the recipientList is found, and the first time I add a contact to it, it increases the "Recipients" to 1 (checked using the /sitecore/system/List Manager/All Lists/E-mail Campaign Manager/Custom/RecipientList).
I also have a message which has this Opt-in recipient list, but when I check that message, it says it will be sent to 0 subscribers.
Any thoughts on this?

See this article listing known issues in Sitecore EXM:
https://kb.sitecore.net/articles/149565
"The recipient list shows "0" total recipients after recipients have been subscribed to the list. (62217)"
I got around this in a sandbox environment by adding a simple list (from csv, one contact) to the message. This upped the total recipient count from 0 to 1 which allows the message to be activated. All recipients in the composite list were sent a message.

Do you have a distributed environment? If so the RecipientCollectionRepository will not work as it is only available on a Content Management server. You could try using the ClientApi:
ClientApi.UpdateSubscriptions(RecipientId recipientId, string[] listsToSubscribe, string[] listsToUnsubscribe, string managerRootId, bool confirmSubscription)
and just add the id of the list you want to subscribe people to in the first string array.
Just a quick note with this option, listToUnsubscribe does not actually remove a contact from a list. You are meant to pass through the ID of the opt out list. This basically excludes them from any future emails. One draw back is that you will no longer be able to resubscribe them.
If this does not work for you you will need to create your own API between your CD server and your CM server where the CM server uses the recipientCollectionRepository to subscribe and unsubscribe

Related

DocuSign pause workflow when creating envelopes from templates c#

Trying to add workflow step to pause the workflow for approval for second signer.
Please note I was able to add the pause when creating envelopes with recipients added.
But was not able to add the workflow step when trying to create envelope from templates. The templates are created in docusign with only roles added.
Later when creating the envelope through API we added TemplateRoles to add signers against each role.
In this case when we tried to add workflow step to pause the workflow it didn’t pause and sent invitation for the second signer. Can you please help us on this and correct us if we are doing any thing wrong.
Please find the snip of the code below:
var workflowStep = new WorkflowStep()
{
Action ="pause_before",
TriggerOnItem = "routing_order",
ItemId = "2"
};
var workflowsteps = new List<WorkflowStep>();
workflowsteps.Add(workflowStep);
TemplateRole signer = new TemplateRole();
signer.Email = "test#example.com";
signer.Name = "Test";
signer.RoleName = "QA";
signer.RoutingOrder = "1";
TemplateRole signer1 = new TemplateRole();
signer1.Email = "test123#example.com";
signer1.Name = "Test123";
signer1.RoleName = "RS";
signer1.RoutingOrder = "2";
var tempRoles = new List<TemplateRole>();
tempRoles.Add(signer);
tempRoles.Add(signer1);
EnvelopesApi envelopesApi = new EnvelopesApi(apiclient);
EnvelopeDefinition envDef = new EnvelopeDefinition();
envDef.TemplateId = templateId;
envDef.Status = "Sent";
envDef.TemplateRoles = tempRoles;
envDef.Workflow = new Workflow { WorkflowSteps = workflowsteps };
EnvelopeSummary envelopeSummary = envelopesApi.CreateEnvelope(accountId, envDef);
I hesitate to add this because I haven't tested it but I'll mention it in case it unblocks you and see if I can verify later...
I believe if you create the envelope/template with status = paused rather than sent, you should be able to update the template with ARR.
The problem is that when you get to the 'pause' in the above code, it's already 'too late'.

Signature disappears when inserting documents to template

I'm trying to insert a word document into a Docusign template. The document inserts properly, but in addition to the two signers set up in the code, one of the signers that was on the template stays. Also, the second signature field is removed. Is there a setting that I'm missing? Here's my code:
List<TemplateRole> roleslist = new List<TemplateRole>();
TemplateRole InternalSignerRole = new TemplateRole(); // Set the Template Roles for the Internal Signer
InternalSignerRole.Email = _currentDocInfo.sInternalSignerEmail;
InternalSignerRole.Name = _currentDocInfo.sInternalSignerName;
InternalSignerRole.RoleName = "SignerInternal";
roleslist.Add(InternalSignerRole); // add to Template Roles list
TemplateRole ExternalSignerRole = new TemplateRole(); // Set the Template Roles for the External Signer
ExternalSignerRole.Email = _currentDocInfo.sExternalSignerEmail; //Create Template Roles for ExternalSigner
ExternalSignerRole.Name = _currentDocInfo.sExternalSignerEmail;
ExternalSignerRole.RoleName = "SignerExternal";
roleslist.Add(ExternalSignerRole);
var apiClient = new ApiClient(conn.Base_URL);
DocuSign.eSign.Client.Configuration.Default.ApiClient = apiClient;
DocuSign.eSign.Client.Configuration.Default.AddDefaultHeader("X-DocuSign-Authentication", authHeader);
string accountId = null;
// login call is available in the authentication api
var authApi = new AuthenticationApi();
var loginInfo = authApi.Login();
// parse the first account ID that is returned (user might belong to multiple accounts)
accountId = loginInfo.LoginAccounts[0].AccountId;
var baseUrl = loginInfo.LoginAccounts[0].BaseUrl;
var separator = new string[] { "/restapi" };
var basePath = baseUrl.Split(separator, StringSplitOptions.None)[0] + "/restapi";
apiClient = new ApiClient(basePath);
EnvelopeDefinition envDef = new EnvelopeDefinition();
envDef.EmailSubject = emailSubject;
envDef.EmailBlurb = emailBody;
//add custom fields you sent over with the document
envDef.CustomFields = letterCustomFields;
// Add a document to the envelope
envDef.Documents = letterDocs;
// Add a recipient to sign the document
envDef.TemplateRoles = roleslist;
envDef.TemplateId = "478d85c2-dc7c-4a89-a985-7d7a9101e36a";
First off, please don't use legacy authentication. That is old, not as secure and should not be used in new code that you're building.
Note that tabs (signature elements) are associated with both a recipient (a specific one) as well as a document (a specific one).
If you are trying to replace a document in a template, when creating an envelope from it (which is what you're doing, you're not replacing the document in the template, but rather in the envelope you create), you need to ensure the tabs refer to the same document and same recipient (based on the role).
Also, another recommendation is to use composite templates, which provide more flexibility for such scenarios. See excellent blog post by Gil Vincent for more information.

Check if any cell in specified range meets 2 conditions

I'm putting together a macro that sends alert e-mails if two conditions are met.
The e-mails are being sent, but indiscriminately and not just when the conditions I want to set are being met.
The conditions: send an e-mail if any cell inside the range (I1:I9999) has white as background colour AND contains the text "QC".
This is what I have tried:
var QCJobRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("WIP").getRange("I1:I9999");
var Location = QCJobRange.getValue();
// Check for white cells with value=QC in Location column
if (Location = "QC") and (Background = "#ffffff");
// Fetch the email address
var emailRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("WIP").getRange("C2");
var emailAddress = emailRange.getValues();
// Send Alert Email.
var message = 'bla';
var subject = 'bla';
MailApp.sendEmail(emailAddress, subject, message);
I'm working directly in the script editor that you can open from Google sheets.
It seems that some operators are not being picked up, f. e. "and" is not even highlighted and I get the following error message: "and" is not defined.
I've been combing the forums for a simple solution but am kind of stuck on the problem with "and".
Any suggestions?
Google Apps Script is based on Javascript
The syntax for "and" is &&
The syntax for an if statement is if(condition1&&condition2){...do something...};
The method getValue() is applicable to a single value (from a single cell), while getValues() is to be used for value ranges, which represent 2-dimentsional arrays
If you want to compare two values, use the operator ==
Here is a sample to modify your code in roder to send a message if the background of cell "I1" is white and its value "QC":
function myFunction() {
//if you do not have 999 rows full of data, please reduce your range - otherwise your code will be slow
var QCJobRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("WIP").getRange("I1:I9999");
var Locations = QCJobRange.getValues();
var firstLocation=Locations[0][0];
// Check for white cells with value=QC in Location column
if (firstLocation == "QC"&& QCJobRange.getBackgrounds()[0][0]== "#ffffff"){
// Fetch the email address
var emailRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("WIP").getRange("C2");
var emailAddress = emailRange.getValue();
// Send Alert Email.
var message = 'bla';
var subject = 'bla';
MailApp.sendEmail(emailAddress, subject, message);
}
}
Please consult the Apps Script tutorial for more samples and information:

MAPI, HrQueryAllRows: Filter messages on subject

I'm pretty much new to MAPI and haven't wrote much C++ Code.
Basically I want to read all emails in the inbox and filter them based on their subject text. So far I'm using the source code provided at the microsoft msdn website which basically reads all emails from the inbox. What I want now is to not get all emails but filter them on the subject, lets say: I want all emails in my Inbox with the subject title "test".
So far I figuered out that the following line of code retrieves all the mails:
hRes = HrQueryAllRows(lpContentsTable, (LPSPropTagArray) &sptCols, &sres, NULL, 0, &pRows);
The parameter &sres is from the type SRestriction.
I tried to implement a filter on 'test' in the subject:
sres.rt = RES_CONTENT;
sres.res.resContent.ulFuzzyLevel = FL_FULLSTRING;
sres.res.resContent.ulPropTag = PR_SUBJECT;
sres.res.resContent.lpProp = &SvcProps;
SvcProps.ulPropTag = PR_SUBJECT;
SvcProps.Value.lpszA = "test";
ScvProps is from the type SPropValue.
If i execute the application then I get 0 lines returned. If I change the String test to an empty String then I get all emails.
I'm assuming i'm using the "filter" option wrong, any ideas?
Edit: When I change the FuzzyLevel to:
sres.res.resContent.ulFuzzyLevel = FL_SUBSTRING;
then I can look for subjects that contain a single character but as soon as I add a second character I get 0 rows as result. I'm pretty sure this is just some c++ stuff that I don't understand that causes all this problems ...
I figured the problem out.
Replacing
sres.res.resContent.ulFuzzyLevel = FL_FULLSTRING;
sres.res.resContent.ulPropTag = PR_SUBJECT;
SvcProps.ulPropTag = PR_SUBJECT;
with
sres.res.resContent.ulFuzzyLevel = FL_SUBSTRING;
sres.res.resContent.ulPropTag = PR_SUBJECT_A;
SvcProps.ulPropTag = PR_SUBJECT_A;
fixed the problem.

Finding the phone company of a cell phone number?

I have an application where people can give a phone number and it will send SMS texts to the phone number through EMail-SMS gateways. For this to work however, I need the phone company of the given number so that I send the email to the proper SMS gateway. I've seen some services that allow you to look up this information, but none of them in the form of a web service or database.
For instance, http://tnid.us provides such a service. Example output from my phone number:
Where do they get the "Current Telephone Company" information for each number. Is this freely available information? Is there a database or some sort of web service I can use to get that information for a given cell phone number?
What you need is called a HLR (Home Location Register) number lookup.
In their basic forms such APIs will expect a phone number in international format (example, +15121234567) and will return back their IMSI, which includes their MCC (gives you the country) and MNC (gives you the phone's carrier). The may even include the phone's current carrier (eg to tell if the phone is roaming). It may not work if the phone is currently out of range or turned off. In those cases, depending on the API provider, they may give you a cached result.
The site you mentioned seems to provide such functionality. A web search for "HLR lookup API" will give you plenty more results. I have personal experience with CLX's service and would recommend it.
This would be pretty code intensive, but something you could do right now, on your own, without APIs as long as the tnid.us site is around:
Why not have IE open in a hidden browser window with the URL of the phone number? It looks like the URL would take the format of http://tnid.us/search.php?q=########## where # represents a number. So you need a textbox, a label, and a button. I call the textbox "txtPhoneNumber", the label "lblCarrier", and the button would call the function I have below "OnClick".
The button function creates the IE instance using MSHtml.dll and SHDocVW.dll and does a page scrape of the HTML that is in your browser "object". You then parse it down. You have to first install the Interoperability Assemblies that came with Visual Studio 2005 (C:\Program Files\Common Files\Merge Modules\vs_piaredist.exe). Then:
1> Create a new web project in Visual Studio.NET.
2> Add a reference to SHDocVw.dll and Microsoft.mshtml.
3> In default.aspx.cs, add these lines at the top:
using mshtml;
using SHDocVw;
using System.Threading;
4> Add the following function :
protected void executeMSIE(Object sender, EventArgs e)
{
SHDocVw.InternetExplorer ie = new SHDocVw.InternetExplorerClass();
object o = System.Reflection.Missing.Value;
TextBox txtPhoneNumber = (TextBox)this.Page.FindControl("txtPhoneNumber");
object url = "http://tnid.us/search.php?q=" + txtPhoneNumber.Text);
StringBuilder sb = new StringBuilder();
if (ie != null) {
ie.Navigate2(ref url,ref o,ref o,ref o,ref o);
ie.Visible = false;
while(ie.Busy){Thread.Sleep(2);}
IHTMLDocument2 d = (IHTMLDocument2) ie.Document;
if (d != null) {
IHTMLElementCollection all = d.all;
string ourText = String.Empty;
foreach (object el in all)
{
//find the text by checking each (string)el.Text
if ((string)el.ToString().Contains("Current Phone Company"))
ourText = (string)el.ToString();
}
// or maybe do something like this instead of the loop above...
// HTMLInputElement searchText = (HTMLInputElement)d.all.item("p", 0);
int idx = 0;
// and do a foreach on searchText to find the right "<p>"...
foreach (string s in searchText) {
if (s.Contains("Current Phone Company") || s.Contains("Original Phone Company")) {
idx = s.IndexOf("<strong>") + 8;
ourText = s.Substring(idx);
idx = ourText.IndexOf('<');
ourText = ourText.Substring(0, idx);
}
}
// ... then decode "ourText"
string[] ourArray = ourText.Split(';');
foreach (string s in ourArray) {
char c = (char)s.Split('#')[1];
sb.Append(c.ToString());
}
// sb.ToString() is now your phone company carrier....
}
}
if (sb != null)
lblCarrier.Text = sb.ToString();
else
lblCarrier.Text = "No MSIE?";
}
For some reason I don't get the "Current Phone Company" when I just use the tnid.us site directly, though, only the Original. So you might want to have the code test what it's getting back, i.e.
bool currentCompanyFound = false;
if (s.Contains("Current Telephone Company")) { currentCompanyFound = true }
I have it checking for either one, above, so you get something back. What the code should do is to find the area of HTML between
<p class="lt">Current Telephone Company:<br /><strong>
and
</strong></p>
I have it looking for the index of
<strong>
and adding on the characters of that word to get to the starting position. I can't remember if you can use strings or only characters for .indexOf. But you get the point and you or someone else can probably find a way to get it working from there.
That text you get back is encoded with char codes, so you'd have to convert those. I gave you some code above that should assist in that... it's untested and completely from my head, but it should work or get you where you're going.
Did you look just slightly farther down on the tnid.us result page?
Need API access? Contact sales#tnID.us.
[Disclosure: I work for Twilio]
You can retrieve phone number information with Twilio Lookup.
If you are currently evaluating services and functionality for phone number lookup, I'd suggest giving Lookup a try via the quickstart.