Skip to content

Tutorial 5

PhuocLe edited this page Aug 26, 2018 · 10 revisions

Task

  • When lead created, send an email (with an email template) notification to all team members of the current user belong to teams

Prerequisites

Coding

Break down tasks

  • A custom workflow get all team members of the current user belong to teams (Custom workflow: RetrieveUsers)
  • A custom workflow send email with email template for all team members of current record (Custom workflow SendUsersMail)
  • Create a real-time workflow when Lead create and use 2 custom workflow RetrieveUsers, SendUsersMail

RetrieveUsers

  1. Add New Project 04. C# Workflow Project to solution.
    • A popup form Add new Workflow Project opened
    • Click button >< to create/select a Dynamics 365 connection
    • After connected PL.DynamicsCrm.DevKit loaded all entities and bind to dropdown Project Name
    • Checkbox Others should be checked
    • Keep the text box Project Name empty
    • Select 9.0.2.4 in the Crm Version PL.DynamicsCrm.DevKit get all Microsoft.CrmSdk.CoreAssemblies version from NuGet
    • Select 4.5.2 in the .Net version
    • Click OK
    • PL.DynamicsCrm.DevKit created workflow project name: Paz.LuckeyMonkey.Workflow
  2. Rebuild solution to restore NuGet packages
  3. Add 01. C# Late Bound Class SystemUser to Entities folder of Paz.LuckeyMonkey.Shared project. If you don't know how, please check again the Tutorial 1: Plugin
  4. Add New Item 03. C# Workflow Class to Paz.LuckeyMonkey.Workflow project
    • A popup form opened
    • Enter RetrieveUsers to texbox Class Name
    • Click OK
    • PL.DynamicsCrm.DevKit created workflow class: RetrieveUsers
  5. Edit the RetrieveUsers.cs like bellow
    public class RetrieveUsers : CodeActivity
    {
        [Output("UserIds")]
        [RequiredArgument]
        public OutArgument<string> UserIds { get; set; }

        protected override void Execute(CodeActivityContext executionContext)
        {
            var workflowContext = executionContext.GetExtension<IWorkflowContext>();
            var serviceFactory = executionContext.GetExtension<IOrganizationServiceFactory>();
            var service = serviceFactory.CreateOrganizationService(workflowContext.UserId);
            var tracing = executionContext.GetExtension<ITracingService>();
            ExecuteWorkflow(executionContext, workflowContext, serviceFactory, service, tracing);
        }

        private void ExecuteWorkflow(CodeActivityContext executionContext, IWorkflowContext workflowContext, IOrganizationServiceFactory serviceFactory, IOrganizationService service, ITracingService tracing)
        {
            Debugger.Trace(tracing, "Begin Execute Workflow: RetrieveUsers");
            //YOUR CUSTOM-WORKFLOW-CODE GO HERE
            var fetchXml = $@"
<fetch version='1.0' output-format='xml-platform' mapping='logical' distinct='true'>
  <entity name='systemuser'>
    <attribute name='systemuserid'/>
    <link-entity name='teammembership' from='systemuserid' to='systemuserid' visible='false' intersect='true'>
      <link-entity name='team' from='teamid' to='teamid' alias='aa'>
        <link-entity name='teammembership' from='teamid' to='teamid' visible='false' intersect='true'>
          <link-entity name='systemuser' from='systemuserid' to='systemuserid' alias='ab'>
            <filter type='and'>
              <condition attribute='systemuserid' operator='eq-userid'/>
            </filter>
          </link-entity>
        </link-entity>
      </link-entity>
    </link-entity>
  </entity>
</fetch>
";
            var users = service.RetrieveAll<SystemUser>(fetchXml);
            var userIds = string.Join(";",
                users
                .Where(u => u.Id != workflowContext.InitiatingUserId)
                .Select(u => u.Id).Distinct().ToList());
            UserIds.Set(executionContext, userIds);

            Debugger.Trace(tracing, "End Execute Workflow: RetrieveUsers");
        }
    }

NOTED Remember to use View-FetchXML to copy/paste code

  1. Open file PL.DynamicsCrm.DevKit.Cli.json by Notepad and edit these information in section: workflows.profile = "DEBUG"
    • workflows.solution = "LuckeyMonkey"
    • workflows.includefiles = "Paz.LuckeyMonkey.*.dll"
  2. Run deploy.bat of project Paz.LuckeyMonkey.Workflow

SendUsersMail

  1. Add New Item 03. C# Workflow Class SendUsersMail to Paz.LuckeyMonkey.Workflow project
  2. Edit code SendUsersMail like bellow
using System;
using System.Activities;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Crm.Sdk.Messages;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Messages;
using Microsoft.Xrm.Sdk.Metadata;
using Microsoft.Xrm.Sdk.Metadata.Query;
using Microsoft.Xrm.Sdk.Query;
using Microsoft.Xrm.Sdk.Workflow;
using Paz.LuckeyMonkey.Shared;
using Paz.LuckeyMonkey.Shared.Entities;

namespace Paz.LuckeyMonkey.Workflow
{
    [CrmPluginRegistration("SendUsersMail", "SendUsersMail", "", "Paz.LuckeyMonkey.Workflow", IsolationModeEnum.Sandbox)]
    public class SendUsersMail : CodeActivity
    {
        [Input("List UserIds")]
        [RequiredArgument]
        public InArgument<string> UserIds { get; set; }

        [Input("Email Template Title")]
        [RequiredArgument]
        public InArgument<string> EmailTemplateTitle { get; set; }

