Blog Page

Fehler bei Plugin-Registrierung – The Web Service plug-in failed

Hallo Leute,

nach längerer Abwesenheit mal wieder was neues zum Thema CRM.
Wir mussten einen unserer CRM-Server neu aufsetzen und hatten nach der Registrierung der Plugins schlimme Probleme.

Fehlermeldung (im Eventviewer) war die folgende:

The Web Service plug-in failed in OrganizationId EntityName: sdk message; Stage: 30; MessageName: RetrieveMultiple

Unsere Recherche hat uns schließlich zum Blog von KAUSTUBH GHANEKAR geführt.

Er schreibt:

I registered following dll files from Server/CRMWeb/bin into the Global Assembly Cache of CRM server.

Microsoft.Xrm.Sdk.dll
Microsoft.Crm.Extensibility.dll

Und tatsächlich, das hat das Problem behoben.

Ich hoffe ich konnte euch ein paar Minuten eurer Zeit sparen 🙂

Beste Grüße
Christian

CRM 2011 – OrganizationRequest – Set Active/Inactive

Hallo Leute,

heute mal was kurzes, was ich hier festhalten möchte, weil nicht in der SDK dokumentiert und im Internet nur schwer zu finden:

http://rajeevpentyala.wordpress.com/2011/10/18/activate-or-deactivate-record-in-crm-2011/

Rajeev Pentyala zeigt in diesem Blog, wie man eine Entität aktiviert bzw. deaktiviert. Die dazu notwendigen Statuscodes sind leider nicht dokumentiert.

[sourcecode language=“csharp“]
private void SetEntityStatus(IOrganizationService service, Guid recordGUID, string entityName) {

SetStateRequest setState = newSetStateRequest();

setState.EntityMoniker = newEntityReference();

//Pass GUID of the record to be activated or Deactivated

setState.EntityMoniker.Id = recordGUID;

setState.EntityMoniker.Name = entityName;

setState.EntityMoniker.LogicalName = entityName;

//Setting ‘State’ i.e., (0 – Active ; 1 – InActive)

setState.State = new OptionSetValue();

setState.State.Value = 0/1;

//Setting ‘Status’ i.e., (1 – Active ; 2 – InActive)

setState.Status = new OptionSetValue();

setState.Status.Value = 1/2;

SetStateResponse setStateResponse = (SetStateResponse)service.Execute(setState);

}
[/sourcecode]

Cheers,
Chris

CRM 2011 – Javascript – Attribute setzen (für alle Datentypen)

Hallo Leute,

nach längerer Zeit mal wieder ein feiner Alltags-Helper.
Um in CRM via Javascript einen Wert in ein Attribut schreiben zu können, bedarf es eigentlich je nach Typ (Lookup, Text, Integer, etc.) eine eigene Methode.
Ich habe mir jetzt einen Helper geschrieben, der selbst prüft um was für einen Typ es sich handelt und entsprechend handelt.

[sourcecode language=“javascript“]
this.SetAttributeValue = function (id, value, valueId, logicalName) {

var control = Xrm.Page.getControl(id);
var isDisabledField = Xrm.Page.getControl(id).getDisabled();

if (isDisabledField) {
Xrm.Page.getControl(id).setDisabled(false);
}

try {
if (control != null) {

if (value == null) {
FormUtils.SetStandardAttributeValue(id, value);
}
else {
var name = id;
var type = control.getAttribute().getAttributeType();

switch (type) {

case "string":
FormUtils.SetStandardAttributeValue(id, value);
break;

case "integer":
var intValue = parseInt(value);
FormUtils.SetStandardAttributeValue(id, intValue);
break;

case "optionset":
FormUtils.SetStandardAttributeValue(id, value);
break;

case "memo":
FormUtils.SetStandardAttributeValue(id, value);
break;

case "boolean":
FormUtils.SetStandardAttributeValue(id, (value == "true"));
break;

case "money":
var intValue = parseFloat(value);
FormUtils.SetStandardAttributeValue(id, intValue);
break;

case "datetime":
// not implemented yet
break;

case "lookup":
FormUtils.SetLookupAttributeValue(id, valueId, value, logicalName);
break;

case "decimal":
var intValue = parseFloat(value);
FormUtils.SetStandardAttributeValue(id, intValue);
break;
}
}

}
}
catch (ex) {

}

if (isDisabledField) {
Xrm.Page.getControl(id).setDisabled(true);
}

}

