Showing posts with label Webparts. Show all posts
Showing posts with label Webparts. Show all posts

Monday, January 5, 2009

Sharepoint Variations

Over the October - December, 2008 time frame, I finally a project that involved variations. I had poked around in the Publishing variations class a little and saw some cool stuff there, but was never able to find any really good posts or webparts that peeked my interest enough to pull me away from my current work load.

Then I got lucky enough to be involved in a project where Variations were a prime need. First, a little info about variations:
  • Variations are part of a Publishing Site. In the site collection settings of a publishing site, you can see the controls at the bottom of the right most column. These settings include:
    1. Variations
    2. Variation Labels
    3. Variation Logs
    4. Translatable Columns
  • Interestingly enough, using variations and creating the heirarchy in Sharepoint creates links to the sites on the quick launch, but does not expose the Variations control, which you have to manually uncomment out in the VariationsLabelMenu.ascx page in ControlTemplates. This control exposes a dropdownlist that contains the list variations and a post back to load the selected variation type. (Note: Exploring the code behind this control in the Mircosoft.Sharepoint.Publishing.VariationsLabelEcbMenu class can teach you a lot about how Sharepoint handles Variation Labels and navigation.)

More Later...

Wednesday, July 18, 2007

Trying to use a closed/disposed webpart.

Coming from a C++ background, I am a stickler for making sure my objects are disposed. I also know that cetain SP objects are extremely heavy, and should always be tossed when you are done with them. These objects include SPWeb and SPSite.

An error I know some developers run into is "Trying to use an SPWeb object that has been closed or disposed and is no longer valid." Let me try to shed some light on this, and also point out the correct way to create these objects.

There are 2 common ways to get our hands on an SPWeb object. One way is to use the SPSite.OpenWeb() method and the other is to use the SPContext.Current.Web method. Both of these methods will return to you the current Web, but the difference is that OpenWeb will return a Web object in a different memory space and CurrentWeb will return the Web object using the current memory space and thread.

As .net 2.0 developers, we are told to wrap our disposable objects in Using clauses, or (correctly) to use try/finally. The problem is that when you dispose of the SPWeb object after you gained a pointer to it from the SPContext, you are disposing of the object being used to render the page. Seldomly, Sharepoint recovers from the error, and goes on it's merry way. But more likely, the page will not render, and you will get the error.

As a best practice, if I am only going to fetch and display data, I use the SPContext object and do not dispose of the object, and when I plan on modifying or changing data, I use OpenWeb and dispose.

We all know that SharePoint tries to cover errors, and if a webpart function catches the error, but does not display it, then you can run into a situation where a container page is failing for an unknown reason. Your best bet is to try to add/remove the webparts on the page until the offensive webpart is discovered.

My friend Eric Stallworth points out that Microsoft covers this in this article
http://msdn2.microsoft.com/en-us/library/ms778813.aspx

Sunday, June 3, 2007

Profile Properties Webpart

There is something innately wrong with working on weekends. When I started working with MOSS back in July, I was fearful that perhaps it would not catch on. I don't feel that way anymore....

I do not have a whole lot of time to post, but I feel compelled to share the knowledge. My current consulting project has over exposed me to all the workings of people and profiles. At the height of the design, we were importing from three sources.

Other developers and users often ask me about the data we store for each user, and how they can view this information. Since I am not partial to giving out the server settings password, I created a small webpart that can be placed on a page and set to a user's id to display that user's information.



using System;
using System.Runtime.InteropServices;
using System.Web.UI;
using System.Collections.Generic;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Xml.Serialization;
using Microsoft.Office.Server;
using Microsoft.Office.Server.UserProfiles;
using Microsoft.SharePoint;
using Microsoft.SharePoint.WebControls;
using Microsoft.SharePoint.WebPartPages;

namespace ProfileProperties
{
[Guid("1fe9ec91-b377-4421-bc00-7cbc39ce3061")]
public class ProfileProperties : System.Web.UI.WebControls.WebParts.WebPart
{
PeopleEditor editor = null;
private Dictionary _userProperties = new Dictionary(50);
private string _user_account_info = string.Empty;
[Personalizable(PersonalizationScope.Shared), WebBrowsable(true),
WebDisplayName("Account Info"),
WebDescription("The account of the profile to return.")]
public string AccountInfo
{
get { return _user_account_info; }
set { _user_account_info = value; }
}
public ProfileProperties()
{
this.ExportMode = WebPartExportMode.All;
}
internal void GetUserProfileProperties() {
try
{
Queue profileFlds = GetPropertyFieldList();
UserProfileManager mgr = new UserProfileManager();
UserProfile profile = mgr.GetUserProfile(_user_account_info);
foreach (string fld in profileFlds)
{
try
{
_userProperties.Add(fld, profile[fld].Value.ToString());
}
catch
{
continue;//lame I know, but its only an example
}
}
}
catch {}
}
internal Queue GetPropertyFieldList() {
Queue profileFlds = new Queue(50);
using (SPSite site = SPContext.Current.Site)
{
try
{
ServerContext context = ServerContext.GetContext(site);
UserProfileConfigManager configManager = new UserProfileConfigManager(ServerContext.Current);
Microsoft.Office.Server.UserProfiles.PropertyCollection propColl = configManager.GetProperties();
foreach (Property property in propColl)
{
profileFlds.Enqueue(property.Name);
}
return profileFlds;
}
catch
{
return null;
}
}
}
protected override void OnPreRender(EventArgs e)
{
base.OnPreRender(e);
if (!string.IsNullOrEmpty(_user_account_info))
{
SPSecurity.CodeToRunElevated mf = new SPSecurity.CodeToRunElevated(GetUserProfileProperties);
SPSecurity.RunWithElevatedPrivileges(mf);
}
}
protected override void RenderContents(HtmlTextWriter writer)
{
int x = 0;
foreach (KeyValuePair prop in _userProperties) {
writer.Write(string.Format(@"{0}. {1} = {2}",++x, prop.Key,prop.Value));
}
editor.RenderControl(writer);
}
}
}



