SharePoint 2007: Programmatically Start workflow hit error - sharepoint

I wanted to programmatically start workflow when itemAdded in Pages Library.
I do as following :
public override void ItemAdded(SPItemEventProperties properties)
{
SPListItem listItem = properties.ListItem;
StartWF(listItem);
}
public void StartWF(SPListItem listItem)
{
using(SPWeb web = listItem.Web) {
using(SPSite site = web.Site) {
SPWorkflowManager manager = site.WorkflowManager;
SPList parentList = listItem.ParentList;
SPWorkflowAssociationCollection associationCollection =
parentList.WorkflowAssociations;
foreach(SPWorkflowAssociation association in
associationCollection) {
if (association.Name == "APWFAnn2010") {
string data =
association.AssociationData;
SPWorkflow wf =
manager.StartWorkflow(listItem,
association,
association.
AssociationData);
}
}
}
}
}
then I hit this error "The system cannot find the file specified. (Exception from HRESULT: 0x80070002)"
"APWFAnn2010" is the workflow name I wanted to start. It is SP default Approval Workflow created in Pages Library.
Please help, thank you in advance.

You can also pass in the name into the function above by doing it this way, and then you'd have a function that can start any workflow on any item:
public void StartWF(SPListItem listItem, SPSite spSite, string wfName)
{
SPList parentList = listItem.ParentList;
SPWorkflowAssociationCollection associationCollection = parentList.WorkflowAssociations;
foreach (SPWorkflowAssociation association in associationCollection)
{
if (association.Name == wfName)
{
association.AutoStartChange = true;
association.AutoStartCreate = false;
association.AssociationData = string.Empty;
spSite.WorkflowManager.StartWorkflow(listItem, association, association.AssociationData);
}
}
}

First of you have your using site within the using web it should be the other way around - on the other hand I don't even see you using the SPWeb, so why have it in there?. And secondly I would rewrite the code a bit:
public override void ItemAdded(SPItemEventProperties properties)
{
SPListItem listItem = properties.ListItem;
spSite = properties.OpenWeb().Site;
spWeb = properties.Web;
using(spSite) {
StartWF(listItem, spSite);
}
}
public void StartWF(SPListItem listItem, SPSite spSite)
{
SPList parentList = listItem.ParentList;
SPWorkflowAssociationCollection associationCollection =
parentList.WorkflowAssociations;
foreach(SPWorkflowAssociation association in associationCollection) {
if (association.Name == "APWFAnn2010") {
association.AutoStartChange = true;
association.AutoStartCreate = false;
association.AssociationData = string.Empty;
spSite.WorkflowManager.StartWorkflow(listItem,
association,
association.AssociationData);
}
}
}

Related

Update customer children from parent account

Page: AR303000 Version:18.203.0006
Good day
I need to update the Child accounts details(Terms, Status, and Email) when the parent account changes. The problem is I do not know how to save the children Customer's contact email field from Customer_RowPersisting.
The customer child accounts do save a, but the Contact details do not.
namespace PX.Objects.AR
{
public class CustomerMaint_Extension : PXGraphExtension<CustomerMaint>
{
#region Event Handlers
protected void Customer_RowPersisting(PXCache cache, PXRowPersistingEventArgs e)
{
Customer row = (Customer)e.Row;
if (row.ParentBAccountID == null)
{
PXResultset<Customer> Children = PXSelectJoin<Customer,
LeftJoin<BAccount, On<Customer.bAccountID, Equal<BAccount.bAccountID>>>,
Where<BAccount.parentBAccountID, Equal<Required<Customer.bAccountID>>>>.Select(Base, row.BAccountID);
if (Children == null) { return; }
Contact ParContact = PXSelect<Contact, Where<Contact.contactID, Equal<Required<Contact.contactID>>>>.Select(Base, row.DefBillContactID);
foreach (Customer item in Children)
{
//Customer
item.TermsID = row.TermsID;
item.Status = row.Status;
cache.Update(item);
//Contact Details
Contact Cur = PXSelect<Contact, Where<Contact.contactID, Equal<Required<Contact.contactID>>>>.Select(Base, item.DefBillContactID);
Cur.EMail = ParContact.EMail;
cache.Update(Cur);
}
//Do not know if this is right
cache.Persist(PXDBOperation.Normal);
}
}
#endregion
}
}
You can override the Persist() method! You should add a reference for the PX.CS.Contracts.dll dll.
public class CustomerMaintExt : PXGraphExtension<CustomerMaint>
{
#region Overrides
public delegate void PersistDelegate();
[PXOverride]
public void Persist(PersistDelegate baseMethod)
{
using (var scope = new PXTransactionScope())
{
Customer row = this.Base.BAccount.Current;
if (row?.ParentBAccountID == null)
{
CustomerMaint businessAccount = PXGraph.CreateInstance<CustomerMaint>();
PXResultset<Customer> Children = PXSelect<Customer, Where<Customer.parentBAccountID, Equal<Required<Customer.bAccountID>>>>.Select(Base, row.BAccountID);
foreach (Customer item in Children)
{
businessAccount.Clear();
businessAccount.BAccount.Current = PXSelectorAttribute.Select<Customer.bAccountID>(this.Base.BAccount.Cache, item.BAccountID) as Customer;
item.TermsID = row.TermsID;
item.Status = row.Status;
Contact defContact = PXSelect<Contact, Where<Contact.bAccountID, Equal<Required<BAccount.bAccountID>>, And<Contact.contactID, Equal<Required<BAccount.defContactID>>>>>.Select(businessAccount, item.BAccountID, item.DefContactID);
defContact.EMail = this.Base.DefContact.Current.EMail;
businessAccount.DefContact.Update(defContact);
businessAccount.BAccount.Update(item);
businessAccount.Save.PressButton();
}
}
baseMethod();
scope.Complete();
}
}
#endregion
}