// Setzt den Wert eines "normalen" Attributes auf einer Entity-Page.
this.SetStandardAttributeValue = function (id, value) {

var isDisabledField = Xrm.Page.getControl(id).getDisabled();

if (isDisabledField) {
Xrm.Page.getControl(id).setDisabled(false);
}

try {
Xrm.Page.getAttribute(id).setValue(value);
Xrm.Page.getAttribute(id).setSubmitMode(‚always‘);
}
catch (err) {
alert(err);
}

if (isDisabledField) {
Xrm.Page.getControl(id).setDisabled(true);
}
}

// Setzt den Wert eines "normalen" Attributes auf einer Entity-Page.
this.SetLookupAttributeValue = function SetLookupAttributeValue(id, valueId, value, entityLogicalName) {

var isDisabledField = Xrm.Page.getControl(id).getDisabled();

if (isDisabledField) {
Xrm.Page.getControl(id).setDisabled(false);
}

try {
if (value == null) {
Xrm.Page.getAttribute(id).setValue(value);
}
else {
Xrm.Page.getAttribute(id).setValue([{ id: valueId, name: value, entityType: entityLogicalName}]);
}
Xrm.Page.getAttribute(id).setSubmitMode(‚always‘);
}
catch (err) {

}

if (isDisabledField) {
Xrm.Page.getControl(id).setDisabled(true);
}
}
[/sourcecode]

Beim setzen eines Optionsets muss der Integer-Wert gesetzt werden, nicht der Label. Zudem werden für das Setzen des Lookup-Attributes mehrere Parameter benötigt, die bei anderen Datentypen NULL sein können.

Am Error-Handling sollte man noch arbeiten, das ist dann aber euch selbst überlassen 😉

Have Fun!

Cheers,
Christian

CRM 2011 – ValueRule und OrRule kombinieren

Hallo Leute,

in einem meiner letzten Blogs habe ich mal über die Verwendung von ValueRules geschrieben (Blog). Was aber tun, wenn man zwei oder mehrere Optionen hat, die man mit einem ValueRule abfragen möchte?

Die Antwort habe ich auf Vikranth’s Blog gefunden.

Im wesentlichen ist die Lösung folgendes:

[sourcecode language=“xml“]
<DisplayRule Id="ShowUndo">
<OrRule>
<Or>
<ValueRule Field="xxx_type" Value="val1"></ValueRule>
</Or>
<Or>
<ValueRule Field="xxx_type" Value="val2"></ValueRule>
</Or>
</OrRule>
</DisplayRule>
[/sourcecode]

Nicht ganz logisch aber eigentlich recht einfach.

Cheers,
Christian

ASP.net – Bitmap (.bmp) in den OutputStream rendern – Render Bitmap into Outputstream

Hallo Leute,

mal wieder was Spannendes. Beim Versuch eine BMP-Grafik direkt in den Outputstream einer ASP.net Webpage zu rendern, tritt eine Exception auf (System.Runtime.InteropServices.ExternalException: A generic error occurred in GDI+.).

Spannende Geschichte, denn der gleiche Quellcode funktioniert mit JPG-Grafiken:

[sourcecode language=“csharp“]
Response.Clear();
Response.ContentType = "image/jpeg"; // "image/bmp" does not work!

Image orgChartImage = (System.Drawing.Image)Cache[imageName];
orgChartImage.Save(this.Response.OutputStream);
orgChartImage.Dispose();
[/sourcecode]

Bei meiner Recherche bin ich über den Blog von Rick Stahl gestolpert, der das gleiche Problem mit PNG-Grafiken beschreibt, siehe:

Rick Stahl’s Blog

In den Kommentaren schreibt u.a. Russ Garner, dass das daran liegen kann, dass der Encoder von PNG-Grafiken einen bi-directional Stream benötigt und postet gleichzeitig auch die Lösung:

Png’s are a special case. Their encoder requires a bi-directional stream. The solution is to stream to a memory stream first and write the contents of that memory stream to OutputStream afterwards.

When you get to caching (which I’ve just implemented) you’ll be writing to a memory stream anyway just to cache the output, so this isn’t a big deal.