The code also utilizes the elevated privilages property for webparts that is new to MOSS.
This allows the webpart to go to the profile manager and get the field list before finding the specified profile and displaying the values.

Friday, June 1, 2007

MediaPlayer Webpart

Situation:
Customer wants to play media files in a structured way on a webpage, without popping up external viewers. The media files will be attached to pages in a list. Normally you would have to click on the attachment and launch a the viewer. This webpart plays the attached file embedded into the page.

Constraints:
Must be able to define player window size.
Must be able to handle multiple media types including .mov, .wma, .avi, .mpeg and flash.
Must be able to define control settings for each media type.

Solution:
Lets start with putting together the webpart class for this control.

using Microsoft.SharePoint;
using Microsoft.SharePoint.WebControls;
using Microsoft.SharePoint.WebPartPages;

namespace MediaPlayer
{
public class MediaPlayer : System.Web.UI.WebControls.WebParts.WebPart
{
private string _media_string = string.Empty;
private string _media_file = string.Empty;
private string _control_width = string.Empty;
private string _control_height = string.Empty;
[Personalizable(PersonalizationScope.Shared), WebBrowsable(true),
WebDisplayName("Media File"),
WebDescription("The location of the media file.")]
public string MediaFile
{
get { return _media_file; }
set { _media_file = value; }
}
[Personalizable(PersonalizationScope.Shared), WebBrowsable(true),
WebDisplayName("Control Width"),
WebDescription("The width of the control.")]
public string ControlWidth
{
get { return _control_width; }
set { _control_width = value; }
}
[Personalizable(PersonalizationScope.Shared), WebBrowsable(true),
WebDisplayName("Control Height"),
WebDescription("The height of the control.")]
public string ControlHeight
{
get { return _control_height; }
set { _control_height = value; }
}
}
}

For lack of space, I will not include the editorpart code that I created for the control. In order to meet a constraint, I used the webpart properties to place default settings that the admin could set on the page and then placed similar settings in the editorpart so that the page creator could override the default settings.
Now that we have out class and properties defined, lets render our control. Inside our class lets add...

protected override void CreateChildControls()
{
MediaFile = GetMediaFile();
_media_string = BuildMediaString();
base.CreateChildControls();
}
protected override void RenderContents(HtmlTextWriter writer)
{
writer.Write(_media_string);
}

Now to the meat of the solution. We are going to generate our own html for this solution. In this way we can ensure exactly what is going to be writen to the browser. I would also like to mention that I am not including any of the browser variant code for other DOMs, you can figure that out :) Lets tackle embedding WMV. You can find the object/embed definitions for all media types at their associated owners sites.

internal string BuildMediaString()
{
StringBuilder sb = new StringBuilder (500);
if (string.IsNullOrEmpty(MediaFile))
{
sb.Append("No media file defined");
}
else
{
string[] tArr = MediaFile.Split((char)'.');
string type = tArr[tArr.GetUpperBound(0)];
switch (type.ToLower())
{
case "wmv":
sb.AppendFormat(
@"<object
style=""width:{0}px;height:{1}px""
classid=""CLSID:22D6F312-B0F6-11D0-94AB-0080C74C7E95""
standby=""Loading Microsoft® Windows® Media Player components...""
type=""application/x-oleobject"" _ codebase=""http://activex.microsoft.com/activex/controls/mplayer/en/nsmp2inf.cab#Version=6,4,7,1112"">
<param name=""filename"" value=""{2}"">
<param name=""autoStart"" value=""true"">
<param name=""showControls"" value=""false"">
<param name=""showstatusbar"" value=""true"">
<param name=""autorewind"" value=""true"">
<param name=""showdisplay"" value=""false"">
<embed
src=""{2}""
width=""{0}""
height=""{1}""
type=""application/x-mplayer2""
autostart=""1""
showcontrols=""0""
showstatusbar=""1""
autorewind=""1""
showdisplay=""0"">
</embed>
</object>
", ControlWidth, ControlHeight, MediaFile);
break;
case("avi")...


This HTML creates an object on the page and embeds the movie at the height and width specified in the webpart settings.

We find out what media type has been requested by splitting the file name or url and using the last index as our media type. For instance if the location is "mysite/lists/Videos/Attachments/my.big.movie.wmv, our array will contain "wmv" in bucket three.

You can redefine the object / embed settings for the control to match your desired look, feel and interactivity.

JMC