CRM Plugin for Publish and Publish All messages - dynamics-crm

I was wondering if we can write plugins that get executed for messages like "publish" and "publish all" in Dynamics CRM (any version). if so can you share any sample references for the same or code snippets.

This is a plugin that works for Publish and PublishAll messages and it will log the event using an entity that I created for this purpose (you can change to do whatever you want).
When the event is Publish, the plugin uses the ParameterXml parameter (MSDN) to log which components are being published. In the case of the PublishAll message, this parameter is not present so there's no detail (which makes sense because you're publishing all).
public class PublishPlugin : IPlugin
{
public void Execute(IServiceProvider serviceProvider)
{
IPluginExecutionContext context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
IOrganizationServiceFactory serviceFactory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);
if (context.MessageName != "Publish" && context.MessageName != "PublishAll")
return;
string parameterXml = string.Empty;
if (context.MessageName == "Publish")
{
if (context.InputParameters.Contains("ParameterXml"))
{
parameterXml = (string)context.InputParameters["ParameterXml"];
}
}
CreatePublishAuditRecord(service, context.MessageName, context.InitiatingUserId, parameterXml);
}
private void CreatePublishAuditRecord(IOrganizationService service, string messageName, Guid userId, string parameterXml)
{
Entity auditRecord = new Entity("fjo_publishaudit");
auditRecord["fjo_message"] = messageName;
auditRecord["fjo_publishbyid"] = new EntityReference("systemuser", userId);
auditRecord["fjo_publishon"] = DateTime.Now;
auditRecord["fjo_parameterxml"] = parameterXml;
service.Create(auditRecord);
}
}
This is how it looks in CRM:
You can download the plugin project and CRM solution from my GitHub.

See here for a list of valid Dynamics CRM messages. Publish and PublishAll are both listed. They're also valid in all version of CRM from 2011 onward.
https://msdn.microsoft.com/en-us/library/gg328576.aspx
Just register your plugin like any other but use Publish or PublishAll for the message and leave the Entity as blank.
In the case of Publish, it seems by looking at the documentation that you can't narrow down which entity is being published. You'll have to take a look at what the Input Parameters give you to see if you can work out which entity you're dealing with, if you need it.

Related

How to create Action and Call action in MS Dynamics CRM?