Method To Return all users email in a People Picker control in share point 2010

Can anyone try getting the username and email of all the users in a Array of the People picker control in SharePoint 2010?
If so can you provide me a Method this?
The method should have parameters like (sharepointgroupname,sharepoint user)
I have tried Like This. But it returns only one user name and email address from people picker control. I want to retrieve for multiple users Sharepoint group and Active Directory group. Please help me.
private void GetNameAndEmailFromPeoplePicker(SPListItem mySourceListItem, out String PeoplePicker, out String CCPeoplePicker, out String UserName)
{
PeoplePicker = "";
CCPeoplePicker = "";
UserName = "";
try
{
SPFieldUserValueCollection Users = new SPFieldUserValueCollection(mySiteWeb, Convert.ToString(mySourceListItem["Point of Contact"]));
foreach (SPFieldUserValue User in Users)
{
UserName = User.User.Name;
PeoplePicker = User.User.Email;
}
SPFieldUserValueCollection BCCUsers = new SPFieldUserValueCollection(mySiteWeb, Convert.ToString(mySourceListItem["Backup Point of Contact"]));
foreach (SPFieldUserValue BCCUser in BCCUsers)
{
CCPeoplePicker = BCCUser.User.Email;
}
}
catch (Exception ex)
{
UlsLogs.LogErrorInULS(ex.InnerException.ToString(), TraceSeverity.High);
}
}
Get USer Names and Emails from a Sharepoint People Picker.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SharePoint;
namespace GETEMAILSFROMPEOPLEPICKER
{
public static class SPFieldUserValueCollectionExtensions
{
public static IEnumerable<string> GetAllEmails(this SPFieldUserValueCollection collection, SPWeb web)
{
//System.Diagnostics.Debugger.Break();
var emails = new HashSet<string>();
foreach (var item in collection)
{
if (item.User == null)
{
try
{
// is it a SharePoint group?
var group = web.SiteGroups.GetByID(item.LookupId);
emails.AddEmailsFromSPGroup(group);
}
catch
{
// ad group
var group = web.EnsureUser(item.LookupValue);
emails.AddIfNotNull(group.Email);
}
}
else
{
emails.AddIfNotNull(item.User.Email);
}
}
return emails;
}
private static void AddEmailsFromSPGroup(this HashSet<string> emails,SPGroup group)
{
if (!string.IsNullOrEmpty(group.DistributionGroupEmail))
{
emails.Add(group.DistributionGroupEmail);
}
else
{
foreach (SPUser user in group.Users)
{
emails.AddIfNotNull(user.Email);
}
}
}
private static void AddIfNotNull(this HashSet<string> set, string s)
{
if(!string.IsNullOrEmpty(s))
{
set.Add(s);
}
}
public static IEnumerable<string> GetAllLoginNames(this SPFieldUserValueCollection collection, SPWeb web)
{
var usernames = new HashSet<string>();
foreach (var item in collection)
{
if (item.User == null)
{
try
{
// is it a SharePoint group?
var group = web.SiteGroups.GetByID(item.LookupId);
usernames.UserNamesFromSPGroup(group);
}
catch
{
// ad group
var group = web.EnsureUser(item.LookupValue);
usernames.IfNotNull(group.Name);
}
}
else
{
usernames.AddIfNotNull(item.User.Name);
}
}
return usernames;
}
private static void UserNamesFromSPGroup(this HashSet<string> usernames, SPGroup group)
{
if (!string.IsNullOrEmpty(group.DistributionGroupEmail))
{
usernames.Add(group.DistributionGroupAlias);
}
else
{
foreach (SPUser user in group.Users)
{
usernames.AddIfNotNull(user.Name);
}
}
}
private static void IfNotNull(this HashSet<string> set, string s)
{
if (!string.IsNullOrEmpty(s))
{
set.Add(s);
}
}
}
}
I'm not sure to understand your question... and in which language do you want to use it?
With JavaScript I use this method to do the same thing as the People Picker...