As far as the GIF end of things goes, the default palletization of the built in GIF encoder leaves something to be desired. I’ve built in support for an Octree quantizer to deal with this.

Seriously – it’s a lot of work. It’s probably easier if I just send you some code 😉

Umgemünzt auf unser Beispiel bedeutet dies folgenden Sourcecode:

[sourcecode language=“csharp“]
string imageName = Request.QueryString["ID"];
Bitmap bm2 = (System.Drawing.Bitmap)Cache[imageName];
Bitmap bm3 = new Bitmap(bm2);

MemoryStream ms = new MemoryStream();

Response.ContentType = "image/bmp";
bm3.Save(ms, System.Drawing.Imaging.ImageFormat.Bmp);
ms.WriteTo(Response.OutputStream);

bm2.Dispose();
bm3.Dispose();
Response.End();
[/sourcecode]

Eine schlüssigere Fehlermeldung wäre mal was gewesen …
Ich hoffe, ich rette euch mit diesen Codesnippets ein paar Stunden eurer wertvollen Zeit!

Cheers,
Chris

CRM 2011 – OnCloseAlert – „Möchten Sie diese Seite wirklich verlassen?“ entfernen/unterdrücken

Hallo Leute,

einigen von euch ist es sicher schon passiert/aufgefallen. Ihr erzeugt eine neue Entität mittels Formular und wollt dieses Fenster dann, ohne es zu speichern, wieder verlassen. Was folgt ist der nachfolgende Dialog:

Das kann durchaus Sinn machen, möchte man z.B. den Benutzer warnen, dass Änderungen verloren gehen würden. Jedoch ist dieser Dialog durchaus problematisch, z.B. wenn man via Script die Seite neu laden/refreshen möchte und bewusst in Kauf nehmen möchte, dass Änderungen verloren gehen können.

Leider beschreibt die SDK keine Möglichkeit, wie man das deaktivieren kann. Sucht man mittels div. Suchmaschinen, bekommt man höchstens von Oberlehrern gebetsmühlenartig vorgekaut, dass man solche Dialoge tunlichst nicht verwenden soll…

Die Lösung ist letztlich recht einfach und besteht aus einer Zeile Code und kann im Javascript aufgerufen werden, nämlich:

[sourcecode language=“javascript“]
this.DetachCloseAlert = function () {
crmForm.detachCloseAlert();
}
[/sourcecode]

Happy Programming!

Cheers,
Chris

CRM 2011 – Required Fields serverseitig Validieren

Hi Leute,

man kennt doch das Problem, ab und zu schlägt irgendein Javascript fehlt (Syntax-Error, neue Browser-Version, etc.) und schon hat man den Salat, dass z.B. ein User eine Funktion auslöst, zu der er eigentlich nicht berechtigt wäre. Oder dass Felder, welche als Muss bzw. Required definiert sind, nicht ausgefüllt werden. Auf letzteren Punkt möchte ich den Fokus in diesem Blog-Entry legen. Nämlich wie man diese Required-Fields z.B. in einem eigenen WS oder in einem Plugin checkt.

CRM agiert hier, wie so oft, etwas eigenartig. Denn, wenn man sich die SDK so durchsieht, findet man ausschließlich einen ValidateRequest, welcher dann aber leider nur für div. Aktivitäten funktioniert, nicht für „normale“ Entitäten.

Contains the data that is needed to verify that an appointment or service appointment (service activity) has valid available resources for the activity, duration, and site as appropriate.

Warum das so ist, immerhin ist eine Aktivität nichts anderes als eine Entität, ist leider unklar. Also blieb nichts anderes übrig, selbst einen Weg zu finden, was auch relativ einfach gelang.

Zunächst mussten wir alle Required-Fields für eine Entität an Hand der Metainformationen herausfiltern:

[sourcecode language=“csharp“]
public static List<KeyValuePair<string, string>> GetRequiredFields(contracts.IOrganization organization, Entity entity)
{
try
{
List<KeyValuePair<string, string>> reqFields = new List<KeyValuePair<string, string>>();

var metadata = GetAllEntityAttributes(organization, entity.LogicalName);
foreach (var attribute in metadata)
{
if (attribute.RequiredLevel.Value == AttributeRequiredLevel.ApplicationRequired)
{
string label = attribute.AttributeOf;
if (attribute.DisplayName.UserLocalizedLabel != null)
label = attribute.DisplayName.UserLocalizedLabel.Label;

reqFields.Add(new KeyValuePair<string, string>(attribute.LogicalName, label));
}
}
return reqFields;
}
catch(Exception ex)
{
throw ex;
}
}
[/sourcecode]

