Une grande partie des personnalisations CRM repose sur l'inclusion de code JavaScript au sein des formulaire de l'application. Malheureusement, pour inclure du JavaScript, il faut passer par un grand nombre d'écrans (
Paramètres
Personnalisation
Personnaliser les entités
Entité
Formulaires et vues
Formulaire
Propriétés du formulaire
Modifier
Autant dire que lorsque l'on doit tester un code et faire de fréquentes modifications, cela peut devenir très vite laborieux.
La solution pour pouvoir modifier son code JavaScript rapidement consiste à l'inclure dynamiquement dans la page. Il faut d'abord créer un fichier .js dans un répertoire virtuel du serveur (par exemple "C:\Inetpub\wwwroot\test.js") et s'assurer qu'il est bien disponible via un navigateur (pour notre exemple : http://srv-crm/test.js).
Maintenant il faut aller dans le code JavaScript du formulaire qu'on souhaite modifier et y inclure ce code qui permet de charger une référence vers un fichier JavaScript externe dans la page :
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = 'http://srv-crm/test.js';
document.getElementsByTagName('head')[0].appendChild(script);
Une publication et le code qu'on aura mis dans le fichier JavaScript deviendra actif sur le formulaire modifié. On peut maintenant ouvrir ce fichier dans notre éditeur JavaScript préféré, le modifier et le sauvegarder pour le tester rapidement.
Par contre une fois que le code n'a plus besoin d'être testé, il ne faut pas oublier de le recopier dans le formulaire afin qu'il soit disponible aussi en mode déconnecté.
Voici une astuce concernant les Picklist et la récupération dynamique des valeurs et textes qu'elles peuvent avoir. Lorsqu'on veut renseigner la Picklist d'une entité en code, il faut obligatoirement fixer la valeur de la Picklist, le texte ne suffit pas.
Ceci ne fonctionnera pas :
Public Sub updateAccountCategoryCode(ByVal accountid As String, ByVal CategoryCode As String)
Dim a As account = CType(CRMHelper.Retrieve(GetType(account), accountid), account)
Dim acc As Picklist
acc = New Picklist
acc.name = CategoryCode
a.accountcategorycode = acc
CRMHelper.Update(a)
End Sub
Pour que cela fonctionne, il faudrait définir la valeur associée à ce texte de cette manière :
Public Sub updateAccountCategoryCode(ByVal accountid As String, ByVal CategoryCode As String)
Dim a As account = CType(CRMHelper.Retrieve(GetType(account), accountid), account)
Dim acc As Picklist
acc = New Picklist
acc.name = CategoryCode
Select Case CategoryCode
Case "Prospect"
acc.Value = 1
Case "Client"
acc.Value = 2
End Select
a.accountcategorycode = acc
CRMHelper.Update(a)
End Sub
Seulement on est obligé d'aller chercher dans l'interface de CRM les valeurs associées aux textes possibles pour la Picklist, autant dire que cela peut vite devenir chronophage si l'on travaille avec beaucoup de Picklist.
Heureusement, de la même manière que l'on a le WebService principal de l'application (~/MSCRMServices/2006/CRMService.asmx) pour la lecture, mise à jour et suppression d'entités, il existe un 2ème WebService (~/MSCRMServices/2006/MetaDataService.asmx) qui nous permet d'obtenir les méta-données de l'application, dont les PicklistAttributeMetadata (A noter qu'en V4, ce WebService permet aussi de créer, modifier et supprimer les méta-données des entités).
Une fois le WebService ajouté comme référence Web dans Visual Studio, on peut maintenant récupérer les informations de la Picklist qui nous intéresse :
Private Shared Function retrievePicklistAttributeMetadata(ByVal entityName As String, ByVal column As String) As PicklistAttributeMetadata
Dim MetaDataService As New MetadataService
MetaDataService.Credentials = System.Net.CredentialCache.DefaultCredentials
Dim attMetaData As AttributeMetadata = MetaDataService.RetrieveAttributeMetadata(entityName, column)
Dim plAttMetaData As PicklistAttributeMetadata = CType(attMetaData, PicklistAttributeMetadata)
Return plAttMetaData
End Function
Et à partir de là, rien de plus facile que de retrouver le couple de valeur/texte qui nous intéresse dans la propriété Options :
Public Shared Function getPicklistFromText(ByVal entityName As String, ByVal column As String, ByVal text As String) As Picklist
Dim pam As PicklistAttributeMetadata = retrievePicklistAttributeMetadata(entityName, column)
For Each o As [Option] In pam.Options
If (o.Description = text) Then
Dim pl As New Picklist
pl.Value = o.OptionValue
pl.name = o.Description
Return pl
End If
Next
End Function
Ce qui au final réduit considérablement la complexité de la mise à jour de la société de mon exemple initial et supprime la maintenance nécessaire en cas de mise à jour de la Picklist.
Public Sub updateAccountCategoryCode(ByVal accountid As String, ByVal CategoryCode As String)
Dim a As account = CType(CRMHelper.Retrieve(GetType(account), accountid), account)
a.accountcategorycode = getPicklistFromText("account", "accountcategorycode", CategoryCode)
CRMHelper.Update(a)
End Sub
Ça peut toujours servir ;)
Ca faisait longtemps que je n' avais plus posté mais maintenant je m'y remet. J'ai eu l'occasion de m'intéresser de près aux développements personnalisés sous Dynamics CRM et je vais tacher de vous faire partager mes expériences sur le sujet.
Mon premier post sur le sujet traitera de la migration puisque j'ai eu à en réaliser une sur un projet comportant des Callouts, c'est à dire des événements levés automatiquement en .NET lors de certaines actions : création/modification/suppression d'entité, changement d'état ou de propriétaire, ...
Si on regarde le SDK de la V4, on peut trouver cette phrase sur la migration des Callouts :
"Microsoft Dynamics CRM 3.0 custom extensions such as callouts and workflow assemblies that use the Microsoft Dynamics CRM 3.0 Web services can be seamlessly upgraded to Microsoft Dynamics CRM On-premise without having to recompile."
Bien évidement, cela n'a pas été aussi simple pour moi. J'ai rencontré quelques erreurs que je vais décrire ici ainsi que les solutions que j'y ai trouvé.
1. Erreur déclenchée dès qu'un événement Callout aurait du être levé
Optimiste, je place mon fichier XML ainsi que mes DDLs dans le dossier approprié et je teste : A chaque création d'entité, j'ai un message d'erreur type. En activant le traçage de logs sur le serveur, j'ai pu avoir un message d'erreur un peu plus explicite que le traditionnel "Veuillez contacter votre administrateur CRM". L'exception déclenchée était :
FileNotFoundException: Impossible de charger le fichier ou l'assembly 'Microsoft.Crm.Platform.Callout.Base, Version=3.0.5300.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' ou une de ses dépendances. Le fichier spécifié est introuvable.
C'est dans cette librairie que l'on trouve les prototypes des méthodes à implémenter pour en faire des Callouts donc il est normal qu'une erreur soit levée si elle n'est pas présente. Après avoir tenté d'ajouter cette assembly partout où je le pouvais, il s'est avéré que la solution était une "simple réparation" du serveur CRM à l'aide du CD d'installation de CRM.
2. Récupération du ColumnSet
Dans la version .NET 1, j'avais une petite méthode qui utilisait la réflexion pour obtenir la liste des attributs par type d'entités pour le paramètre du WebService.
Public Shared Function GetColumnSet(ByVal entityType As Type) As ColumnSet
Dim cols As New ColumnSet
Dim fields As FieldInfo() = entityType.GetFields
Dim colNames(fields.Length - 1) As String
For i As Integer = 0 To fields.Length - 1
colNames(i) = fields(i).Name
Next
cols.Attributes = colNames
Return cols
End Function
Avec le .NET Framework 2 (et donc le 3 aussi), la méthode GetFields ne fonctionne plus, il faut utiliser GetProperties.
Public Shared Function GetColumnSet(ByVal entityType As Type) As ColumnSet
Dim cols As New ColumnSet
Dim pis As PropertyInfo() = entityType.GetProperties
Dim colNames(pis.Length - 1) As String
For i As Integer = 0 To pis.Length - 1
colNames(i) = pis(i).Name
Next
cols.Attributes = colNames
Return cols
End Function
Ce n'est pas un gros changements mais l'erreur est difficile à diagnostiquer puisqu'il faut passer en debug pour voir qu'aucun attribut n'est listé par la méthode GetFields.
3. Distinction du type d'entité à l'aide du paramètre postImageEntityXml
Pour savoir quel type d'entité est crée/modifié/supprimé, on avait recours à un code de ce type (je rappelle qu'on ne peut toujours pas utiliser entityContext.EntityTypeCode à cause du problème des énumérations qui sont numérotées depuis 0 et par ordre alphabétique dans la référence en VB alors que les numéros CRM ne suivent pas la même logique) :
Public Overrides Sub PostCreate(ByVal userContext As CalloutUserContext, ByVal entityContext As CalloutEntityContext, ByVal postImageEntityXml As String)
Dim de As DynamicEntity = CRMHelper.Deserialize(postImageEntityXml, GetType(DynamicEntity))
Select Case de.Name
Case "account"
'Traitement pour les sociétés
Case "contact"
'Traitement pour les contacts
End Select
End Sub
Depuis le passage en version 4, j'ai pu voir en passant en debug dans la méthode que le paramètre postImageEntityXml était null, ce qui peut s'avérer gênant pour dé-sérialiser l'entité et récupérer son type. La solution est donc de faire une classe par type d'entité et de distinguer les classes à appeler au niveau du fichier XML callout.config.xml de cette façon :
<?xml version="1.0" encoding="utf-8" ?>
<callout.config version="2.0">
<callout entity="account" event="PostCreate">
<subscription assembly="MyCallouts.dll" class="MyCallouts.AccountCallouts"></subscription>
</callout>
<callout entity="contact" event="PostCreate">
<subscription assembly="MyCallouts.dll" class="MyCallouts.ContactCallouts"></subscription>
</callout>
</callout.config>
4. Récupération de l'utilisateur connecté : Utilisation du WhoAmI
Ce dernier point sort un peu du contexte des Callouts, puisqu'il s'agit d'un JavaScript permettant de récupérer l'identifiant de l'utilisateur connecté.
En CRM v3, on aurait fait comme ça :
function getUserId()
{
try
{
var command = new RemoteCommand("SystemUser", "WhoAmI", "/MSCRMServices/");
var oResult = command.Execute();
if (oResult.Success)
return oResult.ReturnValue.UserId;
}
catch(e)
{
alert("Error while retrieving userid.");
}
return null;
}
Mais en CRM v4, comme l'objet RemoteCommand n'est plus accessible, on ferait plutôt comme ça (exemple inspiré des Walkthrough du SDK) :
function getUserId()
{
try
{
var serverUrl;
if(IsOnline())
serverUrl = "http://SRVCRM:5555";
else
serverUrl = "http://localhost:2525";
var xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
xmlhttp.open("POST", serverUrl + "/mscrmservices/2007/crmservice.asmx", false);
xmlhttp.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
xmlhttp.setRequestHeader("SOAPAction", "http://schemas.microsoft.com/crm/2007/WebServices/Execute");
var soapBody = "<soap:Body>"+
"<Execute xmlns='http://schemas.microsoft.com/crm/2007/WebServices'>"+
"<Request xsi:type='WhoAmIRequest' />"+
"</Execute></soap:Body>";
var soapXml = "<soap:Envelope " +
"xmlns:soap='http://schemas.xmlsoap.org/soap/envelope/' "+
"xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' "+
"xmlns:xsd='http://www.w3.org/2001/XMLSchema'>";
soapXml += GenerateAuthenticationHeader();
soapXml += soapBody;
soapXml += "</soap:Envelope>";
xmlhttp.send(soapXml);
xmlDoc=new ActiveXObject("Microsoft.XMLDOM");
xmlDoc.async=false;
xmlDoc.loadXML(xmlhttp.responseXML.xml);
var userid = xmlDoc.getElementsByTagName("UserId")[0].childNodes[0].nodeValue;
return userid;
}
catch(e)
{
alert("Error while retrieving userid.");
}
return null;
}
NB : N'oubliez pas de distinguer le cas Online et Offline, puisqu'en v4 on peut se servir des WebService du client comme de ceux du serveur.
Enze
Member of Wygteam
www.wygwam.com
Vous avez surement entendu parler de cet américain, qui aime tellement le Zune qu'il se l'est fait tatoué :

