I am working on a project involving contact synchronization between Outlook and SharePoint. Though there is an out of the box solution provided for this by Microsoft, but it does not cater some specific requirements like custom column synchronization.
For this requirement we had to create an outlook add-in. This add-in handles ItemAdd and ItemChange event for the contact folder created as a result of the synchronization.
In the ItemChange event I check a flag in the Notes field and identify whether the change has been made from SharePoint or Outlook and accordingly update the contact item.
Here is the code for my ItemChange event.
void Items_ItemChange(object Item)
{
try
{
ContactItem ctItem = Item as Outlook.ContactItem;
string customFieldsAndFlag = ctItem.Body;
Dictionary<string, string> customColumnValueMapping = new Dictionary<string, string>();
if (!string.IsNullOrEmpty(customFieldsAndFlag))
{
string[] flagAndFields = customFieldsAndFlag.Split(';');
if (flagAndFields.Length == 2)
{
//SharePoint to Outlook
if (flagAndFields[0] == "1")
{
foreach (string customColumnAndValue in flagAndFields[1].Split('|'))
{
string[] KeyAndValue = customColumnAndValue.Split('=');
if (KeyAndValue.Length == 2)
{
if (ctItem.UserProperties[KeyAndValue[0]] == null)
{
ctItem.UserProperties.Add(KeyAndValue[0], OlUserPropertyType.olText, true, OlUserPropertyType.olText);
ctItem.UserProperties[KeyAndValue[0]].Value = KeyAndValue[1];
}
else
{
ctItem.UserProperties[KeyAndValue[0]].Value = KeyAndValue[1];
}
}
}
ctItem.Body = "2;" + flagAndFields[1];
}
//Outlook to SharePoint
else
{
foreach (string customColumnAndValue in flagAndFields[1].Split('|'))
{
string[] KeyAndValue = customColumnAndValue.Split('=');
if (KeyAndValue.Length == 2)
{
if (ctItem.UserProperties[KeyAndValue[0]] != null && ctItem.UserProperties[KeyAndValue[0]].Value != null)
{
KeyAndValue[1] = ctItem.UserProperties[KeyAndValue[0]].Value.ToString();
}
customColumnValueMapping.Add(KeyAndValue[0], KeyAndValue[1]);
}
}
string newBody = string.Empty;
foreach (KeyValuePair<string, string> kvp in customColumnValueMapping)
{
newBody += kvp.Key + "=" + kvp.Value + "|";
}
if (newBody == flagAndFields[1])
{
return;
}
else
{
ctItem.Body = "0;" + newBody;
}
}
}
}
ctItem.Save();
}
catch(System.Exception ex)
{
// log the error always
Trace.TraceError("{0}: [class]:{1} [method]:{2}\n[message]:{4}\n[Stack]:\n{5}",
DateTime.Now, // when was the error happened
MethodInfo.GetCurrentMethod().DeclaringType.Name, // the class name
MethodInfo.GetCurrentMethod().Name, // the method name
ex.Message, // the error message
ex.StackTrace // the stack trace information
);
// now display a friendly error to the user
MessageBox.Show("There was an application error, you should save your work and restart Outlook.",
"TraceAndLog",
MessageBoxButtons.OK,
MessageBoxIcon.Error);
}
}
Now the issue is, the contact item update works fine from outlook the first time. The contact gets updated properly, the add-in works fine and changes get reflected in SharePoint just fine. But when I try to edit the contact again using outlook, a pop-up comes up saying "Item cannot be changed because it was changed by another user or in another window. Do you want to make a copy in the default folder for the item?" and the oulook add-in stops working thereafter.
Can anyone please suggest a solution for this problem?
I cannot use ctItem.Close() as when I use this function, the synchronization process doesn't work and the changes don't get populated to SharePoint.
Related
I customized the ARPaymentEntry in which it creates a Journal Voucher Entry with created Credit Memo, it retrieves the Credit Memo applies the open invoice that is also applied in the current payment. when I create the instance to call the Credit Memo and add the Invoice in ARAdjust table, an error occurs when trying to insert it, giving a Reference Nbr cannot be found in the system, although when I'm trying to manually applying it I could see the open invoice.
public void ReleaseCreditMemo(string refNbr)
{
try
{
ARPaymentEntry docGraph = PXGraph.CreateInstance<ARPaymentEntry>();
List<ARRegister> list = new List<ARRegister>();
ARPayment payment;
ARRegister invoice = PXSelect<ARRegister, Where<ARRegister.docType, Equal<Required<ARRegister.docType>>, And<ARRegister.refNbr, Equal<Required<ARRegister.refNbr>>>>>.Select(docGraph, ARInvoiceType.CreditMemo, refNbr);
docGraph.Document.Current = PXSelect<ARPayment, Where<ARPayment.docType, Equal<Required<ARPayment.docType>>, And<ARPayment.refNbr, Equal<Required<ARPayment.refNbr>>>>>.Select(docGraph, ARInvoiceType.CreditMemo, refNbr);
payment = docGraph.Document.Current;
list.Add(payment);
foreach (ISARWhTax item in ARWhLine.Select())
{
decimal? _CuryAdjgAmt = payment.CuryOrigDocAmt > invoice.CuryDocBal ? invoice.CuryDocBal : payment.CuryOrigDocAmt;
decimal? _CuryAdjgDiscAmt = payment.CuryOrigDocAmt > invoice.CuryDocBal ? 0m : invoice.CuryDiscBal;
ARAdjust adj = new ARAdjust();
adj.AdjdBranchID = item.AdjdBranchID;
adj.AdjdDocType = ARInvoiceType.Invoice;
adj.AdjdRefNbr = item.AdjdRefNbr;
adj.AdjdCustomerID = item.CustomerID;
adj.AdjdDocDate = invoice.DocDate;
adj.CuryAdjgAmt = _CuryAdjgAmt;
adj.CuryAdjdDiscAmt = _CuryAdjgDiscAmt;
if (docGraph.Document.Current.CuryUnappliedBal == 0m && docGraph.Document.Current.CuryOrigDocAmt > 0m)
{
throw new PXLoadInvoiceException();
}
//This line code below OCCURS THE ERROR
docGraph.Adjustments.Insert(adj);
}
docGraph.Save.Press();
PXLongOperation.StartOperation(docGraph, delegate() { ARDocumentRelease.ReleaseDoc(list, false); });
}
catch (Exception ex)
{
throw new PXException(ex.Message);
}
}
I would look at the selector of the field causing the error ("Reference Nbr.") as having a selector on a field will validate the entered value to the selector's select statement (unless validatevalue=false for the selector). Maybe the selector will give you some pointers as to what is missing or causing the validation to fail.
I figured it out it that after the code below executes it does not immediately updates the View. So what I did is to execute my code at ARPayment_RowSelected event with a conditional statement if the document is released.
PXLongOperation.StartOperation(this.Base, delegate() { ARDocumentRelease.ReleaseDoc(list, false); });
I was wondering if I can make a comment somewhere in Team Foundation Server when I add an AD user to a TFS group or change the group of the user, for auditing purpose.
I have created a PowerShell script to record day2day changes to TFS user databases and query the AD to find out who approved this change.
Now we have a self made db.
For what it's worth - I wrote a C# method to pull the list of all members from every TFS group. Hope that helps!
EDIT: "This is posted as an answer because "xidada" was going to write a script to pull the information and since I already had the code to get the information that needed, I thought the code would be a guide to help him/her with the script"
private void btn_GetNow_Click()
{
TfsTeamProjectCollection tfs = new TfsTeamProjectCollection(new Uri("http://server/collection"));
tfs.EnsureAuthenticated();
TfsConfigurationServer srv = tfs.ConfigurationServer;
CatalogNode configurationServerNode = srv.CatalogNode;
// Query the children of the configuration server node for all of the team project collection nodes
ReadOnlyCollection<CatalogNode> tpcNodes = configurationServerNode.QueryChildren(
new Guid[] { CatalogResourceTypes.ProjectCollection },
false,
CatalogQueryOptions.None
);
Guid tpcId = new Guid(tpcNodes[0].Resource.Properties["InstanceId"]);
TfsTeamProjectCollection tpc = srv.GetTeamProjectCollection(tpcId);
// get a reference to the work item tracking service
var workItemStore = tpc.GetService<WorkItemStore>();
List<Identity> result = new List<Identity>();
// iterate over the projects
foreach (Project project in workItemStore.Projects)
{
Console.WriteLine("\tProject: {0}", project.Name);
try
{
VersionControlServer versionControl = (VersionControlServer)tpc.GetService(typeof(VersionControlServer));
TeamProject teamProject = versionControl.GetTeamProject(project.Name);
IGroupSecurityService gss = (IGroupSecurityService)tpc.GetService<IGroupSecurityService>();
Identity[] appGroups = gss.ListApplicationGroups(teamProject.ArtifactUri.AbsoluteUri);
foreach (Identity group in appGroups)
{
rtb_Users.AppendText(group.DisplayName + "\n");
Identity[] groupMembers = gss.ReadIdentities(SearchFactor.Sid, new string[] { group.Sid }, QueryMembership.Expanded);
foreach (Identity member in groupMembers)
{
if (member.Members != null)
{
foreach (string memberSid in member.Members)
{
Identity memberInfo = gss.ReadIdentity(SearchFactor.Sid, memberSid, QueryMembership.None);
if (memberInfo.Type == IdentityType.WindowsUser)
{
if (!result.Contains(memberInfo))
{
result.Add(memberInfo);
rtb_Users.AppendText("\t\t" + memberInfo.AccountName + " - " + memberInfo.DisplayName + " - " + memberInfo.Domain + "\n");
}
else
{
Console.WriteLine("\t\tUser already available " + memberInfo.AccountName);
}
}
}
}
}
}
}
catch (Exception ex)
{
Console.WriteLine("\tThe Project: '{0}' throws an exception: {1} and will be ignored.", project.Name, ex.Message);
}
}
}
This method is pulled out from my application AS IS and would need to be customized for your needs.
Im new with plugin. my problem is, When the case is created, i need to update the case id into ledger. What connect this two is the leadid. in my case i rename lead as outbound call.
this is my code. I dont know whether it is correct or not. Hope you guys can help me with this because it gives me error. I manage to register it. no problem to build and register but when the case is created, it gives me error.
using System;
using System.IO;
using System.ServiceModel;
using System.ServiceModel.Description;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Query;
using Microsoft.Xrm.Sdk.Messages;
using Microsoft.Xrm.Sdk.Client;
using System.Net;
using System.Web.Services;
/*
* Purpose: 1) To update case number into lejar
*
* Triggered upon CREATE message by record in Case form.
*/
namespace UpdateLejar
{
public class UpdateLejar : IPlugin
{
/*public void printLogFile(String exMessage, String eventMessage, String pluginFile)
{
DateTime date = DateTime.Today;
String fileName = date.ToString("yyyyMdd");
String timestamp = DateTime.Now.ToString();
string path = #"C:\CRM Integration\PLUGIN\UpdateLejar\Log\" + fileName;
//open if file exist, check file..
if (File.Exists(path))
{
//if exist, append
using (StreamWriter sw = File.AppendText(path))
{
sw.Write(timestamp + " ");
sw.WriteLine(pluginFile + eventMessage + " event: " + exMessage);
sw.WriteLine();
}
}
else
{
//if no exist, create new file
using (StreamWriter sw = File.CreateText(path))
{
sw.Write(timestamp + " ");
sw.WriteLine(pluginFile + eventMessage + " event: " + exMessage);
sw.WriteLine();
}
}
}*/
public void Execute(IServiceProvider serviceProvider)
{
ITracingService tracingService = (ITracingService)serviceProvider.GetService(typeof(ITracingService));
IPluginExecutionContext context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
IOrganizationServiceFactory serviceFactory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);
//for update and create event
if (context.InputParameters.Contains("Target") &&
context.InputParameters["Target"] is Entity)
{
// Obtain the target entity from the input parmameters.
Entity targetEntity = (Entity)context.InputParameters["Target"];
// Verify that the entity represents a connection.
if (targetEntity.LogicalName != "incident")
{
return;
}
else
{
try
{
//triggered upon create message
if (context.MessageName == "Create")
{
Guid recordid = new Guid(context.OutputParameters["incidentid"].ToString());
EntityReference app_inc_id = new EntityReference();
app_inc_id = targetEntity.GetAttributeValue<EntityReference>("new_outboundcalllid");
Entity member = service.Retrieve("new_lejer", ((EntityReference)targetEntity["new_outboundcallid"]).Id, new ColumnSet(true));
//DateTime createdon = targetEntity.GetAttributeValue<DateTime>("createdon");
if (app_inc_id != null)
{
if (targetEntity.Attributes.Contains("new_outboundcallid") == member.Attributes.Contains("new_outboundcalllistid_lejer"))
{
member["new_ringkasanlejarid"] = targetEntity.Attributes["incidentid"].ToString();
service.Update(member);
}
}
}
tracingService.Trace("Lejar updated.");
}
catch (FaultException<OrganizationServiceFault> ex)
{
//printLogFile(ex.Message, context.MessageName, "UpdateLejar plug-in. ");
throw new InvalidPluginExecutionException("An error occurred in UpdateLejar plug-in.", ex);
}
catch (Exception ex)
{
//printLogFile(ex.Message, context.MessageName, "UpdateLejar plug-in. ");
tracingService.Trace("UpdateLejar: {0}", ex.ToString());
throw;
}
}
}
}
}
}
Please check,
is that entity containing the attributes or not.
check it and try:
if (targetEntity.Contains("new_outboundcallid"))
((EntityReference)targetEntity["new_outboundcallid"]).Id
member["new_ringkasanlejarid"] = targetEntity.Attributes["incidentid"].ToString();
What is new_ringkasanlejarid's type? You're setting a string to it. If new_ringkasanlejarid is an entity reference, this might be causing problems.
You might want to share the error details or trace log, all we can do is assume what the problem is at the moment.
I have activated a WSP file which includes a website template. This works and I can see the solution int he solution gallery. When I try to create a website, based on that template, its not showing up. But it says "Status: Activated".
Then I tried to deactivate it and activate it again manually. Out of a sudden, there is a new template showing up, which takes the name of my template appended by a "2".
So whats happening here exactly? The code to activate my solution is:
System.IO.MemoryStream input = new System.IO.MemoryStream(System.IO.File.ReadAllBytes(rootDirectory + "\\Templates\\Project.wsp"), true);
SPDocumentLibrary solutionGallery = (SPDocumentLibrary)web.Site.GetCatalog(SPListTemplateType.SolutionCatalog);
try
{
SPFile solutionFile = solutionGallery.RootFolder.Files.Add("Project.wsp", input);
SPUserSolution newUserSolution = web.Site.Solutions.Add(solutionFile.Item.ID);
}
catch { ... }
Ok, I found the answer myself and want to share it here. The solution is, not to provide the solution in the catalog, but also to enable the features! It works like this:
System.IO.MemoryStream input = new System.IO.MemoryStream(System.IO.File.ReadAllBytes(rootDirectory + "\\Templates\\Project.wsp"), true);
SPDocumentLibrary solutionGallery = (SPDocumentLibrary)web.Site.GetCatalog(SPListTemplateType.SolutionCatalog);
try
{
SPFile solutionFile = solutionGallery.RootFolder.Files.Add("Project.wsp", input);
SPUserSolution newUserSolution = web.Site.Solutions.Add(solutionFile.Item.ID);
Guid solutionId = newUserSolution.SolutionId;
SPFeatureDefinitionCollection siteFeatures = web.Site.FeatureDefinitions;
var features = from SPFeatureDefinition f
in siteFeatures
where f.SolutionId.Equals(solutionId) && f.Scope == SPFeatureScope.Site
select f;
foreach (SPFeatureDefinition feature in features)
{
try
{
web.Site.Features.Add(feature.Id, false, SPFeatureDefinitionScope.Site);
}
catch { }
}
SPWebTemplateCollection webTemplates = web.Site.RootWeb.GetAvailableWebTemplates(1033);
SPWebTemplate webTemplate = (from SPWebTemplate t
in webTemplates
where t.Title == "Projekt"
select t).FirstOrDefault();
if (webTemplate != null)
{
try
{
web.Site.RootWeb.ApplyWebTemplate(webTemplate.Name);
}
catch { }
}
}
catch { ... }
I'm getting a stuck. Suppose that I create 5 sites. For each one, I create a few sharepoint groups. So, I create a dropdownlist control to bind 5 sites and when I click on any site there, I will get sharepoint groups created on it. But I always see the dropdownlist used to bind these groups still never change. I mean it only binds a few default groups in sahrepoint everytime. The new created groups is not.
And I have confusion like this
web.AssociatedGroups
web.Groups
web.SiteGroups
which one we will use this case ? Please guide me
Here my snippet code
private void BindSPGroupsToDropDownList(DropDownList ddl, string siteUrl)
{
ddl.Items.Clear();
try
{
SPSecurity.RunWithElevatedPrivileges(delegate()
{
using (SPSite site = new SPSite(siteUrl))
{
using (SPWeb web = site.OpenWeb())
{
//web.SiteGroups
foreach (SPGroup spGroup in web.Groups)
{
ddl.Items.Add(new ListItem(spGroup.Name.Trim(), spGroup.ID.ToString()));
}
}
}
});
}
}
thanks in advance
You don't show how you add groups, so it is hard to see why you can't list them.
Some considerations:
groups are scoped at the site collection level, i.e. there is no notion of groups at the web (SPWeb) level; only at the SPSite level
I wrote code a couple of years ago for managing groups, so I don't remember the difference between the different properties. Looking at my code, I used SPWeb.SiteGroups to test if a group exists and to add a new group.
Here is my - slightly anonymized - code:
public static void CreateSiteGroup(SPSite site, string strGroupName, string strGroupDesc, string strGroupOwner) {
if (site == null) {
string message = GetMessagePrefix() + " Site is null";
XxxLog.Error(XxLogEventId.Common, message, XxxLogCategory.CommonBusiness);
} else if (String.IsNullOrEmpty(strGroupName)) {
string message = GetMessagePrefix() + " The group name is empty";
XxxLog.Error(XxxLogEventId.Common, message, XxxLogCategory.CommonBusiness);
} else {
try {
using (SPWeb rootWeb = site.RootWeb) {
SPMember owner;
if (String.IsNullOrEmpty(strGroupOwner)) {
owner = rootWeb.CurrentUser;
} else {
if (!ContainsGroup(site, strGroupOwner)) {
string message = GetMessagePrefix() + " Can not find owner group name: " + strGroupOwner;
XxxLog.Error(XxxLogEventId.Common, message, XxxLogCategory.CommonBusiness);
return;
} else {
owner = rootWeb.SiteGroups[strGroupOwner];
}
}
if (!ContainsGroup(site, strGroupName)) {
rootWeb.SiteGroups.Add(strGroupName,
owner,
null, // no default user
strGroupDesc);
} else {
string message = GetMessagePrefix() + " The group " + strGroupName + " was already present";
XxxLog.Info(message, XxxLogCategory.CommonBusiness);
}
}
} catch (Exception e) {
string message = GetMessagePrefix() + " Cannot create " + strGroupName + " group";
XxxLog.Error(XxxLogEventId.Common, message,e, XxxLogCategory.CommonBusiness);
}
}
}
public static Boolean ContainsGroup(SPSite site, string name) {
SPGroup group = null;
using (SPWeb rootWeb = site.RootWeb) {
foreach (SPGroup g in rootWeb.SiteGroups) {
if (g.Name.ToUpper().Equals(name.ToUpper())) {
group = g;
break;
}
}
}
return (group != null);
}