Wir prüfen dabei nur auf „ApplicationRequired„-Fields, was den selbst definierten Pflichtfeldern entspricht. Man könnte noch „SystemRequired“ verwenden, was dann auch Felder wie „ownerid“, etc. zurückliefert. Es gibt noch weitere Optionen, hier lohnt sich der Blick ins CRM-SDK.

ApplicationRequired – The attribute is required to have a value.
None – No requirements are specified.
Recommended – It is recommended that the attribute has a value.
SystemRequired – The attribute is required to have a value.

Letztlich fehlt nur noch jene Methode, die überprüft, ob die gewünschte Entität auch die als required definierten Felder hält. Das funktioniert in CRM sehr einfach, denn die Attribute-Collection eines Microsoft.Xrm.Sdk.Entity-Objekts hält prinzipiell nur jene Attribute, die gesetzt wurden. Fehlt in dieser Collection also ein required Field, wurde es nicht gesetzt:

[sourcecode language=“csharp“]
public static List<KeyValuePair<string, string>> ValidateEntity(IOrganization organization, Entity entity)
{
try
{
// List<KeyValuePair<string, string>> listOfAlerts = new List<KeyValuePair<string, string>>();
List<ValidationResult> listOfErrors = new List<ValidationResult>();
// get required fields
var reqFields = GetRequiredFields(organization, entity);

// get displayname from entitymeta
var meta = GetEntityMeta(organization, entity.LogicalName);
var displayName = meta.DisplayName.UserLocalizedLabel.Label;

foreach(var field in reqFields)
{
if(!entity.Attributes.ContainsKey(field.Key))
listOfErrors.Add(new ValidationResult(field.Value, displayName, field.Key)); // Entitäts-Label | attribute-label | prefix_attributename
}

return listOfErrors;
}
catch (Exception ex)
{
throw ex;
}
}
[/sourcecode]

Die Methode liefert eine Generic-List retour, welche jene Felder beinhält, welche zwar required sind aber nicht gesetzt wurden.

Happy Programming 😉

Cheers,
Chris

CRM 2011 – RibbonsDiffXml – Verwenden von DisplayRules (ValueRule)

Hi Leute,

heute möchte ich euch ein einfaches Beispiel liefern, wie sich (mittels CRM-Standard) div. Ribbon-Buttons an Hand div. Regeln ein bzw. ausblenden lassen.
Dafür ist es, wie (fast) immer bei Änderungen an Ribbon-Buttons notwendig, dass Ihr zunächst das Menüband in eure Lösung aufnehmt und die Lösung downloaded und extrahiert. Wie das genau geht, habe ich schon mehrfach beschrieben, u.a. hier.

Dieses Beispiel geht davon aus, dass Ihr bereits einen eigenen Ribbon-Button hinzugefügt habt. Wie das geht, beschreibe ich ebenfalls hier.

Öffnet nun die „Customizations.xml“ und sucht die Entität die Ihr verändern wollt und öffnet dort die Node „RibbonDiffXml“. Euer Custom-Ribbon-Button hat das Attribute „Command“, wo auf eine „CommandDefinition“ verwiesen wird. Sucht dieses Element. Es sollte in etwa wie folgt aussehen:

[sourcecode language=“xml“]
<CommandDefinition Id="Sample.SubGrid.xyz_auftrag.Button1">
<EnableRules />
<DisplayRules />
<Actions>
<JavaScriptFunction Library="$webresource:xyz_cs_auftrag" FunctionName="Storno"></JavaScriptFunction>
</Actions>
</CommandDefinition>
[/sourcecode]

Fügt nun den DisplayRules eine Regel hinzu, das geht wie folgt:

[sourcecode language=“xml“]
<DisplayRules>
<DisplayRule Id="ShowStorno" />
</DisplayRules>
[/sourcecode]