Avec ces tatouages et le buzz qu'ils ont crées, il a réussi à attirer l'attention de Microsoft Corp et s'est fait invité à Redmond le 5 Juillet. Il tournera même une interview pour Channel 10.
Vous croyez que si je me tatoue "I Love ASP.NET" sur la fesse droite, je serais invité à rencontrer Scott Guthrie ?
Via Engadget.
Vous l'aurez compris, je vous parle de Bill Gates et Steve Jobs réunis lors de la conférence All Things Digital organisée par le Wall Street Journal.
Après plusieurs années de bon et loyaux services, Google IG (la page personnalisée de Google personnalisable par l'ajout de gadgets) vient de changer de nom pour iGoogle. L'intérêt de ce changement de nom consiste surtout à mieux identifier le service pour pouvoir le positionner plus efficacement vis à vis d'autres services de personnalisation tels que Live, Netvibes ou WebWag.

Mais ce ne sont pas les seuls changements qui apparaissent en ce moment...
On peut voir apparaitre une fonctionnalité en version française permettant de skinner son iGoogle.

Ici le théme "Ville" qui change les couleurs des fenêtres en fonction de l'heure.
L'autre grande nouveauté et la possibilité de créer soi-même ses gadgets et de les faire partager à ses amis/contacts. Le service est disponible sur cette page (service uniquement en anglais donc pensez à changer la langue préférée de vos pages dans votre navigateur).

Une gadget permettant d'indiquer ce que l'on fait et ce qui nous intéresse actuellement.

Le traditionnel compte à rebours.

Une galerie de photos.

Une liste personnalisable à souhait.

Le résultat sur une page iGoogle.
M'est d'avis que ça va inspirer plus d'un fournisseur de pages gadgetisés et gadgetisables ;)
Sources et ressources.
Vous vous souvenez surement des publicités GetAMac© dont Renaud nous avait parlé ?
Et bien après le remake version Dell XPS (publicité 1, publicité 2) voici la version PS3 vs Wii :
Je n'ai pas encore réussi à savoir si c'était une publicité officielle ou non, mais si c'en est une je trouve cela osé de la part de Nintendo de lister ce que la PS3 peut faire et de n'y opposer que les formes d'une jolie blonde.
Remarquez que ça marche plutôt bien, puisque que j'ai du regarder la publicité plusieurs fois avant de pouvoir m'intéresser à ce que la PS3 disait ;)
Il est possible depuis la version 7.5 de MSN Messenger (devenu Windows Live Messenger) d'ajouter des contacts à sa liste directement depuis un lien dans un internet Explorer.
A ma connaissance il existe 4 types de liens pris en charge par WLM. Il suffit de les mettre dans le href de la balise a et Internet Explorer se charge du reste, testez donc avec le CodyxBot :
Ces liens sont toutefois soumis à 3 contraintes :
- Le "protocole" n'est reconnu que sous Internet Explorer.
- Cela ne marche que sur les versions postérieures à MSN Messenger 7.5 (inclus)
- Il ne faut pas avoir décoché l'option correspondante dans Windows Live Writer.