No permission to access webApplication properties

When activating a site feature I automaticly want to set a WebApplication property. This is the code:
public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
SPWeb currentWeb = ContentTypes.ValidateFeatureActivation(properties);
using (SPSite site = new SPSite(currentWeb.Site.Url))
{
SPWebApplication currentApplication = site.WebApplication;
if (currentApplication.MaxQueryLookupFields < 20)
{
SPSecurity.RunWithElevatedPrivileges(delegate()
{
try
{
currentApplication.MaxQueryLookupFields = 20;
}
catch (System.Security.SecurityException ex)
{
_log.ErrorFormat("no permission");
}
});
}
}
}
Even if I a farm administrator activated the feature, the security exception is thrown ("access denied"). at line
currentApplication.MaxQueryLookupFields = 20;
AFAIK SPSecurity.RunWithElevatedPrivileges runs as a site admin, not farm admin. But HOW can this be done? (WITHOUT RunWithElevatedPrivileges I get the same exception.
You need to create new SPSite, SPWeb and SPWebApplication objects inside the SPSecurity.RunWithElevatedPrivileges otherwise you will be running them with the same permissions as the current user. E.g.
public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
SPSecurity.RunWithElevatedPrivileges(delegate()
{
SPWeb currentWeb = ContentTypes.ValidateFeatureActivation(properties);
using (SPSite site = new SPSite(currentWeb.Site.Url))
{
SPWebApplication currentApplication = site.WebApplication;
if (currentApplication.MaxQueryLookupFields < 20)
{
try
{
currentApplication.MaxQueryLookupFields = 20;
}
catch (System.Security.SecurityException ex)
{
_log.ErrorFormat("no permission");
}
}
}
});
}
You should instantiate another SPSite object inside RWEP to get the application pool identity context because the first SPSite which is created outside the RWEP block is created with the SPSite current user context. So try this one:
public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
SPWeb currentWeb = ContentTypes.ValidateFeatureActivation(properties);
using (SPSite site = new SPSite(currentWeb.Site.Url))
{
SPSecurity.RunWithElevatedPrivileges(delegate()
{
using (SPSite _site = new SPSite(site.ID))
{
SPWebApplication currentApplication = _site.WebApplication;
if (currentApplication.MaxQueryLookupFields < 20)
{
try
{
currentApplication.MaxQueryLookupFields = 20;
}
catch (System.Security.SecurityException ex)
{
_log.ErrorFormat("no permission");
}
}
}
});
}
}

How to get the context from workflow event handler in Sharepoint 2010