        [Input("Record Url")]
        [RequiredArgument]
        public InArgument<string> RecordUrl { get; set; }

        protected override void Execute(CodeActivityContext executionContext)
        {
            var workflowContext = executionContext.GetExtension<IWorkflowContext>();
            var serviceFactory = executionContext.GetExtension<IOrganizationServiceFactory>();
            var service = serviceFactory.CreateOrganizationService(workflowContext.UserId);
            var tracing = executionContext.GetExtension<ITracingService>();
            ExecuteWorkflow(executionContext, workflowContext, serviceFactory, service, tracing);
        }

        private void ExecuteWorkflow(CodeActivityContext executionContext, IWorkflowContext workflowContext, IOrganizationServiceFactory serviceFactory, IOrganizationService service, ITracingService tracing)
        {
            Debugger.Trace(tracing, "Begin Execute Workflow: SendUsersMail");
            //YOUR CUSTOM-WORKFLOW-CODE GO HERE
            var templateId = GetTemplateId(executionContext, service);
            if (templateId == Guid.Empty) return;
            var objectId = GetRecordId(executionContext);
            var objectType = GetEntityName(executionContext, service);
            var request = new InstantiateTemplateRequest
            {
                TemplateId = templateId,
                ObjectId = objectId,
                ObjectType = objectType
            };
            var response = (InstantiateTemplateResponse)service.Execute(request);
            if (response != null)
            {
                var email = new Email(response.EntityCollection[0]);
                email.from = new List<ActivityParty>()
                {
                    new ActivityParty { PartyId = new EntityReference(SystemUser.EntityLogicalName, workflowContext.InitiatingUserId) }
                };
                email.to = GetToEmail(executionContext);
                email.RegardingObjectId = new EntityReference(objectType, objectId);
                var emailId = service.Create(email.GetCreateEntity());
                var sendRequest = new SendEmailRequest()
                {
                    EmailId = emailId,
                    TrackingToken = $"[CRM-TRACKING]-{objectId}",
                    IssueSend = true
                };
                service.Execute(sendRequest);
            }
            Debugger.Trace(tracing, "End Execute Workflow: SendUsersMail");
        }

        private List<ActivityParty> GetToEmail(CodeActivityContext executionContext)
        {
            var userIds = UserIds.Get<string>(executionContext);
            var rows = userIds.Split(";".ToCharArray());
            var list = new List<ActivityParty>();
            foreach (var row in rows)
                list.Add(new ActivityParty { PartyId = new EntityReference(SystemUser.EntityLogicalName, Guid.Parse(row)) });
            return list;
        }

        private Guid GetTemplateId(CodeActivityContext executionContext, IOrganizationService service)
        {
            var emailTemplateTitle = EmailTemplateTitle.Get<string>(executionContext);
            var fetchData = new
            {
                title = emailTemplateTitle
            };
            var fetchXml = $@"
<fetch version='1.0' output-format='xml-platform' mapping='logical' distinct='false'>
  <entity name='template'>
    <attribute name='templateid'/>
    <attribute name='createdon'/>
    <order attribute='createdon' descending='true'/>
    <filter type='and'>
      <condition attribute='title' operator='eq' value='{fetchData.title}'/>
    </filter>
  </entity>
</fetch>
";
            var rows = service.RetrieveAll<Template>(fetchXml);
            if (rows.Count == 0) return Guid.Empty;
            return rows[0].Id;
        }

        private Guid GetRecordId(CodeActivityContext executionContext)
        {
            var recordUrl = RecordUrl.Get<string>(executionContext);
            string[] urlParts = recordUrl.Split("?".ToArray());
            string[] urlParams = urlParts[1].Split("&".ToCharArray());
            string objectTypeCode = urlParams[0].Replace("etc=", "");
            var objectId = urlParams[1].Replace("id=", "");
            return Guid.Parse(objectId);
        }

        public string GetEntityName(CodeActivityContext executionContext, IOrganizationService service)
        {
            var recordUrl = RecordUrl.Get<string>(executionContext);
            string[] urlParts = recordUrl.Split("?".ToArray());
            string[] urlParams = urlParts[1].Split("&".ToCharArray());
            string objectTypeCode = urlParams[0].Replace("etc=", "");
            var entityFilter = new MetadataFilterExpression(LogicalOperator.And);
            entityFilter.Conditions.Add(new MetadataConditionExpression("ObjectTypeCode", MetadataConditionOperator.Equals, Convert.ToInt32(objectTypeCode)));
            var entityQueryExpression = new EntityQueryExpression()
            {
                Criteria = entityFilter
            };
            var retrieveMetadataChangesRequest = new RetrieveMetadataChangesRequest()
            {
                Query = entityQueryExpression,
                ClientVersionStamp = null
            };
            var response = (RetrieveMetadataChangesResponse)service.Execute(retrieveMetadataChangesRequest);
            var entityMetadata = (EntityMetadata)response.EntityMetadata[0];
            return entityMetadata.SchemaName.ToLower();
        }
    }
}

NOTED You should add these 01. C# Late Bound Class class: ActivityParty, Email, Template to Entities folder of Paz.LuckeyMonkey.Shared project

  1. Run deploy.bat of project Paz.LuckeyMonkey.Workflow

Dynamics 365 Real-time workflow

  1. Create an email template type: Lead with Title: Lead - New Lead
  2. Create a real-time workflow name Lead - New Lead to use 2 custom workflow
  3. Check-in all files to your source control
  4. You finished this tutorial

Summary

This tutorial, you know howto

Clone this wiki locally