Etwas weiter unten im RibbonDiffXml müssen wir die Rule, auf die wir eben verwiesen haben („ShowStorno“) auch noch definieren. Sucht dafür den Node „RuleDefinitions“ und fügt eine DisplayRule hinzu. Das könnte nun wie folgt aussehen:

[sourcecode language=“xml“]
<RuleDefinitions>
<DisplayRules>
<DisplayRule Id="ShowStorno">
[ …. ]
</DisplayRule>
</DisplayRules>
<EnableRules />
</RuleDefinitions>
[/sourcecode]

Eine DisplayRule kann nun mehrere Arten von Conditions hinterlegt bekommen. Lt. SDK sind das die folgenden:

[sourcecode language=“xml“]
<CrmClientTypeRule />
<CrmOfflineAccessStateRule />
<CrmOutlookClientTypeRule />
<CrmOutlookClientVersionRule />
<EntityPrivilegeRule />
<EntityPropertyRule />
<EntityRule />
<FormEntityContextRule />
<FormStateRule />
<MiscellaneousPrivilegeRule />
<OrganizationSettingRule />
<OrRule />
<OutlookRenderTypeRule />
<OutlookVersionRule />
<PageRule />
<ReferencingAttributeRequiredRule />
<RelationshipTypeRule />
<SkuRule />
<ValueRule />
[/sourcecode]

Ich denke die meisten von uns werden die Notwendigkeit haben, an Hand eines bestimmten Attributs im Formular, einen Button ein oder ausblenden zu müssen. Deswegen gehe ich in diesem Beispiel nur auf die ValueRule ein.

Diese wiederum gestaltet sich in der Regel sehr einfach:

[sourcecode language=“xml“]
<ValueRule Field="xyz_type" Value="908940000"></ValueRule>
[/sourcecode]

Solltet Ihr an anderen Rules interessiert sein, postet einfach einen Kommentar und ich helfe euch weiter. Oder seht selbst in der SDK nach.

Verpackt nun wieder sämtliche Dateien in ein ZIP-Archiv und importiert dieses in eure CRM-Instanz.

Cheers,
Christian

CRM 2011 – Original Ribbon Button – Andere (Javascript) Aktion hinterlegen

Hallo Leute,

aufbauend auf meinem letzten Beispiel (siehe Original Ribbon Button ausblenden) möchte ich euch nun zeigen, wie Ihr die Aktion, die hinter einem Original-Ribbon-Button liegt verändern könnt.

Im Konkreten möchte ich euch zeigen, wie Ihr dem Button „Verkaufschance als verloren schließen“ (im Form, nicht auf der Hauptseite) eine eigene Javascript-Funktion zuweisen könnt.

Dafür geht Ihr bitte wie im Artikel Original Ribbon Button ausblenden beschrieben vor.

Extrahiert also das ZIP-File und öffnet die Customizations.xml. Parallel dazu öffnet Ihr bitte wieder die „opportunityribbon.xml“-Datei aus dem SDK-Sample-Projekt.
Sucht in der XML-Datei nun nach „MarkAsLost“. Ihr werden nun folgende Zeile finden:

Kopiert das gesamte -Element am besten temporär in einen Text-Editor (Notepad) und merkt euch die ID des übergeordneten Elements. Wechselt in die Customizations.xml. Sucht dort (wie im anderen Artikel beschrieben) nach dem RibbonDiffXml-Node und fügt dort die folgenden Zeilen ein:

[sourcecode language=“xml“]
<CustomAction Id="eigeneid" Location="Mscrm.Form.opportunity.MainTab.Actions.Controls._children">
<CommandUIDefinition>

</CommandUIDefinition>
</CustomAction>
[/sourcecode]

Als Location gebt bitte die ID des übergenordneten Elements ein und fügt ein „._children“ an.
Zwischen die beiden CommandUIDefinition-Tags kopiert nun das zwischengespeichert Button-Element. Fertig sieht das nun ungefähr wie folgt aus:

