3/25/2011 Date: 3:04:00 AM Posted By SharePoint
Tag: SharePoint2010
3/24/2011 Date: 4:39:00 AM Posted By SharePoint
private void DeleteEventReceiverFromAList(string siteUrl) { using (SPSite site = new SPSite(siteUrl)) { using(SPWeb web = site.OpenWeb()) { try { SPList list = web.Lists["myList"]; if (list != null) { string className = "EventReceiverClass"; string asmName = "EventReceiverAssemblyName, Version=1.0.0.0, Culture=neutral, PublicKeyToken=a865f0ecc234ea51"; web.AllowUnsafeUpdates = true; int receivers = list.EventReceivers.Count; bool isAddedReceiverExist = false; bool isUpdatedReceiverExist = false; for (int i = 0; i < receivers; i++) { SPEventReceiverDefinition eventReceiver = list.EventReceivers[i]; if (eventReceiver.Class == className && eventReceiver.Type == SPEventReceiverType.ItemAdded) { eventReceiver.Delete(); break; } } } } catch { } finally { web.AllowUnsafeUpdates = false; } } } }In this code also, there is nothing to explain very detail. Please let me
Tag: SharePoint2007
Date: 4:37:00 AM Posted By SharePoint
This is very generic and everyone knows how to add an event receiver. But,
usually we attach the event receiver on a list template, site etc. This post
deals with adding event receiver to a specific list.
private void AddEventReceiverToAList(string siteUrl) { using (SPSite site = new SPSite(siteUrl)) { using (SPWeb web = site.OpenWeb()) { try { SPList list = web.Lists["myList"]; if (list != null) { int receivers = list.EventReceivers.Count; string className = "EventReceiverClass"; string asmName = "EventReceiverAssemblyName, Version=1.0.0.0, Culture=neutral, PublicKeyToken=a865f0ecc234ea51"; web.AllowUnsafeUpdates = true; bool isAddedReceiverExist = false; for (int i = 0; i < receivers; i++) { SPEventReceiverDefinition eventReceiver = list.EventReceivers[i]; if (eventReceiver.Class == className && eventReceiver.Type == SPEventReceiverType.ItemAdded) { isAddedReceiverExist = true; break; } } if (!isAddedReceiverExist) list.EventReceivers.Add(SPEventReceiverType.ItemAdded, asmName, className); } } catch { } finally { web.AllowUnsafeUpdates = false; } } } }This is very straight forward code and hope you got it.
Tag: SharePoint2007
Date: 3:45:00 AM Posted By SharePoint
In SharePoint customization we have the requirements to write custom code for
web parts, timer jobs etc. In this post I want to tell you some interesting
things in SharePoint. I have written some timer jobs to deploy into my
SharePoint server to match some requirements or to solve some problems.
My requirement is, I want to access the web application or web site
configuration [web.config] file in the SharePoint timer job. But, the
limitations in SharePoint stops me to access the configuration files in timer
job. Below are the problems I faced.
Tag: SharePoint2007
Date: 2:31:00 AM Posted By SharePoint
Today at my work, I need to implement JQuery in SharePoint on a module. I
need to make AJAX calls to get the data from the database depending on the value
in a text box. So, I choose JQuery, because I know it well and it will take very
less time to implement. But for JQuery I need to create page web methods. But
SharePoint doesn't support Page web methods because SharePoint is completely
built upon ASP.NET 2.0.
So, what is the solution, how can I make a call to the server and get the
data from DB? After thought some time about it, finally I got a brilliant and
better idea of using web services. Please follow the steps below to implement
Web services in SharePoint.
After you are ready with all the above steps, then please follow the steps
below.
using System.Web.Services;
using
System.Web.Script.Services;
There we are done with the process. This is really working great and we can
solve really very difficult problems like all scenarios where we need to
communicate with DB without doing post back etc. This is very smooth and fast
way of retrieving results. I like to hear feedback. This is the one of the best
solutions I found. Please post any problems if you face while implementing this
process.
Tag: SharePoint2007
Date: 2:28:00 AM Posted By SharePoint
Please read the post "Add a Web service 2.0 in SharePoint 2007 step by step " before
continue here.
I think, most of the people don't know about blocked file types and where can
we set them. SharePoint by default, won't allow all the file extensions because
of some security issues.
Why the SharePoint team didn't allow them by default. There is a strong
reason behind it. i.e. not other than Security reasons. For example, EXE
extension file, script files etc.
How to remove a blocked file type?
So, if you are facing the problem of not able to upload the files of type
.asmx, .exe, .chm etc.. This is the place where you set it. Happy
SharePointing.
Tag: SharePoint2007
Date: 2:25:00 AM Posted By SharePoint
Please read the article "Add a Web service 2.0 in SharePoint 2007 step by step " before
continue this post. Then you will get some idea on what is the need of change in
web.config for implementing custom web service 2.0 in SharePoint.
SharePoint framework don't allow to call web services from java script by
default. For this, we explicitly tell the web application that when any request
from javascript to web services then use this http handlers, http modules and
allow *.asmx files to serve in SharePoint.
So, please follow the steps below.
Changes required in the sections.
Please add the sections as it is in the web.config file of your SharePoint
web site to get the web services 2.0 work.
1. <httpHeaders> section:
<remove verb="*"
path="*.asmx"/>
<add verb="*" path="*.asmx" validate="false"
type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions,
Version=3.5.0.0, Culture=neutral,
PublicKeyToken=31BF3856AD364E35"/></httpHandlers>
2. <httpModules> section:
<add name="ScriptModule" type="System.Web.Handlers.ScriptModule,
System.Web.Extensions, Version=3.5.0.0, Culture=neutral,
PublicKeyToken=31BF3856AD364E35"/>
3. <System.WebServer> section:
<system.webServer>
<validation
validateIntegratedModeConfiguration="false"/>
<modules>
<remove name="ScriptModule"/>
<add
name="ScriptModule" preCondition="managedHandler"
type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0,
Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
</modules>
<handlers>
<remove
name="WebServiceHandlerFactory-Integrated"/>
<remove
name="ScriptHandlerFactory"/>
<remove
name="ScriptHandlerFactoryAppServices"/>
<remove
name="ScriptResource"/>
<add name="ScriptHandlerFactory"
verb="*" path="*.asmx" preCondition="integratedMode"
type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions,
Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
<add name="ScriptHandlerFactoryAppServices" verb="*"
path="*_AppService.axd" preCondition="integratedMode"
type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions,
Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
<add name="ScriptResource" preCondition="integratedMode"
verb="GET,HEAD" path="ScriptResource.axd"
type="System.Web.Handlers.ScriptResourceHandler, System.Web.Extensions,
Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
</handlers>
</system.webServer>
4. <runtime> Section:
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Web.Extensions"
publicKeyToken="31bf3856ad364e35"/>
<bindingRedirect
oldVersion="1.0.0.0-1.1.0.0" newVersion="3.5.0.0"/>
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Web.Extensions.Design"
publicKeyToken="31bf3856ad364e35"/>
<bindingRedirect
oldVersion="1.0.0.0-1.1.0.0" newVersion="3.5.0.0"/>
</dependentAssembly>
</assemblyBinding>
5. <assemblies> section:
Add this entry to <assemblies> section.
<add assembly="System.Web.Extensions, Version=3.5.0.0,
Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
That's it!!! With these changes it will allow you to access web services from
java script. I spent my valuable time to find this way to solve so many
problems. Do you like this?
Tag: SharePoint2007
Date: 2:20:00 AM Posted By SharePoint
On the way of explaining the process of implementing custom web services in
SharePoint, I thought of telling you the advantage of App_Code folder in
SharePoint. So, Please read Add a Web service 2.0 in SharePoint 2007 step by step before continue here.
If you are a .NET developer then you know the advantage of App_Code folder.
When I am new to SharePoint technology, I thought like we can't add App_Code
folder in SharePoint root folder because i didn't find it in the virtual
directory path. […/Inetpub/wwwroot/…../] But few days back, when I got time, I
want to do experiment on the App_Code folder in SharePoint. So, what are the
advantages and how to do it please follow below steps.
Now, you can access this class any where in the SharePoint web site. Very
useful one right?
Tag: SharePoint2007
3/23/2011 Date: 11:11:00 PM Posted By SharePoint
public Page() { string controlid = "divImgs"; //You can also use intiparams of Silverlight params to get the id. InitializeComponent(); string xmlstring = string.Empty; if (controlid != null) { HtmlElement ctl = HtmlPage.Document.GetElementById(controlid); if (ctl != null) xmlstring = (string)ctl.GetProperty("innerHTML"); } if (!string.IsNullOrEmpty(xmlstring)) { ProcessXML(xmlstring); } }The above code is the Page constructor of the Page. Xaml.cs file.
private void ProcessXML(string xml) { images = new List<string>(); if (xml != string.Empty) { try { StringReader textStream = null; textStream = new StringReader(xml); if (textStream != null) { using (XmlReader reader = XmlReader.Create(textStream)) { while (!reader.EOF) { if ((reader.IsStartElement()) && (reader.LocalName == "slides")) { if (reader.HasAttributes) { reader.MoveToAttribute("baseUrl"); } } else { if ((reader.LocalName == "slide") && (reader.HasAttributes)) { reader.MoveToAttribute("imageUrl"); string imageName = reader.Value.ToLower(); if ((imageName.Contains(".jpg") || imageName.Contains(".png"))) images.Add(reader.Value); } } reader.Read(); } } } } catch (Exception ex) { } } }3. In the above code, images is the global variable of type
Tag: SharePoint2007
Date: 11:09:00 PM Posted By SharePoint
Hi,
Silverlight works on client side, so it's not possible to add
SharePoint libraries in Silverlight project. But we have a way that we can
access SharePoint data and show it up in Silverlight in SharePoint site.
We
have a requirement where we need to pull data from the SharePoint libraries and
lists and show the data in Silverlight. Here i will explain a scenario where we
can pull the images from image library and show it in Silverlight.
Follow the steps below to do this.
Get data from SharePoint library and make it in the
form of XML.
Tag: SharePoint2007
Date: 10:45:00 PM Posted By SharePoint
private void
IfMethod(object sender, ConditionalEventArgs e)
{
if (isTrue)
e.Result = true;
else
e.Result = false;
}
private void
ElseMethod(object sender, ConditionalEventArgs e)
{
if (!isTrue)
e.Result
= true;
else
e.Result = false;
}
Tag: SharePoint2007
Date: 10:43:00 PM Posted By SharePoint
Tag: SharePoint2007
Date: 10:39:00 PM Posted By SharePoint
In SharePoint 2007, very good feature I like much is that framework
supports for writing custom events, features, workflows or any other stuff... We
can customize complete SharePoint system at any context.
While working with
SharePoint custom workflows, we get some scenarios where we need to stop a
workflow or start a new instance of workflow through coding. So, I did some
research on the MSDN library and saw all the dll's by doing some reflection on
the libraries and found some functions and classes which supports
this.
Here, I want to present a small code snippet which does terminating
currently running wokflow and start a new instance of the workflow on the same
list item.
SPListItem listItem = workflowProperties.Item; SPList spList = workflowProperties.List; string initData = workflowProperties.InitiationData; const string WF_GUID = "The GUID of workflow template"; workflowProperties.Site.WorkflowManager.RemoveWorkflowFromListItem(workflowProperties.Workflow); workflowProperties.Site.WorkflowManager.Dispose(); SPWorkflowAssociation associationTemplate= spList.WorkflowAssociations.GetAssociationByBaseID(new Guid(WF_GUID)); workflowProperties.Site.WorkflowManager.StartWorkflow(listItem, associationTemplate, initData);
Hope this will give you a good start to code on SharePoint workflow start and
terminate through coding. Let me know, what you think.
Tag: SharePoint2007
Date: 10:36:00 PM Posted By SharePoint
Give anonymous access to the Application.
1. Go to IIS.
2. Select
reports application –> properties –> Directory Security –>
Authentication and Access control –> Edit –> Enable Anonymous
access
Give anonymous access to a report.
1. Open the reports on
the report server located at http://<servername>/reports
2. Browse
to the report that needs to be accessed anonymously.
3. Select properties
tab for the report.
4. Select security from the left
navigation.
5. Select New Role Assignment from this page.
a. Give
a group name - everyone (in the "group or user name" text box).
b. Select
the option Browser role and then click OK.
[Browser role is for READ -
ONLY access].
Tag: SharePoint2007
Date: 10:32:00 PM Posted By SharePoint
using System; using System.Collections.Generic; using System.Text; using Microsoft.SharePoint.Portal.WebControls; using Microsoft.SharePoint.Portal.Topology; using Microsoft.SharePoint.Portal; using Microsoft.SharePoint.Portal.UserProfiles; using System.Runtime.InteropServices; using System.Web.UI; using System.Web.UI.WebControls; using System.Web.UI.HtmlControls; using System.Configuration;namespace MyCalendarWebPart { [Guid("15241046-2128-4a1d-b6d5-24c8a67c4d28")] public class MyCalendarWebPart : Microsoft.SharePoint.WebPartPages.WebPart { private OWACalendarPart wpCalendar; HtmlGenericControl litMsg = null; HtmlGenericControl roundedCorner; protected override void CreateChildControls() { wpCalendar = new OWACalendarPart(); litMsg = new HtmlGenericControl(); roundedCorner = new HtmlGenericControl(); roundedCorner.InnerHtml = ""; Controls.Add(configureCalendar()); Controls.Add(litMsg); Controls.Add(roundedCorner); base.CreateChildControls(); } private OWACalendarPart configureCalendar() { try { //Connect to the portal and get the portal context. TopologyManager topology = new TopologyManager(); PortalSite portal = topology.PortalSites[new Uri(ConfigurationManager.AppSettings["SiteUrl"])]; PortalContext context = PortalApplication.GetContext(portal); //initialize user profile config manager object UserProfileManager profileManager = new UserProfileManager(context); UserProfile profile = profileManager.GetUserProfile(true); wpCalendar.Title = "My Calendar"; wpCalendar.ViewName = "Weekly"; wpCalendar.CssClass = ""; // use the profile object to retrieve the properties you need in your company to // retrieve the mail box name string workmail = profile[PropertyConstants.WorkEmail].ToString(); wpCalendar.MailboxName = workmail; wpCalendar.OWAServerAddressRoot = ConfigurationManager.AppSettings["ExchangeServerUrl"]; wpCalendar.Height = "655″; wpCalendar.Width = "600″; wpCalendar.ImportErrorMessage = "No EmailID found for your account."; } catch { litMsg.InnerHtml = "No EmailID found for your account."; } return wpCalendar; } protected override void RenderWebPart(HtmlTextWriter output) { try { wpCalendar.RenderControl(output); litMsg.RenderControl(output); roundedCorner.RenderControl(output); } catch (Exception ex) { output.Write(ex.ToString()); } } public override void RenderControl(HtmlTextWriter writer) { base.RenderControl(writer); } } }
Tag: SharePoint2007
3/22/2011 Date: 12:47:00 AM Posted By SharePoint
//Get data from SharePoint
SPWeb _web = GetWeb();
SPList list = _web.Lists["List"];
SPQuery query = new SPQuery();
query.Query = "" + _user.Name.Trim()
+ ""
+ selectedYear.Trim() + "";
DataTable dt = list.GetItems(query).GetDataTable();
//Get DataRows
if (dt != null && dt.Rows.Count > 0)
{
//Group the data
var groupedRows = from row in dt.AsEnumerable()
group row by row.Field(dt.Columns["Department"]) into grp
orderby grp.Key
select new
{
Name = grp.Key,
_grp = grp
};
foreach (var grp in groupedRows)
{
AccordionPane currentPane = CreateAccordinHeaderPane(mainacc, grp.Name);
groupedRows.ToList();
List rows = grp._grp.ToList();
AddContentToPane(currentPane, rows);
}
}
DataTable dt = new DataTable();
dt = rows.CopyToDataTable(); -> Gets
Tag: SharePoint2007
Date: 12:44:00 AM Posted By SharePoint
The first things would be to create a custom screen for uploading a picture into the User Photos Picture library (url – http://SpSite/User%20Photos/Profile%20Pictures).
Then updating the user profile to refer the new uploaded picture. Below is the code that will do just that.
public void UpdateUserPicture(SPSite site)
{
string userAccount = "domain\\account";
ServerContext ctx = ServerContext.GetContext(site);
UserProfileManager upm = new UserProfileManager(ctx);
UserProfile up = upm.GetUserProfile(userAccount);
UploadUserPircutre();
up["PictureUrl"].Value = "http://SpSite/User%20Photos/Profile%20Pictures/pic.jpg";
up.Commit();
}
Tag: SharePoint2007
Date: 12:42:00 AM Posted By SharePoint
A short code snippet about how to add and retrieve values from multichoice column using client object model.
ListItemCollection listItems = list.GetItems(camlQuery);
//Add values -
listItems[0]["MultiChoiceField"] = newstring[] {"Choice 1″,"Choice 4″ };
//Retrieve -
if (listItems[0]["testmultichoice"] != null)
{
string[] values = listItems[0]["testmultichoice"] as string[];
if (values != null)
{
var result = from c in values where c.Contains(searchChoice) select c;
if (result != null)
choices = result.ToList();
}
}
return choices;
Tag: SharePoint2007
Date: 12:41:00 AM Posted By SharePoint
I want to update the theme for a meeting workspace during its creation.
I have added this code to the ONET.xml file (Configuration ID=0) in the MPS Site Template:
<Feature ID="39DD29FB-B6F5-4697-B526-4D38DE4893E5″ />
</WebFeatures>
<ExecuteUrl Url="_layouts/SiteMap/SiteMap.aspx?thmx=1″/>
</Configuration>
So in the URL specified above, it passes a parameter used to identify a portion of code to execute in the page_load event.
here is the code in the SiteMap page:
string theme = Request.QueryString.Get("thmx"); if (theme != null) { SPWeb thisWeb = SPContext.Current.Web; SPWeb TempWeb = SwitchUser(thisWeb, thisWeb.Url, "System"); TempWeb.AllowUnsafeUpdates = true; TempWeb.Update(); SPSite site = new SPSite(SPContext.Current.Site.RootWeb.Url); ReadOnlyCollection<ThmxTheme> managedThemes = null; managedThemes = ThmxTheme.GetManagedThemes(site); foreach (ThmxTheme theme2 in managedThemes) { if (theme2.Name == "IH_Custom") { //theme2.ApplyTo(TempWeb, true); ThmxTheme.SetThemeUrlForWeb(TempWeb, theme2.ServerRelativeUrl); TempWeb.Update(); break; } } TempWeb.AllowUnsafeUpdates = false; Response.Redirect(TempWeb.Url); }
Ignore the switchUser routine; I was just testing to see if the System account was getting the same error. In both cases (as current user or System) I was getting this error when it hit either of these lines of code to execute:
//theme2.ApplyTo(thisWeb, true); OR
ThmxTheme.SetThemeUrlForWeb(TempWeb,
theme2.ServerRelativeUrl);
Tag: SharePoint2007
3/21/2011 Date: 7:31:00 AM Posted By SharePoint
In this article series:
In contrast to my earlier posts, this article isn't a detailed walkthrough – rather, it is a compendium of techniques and information which should help you implement ribbon customizations for web parts, custom field controls or indeed, any other kind of page control. At the time of writing I haven't really seen a great deal written on this, aside from a couple of resources I'll mention. I would have loved to have written detailed walkthroughs here, but alas I can't because a) Microsoft have recently published some good info for one of the scenarios, so I'd prefer to point you to that, b) To run through all the scenarios here in depth would take weeks and c) Because if I don't stop writing about the ribbon soon I'll still be on this topic this time next year, and frankly I have a book chapter to write on a completely different area of SharePoint 2010 which I need to crack on with! So we'll look at each requirement and I'll discuss what I think are the key techniques you'd use along with some observations from me.
Note that due to the general lack of documentation so far on this topic (and the fact I haven't bottomed everything out in all the scenarios), a couple of things here are speculation rather than hard fact. I'll make these items clear, and will endeavour to come back to this article and add updates as new information emerges.
Before we dive in, remember that if you're abiding by ribbon design principles you should most likely be working with a ContextualGroup (discussed towards the end of Adding ribbon items into existing tabs/groups) – this is the container to use for ribbon elements which are only relevant depending on what the user is doing (shown visible but not active here):
If this isn't what you want, note that things are probably easier if you just need to add some controls on an existing tab or group which get activated under certain circumstances. In this case you can just supply some JavaScript to the 'EnabledScript' attribute of your controls – I showed this in my Notifications/Status demo in Customizing the ribbon (part 1) – creating tabs, groups and controls. The rest of this article focuses on how you might get a contextual group (see image above) to show in different scenarios.
Adding ribbon items from a web part
In SharePoint 2010 the web part framework now has special provision for ribbon customizations, which means a couple of things are taken care of for you. Microsoft have now published some guidance on this scenario in the form of an article on the SharePoint Developer Documentation blog - How to Create a Web Part with a Contextual Tab. There's a lot of golden info in there, but I'll distil the main points here:
Adding ribbon items from a field control
Like web parts, I think (speculating here) there is special provision in the framework for ribbon customizations from a field control. Consider that, like web parts, field controls should typically only show their contextual ribbon options when they have focus - since this would be the case for all field controls, it makes sense to me that Microsoft might abstract some handling around this for us. If not, you would be responsible for writing JavaScript to detect when the user clicked into your field so that you could show your ContextualGroup.
Digging around, I notice that all SharePoint field controls have 4 new properties (since they are implemented on one of the base classes, Microsoft.SharePoint.WebControls.FormComponent):
I'm surmising that these control properties would be set declaratively in the hosting page and are designed to match up with commands specified in an accompanying page component - this would enable you to run client-side code similar to my sample last time, perhaps to initialize data for your ribbon controls and so on. However, when I try to implement these commands, my page component's 'handleCommand' method never receives these commands. So either I'm doing something wrong or this theory is incorrect. In which case, not to worry ribbon customizations for field controls should still be entirely possible, there will just be more work to do. Read on.
Using server-side code to show ribbon items
Thinking outside of any 'framework support' for where our ribbon customizations are targeted at, we can always write server-side or client-side code to show a contextual group. In fact, I already showed the server-side code for this in Adding ribbon items into existing tabs/groups (ribbon customization part 2):
protected override void OnPreRender(EventArgs e)
{
SPRibbon currentRibbon = SPRibbon.GetCurrent(this.Page);
currentRibbon.MakeTabAvailable("COB.SharePoint.Ribbon.ContextualTab");
currentRibbon.MakeContextualGroupInitiallyVisible("COB.SharePoint.Ribbon.ContextualGroup", string.Empty);
base.OnPreRender(e);
}
This is probably only appropriate if your stuff is contextual-ish – an application page would be a good example of this. In this example all we care about is that the user is on our page, then we can show our options. It doesn't matter which control has focus, effectively our options are OK to show by default when the page loads. However, if you need page-level 'contextuality' (I may have just invented that word by the way - use it in a sentence to your boss today) then most likely you'll be wanting to use JavaScript to show your contextual group when the user is doing something specific on the page e.g. editing a certain field. You'd then be looking to some client-side code to detect this and respond accordingly.
Using client-side code to show ribbon items
So, final scenario - what if you need to show ribbon items from something which isn't a web part or field control (or like me you couldn't get there with anything in the field control framework which may or may not be designed to help), and it has to be done client-side to be fully contextual? Well, disappointingly I'm drawing another blank here so far – I'd love to hear from anyone who knows the answer to this. In case you're doing ribbon development and are interested, here's a summary of my journey:
Needless to say, I'd love to hear what I'm missing on this. If nothing else, hopefully MS will release some more information soon which will shed some light on how to handle this scenario.
Summary
This post doesn't claim to have all the answers, but it might serve as a "leg up" if you're trying to build any of these scenarios now. I'm hoping that the lack of deep information in this area is a reflection on the fact that RTM is still some time away, and that ribbon dev will get some love in the SDK between now and then. The key scenarios I discussed here are displaying custom ribbon elements from web parts and field controls, but also the more generic cases of displaying customizations with server or client-side code.
Tag: SharePoint2010
Date: 7:25:00 AM Posted By SharePoint
In this article series:
Once you understand how to get your customizations into the right place in the ribbon, you may find you want to go beyond simply adding buttons and make use of other controls such as dropdowns, checkboxes, flyout anchors and so on. This type of ribbon development is fairly involved, but as with so many things in SharePoint development, once you've done it the first time you know the overall "template" for subsequent occasions - hopefully what I'm showing here is a good starting point for quite a few advanced things you might want to do. The key is that a JavaScript "page component" is generally required in addition to the declarative XML we've seen in my earlier posts.
[Beta sidenote] At the time of writing (Feb 2010, still in beta), after some time on this I'm forming the opinion that ribbon development could be one of the more complex development areas in SP2010. Maybe some stellar documentation from Microsoft could change this, but right now there are many dark corners which are quite difficult to understand – currently there is practically no coverage of much of this stuff in the SDK (and very little anywhere really), so unless you have inside information it's mainly blood, sweat and tears all the way. I've mentioned this to the PM in the Product Group (Elisabeth Olson), and it sounds like more MS guidance is on the way soon, so let's hope.
The sample
My sample shows the use of a custom dropdown in the ribbon – I'll go into more detail later, but the concepts I'm showing here can be used for many controls in the ribbon, not just a dropdown. So if what you're wanting to do doesn't specifically involve a dropdown, I'd suggest reading on anyway as this technique is still probably what you will use.
When clicked the first time, it uses the Client Object Model to fetch a collection of lists in the current web, then presents them as options in the dropdown:
When an item is selected, a simple JavaScript alert is raised with the name of the selected list, though a real-life implementation would of course do something more useful with the value. The goal here is to illustrate how to work with ribbon controls other than buttons, and also how to write code behind them – once you can do this, you'll be able to build a wide range of solutions.
One key thing to note – it IS possible to add items to a dropdown or other control entirely in XML. I'm choosing to use a JavaScript page component to illustrate what happens when you need "code-behind" e.g. to iterate all the lists in a web in my case.
What's required – summary
1. Declarative XML
I used the following XML – here I'm actually showing a cut-down extract which is just for the group containing my controls. Really it's just the 'Controls' section which is the most interesting bit, the surroundings would depend on whether you are wanting to create a new tab or add the items into an existing tab/group, see my previous articles for those details.
Key points of note are the Command, PopulateQueryCommand, and QueryCommand attributes on the dropdown – these will link into our JavaScript page component:
<Group
Id="COB.SharePoint.Ribbon.WithPageComponent.PCNotificationGroup"
Description="Contains advanced ribbon controls"
Title="Page component sample"
Sequence="53"
Template="Ribbon.Templates.COB.OneLargeExample">
<Controls Id="COB.SharePoint.Ribbon.WithPageComponent.PCNotificationGroup.Controls">
<Label Id="COB.SharePoint.Ribbon.WithPageComponent.PCNotificationGroup.Label"
ForId="COB.SharePoint.Ribbon.WithPageComponent.PCNotificationGroup.Dropdown"
Command="LabelCommand"
LabelText="Select list:"
Sequence="16"
TemplateAlias="c1"/>
<DropDown
Id="COB.SharePoint.Ribbon.WithPageComponent.PCNotificationGroup.Dropdown"
Sequence="17"
Command="COB.PageComponent.Command.DoAction"
PopulateDynamically="true"
PopulateOnlyOnce="true"
PopulateQueryCommand="COB.PageComponent.Command.PopulateDropDown"
QueryCommand="COB.PageComponent.Command.QueryDoAction"
Width="75px"
TemplateAlias="c2" />
</Controls>
</Group>
2. JavaScript page component
This is the complex bit, the first time at least. We are effectively writing object-oriented JavaScript which contains a class which powers our ribbon control. Consider JavaScript such as this the 'template' to use for page components, where you'll modify the actual implementation bits each time. I've commented some key points, suggest having a scroll through and then we'll walk through the highlights:
Type.registerNamespace('COB.SharePoint.Ribbon.PageComponent');
COB.SharePoint.Ribbon.PageComponent = function () {
COB.SharePoint.Ribbon.PageComponent.initializeBase(this);
}
// the initialize function needs to be called by some script added to the page elsewhere - in the end, it does the important work
// of calling PageManager.addPageComponent()..
COB.SharePoint.Ribbon.PageComponent.initialize = function () {
ExecuteOrDelayUntilScriptLoaded(Function.createDelegate(null, COB.SharePoint.Ribbon.PageComponent.initializePageComponent), 'SP.Ribbon.js');
}
COB.SharePoint.Ribbon.PageComponent.initializePageComponent = function() {
var ribbonPageManager = SP.Ribbon.PageManager.get_instance();
if (null !== ribbonPageManager) {
ribbonPageManager.addPageComponent(COB.SharePoint.Ribbon.PageComponent.instance);
}
}
COB.SharePoint.Ribbon.PageComponent.prototype = {
init: function () { },
getFocusedCommands: function () {
return ['COB.PageComponent.Command.FieldControl.GroupCommand', 'COB.PageComponent.Command.FieldControl.TabCommand', 'COB.PageComponent.Command.FieldControl.ContextualGroupCommand', 'COB.PageComponent.Command.FieldControl.RibbonCommand'];
},
getGlobalCommands: function () {
return ['COB.PageComponent.Command.DoAction', 'COB.PageComponent.Command.PopulateDropDown', 'COB.PageComponent.Command.QueryDoAction'];
},
canHandleCommand: function (commandId) {
if ((commandId === 'COB.PageComponent.Command.DoAction')
(commandId === 'COB.PageComponent.Command.PopulateDropDown') (commandId === 'COB.PageComponent.Command.QueryDoAction')) {
return true;
}
else {
return false;
}
},
handleCommand: function (commandId, properties, sequence) {
if (commandId === 'COB.PageComponent.Command.FieldControl.GroupCommand') {
alert("COB.PageComponent.Command.FieldControl.GroupCommand fired");
}
if (commandId === 'COB.PageComponent.Command.FieldControl.TabCommand') {
alert("COB.PageComponent.Command.FieldControl.TabCommand fired");
}
if (commandId === 'COB.PageComponent.Command.FieldControl.ContextualGroupCommand') {
alert("COB.PageComponent.Command.FieldControl.ContextualGroupCommand fired");
}
if (commandId === 'COB.PageComponent.Command.FieldControl.RibbonCommand') {
alert("COB.PageComponent.Command.FieldControl.RibbonCommand fired");
}
if (commandId === 'COB.PageComponent.Command.QueryDoAction') {
// this command executes as soon as tab is requested, so do initialization here ready for if our dropdown gets requested..
loadCurrentWebLists();
}
if (commandId === 'COB.PageComponent.Command.PopulateDropDown') {
// actually build the dropdown contents by setting the PopulationXML property to a value with the expected format. We have to deal with possible
// timing issues/dependency on core SharePoint JS code with an ExecuteOrDelay..
ExecuteOrDelayUntilScriptLoaded(Function.createDelegate(null, getDropdownItemsXml), 'SP.js');
properties.PopulationXML = getDropdownItemsXml();
}
if (commandId === 'COB.PageComponent.Command.DoAction') {
// here we're using the SourceControlId to detect the selected item, but more normally each item would have a unique commandId (rather than 'DoAction').
// However this isn't possible in this case since each item is a list in the current web, and this can change..
var selectedItem = properties.SourceControlId.toString();
var listName = selectedItem.substring(selectedItem.lastIndexOf('.') + 1);
alert("You selected the list: " + listName);
}
},
isFocusable: function () {
return true;
},
receiveFocus: function () {
return true;
},
yieldFocus: function () {
return true;
}
}
// **** BEGIN: helper code specific to this sample ****
// some global variables which we'll use with the async processing..
var lists = null;
var querySucceeded = false;
// use the Client Object Model to fetch the lists in the current site..
function loadCurrentWebLists() {
var clientContext = new SP.ClientContext.get_current();
var web = clientContext.get_web();
this.lists = web.get_lists();
clientContext.load(lists);
clientContext.executeQueryAsync(
Function.createDelegate(this, this.onQuerySucceeded),
Function.createDelegate(this, this.onQueryFailed));
}
function onQuerySucceeded() {
querySucceeded = true;
}
function onQueryFailed(sender, args) {
querySucceeded = false;
}
function getDropdownItemsXml() {
var sb = new Sys.StringBuilder();
sb.append('<Menu Id=\'COB.SharePoint.Ribbon.WithPageComponent.PCNotificationGroup.Dropdown.Menu\'>');
sb.append('<MenuSection DisplayMode=\'Menu\' Id=\'COB.SharePoint.Ribbon.WithPageComponent.PCNotificationGroup.Dropdown.Menu.Manage\'>');
sb.append('<Controls Id=\'COB.SharePoint.Ribbon.WithPageComponent.PCNotificationGroup.Dropdown.Menu.Manage.Controls\'>');
if (querySucceeded)
{
var listEnumerator = lists.getEnumerator();
while (listEnumerator.moveNext()) {
var oList = listEnumerator.get_current();
sb.append('<Button');
sb.append(' Id=\'COB.SharePoint.Ribbon.WithPageComponent.PCNotificationGroup.Dropdown.Menu.Manage.');
sb.append(oList.get_title());
sb.append('\'');
sb.append(' Command=\'');
sb.append('COB.PageComponent.Command.DoAction');
sb.append('\'');
sb.append(' LabelText=\'');
sb.append(SP.Utilities.HttpUtility.htmlEncode(oList.get_title()));
sb.append('\'');
sb.append('/>');
}
}
sb.append('</Controls>');
sb.append('</MenuSection>');
sb.append('</Menu>');
return sb.toString();
}
// **** END: helper code specific to this sample ****
COB.SharePoint.Ribbon.PageComponent.registerClass('COB.SharePoint.Ribbon.PageComponent', CUI.Page.PageComponent);
COB.SharePoint.Ribbon.PageComponent.instance = new COB.SharePoint.Ribbon.PageComponent();
NotifyScriptLoadedAndExecuteWaitingJobs("COB.SharePoint.Ribbon.PageComponent.js");
1: <Menu Id="">
2: <MenuSection Id="">
3: <Controls Id="">
4: <Button Command="" Id="" LabelText="" />
5: ..a 'Button' element here for each item in the collection..
6: </Controls>
7: </MenuSection>
8: </Menu>
3. Page-level JavaScript
The final element is the JavaScript you need to add to the page to call into the page component. In my example I'm happy for this JavaScript to be added to every page in my site (since that's the scope of my ribbon customization), so I used a delegate control in AdditionalPageHead to add a custom user control, the body of which looks like this:
<SharePoint:ScriptLink Name="sp.js" LoadAfterUI="true" OnDemand="false" Localizable="false" runat="server" ID="ScriptLink1" />
<SharePoint:ScriptLink Name="CUI.js" LoadAfterUI="true" OnDemand="false" Localizable="false" runat="server" ID="ScriptLink3" />
<SharePoint:ScriptLink Name="/_layouts/COB.SharePoint.Demos.Ribbon/COB.SharePoint.Ribbon.PageComponent.js" LoadAfterUI="true" OnDemand="false" Localizable="false" runat="server" ID="ScriptLink2" />
<script type="text/javascript">
//<![CDATA[function initCOBRibbon() {COB.SharePoint.Ribbon.PageComponent.initialize();
}
ExecuteOrDelayUntilScriptLoaded(initCOBRibbon, 'COB.SharePoint.Ribbon.PageComponent.js');////]]></script>
The important things here are that we ensure required system JS files are loaded with the ScriptLink tag, do the same for our JS file, then call the .initialize() function of our page component.
So those the component pieces for complex controls in the ribbon! A wide variety of ribbon customizations should be possible by tailoring this information/sample code as needed (remember 'handleCommand' is the key implementation hook), and I definitely think that starting from such a template is the way to go.
Appendix - considerations for using the Client Object Model in the ribbon
When working with the ribbon it quickly becomes apparent that if the Client Object Model didn't exist, things would be much trickier – they are a natural pairing for many requirements. Despite this, some challenges arise – consider that a control (e.g. dropdown) will have it's items collection populated as late as possible if 'PopulateDynamically' is set to true (generally a good idea) i.e. when the dropdown is actually clicked to select an item! This is because the ribbon is designed around a "script on demand" model (you'll often see "SOD" references in Microsoft's JavaScript) – this ensures only the required JavaScript is downloaded to the client, and no more. This solves the issue where on SharePoint 2007 WCM sites, we would suppress the core.js file for anonymous users because it was big and not required for these users. Anyway, when the dropdown is clicked, at this point the ribbon framework calls 'handleCommand' with the command specified for the 'PopulateQueryCommand' value. If you run your Client OM code here it's no good, since you won't get the result there and then due to the async model – the result will be provided to the callback function, long after 'handleCommand' has completed, so the end result is your control will be empty.
Consequently, you need to do the actual processing before the 'PopulateQueryCommand' is called. You could choose to do as soon as the page loads, but in most cases this could be inefficient – what if the user doesn't come near your ribbon control on this page load? In this case we would have incurred some client-side processing and an extra request to the server which was completely unnecessary – on a high-traffic page, this could be bad news. Without any documentation it's hard to be sure at this stage, but it seems the 'QueryCommand' is a good place to put such Client OM code – this seems to be called when the parent container (e.g. tab) is made visible (at which point there's now a chance the user could use our control). In my code I have the actual Client OM query run here and store the result in page-level variable - this is then picked up and iterated for the 'PopulateQueryCommand'. By the time the script runs this command to populate the control, the query has already executed and has the data ready – happy days. I'll be interested to see what emerges in this area to see whether this is the expected pattern or, well, if I've got things completely wrong.
Summary
Complex ribbon customizations are likely to require a JavaScript page component - in general page components are somewhat complex (partly because of the current lack of documentation perhaps), but once you have a suitable template, subsequent implementations should be easier. If you need to use Client Object Model code, beware of the "async/lifecycle issue" discussed here and ensure your code has data ready for the ribbon framework.
Tag: SharePoint2010