I’m currently trying to use web part connections to connect an out the box List to my visual webpart. Now I can find lots of examples of connecting 2 custom web parts together using a user defined interface but am having trouble finding examples of the functionality I require.
Scenario 1 : Ability to add my Visual Web part to a list view and connect them so my WP is the consumer of the List and that when the user clicks/selects a row in the list view my web part would get sent the id of the item.
Scenario 2 : Ability to add my Visual Web part to a list display form and connect them so my WP is the consumer of the List item and the Id of the list item get passed to the WP when the form loads/displays.
Now I know that this is what I need to consume the data
[ConnectionConsumer("Parameter", AllowsMultipleConnections = false)]
public void ThisNameDoesNotMatter(IWebPartParameters provider)
{
_provider = provider ;
}
but unsure of whether I should be using the IWebPartParameters, IWebPartRow or the IFilterConsumer.
I guess I’m starting to wonder if this is even possible as I'm struggling to find any examples of this functionality.
Any help would be great.
Cheers
Lee
Ok so I found what I was looking for partialy, below is the code that captures the Connection object
private IWebPartField _provider = null;
[ConnectionConsumer("Parameter", AllowsMultipleConnections = false)]
public void ThisNameDoesNotMatter(IWebPartField provider)
{
_provider = provider ;
}
the IWebPartField Interface was what I was looking for to consume a value from anouther webpart (in my case a List column field).
To get the Field I needed if my custom webpart is on the display form of the list I just get it from the query string.
Related
I need to extract som information from a SharePoint list. Let us call this list for list_A. And in list_A I have column containing sharepoint users. That show their lync status (online, offline, busy etc), and when I hover over that user name I will get an "toolbox" that shows additional information. (See the picture)
What I now want is to extract that list of users into an SharePoint web part and have the ability to show the same information as in list_A. In other words I want to get the lync integration, with their status and the info box on hover. I figure I will manage to extract the information from the list without any problems using a method somewhat like the one below:
using (SPSite site = new SPSite(url))
{
using (SPWeb web = site.OpenWeb())
{
SPList lists = web.Lists["listName"];
foreach (SPListItem item in lists.Items)
{
string test = Convert.ToString(item["columnName"]);
TextBox1.Text += test;
}
}
}
But the question is if there is an asp or sharepoint controller that will allow me to show this kind field on the web page/in the web part?
Create a LiteralControl and use the following code to display the user presence in your webpart
("<span><img border=\"0\" height=\"12\" src=\"/_layouts/images/imnhdr.gif\" onload=\"IMNRC('" + userEmail+ "')\" ShowOfflinePawn=\"1\" alt=\"\" id=\"user_presence_icon\"/></span>");
There is a forum discussion on this topic and can be refered here -
Hope this helps
I'm just starting to use sharepoint designer and realised there's a lot that can be done to extend the basic features in sharepoint. We have an email alert sent out when a new task is created (by the user) and I want to customise the email so that it also includes a link called 'Assign'. When clicked, I want this link to automatically update the task with the assigned to field for the person that clicked it.
So I think the way to do this would be to hard-code the assign to value in the url behind this link, but I have no idea if this is possible or if there is an easier/better way to do this.
Any advice would be appreciated as I'm a complete beginner.
thanks.
I will not cover "How to modify the contents of an eamil alert" here as that is a seperate question and there are a lot of articles that cover that already.
For the Assigned link :-
You would need to create a custom page (or web part on an existing page) as the destination of your Assign link - this would take the Task ID as a query string param and then update the assigned to with the current user.
You could make this flexible by also taking the ListID but you may want to think about how this could be abused and put appropriate measures in place.
EDIT - in response to comment.
This is top of my head, not checked in compiler. This would have to sit on the same server as SharePoint to work as its using the OM - if you want to use a different server (why would you though) then look in the web services.
private void updateAssignedTo(Guid listId, int itemID)
{
SPWeb web = SPContent.Current.Web();
SPList list = web.Lists[listId];
SPListItem item = list.GetItemById(itemID);
item["Assigned To"] = web.CurrentUser;
item.Update();
}
You're going to have to work out how to get this code into to page or web part (SharePoint Designer is not going to cut it I think, you need Visual Studio) but its a starting point.
I created a custom column in a custom content type in a Sharepoint Web manually (e.g. /MySite/MyWeb). I now want to programmatically copy this content type across to another web (e.g. /MySite/MyWeb2). However, upon looping through the custom content type in code, I could only find 2 fields: Content Type and Title (expected: Title and custom column). The custom column was missing. I'm very sure that the content type and field are added at the web level.
The custom content type is inherited from Item.
When I loop through the web's fields, I can see the custom column, and that was copied to the new web. It is only within the content type that the custom column is not showing up.
Any ideas why this is happening?
Thanks.
It took me over a quarter dozen methods to finally be able to successfully and programmatically copy a content type from one web to another. Now, of the methods that failed, only one of them would replicate your exact problem and not completely combust, so I'll assume that is the method you attempted and analyze why it fails. Then I'll show you a method that works properly.
You cannot programmatically add fields to a content type instance which is only scoped on the web level. Attempting to do so provides the following clear error:
This functionality is unavailable for field collections not associated with a list.
So if your method was to basically create a content type in the new web that inherits from Item, and then just add your custom column, it would fail to add the custom column. I'm assuming that is how you tried to copy the content type, as every other approach I tried either failed, or worked and retained the field.
To make it work, create a new content type that is scoped to Web2's web level, but inherits from the original content type from Web1. So, using the ALICE content type I tested it with, you would have something like this.
SPContentType ct2 = new SPContentType(Web1.ContentTypes["ALICE"], Web2.ContentTypes, "ALICE");
Web2.ContentTypes.Add(ct2);
Web2.Update();
This created the ALICE content type on the second web, which has the custom column that the original ALICE content type has. Now, since the original ALICE content type isn't on Web2, then this ALICE doesn't actually inherit from that ALICE. It'll instead inherit from Item, which you can confirm both in the UI and programmatically. So it becomes a successful copy.
EDIT
Here is the exact code I used, minus a couple lines for outputting debug and not stating my actual Web2 address. It was run from a workflow on Web1. I guess instantiating a content type separately is the major difference. Let me know if this continues to produce that same error.
SPWeb website = null; //Web1, which has ALICE already.
SPWeb web = null; //Web2, which does not yet have ALICE.
SPSite site = null;
try {
website = workflowProperties.Web;
site = new SPSite('webaddress of web2');
web = site.OpenWeb();
SPContentType ct = website.ContentTypes["ALICE"];
SPContentType act = new SPContentType(ct, web.ContentTypes, "ALICE");
act.Group = "Custom Content Types";
web.ContentTypes.Add(act);
web.Update();
}
finally { if (web != null) { web.Dispose(); } if (website != null) { website.Dispose(); } if (site != null) { site.Dispose(); } }
How can I enable the functionality to sync my custom list with Outlook 2007 todo or calendar list? I assume it's possible with custom lists too, but I cannot find any documentation on it.
To my knowledge this is not possible. If you need to sync a todo list, create a task list. Then you can delete all the columns that don't interest you, and add whatever columns you want to use.
The same goes for calendar, if you want a list syncronised with your outlook calendar, then create a calendar list and customize it to your needs.
To expand upon Filip's great answer . . .
According to this MSDN post, the custom list template has a different type designation (100) than the Task Template (106 in the article above, 107 in my research of SP2010). This is what the UI looks at to disable the Outlook Sync button. So, if your custom list is based on a custom content type you created then you can recreate your list to be Outlook compatible.
If your list already contains data, export it to Excel
Create a new list based on the Task template (instead of Custom)
Add your CT and delete the Task CT (if your content type is based on the Task CT i.e. has the columns) and any others your list doesn't need
Add any other columns your list needs but may not be part of your CT (or if you don't use a custom CT)
Use Datasheet view to copy and paste your data into the new list (observe column order)
Caveats: Not all data columns can be copied using this method (e.g. Attachments, system fields, etc.)
Hope this helps clarify
This is available / possible (well it takes some programming to achieve it) - see here.
For my solution, I aggregate data between sites and sync them with outlook. There are some tricky things like to map item's uniqueId to integer ID (probably a database table with mappings) to refer to specific item correctly, but it's not impossible.
But it's not something you can use right away - coding required. However if you really, really need it, then it's a some-way to go.
If your custom list is based off of a sharepoint list, then you can connect it to outlook by generating a link formatted based off the stssync protocol.
I created a custom list definition based off the events (Calendar) list.
It uses the base calendar list template plus some custom fields and forms.
I created a link formatted in accordance with the stssync protocol, and successfully connected it with outlook:
stssync://sts/?ver=1.1&type=calendar&cmd=add-folder&base-url=[site url]&list-url=[your list url]&guid=[GUID of list]&site-name=[SiteName]&list-name=[ListName]
Yeah not possible sadly. Also, if you create custom columns in a task list or calender, Outlook won't sync those columns.
I have been struggling with the same issue and found that it cannot be done with the built in functionality that SharePoint has, but there is a 3rd party program that allows this to be done. You can try with ProperSync. It will allow you to connect the custom fields of a custom list from SharePoint and view and edit them using Outlook.
This is the link to the main site: http://propersync.com/default.aspx
You can override List Ribbon Button, which holds sync logic, and create your custom action on it.
Here is good article about creating ribbons: http://blogs.msdn.com/b/sharepointdeveloperdocs/archive/2009/12/07/sharepointfoundationhowtoaddtabtoribbon.aspx
For List View OutlookSyncButton you should in CommandUIDefinition specify Location="Ribbon.List.Actions.ConnectToClient".
For Calendar View - you should specify Location="Ribbon.Calendar.Calendar.Actions.ConnectToClient".
Now, in CommandUIHandler/CommandAction you specifing JS code that connects to outlook (simply I grab it from SP :)):
alert("Let's go!");
var listid = '{ListId}';
var list;
ExecuteOrDelayUntilScriptLoaded(GetParamsAndSync, 'sp.js');
function GetParamsAndSync()
{
var clientContext = new SP.ClientContext.get_current();
var web = clientContext.get_web();
list = web.get_lists().getById(listid);
clientContext.load(list);
clientContext.load(web);
rf = list.get_rootFolder()
clientContext.load(rf);
clientContext.executeQueryAsync(
function() {
viewUrl=rf.get_serverRelativeUrl();
ExportHailStormOverride('calendar','{SiteUrl}','{ListId}', web.get_title(),list.get_title(),viewUrl,viewUrl,'')
},
function() {alert('Error')});
}
//This function was taken from SharePoint sp.init.js
function ExportHailStormOverride(type,weburl,guid,webname,listname,viewurl,passport,listrooturl,folderurl,folderid)
{ULSA13:;
var strAppName=GetCookie('stsSyncAppName');
var strIconName=GetCookie('stsSyncIconPath');
var maxLinkLength=500;
var maxNameLength=20;
var link='stssync://sts/?ver=1.1'
+'&type='+escapeProperly(type)
+'&cmd=add-folder'
+'&base-url='+escapeForSync(weburl)
+'&list-url='+escapeForSync('/'
+ makeAbsUrl(viewurl).substr(weburl.length+1)+'/')
+'&guid='+escapeProperly(guid);
if (window.self.offlineBtnUser !=undefined)
link+='&user-id='+offlineBtnUser;
var names='&site-name='+escapeForSync(webname)
+'&list-name='+escapeForSync(listname);
var context='';
if (folderurl)
context+='&folder-url='
+ escapeForSync('/'+folderurl.substr(listrooturl.length+1));
if (folderid)
context+='&folder-id='+folderid;
if (link.length+names.length+context.length > maxLinkLength &&
(webname.length > maxNameLength || listname.length > maxNameLength))
{
if (webname.length > maxNameLength)
webname=webname.substring(0, maxNameLength-1)+'...';
if (listname.length > maxNameLength)
listname=listname.substring(0, maxNameLength-1)+'...';
names='&site-name='+escapeForSync(webname)
+'&list-name='+escapeForSync(listname);
}
link=link+names+context;
var L_StssyncTooLong_Text='The name of site or list is too long. Make it shorter.';
if (link.length > maxLinkLength)
alert(L_StssyncTooLong_Text);
else
{
try
{
window.location.href=link;
}
catch (e)
{
alert('Sync error.');
}
}
}
Say you have several webparts, one as a controller and several which take information from the controller and act on it. This is fairly easy to model using the Consumer/Producer interface introduced in ASP 2.0.
How would you be able to add interactions the other way around whilst still maintaining the above?
A simple example would be: the user enters information into webpart A which performs a search and the results would be displayed on webpart B. Webpart C allows you to filter the results which should trigger webpart A to re-submit the query and hence update the results in B.
It doesn't seem possible to do in WSS 3.0 because you are only allowed 1 interface to be used in all of the connections at any one time.
Does this even make sense ? :-)
A quick and dirty solution to enable arbitrary control communication is to use recursive find control and events. Have the controls search the control tree by control type for what they need and then subscribe to publicly exposed events on the publishing control.
I have previous used the trick to enable standard server controls to find each other when embedded in CMS systems from different vendors to avoid a specific communication API entirely.
I don't see anything wrong with webpart A getting a reference to webpart B and calling public/internal methods/properties or subscribing handlers to public/internal events. One point of mention when doing this though: EnsureChildControls. I have witnessed with my own eyes one webpart being run clear to PreRender while another webpart hadn't even run CreateChildControls.
From webpart A, fetch your reference to webpart B (in this case webpart B is of type Calendar) like so:
private Calendar _calendarWP = null;
public Calendar CalendarWP
{
get
{
if (_calendarWP != null)
return _calendarWP;
else
foreach (System.Web.UI.WebControls.WebParts.WebPartZone zone in this.WebPartManager.Zones)
foreach (System.Web.UI.WebControls.WebParts.WebPart webpart in zone.WebParts)
if (webpart is Calendar)
{
_calendarWP = (Calendar)webpart;
_calendarWP.EnsureChildControls();
return _calendarWP;
}
return null;
}
}
Now you can do things like fetch some new data and update the Calendar like so:
IEnumerable newData = SomeDataProvider.GetNewData(args);
CalendarWP.someGridView.DataSource = newData;
CalendarWP.someGridView.DataBind();
Or perhaps let webpart A toss a reference to itself over to webpart B so it can use webpart A's public/internal properties to go fetch data for itself:
CalendarWP.UseWPAToFetchData(this);