[sourcecode language=“xml“]
<CustomAction Id="eigeneid" Location="Mscrm.Form.opportunity.MainTab.Actions.Controls._children">
<CommandUIDefinition>
<Button Id="Mscrm.Form.opportunity.MarkAsLost" Command="Mscrm.Form.opportunity.MarkAsLost" Sequence="4" Alt="$Resources:Ribbon.Form.opportunity.MainTab.Actions.MarkAsLost" LabelText="$Resources:Ribbon.Form.opportunity.MainTab.Actions.MarkAsLost" Image16by16="/_imgs/SFA/MarkAsLost_16.png" Image32by32="/_imgs/SFA/MarkAsLost_32.png" TemplateAlias="o1" ToolTipTitle="$Resources:Mscrm_Form_opportunity_MainTab_Actions_MarkAsLost_ToolTipTitle" ToolTipDescription="$Resources:Mscrm_Form_opportunity_MainTab_Actions_MarkAsLost_ToolTipDescription" />
</CommandUIDefinition>
</CustomAction>
[/sourcecode]

Nun ist es notwendig den „Command“ des Button-Elements zu verändern, z.B. auf: Mscrm.Isv.Form.opportunity.MarkAsLost.
Nachdem dieser „Command“ noch nicht existiert, muss er nun hinzugefügt werden, z.B. so:

[sourcecode language=“xml“]
<CommandDefinition Id="Mscrm.Isv.Form.opportunity.MarkAsLost">
<EnableRules />
<DisplayRules />
<Actions>
<JavaScriptFunction Library="$webresource:prefix_cs_opportunity" FunctionName="MarkAsLost" />
</Actions>
</CommandDefinition>
[/sourcecode]

Beachtet hier vor allem die JavaScriptFunction. Dort wird die Webresource angegeben, in der sich die JS-Methode befinden soll und der Name der JS-Methode selbst angegeben.

Passt das entsprechend euren Wünschen an und erstellt im Anschluss ein ZIP-File. Dieses kann dann wieder in gewohnter Manier in das CRM importiert werden.

Cheers,
Christian

CRM 2011 – Original Ribbon Button ausblenden

Hallo Leute,

heute mal wieder etwas „on standard“. CRM 2011 bietet ja von Haus aus einige nützliche Funktionalitäten. Nur manchmal erfüllen diese nicht ganz den Sinn und Zweck oder „overrulen“ die eigene Business-Logik.

Mit Hilfe der „HideCustomAction“ kann man ganz einfach bestehende Ribbons, Gruppen oder Tabs ausblenden.

Wie das geht möchte ich euch am Beispiel der Verkaufschance zeigen. Dieses Beispiel zeigt, wie man den „Verkaufschance als gewonnen abschließen“-Ribbon Button entfernt.

Zunächst ist es einmal wichtig zu wissen, wie der Ribbon-Button heißt. Leider kann man das dem Ribbon nicht direkt entnehmen. Es ist daher notwendig die SDK auf dem Rechner installiert zu haben. Im SDK befindet sich unter „sdksamplecodecsclientribbonexportribbonxml“ eine kleine Konsolenanwendung, die euch für jede Entität eien XML-Datei generiert, in der ALLE Ribbon Buttons (auch potentiell selbst hinzugefügte) der jeweiligen Entität enthält.

Die zur Verkaufschance passende Datei heisst „opportunityribbon.xml“. Die englische Bezeichnung für diesen Button heißt „MarkAsWon“, muss man sich also irgendwie zusammenreimen, die Lokalisierungen sind in dieser Datei leider (?) nicht enthalten, würde die Geschichte vermutlich auch unübersichtlich machen.

Nun findet man den Button zwei Mal. Ein Mal für das „Homepagegrid“ (damit ist das kontextbezogene Ribbon-Menü im Hauptfenster gemeint) und einmal für das Formular selbst.

Damit wissen wir nun welche ID’s wir ausblenden wollen.

Nun ist es notwendig die Lösung zu exportieren (Wichtig, Eure Lösung muss das Element „Anwendungsmenübänder“ enthalten!

Nun könnt Ihr eure Lösung exportieren und das heruntergeladene ZIP-File extrahieren. IM ZIP-File findet Ihr die Datei „Customizations.xml“.
Sucht in der Datei nun die Entität „opportunity“ und sucht die „RibbonDiffXml“-Node.

An dieser Stelle können wir nun die „HideCustomAction“s hinzufügen. Fertig sieht das dann wie folgt aus:

Nun aus den extrahierten Dateien wieder ein ZIP-File erstellen und die Lösung importieren.

That’s it.

Cheers
Christian