Advertisements

Archive

Author Archive

ADX Portals 7.x – ‘Page_Validators’ is undefined

Other day, while adding a custom validator using JScript on an Entity Form, I was getting ‘Page_Validators’ is undefined error, at below line of code.

// Add the custom validator to Web Page’s validators array
Page_Validators.push(spouseValidator);

Reason:

  • The dynamics CRM form, associated to the ADX Entity Form, does’t have any required field, hence ‘Page_Validators’ array is NULL as ADX did’t need to load any validators.

Fix:

  • ‘Page_Validators’ is a validator collection and we dont have control over this.
  • Below workaround worked for us:
    • Make one of the field as ‘Required’ by adding ‘Entity Form Metadata’. ADX instantiates ‘Page_Validators’ array, as there is Required field now.
    • You can make the field as Non-required in the JScript.

Metadata_1

Note:

  • This behavior is fixed in further versions of ADX and Dynamics Portals.

🙂

 

 

D 365 – How to access Customization’s and Solutions in UCI Only Mode

To know more about ‘Unified Interface Only mode’ refer my previous article

Below are the steps to access environment settings, customization’s and solutions when the ‘Unified Interface Only mode’ turned on.

  • Click on ‘Advanced Settings’ from the ribbon Gear button.

FUI_3

  • It will open up ‘Settings’ tab in classic web mode.

FUI_6

  • You can access your solutions and make customizations how you used to do earlier.

Refer the documentation to know more about ‘Unified Interface’.

🙂

D365 – How to turn On/Off ‘Unified Interface Only mode’

April 7, 2019 1 comment

Today, I subscribed to my new D365 30 days trail and noticed the ‘Sales’ web Application opening up in ‘Unified Interface’ mode rather than in Classic web mode by default.

FUI_2.PNG

When noticed, the URL of the Dynamics landing page (My Apps) has a parameter forceUCI=1

FUI_1

It seems that Unified Interface ONLY mode has been implied on my new instance.

What is Unified Interface Only mode?

  • As per the documentation, Microsoft product team wants us to adopt to UCI (Unified Client Interface), to provide superior performance and usability.
  • All the new Dynamics 365 for Customer Engagement environments (instances) and Common Data Service environments, will be provisioned in Unified Interface Only mode.

Enable/Disable Unified Interface Only mode:

  • To Enable/Disable Unified Interface Only mode, navigate to ‘Settings’ tab by clicking on ‘Advanced Settings’ from the ribbon Gear button.

FUI_3

  • Open Settings -> Administration -> System Settings

FUI_4

  • Select the option under ‘Use the new Unified Interface only (recommended)’ section.

Accessing the legacy web client app:

  • The legacy web client app, also known as Dynamics 365 – custom, is hidden from end users when a new environment is provisioned.
  • It is always visible to those with System Administrator and System Customizer roles, and to other custom roles with similar privileges.
  • To enable the Legacy App to non-admin users,
    • Go to Settings > Administration > System Settings > General tab. Under Show legacy Dynamics 365 – custom app, select Yes for Show this app to all users, not just administrators.
    • In the Power Platform Admin center, go to Environments and select an environment. Go to Settings > Behavior > Interface settings and then turn on Show legacy app to everyone, not just admins.

FUI_5

  • Note:
    • When ‘Unified Interface Only mode’ is enabled, Dynamics 365 – custom opens in Unified Interface and not in the legacy web client.
    • Legacy app opens only If users only have access to the Dynamics 365 – custom app and no other model-driven apps.

🙂

 

ADX/Dynamics Portals Issue – Missing Entity ‘Tab’ in ‘Web Form Step’

We have ADX portal 7.0 solution installed on our Dynamics 365 8.2 application. We got a requirement to create a Web Form steps on a Custom Entity ‘Employee’.

‘Employee’ entity has an OOB ‘Information’ of ‘Form Type’ Main, with a tab named ‘Personal Details’.

PWF_1

Issue:

  • While adding a Web Form step, there was no ‘Personal Details’ tab and could get only ‘general’ tab.
  • In the new ‘WEB FORM STEP’, select the ‘Form Name’

PWF_2

  • In the ‘Tab Name’ drop down, there is no ‘Personal Details’ tab and all it had was ‘general’ tab.

PWF_3

Reason:

  • As there were 2 OOB forms (i.e., Form Type ‘Main’, ‘Mobile – Express’) with the same name ‘Information’, ADX solution always picking ‘Mobile – Express‘ form which had a tab ‘general’