C'est pas grand chose mais ça peut servir quand on travaille avec les Live Agent par exemple.
Bonjour à tous,
A la question du titre, la réponse est évidemment que non. C'est le Wygwam Spririt qui vous y pousse.
Donc me voila : Enze Winneris (cherchez le jeu de mot) alias Cédric Helle, développeur anonyme chez Wygwam depuis maintenant un peu plus d'un an. J'ouvre mon blog maintenant (seulement) parce que j'estimais que pour avoir un blog il fallait avoir quelque chose à dire, et que grace à la WygTeam j'éstime que c'est le cas.
Ce blog s'orientera principalement vers le .NET et l'ASP.NET et j'espére résister à la tentation de bloger sur Dark Age of Camelot (MMORPG) ce qui n'est pas le but de cet espace.
En parlant d'espace, je remercie Nix pour avoir crée ce blog un lendemain de jour férié et un jour de Newsletter :p
Cordialement,
Les 10 derniers blogs postés
-
Merci par
Blog de Jérémy Jeanson le 10-01-2019, 20:47
-
Office 365: Script PowerShell pour auditer l’usage des Office Groups de votre tenant par
Blog Technique de Romelard Fabrice le 04-26-2019, 11:02
-
Office 365: Script PowerShell pour auditer l’usage de Microsoft Teams de votre tenant par
Blog Technique de Romelard Fabrice le 04-26-2019, 10:39
-
Office 365: Script PowerShell pour auditer l’usage de OneDrive for Business de votre tenant par
Blog Technique de Romelard Fabrice le 04-25-2019, 15:13
-
Office 365: Script PowerShell pour auditer l’usage de SharePoint Online de votre tenant par
Blog Technique de Romelard Fabrice le 02-27-2019, 13:39
-
Office 365: Script PowerShell pour auditer l’usage d’Exchange Online de votre tenant par
Blog Technique de Romelard Fabrice le 02-25-2019, 15:07
-
Office 365: Script PowerShell pour auditer le contenu de son Office 365 Stream Portal par
Blog Technique de Romelard Fabrice le 02-21-2019, 17:56
-
Office 365: Script PowerShell pour auditer le contenu de son Office 365 Video Portal par
Blog Technique de Romelard Fabrice le 02-18-2019, 18:56
-
Office 365: Script PowerShell pour extraire les Audit Log basés sur des filtres fournis par
Blog Technique de Romelard Fabrice le 01-28-2019, 16:13
-
SharePoint Online: Script PowerShell pour désactiver l’Option IRM des sites SPO non autorisés par
Blog Technique de Romelard Fabrice le 12-14-2018, 13:01