Using Telerik RadUpload Control with SharePoint Document Libraries Silverlight

Having examined the RadUpload control now from the ASP.NET perspective in this post, it is time to investigate the other platform of interest, Silverlight. This is a little bit trickier then working with the ASP.NET port of the control since it requires developing a HTTP handler to manage the upload, and, constructing the stream to push into SharePoint requires a temporary location on the server to host the file pre-upload operation into the SharePoint Document Library. So essentially, the file goes from client, to physically on the server, then to SharePoint, with subsequent removal of the file physically off the server. I was not able to build a direct stream successfully without problems, but would be interested if others have explored this route.

From the Silverlight perspective, the handler needs to be explicitly specified when invoking the RadUpload object. In general, the URL itself is constructed ala a configuration value in order to ease environment transitions in a normal ALM cycle. I am going to use a URI object since I am interested in a replacement token to a common WCF directory that contains my ASHX file and I only want the host value. This is useful if you are using the variable throughout the software.

[csharp]
Uri uri = new Uri(You configuration read goes here);
string newUrl = uri.Host;
string value = string.Format(“http://{0}/_wcf/UploadHandler.ashx”, newUrl);
Uploader.UploadServiceUrl = value;
[/csharp]

As you can see, we are setting the RadUpload.UploadServiceUrl property to my custom handler path. Now, onto the handler itself! The part to pay close attention to is that I am also demonstrating passing a dynamic parameter to hydrate the SharePoint Document Library SPFile with metadata.

[csharp]
namespace UploadHandler
{
public class UploadHandler : RadUploadHandler
{
public string MyParam;
public override bool SaveChunkData(string filePath, long position, byte[] buffer, int contentLength, out int savedBytes)
{
MyParam = GetQueryParameter(“Param”);
return base.SaveChunkData(filePath, position, buffer, contentLength, out savedBytes);
}

public override void ProcessStream()
{
base.ProcessStream();
for (int i = 0; i < Request.Files.Count; i++) { HttpPostedFile file = Request.Files[i]; string fileName = GetFileName(); string fullPath = string.Format("{0}\\_wcf\\TempFiles\\", HttpContext.Current.Server.MapPath(HttpContext.Current.Request.ApplicationPath)); string filename = fullPath + Path.GetFileName(file.FileName); string path = fullPath + fileName; if (file.ContentLength != 0) { file.SaveAs(filename); string requestUrl = WebConfigurationManager.AppSettings["RootSite"]; FileStream stream = new FileStream(filename, FileMode.Open); SPSite site = new SPSite(requestUrl); using (SPSite elevatedSite = new SPSite(requestUrl, site.SystemAccount.UserToken)) { using (SPWeb web = elevatedSite.OpenWeb()) { Hashtable hashtable = new Hashtable(); hashtable.Add("Param", MyParam); Hashtable properties = hashtable; web.AllowUnsafeUpdates = true; SPDocumentLibrary library = (SPDocumentLibrary) web.Lists["Document Library"]; SPFolder rootFolder = library.RootFolder; rootFolder.Files.Add(string.Format("{0}/{1}", rootFolder.Url, fileName), stream, properties, true); stream.Close(); } } File.Delete(path); File.Delete(filename); } } } } } [/csharp] The two overridden methods, SaveChunkData and ProcessStream basically are there because the handler manages the upload stream in variable size chunks. That’s the behavior you want to override. First thing we are doing is hydrating the class level parameter field so that we can have it available later when we want to push it into a HashTable for metadata support on the SPFile object. Within the ProcessSteam method where the meat of the application resides, we are getting all the posted files in the control since it support a mutable amount, and moving them onto the server. Subsequently when the file is constructed on the server, we are going to make it a stream and throw it into the RootFolder of the SPDocumentLibrary. Simple in theory, a pain in the ass to figure out!

Share

Working Around BehaviorNotImplementedException When Instantiating Stubs

I got this exception in this morning, but thankfully there is a workaround. Do not despair!

Assume the following code. In the Concrete class we have a global level _internalString field and the virtual TestString property with returning the string literal “Pex Is Pissing Adam Off!” (as it was this morning!) in the Concrete base class constructor we are simply backing field settings, nothing amazing. Yeah the virtual member call in the concrete class constructor is bad since it will cause the most derived override to be called, even though the most derived constructor has not been fully run yet (may not be in a suitable state to have that method called). But bah! It’s part of the code and it’s not like that warning isn’t already thrown in nearly every SharePoint artifact.

Anyways, so in the rest of the code there is nothing amazing that is happening, as you can see the class containment hierarchy is using the Concrete base class for the child Superclass derived class, which is just overriding a simple property.

[csharp]
namespace AdamHatingPex
{
public class Concrete
{
private string _internalString;
public virtual string TestString{ get { return “Pex Is Pissing Adam Off!”; } }
public Concrete()
{
_internalString = TestString;
}
}
public class SuperClass : Concrete
{
public override string TestString
{
get { return “But I’ll Get Over It”; }
}
}
}
[/csharp]

Assuming this code, if you stub out the derived class you are going to get a Microsoft.Moles.Framework.Behaviors.BehaviorNotImplementedException error, as you can guess since it’s the only thing in the class it occurs on the overridden property. Quick hint, using CallBase with your call won’t help since base constructors should be run before derived ones (going back to what was discussed previously).
However, it is solvable by changing the behavior. I am not going to get into behaviors yet but you can adjust behavior pre-instantiation of the stub and let it Fall Through or Default Provide A Value. By default the instance will use the BehavedBehaviors.Current property, which will by default throw a BehavedBehaviors.NotImplemented. You can switch this around by setting the InstanceBehavior property on any stub instance. This is pretty easy, demonstrated below:

[csharp]
BehavedBehaviors.Current = BehavedBehaviors.Fallthrough;
var x = new Test.Moles.SSuperClass();
[/csharp]

When you are done with your stuff, you can return to default behavior:

[csharp]
BehavedBehaviors.Current = BehavedBehaviors.NotImplemented;
[/csharp]

If you are interested in this, you should look into the [assembly: PexChooseAsBehavedBehavior] attribute further as well.

Share

Custom Field Validation With SPFieldValidationException

There was a post in the newsgroups wondering how when constructing a custom field type how one might build a small generic validation function in order to verify the harvested user input. There is provided the SPField.GetValidatedString method, which is overridable, that can be used in order to achieve this since it will take the object input provided by the custom field type and provide a workable string representation.

An example of this method is this:

[csharp]

private readonly string m_strValidationException = “Hmmm, your string length wasen’t 10!”;

public override string GetValidatedString(object oValue)
{
if (oValue.ToString().Length != 10)

{
throw new SPFieldValidationException(m_strValidationException);
}

else

{
return oValue.ToString();
}

}

[/csharp]

Share