I'm new to programming in Dynamics CRM Online and I'm having a problem updating a deployed plugin. I'm using Visual Studio 2012 as my IDE. I deployed a plugin that I need to modify and when I re-deploy it thru VS the modified date in CRM is correct but the changes are not there. Here is my code..
if (context.InputParameters.Contains("Target")
&& context.InputParameters["Target"] is Entity)
{
Entity entity = (Entity)context.InputParameters["Target"];
if (entity.LogicalName == "lead")
{
if (entity.Attributes.Contains("companyname") == true)
{
if (entity["firstname"].ToString() != "null")
firstName = entity["firstname"].ToString();
else
firstName = "";
if (entity["lastname"].ToString() != "null")
lastName = entity["lastName"].ToString();
else
lastName = "";
entity["companyName"] = "This is a test";
//entity["companyname"] = firstName + " " + lastName;
}
else
throw new InvalidPluginExecutionException(
"The company name can only be set by the system.");
}
}
When I create a lead the company name is not "This is a test". I'm not sure what I'm doing wrong.
Thanks for the help!
You detect whether the field with company name exists by:
if (entity.Attributes.Contains("companyname") == true)
but you write to an other, namely:
entity["companyName"] = "This is a test";
The value is put in the entity but since it has no counterpart in the metadata, it's not being stored. Set the field name to it's to schema name, i.e. lower cased.
A few other things to consider in case you get additional errors.
You need to call Update method on the service after you've set the field's value.
The field should have some kind of prefix (e.g. new_something, beep_something).
The camel casing is not applicable here (schema name is), so go alltolowercase.
What do you get as the company name? Do you get the exception being thrown?
Also, some pointers on the code quality. I've rebuilt the logic to eliminate unnecessary complexity of scopes. I removed the superfluous else statement and comparisons to true. I'd also recommend that you split the process into different methods but I'm sure you've got that covered already. And you might want to use an auxiliary method to obtain the values from the fields. See in this post at my suggestion.
if (!context.InputParameters.Contains("Target") ||
context.InputParameters["Target"] is Entity)
return;
Entity entity = context.InputParameters["Target"] as Entity;
if (entity.LogicalName != "lead")
return;
if (!entity.Attributes.Contains("companyname"))
throw new InvalidPluginExecutionException(
"The company name can only be set by the system.");
String firstName = String.Empty;
if (entity.Contains("firstname"))
firstName = entity["firstname"] as String;
String lastName = String.Empty;
if (entity.Contains("lastname"))
lastName = entity["lastname"] as String;
entity["companyname"] = "This is a test";
//entity["companyname"] = firstName + " " + lastName;
EDIT:
If you still don't get the requested behavior, try the following. (I'm not sure on what level of expertise you are so olease accept my apology if you feel insulted by me mentioning some very basic stuff you've already tried a gazillion times.)
Technical tricks.
Publish all customizations (I do it very often, just in case).
Hit F5 to reload.
Login/logout.
Reboot IIS (if on-premise).
Unregister the plug in, see if the behavior remains. Then re-register it.
Check for forgotten workflows running.
There might be some delays and lags. Once, I actually had both the old old and the new version of a plugin being fired, depending on whether I created a record from Settings or Workplace. That was weird but resolved itself after a few hours. Seriously. That was weird!
Programmatic tricks.
Check if there are other plugins you might have forgotten to deactivate.
Remove all the leads and make sure that the plugin fires upon the new one being created.
Change the text to e.g. I'm a giant moose, just to make sure that the change goes through.
Slash away all code (or place return in the beginning of Execute. Then, move it down step by step to detect when the weirdness commences.
Of what you've shown, it should work so either you didn't mention something relevant (we do appreciated that you didn't post 100000 lines of code, of course) or it's CRM that's weirding off (which is equally annoying and confusing). So, let's trouble-shoot this thing. What happens when you try the tricks above?
As for the code stub, yeah - I'm not too proud of MS effort there. Try to post that code under the tag C# on Programmers for code review. Be prepared for an angry discussion. :)
Related
I get the error "Exception occurred calling method NotesDatabase.createDocument() null" for the following:
var db:NotesDatabase = session.getDatabase("", viewScope.targetDb);
if (db != null) {
if(db.isOpen()){
}else{
db.open();
}
} else {
}
var doc:NotesDocument = db.createDocument();
Comments:
The database db is available and "open".
The user has enough rights in the targetDb to create documents.
What is wrong?
I changed db.isOpen to db.isOpen(), according to Paul Stephen Withers tips.
And now db.open() gives the error "Exception occurred calling method NotesDatabase.open() null" although that I can get, in viewScope variables, the server, FilePath, etc.
Change
var db:NotesDatabase = session.getDatabase("", viewScope.targetDb);
to
var db:NotesDatabase = session.getDatabase(currentDatabase.getServer(), viewScope.targetDb);
This works on the web as well as XPinC.
XPages isn't the same as formula, it doesn't like the empty string definition for the server name, which is contrary to the documentation which states (for the server parameter of NotesSession.getDatabase - javascript):]
The name of the server on which the database resides. Use null to
indicate the session's environment, for example, the current computer
Using null or "" gives error 500, it would appear.
The code from the question will work, if :
the if block is removed entirely
the viewScope.targetDb variable has a properly specified notes database file path, which exists on the same server as the current database
the current user has access to the target database ( by the ACL ), with the Create Database right
the target database has a Maximum Internet name and password above Reader as per #Paul
I suspect the cause is you're checking db.isOpen. You should check db.isOpen().
Worth bearing in mind is session.getDatabase(String, String) doesn't return null (unless you're using OpenNTF Domino API). It returns a Database object that is not open. So that if statement is irrelevant. Also best practice is to pass a server name to session.getDatabase() - you'll get a different outcome if the application is ever used in XPiNC with your current code.
Regardless of individuals' user access, "Maximum Internet name and password" on the Advanced tab of the ACL will override that. If maximum internet access is No Access, no one will be able to create documents. But I suspect that's not a factor here.
Pro Tip -- Get the Debug Toolbar and use that to print out messages to the XPage debug toolbar to see what is going on and if your viewScope variable is being set. Also, learn to use the try catch block to catch your exceptions and print out the error message to the debug toolbar. You will find your issue that way.
https://www.openntf.org/main.nsf/project.xsp?r=project/XPage%20Debug%20Toolbar
I am trying to write a plugin that will trigger when an account is created. If there is a originating lead I want to fetch the company name in the lead and put it in the account name field. What I'm not sure how to do is to obtain the information out of the lead entity.
I have the following code (I'll keep updating this)...
Entity member = service.Retrieve("lead",
((EntityReference)account["originatingleadid"]).Id, new ColumnSet(true));
if (member.Attributes.Contains("companyname"))
{
companyName = member.Attributes["companyname"].ToString();
}
if (context.PostEntityImages.Contains("AccountPostImage") &&
context.PostEntityImages["AccountPostImage"] is Entity)
{
accountPostImage = (Entity)context.PostEntityImages["AccountPostImage"];
companyName = "This is a test";
if (companyName != String.Empty)
{
accountPostImage.Attributes["name"] = companyName;
service.Update(account);
}
}
I'm not going to spoil the fun for you just yet but the general idea is to:
Catch the message of Create.
Extract the guid from your Entity (that's your created account).
Obtain the guid from its EntityReference (that's your lead).
Read the appropriate field from it.
Update the name field in your account.
Store the information.
Which of the steps is giving you issues? :)
As always, I recommend using query expressions before fetchXML. YMMV
Is lead connected to the account? Just use the IOrganizationService.Retrieve Method
To retrieve the correct lead (assuming you have the lead id from the account entity)..
Create the organizationService in the execute method of your plugin.
http://msdn.microsoft.com/en-us/library/gg334504.aspx
Also here is a nice example to write the plugin:
http://mscrmkb.blogspot.co.il/2010/11/develop-your-first-plugin-in-crm-2011.html?m=1
Is there any solution to create record in other DBs from the CRM 2011 records? When a record such as "cost" was created in CRM 2011, we want a record would be created in out Oracle DB. Could it be done through a plugin? Or a service should be created for this?
Could you please provide me references or solutions for this.
Any helps would be greatly appreciated.
We had a similar request from a customer a while ago. They claimed that CRM's database wasn't to be trusted and wanted to securely store a copy of the records created in - guess what - SQL Server too. (Yes, we do understand the irony. They didn't.)
The way we've resolved it was to create a plugin. However, bear in mind that simply reacting to the message of Create won't really do. You need to set up a listener for three of the CRUD operations (retrieval doesn't affect the external database so it's rather C_UD operations, then).
Here's the skeleton of the main Execute method.
public void Execute(IServiceProvider serviceProvider)
{
Context = GetContextFromProvider(serviceProvider);
Service = GetServiceFromProvider(serviceProvider);
switch (Context.MessageName)
{
case "Create": ExecuteCreate(); break;
case "Update": ExecuteUpdate(); break;
case "Delete": ExecuteDelete(); break;
}
}
After this dispatcher, you can implement the actual calls to the other database. There are three gotchas I'd like to give you head-up on.
Remember to provide a suitable value to the outer DB when CRM doesn't offer you one.
Register the plugin as asynchronous since you'll be talking to an external resource.
Consider the problem with entity references, whether to store them recursively as well.
Walk-through for plugin construction
Link to CRM SDK if you haven't got that
Information on registering the plugin
And besides that, I've got a walk-through (including code and structure) on the subject in my blog. The URL to it, you'll have to figure out yourself - I'm not going to self-promote but it's got to do with my name and WP. Google is your friend. :)
You could use a plugin to create a record in another system, although you would need to think about syncing and ensure you don't get duplicates, but it certainly can be done.
Tutorial on plugins can be found here.
You need to write a plugin that runs on Create and uses the information on the created Cost entity to create a record in your Oracle DB.
As an example:
public void Execute(IServiceProvider serviceProvider)
{
var context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
//get the created entity from CRM
var theCreatedEntity = context.InputParameters["Target"] as Entity;
//build up a stored procedure call
using (OracleConnection objConn = new OracleConnection("connection string"))
{
var cmd = new OracleCommand();
cmd.Connection = objConn;
cmd.CommandText = "stored procedure name";
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("param1", OracleType.Number).Value = theCreatedEntity.GetAttributeValue<int>("Attribute1");
cmd.Parameters.Add("param2", OracleType.Number).Value = theCreatedEntity.GetAttributeValue<int>("Attribute2");
//etc
cmd.ExecuteNonQuery();
}
}
That should give you enough to get going
I am trying to sync Sharepoint task list with Outlook. When the users connect the task list to outlook, the task for all users are visible in outlook. Rather than applying filtering in Outlook, can I provide a filtering at the source itself? There are considerable number of users for my application, it wouldn't be good to ask all users to apply filters on their own.
Any other suggestions?
Thanks.
I`v asked the same question: Sync list with outlook only with items in current view.. In this case it was possible to use stssync protocol to do whatever you want. It takes much effort (luckily someone already wrote an implementation)
But there was another solution i ended up using - implementing a wrapper for Lists.asmx webservice and rewriting outlook requests (by using custom Global.asax file) to use this new webservice instead of Lists.asmx, that only queries specific view in a list.
if (ctx.Request.UserAgent.Contains("Microsoft Office Outlook") && path.ToLower().IndexOf("_vti_bin/lists.asmx") >= 0)
{
ctx.RewritePath("/_layouts/OutlookLists.asmx");
}
I'm not sure you would want a solution like this. If you do, you may ask and i may publish the solution source for the webservice, however i'm not using this webservice myself anymore. And you could use it as a draft, not a production ready code.
The source has been published on CodePlex.
Regarding to the script problem
I don't know why list id isn't being replaced by view id. I tried to run the function within script console (F12 for IE8/9)
>> var menuItems = document.getElementsByTagName('ie:menuitem');
for (var i = 0; i < menuItems.length; i++) {
itm = menuItems(i);
if (itm.id.match('OfflineButton') != null) {
console.log('listName:' + ctx.listName.toLowerCase() + 'viewName:' + ctx.view.toLowerCase());
if (ctx != null && ctx.listName != null && ctx.view != null) {
console.log('Inside if block');
//Replace listId to viewId being used so outlook will query only items in current view.
//Must have custom web service in place to handle that request, because it iwll not work OOTB.
console.log("Before: " + itm.onMenuClick);
itm.onMenuClick = itm.onMenuClick.replace(ctx.listName.toLowerCase(), ctx.view.toLowerCase());
console.log("After: " + itm.onMenuClick);
break;
}
}
}
LOG: listName:{fe89e809-7de4-4f43-9bc2-7e8ce6624ed0}viewName:{7364a843-c7f2-47d8-b4a3-5dc7381b6248}
LOG: Inside if block
LOG: Before: javaScript:ExportHailStorm('tasks','https:\u002f\u002fserver\u002fsapulces\u002fdarbu_parskata','{fe89e809-7de4-4f43-9bc2-7e8ce6624ed0}','Uz\u0146\u0113muma darbu p\u0101rskata sapulce','Uzdevumi','\u002fsapulces\u002fdarbu_parskata\u002fLists\u002fUzdevumi','','\u002fsapulces\u002fdarbu_parskata\u002fLists\u002fUzdevumi');
LOG: After: javaScript:ExportHailStorm('tasks','https:\u002f\u002fserver\u002fsapulces\u002fdarbu_parskata','{7364a843-c7f2-47d8-b4a3-5dc7381b6248}','Uz\u0146\u0113muma darbu p\u0101rskata sapulce','Uzdevumi','\u002fsapulces\u002fdarbu_parskata\u002fLists\u002fUzdevumi','','\u002fsapulces\u002fdarbu_parskata\u002fLists\u002fUzdevumi');
As you can see, the function argument (third one) has been replaced with a view id instead of list id.
Don't forget to remove console.log statements before deploying, because if IE doesn't have web developer tools, javascript will crash there.
Were these tasks created from a workflow? this is a known issue with SharePoint 2007.
http://social.technet.microsoft.com/Forums/en/sharepointadmin/thread/64b3b124-085c-4d8e-8e85-8bd20736e0e7
http://blah.winsmarts.com/2007-4-SharePoint_2007__Fine_grained_permission_control.aspx
You could try setting the read/edit permissions to "only their own", but i think that breaks approval/alerts from working
I believe the problem is fixed in SharePoint 2010, i think tasks get created with fine-grained permissions per task.
I have the following helper method that returns the value from a field.
public static string GetValueFrom(SPListItem item, string fieldName)
{
string value = string.Empty;
if (item.Fields.ContainsField(fieldName))
{
SPField field = item.Fields.GetField(fieldName);
if (item[field.InternalName] != null)
{
value = item[field.InternalName].ToString();
}
}
return value;
}
However for one Field (normal Choice Field) I am getting a ArgumentExecption on this line
if (item[field.InternalName] != null)
I am using
SPListItem item = list.GetItemById(itemId);
To get the item.
I cant find why I am getting the exception when I am checking to see if the field exists?
Any ideas as to why I am getting this Exception for only one field.
Update.
When debugging
The call to GetField() returns the correct field object.
Field.InternalName contains the correct Internal name of the field
If I try and access the value using item["internal name of the field"] it still throws and exception for only this one field.
Sometimes strange things happens and we do not have logical answer to those questions. Try by deleting the list and then creating the list again from scratch. DO NOT try to save it as template and DO NOT try to create the list from that template.
One possible reason of such type of ugly messages is that the security/permissions are not allowing to manipulate that field/column.
Another possible reason of such type of unwanted/unexpected messages is that when the field was created for the first time, its data type was different and later on it was changed to choice. Technically there should be no problem in doing so but sometimes we face odd behavior.
Have you tried debugging? Questions you should answer (because we can't):
Is field a valid value, or null, after the call to GetField()?
If field is not null, what does field.InternalName actually return?
If field.InternalName returns a valid value, can you access it by hard-coding that value in the indexer? i.e. item["fieldInternalName"]
Finding that information may help you solve the problem yourself, but if it doesn't add it to your post so the community has a better chance of helping you.
I do experienced this many a times. The reason for this is if you are logged-in as a non Admin Account(System Account) the default List View Lookup Threshold for the User is 8 for the lookup columns. i.e for the default view the user can access upto the 8 lookup fields only. If you change the List Throttling to >8 it will be resolved. But increasing this will degrade the performance.
Go to Central Admin >> Manage Web Applications >> Select the Web Application >> General Settings Dropdown >> Resource Throttling >> Change the "List View Lookup Threshold" to more than 8
Thanks,
-Codename "Santosh"