How to create Action step by step and Call action in MS Dynamics CRM?
How many ways to call action in MS Dynamics CRM?
What the benefits of action instead of Workflow/plugin?
Actions
Actions are a type of process in Microsoft Dynamics 365. You can invoke actions, including custom actions, directly from a workflow or dialog, without writing code! More information: Invoke custom actions from a workflow or dialog
Actions can also be invoked by running custom code that uses the Microsoft Dynamics 365 Web services.
You can call actions:
It can be called from both client & server side, enabling the Single Point of Approach (Implement once, consume anywhere), for ex:- From code that executes within a plug-in, custom workflow and any C# code.
From a command that is placed in the application and executes the operation using JavaScript code.
Can receive input parameters and return output parameters in a straight forward manner, similar to an Organization level Web Service
From an integration with another system that uses the Microsoft Dynamics 365 web services.
From a custom client application that uses the Microsoft Dynamics 365 web services.
Why use actions?
Actions open a range of possibilities for composing business logic. Before Actions, the primary way to implement business processes was limited to plug-ins or custom workflow activities. With Actions, you can perform operations, such as Create, Update, Delete, Assign, or Perform Action. Internally, an action creates a custom Dynamics 365 message. With Actions you can create a custom message (for example: submitquote , leadtoax etc. Once an action is defined and activated, a developer can use that message like any of the other messages provided by the Microsoft Dynamics 365 platform.
Suppose you have button on Quote form which sends information from CRM to another platform (for ex another platform is AX).
Create and activate a Custom Action (Settings > Process)
Now you can call this Action(ofs_submitquotetoax) from JavaScript on some event (OnLoad, OnSave,etc). In this example I am calling the action from SUBMIT QUOTE button on Quote form which sending quote information to other system (AX).
// Call this below method from Button click event or from any event on the form
// For Alert.showLoding method you can see another Alert.js library
// For Process.callAction method you can see another Process.js library
// You can download Alert.js & Process.js from this Path: https://drive.google.com/drive/folders/0B2CUbevE8v9YMkZlMEhUZ3NJc1U
function submitquote() {
var actionName = "ofs_submitquoteax";
var entityName = Xrm.Page.data.entity.getEntityName();
var entityId = Xrm.Page.data.entity.getId();
Alert.showLoading("Submitting...", 400, 150);
var inputParams = [
{
key: "EntityRef", type: Process.Type.EntityReference,
value: new Process.EntityReference(entityName, entityId)
}
];
// call process callection method
Process.callAction(actionName, inputParams, cloneSuccessCallback, errorCallback);
}
function cloneSuccessCallback() {
Alert.hide();
Alert.show("Action Success", "", null, "SUCCESS");
Alert.hide();
var entityName = Xrm.Page.data.entity.getEntityName();
var entityId = Xrm.Page.data.entity.getId();
Xrm.Utility.openEntityForm(entityName, entityId);
//Xrm.Page.data.refresh();
}
function errorCallback(error, trace) {
alert(error);
alert(alert(error));
}
e, for this event you can register & trigger a Plugin and receive the input-parameter (in our case we sending input-parameter key as EntityRef in which we are sending entityName and entityId.
Parameters: Action Unique Name, Input Parameters (array), Success Callback (function), Error Callback (function), CRM Base URL (not required on forms/views)
Each Input Parameter object should contain key, value, and type. Types are defined by the Process.Type enum. EntityReference values should be an object containing id and entityType.
The Success Callback function should accept one argument which is an array of output parameters, each containing key, and value.
You can write plugin in below way and access input parameter
protected override void ExecuteCrmPlugin(LocalPluginContext localContext)
{ // Register the plugin in PreValidation stage as this plugin will trigger from Javascript (Action)
if (localContext == null)
{
throw new InvalidPluginExecutionException("localContext");
}
IPluginExecutionContext context = localContext.PluginExecutionContext;
if (context.Depth > 1) { return; }
IOrganizationService service = localContext.OrganizationService;
ITracingService trace = localContext.TracingService;
Entity quoteEntity = null;
EntityReference qEntity = null;
if (context.InputParameters.Contains("EntityRef") && context.InputParameters["EntityRef"] is EntityReference)
{
//if (context.PrimaryEntityName.ToLower() != "quote") { return; }
qEntity = context.InputParameters["EntityRef"] as EntityReference;
if (qEntity.LogicalName.ToLower().Equals("quote"))
{
try
{
quoteEntity = service.Retrieve("quote", qEntity.Id, new ColumnSet("ofs_parentaccountid", "quotenumber", "revisionnumber", "ofs_well"));
// Execute Your logic
}
catch (Exception ex)
{
trace.Trace(string.Format("Exception Quote_Create_AXIntegration Plugin: {0}", new[] { ex.ToString() }));
}
}
}
else { return; }
}
Register your plugin and then register the step in below way, you can notice your custom message name in below screen-shot "ofs_submitquoteax";
Ref:https://community.dynamics.com/crm/b/mylifemicrosoftdynamiccrm/archive/2017/04/17/microsoft-dynamics-crm-actions

When creating an IServiceManagement for Dynamics 365, why does the authentication endpoint respond with an HTML sign in page?

I have some integration code that intends to use the Organization Service via the CRM SDK.
On one environment, creating an IServiceManagement<IOrganizationService>:
IServiceManagement<IOrganizationService> orgServiceManagement = ServiceConfigurationFactory.CreateManagement<IOrganizationService>(new Uri("dynamics uri")));
and then authenticating with service account credentials:
AuthenticationCredentials authCredentials = new AuthenticationCredentials();
authCredentials.ClientCredentials.UserName.UserName = _config.GetValue<string>("Dynamics:Username");
authCredentials.ClientCredentials.UserName.Password = _config.GetValue<string>("Dynamics:Password");
AuthenticationCredentials tokenCredentials = orgServiceManagement.Authenticate(authCredentials);
works fine.
On another Dynamics environment, the call to GetServiceManagement fails with the following error message:
System.InvalidOperationException
HResult=0x80131509
Message=Metadata contains a reference that cannot be resolved: 'https://login.microsoftonline.com/[guid]/oauth2/authorize?client_id=[some client id]&response_mode=form_post&response_type=code+id_token&scope=openid+profile&state=OpenIdConnect.AuthenticationProperties%[some base-64]RedirectTo%3dhttps%253a%252f%252ftst-success.crm4.dynamics.com%252f&nonce=[some nonce]&redirect_uri=https:%2f%2fcloudredirector.crm4.dynamics.com%2fG%2fAuthRedirect%2fIndex.aspx&max_age=86400'.
Source=System.ServiceModel
StackTrace:
at System.ServiceModel.Description.MetadataExchangeClient.MetadataRetriever.Retrieve(TimeoutHelper timeoutHelper)
at System.ServiceModel.Description.MetadataExchangeClient.ResolveNext(ResolveCallState resolveCallState)
at System.ServiceModel.Description.MetadataExchangeClient.GetMetadata(MetadataRetriever retriever)
at System.ServiceModel.Description.MetadataExchangeClient.GetMetadata(Uri address, MetadataExchangeClientMode mode)
at Microsoft.Xrm.Sdk.Client.ServiceMetadataUtility.RetrieveServiceEndpointMetadata(Type contractType, Uri serviceUri, Boolean checkForSecondary)
at Microsoft.Xrm.Sdk.Client.ServiceConfiguration`1..ctor(Uri serviceUri, Boolean checkForSecondary)
at Microsoft.Xrm.Sdk.Client.ServiceConfigurationFactory.CreateConfiguration[TService](Uri serviceUri, Boolean enableProxyTypes, Assembly assembly)
at Microsoft.Xrm.Sdk.Client.ServiceConfigurationFactory.CreateConfiguration[TService](Uri serviceUri)
at CrmAuthTest.Program.Main(String[] args) in c:\users\t.wolverson\Source\Repos\CrmAuthTest\CrmAuthTest\Program.cs:line 18
Inner Exception 1:
XmlException: CData elements not valid at top level of an XML document. Line 1, position 3.
(I have masked the bits which look identifying or cryptographic)
POSTing to this URL in PostMan yields the HTML for a browser login page, which explains the failure; this isn't what the ServiceConfigurationFactory expects. The scenario is not user-interactive, so this would never make sense, there is no browser and no user able to interact with it.
What do I have to change in Dynamics CRM Online to stop it doing this, and make it just work normally?
Do you instantiate your OrganizationServiceProxy depending on the AuthenticationProviderType right after the lines of code you have posted? Like this
var orgServiceManagement = ServiceConfigurationFactory.CreateManagement<IOrganizationService>(new Uri(ConfigurationManager.AppSettings["CrmUrlService"]));
var authCredentials = new AuthenticationCredentials();
authCredentials.ClientCredentials.UserName.UserName = ConfigurationManager.AppSettings["CrmUserName"];
authCredentials.ClientCredentials.UserName.Password = ConfigurationManager.AppSettings["CrmPassword"];
var tokenCredentials = orgServiceManagement.Authenticate(authCredentials);
IOrganizationService _service;
switch (orgServiceManagement.AuthenticationType)
{
case AuthenticationProviderType.ActiveDirectory:
_service = new OrganizationServiceProxy(orgServiceManagement, tokenCredentials.ClientCredentials);
break;
default:
_service = new OrganizationServiceProxy(orgServiceManagement, tokenCredentials.SecurityTokenResponse);
break;
}
Even if this solves your problem, I recommend that you use CrmServiceClient instead. This class can be found in Microsoft.Xrm.Tooling.Connector dll. It is the go to authentication class when building Windows client applications that connect to Microsoft Dynamics 365. More information on this can be found here
Here is an example on how to initialize CrmServiceClient when connecting to Dynamics 365 online using Office 365:
var myConnectionString = "Url=https://[YourOrganization].crm4.dynamics.com;Username=[YourUser];Password=[YourPassword];AuthType=Office365;";
var crmClient = new CrmServiceClient(myConnectionString);
//Do your stuff
var response = crmClient.Execute(new WhoAmIRequest());
If you need other authentication methods in Dynamics Online check how to build your connection string here.
For on-premises check how to build your connection string here.

How to reference my added web service

This is my first time dealing with Web Services. I have successfully Added a Web Service to one that I have created in VS 2010. What I'm trying to do is access the functions of the added web service in this .asmx file This is what I see now along with all the auto added code.
Service1.asmx.vb
Public Class Service1
Inherits System.Web.Services.WebService
<WebMethod()> _
Public Function HelloWorld() As String
Return "Hello World"
End Function
The added web service that I have added is called blahService. So I'm just curious as how do I access the functions that are in the added web service? Do I have to do something like this...?
Dim foo as new blahService()
Then when I go to access a function just do
foo.function()
1) From within your Solution Explorer of your project, right-click on “Service References”, then click on “Add Service Reference”
2) The dialog box which appears allows you to put in the URL of your web service. Enter it, then press the “Go” Button”
3) You can see that the name of the web service appears in the Services pane. Give your webservice a namespace (anything you like) that will be used to refer to it from within your project. Press the OK button. That namespace will now appear in the list of Service References
A web service is considered to have anonymous authentication if no specific permission is required to access it. The server is allowed to fulfill every request without regard to the entity who is requesting the information. This is the case for many web services on the internet.
For reference, this is the source code for the method which I will be calling from my application:
[WebMethod]
public List<string> GetStrings(int StartNumber, int EndNumber)
{
List<string> MyList = new List<string>();
for (int i = StartNumber; i <= EndNumber; i++)
{
MyList.Add("AuthASMXService String #" + i.ToString());
}
return MyList;
}
and here’s the code that will call the method in the web service instantiated above:
private void ASMXWebServiceInvoke_Click_1(object sender, RoutedEventArgs e)
{
ASMXWebServiceReference.WebService1SoapClient MyASMXWebServiceClient
= new ASMXWebServiceReference.WebService1SoapClient();
ASMXWebServiceReference.GetStringsResponse MyStringsResponse =
MyASMXWebServiceClient.GetStrings(10, 20);
ASMXWebServiceReference.ArrayOfString MyStrings =
MyStringsResponse.Body.GetStringsResult;
ASMXGridView.ItemsSource = MyStrings;
}
How do I connect to an ASMX web service?

Updating an Activity in MS CRM via web service?

I've been trying to do this all morning. Anyone have a code snippet (C#) showing how to update an "activity" within CRM via the webservice?
I can CreateReadUpdateDelete with entities, but I'm not sure how to do it with Activities.
Can't find anything on google either...
What are you specifically looking to update? Basically, updating an activity is just like updating any other entity, you just have to use the task entity.
public void CloseTask(CrmService crmsvc, Guid activityid, DateTime start, DateTime end)
{
ColumnSet cols = new ColumnSet();
cols.Attributes = new string[] { "activityid", "statecode" };
task tsk = (task)crmsvc.Retrieve(EntityName.task.ToString(), activityid, cols);
if(tsk.statecode.Value != TaskState.Open)
return;
tsk.actualstart = new CRMDateTime();
tsk.actualstart.value = start.ToString();
tsk.actualend = new CRMDateTime();
tsk.actualend.value = end.ToString();
crmsvc.Update(tsk);
SetStateTaskRequest state = new SetStateTaskRequest();
state.EntityId = activityid;
state.TaskState = TaskState.Completed;
state.TaskStatus = -1; // Let MS CRM decide this property;
SetStateTaskResponse stateSet = (SetStateTaskResponse)crmsvc.Execute(state);
}
Edit: added some sample code. note, I had to modify what I had to strip some proprietary code, so I don't know if this will actually compile. It's close though.
We can also update a Custom Workflow Activity Using Assembly Versioning. Below link gives more information:
http://msdn.microsoft.com/en-us/library/gg328011.aspx

DomainProjectPicker class is obsolete in VSTS 2010?

What is the alternative for DomainProjectPicker if I want to select a server plus its projects? I am aware of a new class called TeamProjectPicker, but that doesn't help me. Anyone know how to select the server from this type of dialog?
Thanks,TS.
As far as I can figure it out it's more or less the same as the DomainProjectPicker.
Here's a code sample of how I was working with it:
if (tpp.ShowDialog() == DialogResult.OK)
{
try
{
//here you get the TfsTeamProjectCollection (the TeamFoundationServer class is also obsolete)
TfsTeamProjectCollection tfsProj = tpp.SelectedTeamProjectCollection;
//here you authenticate
tfsProj.Authenticate();
}
etc...
You can use the TeamProjectPicker class from Microsoft.TeamFoundation.Client.dll. There is a great blog post that describes how to wrangle the dialog: Using the TeamProjectPicker API in TFS 2010
Here's the code sample for selecting multiple team projects:
Application.EnableVisualStyles(); // Makes it look nicer from a console app.
//"using" pattern is recommended as the picker needs to be disposed of
using (TeamProjectPicker tpp = new TeamProjectPicker(TeamProjectPickerMode.MultiProject, false))
{
DialogResult result = tpp.ShowDialog();
if (result == DialogResult.OK)
{
System.Console.WriteLine("Selected Team Project Collection Uri: " + tpp.SelectedTeamProjectCollection.Uri);
System.Console.WriteLine("Selected Projects:");
foreach(ProjectInfo projectInfo in tpp.SelectedProjects)
{
System.Console.WriteLine(projectInfo.Name);
}
}
}
If you don't care about the project and only want the user to be able to select a server and collection, use TeamProjectPickerMode.NoProject in the constructor.

Resources