I am in the process of upgrading our custom solutions to Sharepoint 2010. I wanted to utilize the WorkflowCompleted event handler but I don't seem to be able to get the relevant SPListItem from the event properties.
I tried using SPWorkflowEventProperties.ActivationProperties but this always returns null (even in the WorkflowStarted event handler).
How do I get the context from workflow event handlers (SPListItem, SPWeb, SPSite etc)?
I've found the same thing. SPWorkflowEventProperties is practically useless since just about everything is null. It doesn't tell status (Approved, Rejected, etc). And, most importantly, it doesn't (directly) tell what item was completed. Hopefully this will be addressed in future versions. In the meantime, I used the following:
public override void WorkflowCompleted(SPWorkflowEventProperties properties)
{
using (SPSite site = new SPSite(properties.WebUrl))
{
using (SPWeb web = site.OpenWeb())
{
SPListItem task = GetApprovedTask(properties, web);
SPListItem item = GetApprovedItem(web, task);
if (null != item)
{
// TODO : process approved item
}
}
}
}
private SPListItem GetApprovedItem(SPWeb web, SPListItem task)
{
SPListItem item = null;
if (null != task)
{
SPList list = web.Lists[new Guid(task[SPBuiltInFieldId.WorkflowListId].ToString())];
item = list.GetItemById((int)task[SPBuiltInFieldId.WorkflowItemId]);
}
return item;
}
private SPListItem GetApprovedTask(SPWorkflowEventProperties properties, SPWeb web)
{
SPListItem item = null;
string caml = #"<Where><And><And><And><Eq><FieldRef Name='WorkflowOutcome' /><Value Type='Text'>Approved</Value></Eq><Eq><FieldRef Name='WorkflowInstanceID' /><Value Type='Guid'>{0}</Value></Eq></And><IsNotNull><FieldRef Name='WorkflowListId' /></IsNotNull></And><IsNotNull><FieldRef Name='WorkflowItemId' /></IsNotNull></And></Where>";
SPQuery query = new SPQuery();
query.Query = string.Format(caml, properties.InstanceId);
query.RowLimit = 1;
SPList list = web.Lists["Tasks"];
SPListItemCollection items = list.GetItems(query);
if (items.Count > 0)
{
item = items[0];
}
return item;
}
You can use the InstanceId property to retrieve the SPListItem from the workflow task list as shown in this post
http://blog.symprogress.com/2011/09/sp-2010-get-workflow-status-workflowcompleted-event/
If you can be more generous with details I can be more specific with answer.
But this is generally what you need to do:
Set context specific properties
public static DependencyProperty _ContextProperty
= System.Workflow.ComponentModel.DependencyProperty.Register("_Context",
typeof(WorkflowContext), typeof(MyCustomActivity));
[Description("Site Context")]
[Category("User")]
[Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public WorkflowContext __Context
{
get
{
return ((WorkflowContext)(base.GetValue(MyCustomActivity.__ContextProperty)));
}
set
{
base.SetValue(MyCustomActivity.__ContextProperty, value);
}
}
public static DependencyProperty __ListIdProperty
= System.Workflow.ComponentModel.DependencyProperty.Register("__ListId",
typeof(string), typeof(MyCustomActivity));
[ValidationOption(ValidationOption.Required)]
public string __ListId
{
get
{
return ((string)(base.GetValue(MyCustomActivity.__ListIdProperty)));
}
set
{
base.SetValue(MyCustomActivity.__ListIdProperty, value);
}
}
public static DependencyProperty __ListItemProperty
= System.Workflow.ComponentModel.DependencyProperty.Register("__ListItem",
typeof(int), typeof(MyCustomActivity));
[ValidationOption(ValidationOption.Required)]
public int __ListItem
{
get
{
return ((int)(base.GetValue(MyCustomActivity.__ListItemProperty)));
}
set
{
base.SetValue(MyCustomActivity.__ListItemProperty, value);
}
}
public static DependencyProperty __ActivationPropertiesProperty
= DependencyProperty.Register("__ActivationProperties",
typeof(Microsoft.SharePoint.Workflow.SPWorkflowActivationProperties), typeof(MyCustomActivity));
[ValidationOption(ValidationOption.Required)]
public Microsoft.SharePoint.Workflow.SPWorkflowActivationProperties __ActivationProperties
{
get
{
return (Microsoft.SharePoint.Workflow.SPWorkflowActivationProperties)base.GetValue(MyCustomActivity.__ActivationPropertiesProperty);
}
set
{
base.SetValue(MyCustomActivity.__ActivationPropertiesProperty, value);
}
}
You will get context in __Context and everything else from __Context like this:
protected override ActivityExecutionStatus
Execute(ActivityExecutionContext executionContext) {
// Raise Invoke Event to execute custom code in the workflow.
this.RaiseEvent(MyCustomActivity.InvokeEvent,
this, EventArgs.Empty);
SPWeb _cxtWeb = null;
String _strLinfo = "Dll;";
String _strTo = String.Empty;
// Set Context
try
{
SPSecurity.RunWithElevatedPrivileges(delegate()
{
using (_cxtWeb = __Context.Web)
{
//_cxtWeb = __Context.Web;
Guid _cListId = new Guid(__ListId);
SPList _cSPList = _cxtWeb.Lists[_cListId]; // TimeLog
SPListItem _cListItem = _cSPList.GetItemById(__ListItem);
SMTPSERVER = _cxtWeb.Site.WebApplication
.OutboundMailServiceInstance.Server.Address;
}
});
}
catch { /**/ }
}

How to get the URL for a SharePoint Discussion Board entry?

How do you retrieve the URL for a discussion board item? That is, the URL displayed when you mouse over the subject line (once the list has been added to the page as a web part).
protected global::System.Web.UI.WebControls.GridView gvForum;
public string Region
{
get
{
return "";
}
}
public string DefaultRegion { get; set; }
public int Top { get; set; }
public string ListName
{
get
{
string listName=string.Empty;
if (!string.IsNullOrEmpty(this.Region))
listName=string.Format("{0} {1}","Forum",this.Region);
else
listName = string.Format("{0} {1}", "Forum", this.DefaultRegion);
return listName;
}
}
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
BindGrid();
}
}
private void BindGrid()
{
string region = this.Region;
string caml=#"<OrderBy><FieldRef Name=""Modified"" /></OrderBy>";
try
{
using (SPSite spSite = new SPSite(SPContext.Current.Site.Url))
{
using (SPWeb spWeb = spSite.OpenWeb())
{
SPQuery spQ = new SPQuery();
spQ.Query = caml;
spQ.RowLimit = (uint)this.Top;
SPList spList = spWeb.Lists[ListName];
SPListItemCollection items = spList.GetItems(spQ);
if (items != null && items.Count > 0)
{
gvForum.DataSource = items;
gvForum.DataBind();
}
else
{
this.Visible = false;
}
}
}
}
catch (Exception ex)
{
Logger.Log(ex.Message, System.Diagnostics.EventLogEntryType.Error);
}
}
protected void gvForum_RowDataBound(object sender, GridViewRowEventArgs e)
{
SPListItem item = e.Row.DataItem as SPListItem;
Label lblTitle = e.Row.FindControl("lblTitle") as Label;
HtmlAnchor aURL = e.Row.FindControl("aURL") as HtmlAnchor;
if (item != null)
{
if (lblTitle != null && aURL != null)
{
aURL.HRef = "~/" + item.Url;
lblTitle.Text = item["Title"].ToString();
}
}
}
protected void gvForum_RowDataBound(object sender, GridViewRowEventArgs e)
{
SPListItem item = e.Row.DataItem as SPListItem;
Label lblTitle = e.Row.FindControl("lblTitle") as Label;
HtmlAnchor aURL = e.Row.FindControl("aURL") as HtmlAnchor;
if (item != null)
{
if (lblTitle != null && aURL != null)
{
aURL.HRef = "~/" + item.Url;
lblTitle.Text = item["Title"].ToString();
}
}
}
You can use use "TopicPageUrl" field column to directly get the discussion topic by using REST api URL
http://sp2013.in/_api/web/Lists/GetByTitle('Discussion')/Items?$select=Title,TopicPageUrl,DiscussionLastUpdated,Folder/ItemCount,LastReplyBy/Title,Author/Title&$expand=Folder,LastReplyBy,Author&$orderby=DiscussionLastUpdated desc
The above code is also useful to get discussion last updated, reply count (its saved in folder), last replied by.
Are you asking how to find the URL for an individual discussion in a discussion board? Or an individual reply to a discussion?
You can give just subject name like http://site/discussion/lists/discussionboard/discusontitlename or subject
You may not have the list item, but if you do just look at the "FileRef" property. It will look like "https://mycompany.sharepoint.com/sites/Lists/discussion/". If I put that URL in the browser (I'm using SharePoint Online), it redirects me to the https://mycompany.sharepoint.com/Lists/Discussion/Flat.aspx?RootFolder=... URL.
To generate the direct url to a particular discussion item, on Client side (using a REST API call), you could try this:
var jqXhr = $.ajax({
url:"/DiscussionSite/_api/lists/getByTitle('Discussions')/items?
$select=ID,FileRef,ContentTypeId,Title,Body&
$filter=ContentType eq 'Discussion'",
headers: { 'Accept': 'application/json;odata=verbose'}
});
// Fetch only the discussions from the Discussion list (excl. Messages)
jqXhr.done(function(data){
// Picking only the first item for testing purpose
// Feel free to loop through the response if necessary
var firstItem = data.d.results[0],
firstItemUrl = '/DiscussionSite/Lists/Discussions/Flat.aspx?RootFolder=' + firstItem.FileRef + '&FolderCTID' + firstItem.ContentTypeId;
// Result - /DiscussionSite/Lists/Discussions/Flat.aspx?RootFolder=/DiscussionSite/Lists/Discussions/My Discussion Topic 1&FolderCTID0x01200200583C2BEAE375884G859D2C5A3D2A8C06
// You can append "&IsDlg=1" to the Url for a popup friendly display of the Discussion Thread in a SharePoint Modal Dialog
console.log(firstItemUrl);
});
Hope this helps!

Resources