Fix:

  • Renamed ‘Information’ Main form to ‘Employee’.

PWF_4

  • Publish the solution
  • Refresh the ‘Web Form Step’ and you should see ‘Personal Details’ tab once you select the ‘Employee’ form.

PWF_5

🙂

Categories: ADX, Dynamics 365 Tags: , ,

ADX/Dynamics Portals – Date field custom validation using JScript

April 6, 2019 1 comment

Other day, In one of our Portal Web Form step, we got a requirement to validate ‘Date Of Birth’ where the value should not be  the future date.

We can achieve this by adding a custom validator using JScript and register in your ‘Web Form Step’.

Below is the JScript code snippet which you need to paste in your Web Form Step’s ‘Custom JavaScript’ control.

DOB_Validator_1

Note:

  • In below script replace the ‘new_dateofbirth’ with your field’s schema name.
  • ‘new_dateofbirth_label‘ is the label of the DOB field on your Web Form Step.

$(document).ready(function () {
try{
if (typeof (Page_Validators) == ‘undefined’) return;
// Create new DOB validator
var dobValidator = document.createElement(‘span’);
dobValidator.style.display = “none”;
dobValidator.id = “dobValidator”;
dobValidator.controltovalidate = “new_dateofbirth”;
dobValidator.errormessage = “<a href=’#new_dateofbirth_label’>Date of birth should be in past.</a>”;
dobValidator.validationGroup = “”;
dobValidator.initialvalue = “”;
dobValidator.evaluationfunction = function () {
var dobValue = $(“#new_dateofbirth”).val();
var dob=new Date(dobValue);
var today = new Date();
if (dob > today) {
return false;
}
return true;
};
// Add the dobValidator to Web Page’s validators array
Page_Validators.push(dobValidator);
// Attach event handler of the validation summary link
$(“a[href=’#new_dateofbirth_label’]”).on(“click”, function () {  scrollToAndFocus(‘new_dateofbirth_label’,’new_dateofbirth’);

});
}
catch(e)
{
alert(“Error during DOB validation  – “+e.description);
}
});

  • Save the Web Form Step and test in portal.
  • If you try to save DOB with future date. It shows warning in the banner as below:

DOB_Validator_2

  • Click on the message and it will take you to the DOB control.

Refer my other article to get familiar with Portal syntax’s.

🙂

 

 

[Code Snippet] Send Email To Bulk and Track

In this article, lets see how we can send email to bulk records by forming a query and Email template.

To understanding this better, lets send an email to all the ‘Active Contacts’ in the system.

Below are the required components to send the email to ‘Active Contacts’:

  • Query Expression of ‘Active Contacts’
    • You need to form your query as per your business requirement.
  • Email Template
    • As we are targeting ‘Active Contacts’, Email Template Type must be of ‘Contact’.

Bulk_5

  • Email From – Entity Reference of the ‘Sender’
  • Email Regarding – Record of which all the created Emails will get associated with.

Lets see the code.

Below is the main method which sends out emails.

public static Guid SendBulkEmail(EntityReference sender, EntityReference mailRegarding, Guid emailTemplateId, QueryExpression querySenders, IOrganizationService crmService){
// Set trackingId for bulk mail request.
var trackingId = Guid.NewGuid();
try{
var bulkMailRequest = new SendBulkMailRequest() {
Query = querySenders,
Sender = sender,
RegardingId = mailRegarding.Id,
RegardingType = mailRegarding.LogicalName,
TemplateId = emailTemplateId,
RequestId = trackingId
};

// Execute the async bulk email request
var resp = (SendBulkMailResponse)crmService.Execute(bulkMailRequest);
}
catch (Exception ex)
{
throw;
}

return trackingId;
}

Below are the helper methods which pass required inputs for the SendBulkEmail() method.

Get ‘Email Template ID’:

public static Guid GetEmailTemplateId(string emailTemplateName, IOrganizationService crmService)
{
Guid emailTemplateId = Guid.Empty;
var queryBuildInTemplates = new QueryExpression{
EntityName = “template”,
ColumnSet = new ColumnSet(“templateid”, “templatetypecode”),
Criteria = new FilterExpression(LogicalOperator.And)
};

queryBuildInTemplates.Criteria.AddCondition(“templatetypecode”, ConditionOperator.Equal, “contact”);
queryBuildInTemplates.Criteria.AddCondition(“title”, ConditionOperator.Equal, emailTemplateName);

var templateEntityCollection = crmService.RetrieveMultiple(queryBuildInTemplates);

if (templateEntityCollection.Entities.Count > 0){
emailTemplateId = (Guid)templateEntityCollection.Entities[0].Attributes[“templateid”];
}
else
{
throw new ArgumentException(“Standard Email Templates are missing”);
}

return emailTemplateId;
}

Get the ‘From’ and ‘Regarding’:

  • Note: For simplicity, I am using current user as ‘From’ and ‘Email Regarding’

public static EntityReference GetCurrentUserId(IOrganizationService crmService){
var systemUserRequest = new WhoAmIRequest();
var systemUserResponse = (WhoAmIResponse)crmService.Execute(systemUserRequest);
return new EntityReference(“systemuser”, systemUserResponse.UserId);
}

Get the Recipients (i.e., Active Contacts) ‘Query Expression’:

public static QueryExpression GetEmailRecipientsQuery(){
var queryContacts = new QueryExpression
{
EntityName = “contact”,
ColumnSet = new ColumnSet(“contactid”),
Criteria = new FilterExpression(LogicalOperator.And)
};

queryContacts.Criteria.AddCondition(“statecode”, ConditionOperator.Equal, 0);
//queryContacts.Criteria.AddCondition(“createdby”, ConditionOperator.EqualUserId);
return queryContacts;
}

That’s it, lets see how we do we call the main method

var currentUser = GetCurrentUserId(_service);

var trackingID = SendBulkEmail(currentUser, currentUser, GetEmailTemplateId(“Contact Welcome”, _service), GetEmailRecipientsQuery(), _service);

Execute above lines of code in console and you would get the response as below.

Bulk_6

What happens on the execution of the code?:

  • A new ‘Bulk Email’ system job gets created. Go to Settings -> System Jobs

Bulk_1

  • Once the ‘Status Reason’ turns in to ‘Succeeded’, Go to ‘Advance Find’ and query ‘Email Messages’.
  • You should see a new ‘Email’ records for each ‘Active Contact’, as the Query Expression we formed is for ‘Active Contacts’

Bulk_3

  • Open one of the Emails and you should see the email body copied from ‘Email Template’.

Bulk_4

  • We can also track the ‘Bulk Email’ job from the code, with below code snippet by passing the ‘Tracking ID’ (i.e., GUID) returned by SendBulkEmail() method.

private const int ARBITRARYMAXPOLLINGTIME = 60;

public static void TrackMailDelivery(Guid trackingId, IOrganizationService crmService){
var bulkQuery = new QueryByAttribute(){
EntityName = “asyncoperation”,
ColumnSet = new ColumnSet(new string[] { “requestid”, “statecode” }),
Attributes = { “requestid” },
Values = { trackingId }
};

// Retrieve the bulk email async operation.
var emailResponse = crmService.RetrieveMultiple(bulkQuery);Console.WriteLine(” Retrieved Bulk Email Async Operation.”);

// Monitor the async operation via polling.
int secondsTicker = ARBITRARYMAXPOLLINGTIME;

Entity createdBulkMailOperation = null;

Console.WriteLine(“Checking operation’s state for ” + ARBITRARYMAXPOLLINGTIME + ” seconds.”);
Console.WriteLine();

while (secondsTicker > 0){
// Make sure the async operation was retrieved.
if (emailResponse.Entities.Count > 0){
// Grab the one bulk operation that has been created.
createdBulkMailOperation = emailResponse.Entities[0];

// Check the operation’s state.
if (((OptionSetValue)createdBulkMailOperation[“statecode”]).Value != 3){
// The operation has not yet completed.
// Wait a second for the status to change.
System.Threading.Thread.Sleep(1000);
secondsTicker–;

// Retrieve a fresh version the bulk delete operation.
emailResponse = crmService.RetrieveMultiple(bulkQuery);
}
else{
// Stop polling because the operation’s state is now complete.
secondsTicker = 0;
}
}
else{
// Wait a second for the async operation to activate.
System.Threading.Thread.Sleep(1000);
secondsTicker–;

// Retrieve the entity again
emailResponse = crmService.RetrieveMultiple(bulkQuery);
}
}

// Validate async operation succeeded
if (((OptionSetValue)createdBulkMailOperation[“statecode”]).Value == 3){
Console.WriteLine(“Operation Completed.”);
}
else{
Console.WriteLine(“Operation not completed yet.”);
}
}

🙂

Categories: CRM