If you would like to discuss anything, please contact me via email: Howard.Bayliss@sequence.co.uk
Showing posts with label Code. Show all posts
Showing posts with label Code. Show all posts

Tuesday, July 3, 2012

SharePoint ItemAdded OpenBinary Zero

I created an ItemAdded event receiver for a document library. This sets metadata values for an uploaded file. Everything worked well for documents added using SharePoint's single, or multiple file upload forms.

However, when using Windows Explorer to upload a file to the library, I found that a call to the OpenBinary method for the file returned a zero length byte array.

In fact the wider issue was that the length of the file object was zero. Here's some skeleton code:

public override void ItemAdded(SPItemEventProperties properties)
{
  long length = properties.ListItem.File.Length; // zero
}

Other people that have found the same issue suggest using Thread.Sleep() in the event receiver to add an artifical delay.

However, I chose to work around this issue another way:

  1. In the ItemAdded receiver, do a check for a zero length file. Don't do any processing if it is zero. Obviously this won't work for a file uploaded using Windows Explorer. So I also needed to...
  2. Add an ItemUpdated event receiver (which just so happens to called when a user uploads a file using Windows Explorer.
  3. Both receivers call some shared code which updates the file's metadata. However, to avoid this being done multiple times, the shared code runs a check to verify that the metadata is blank before it is set.

Thursday, June 21, 2012

TagLib Stream Buffer

Do you want to load TagLib mp3 properties from a buffer or stream?

As I write this, the latest version in the TagLib Master branch on GitHub supports this.

Here's some C++ code that I've hacked together to do it - thanks to Lukáš Lalinský for his help.

Note that I'm not a C++ Developer ;)

I need to use C++ in this instance as I'm working on a project that cannot use the .Net framework - more in a later post.


// Load a sample track into a stream. std::ifstream is("Track1.mp3");

if (!is.bad())
{
// Calculate the size of the stream.
long l = is.tellg();
is.seekg (0, ios::end);
long m = is.tellg();
is.seekg (0, ios::beg);

long diff = (m - l);

// Save the stream to a buffer.
char* buffer = new char[diff];
is.read(buffer, diff);

ByteVector v(buffer, diff);
TagLib::IOStream* stream = new TagLib::ByteVectorStream(v);

TagLib::ID3v2::FrameFactory *frameFactory = TagLib::ID3v2::FrameFactory::instance();
TagLib::MPEG::File* mpegFile =new TagLib::MPEG::File (stream,frameFactory,true,TagLib::AudioProperties::Accurate);

TagLib::FileRef* f = new TagLib::FileRef(mpegFile);

cout << f->tag()->title() << endl;
cout << mpegFile->ID3v1Tag()->artist() << endl;

is.close();
}

Monday, June 18, 2012

SharePoint check user in group

I created some code to check if the current user belonged to a specific SharePoint group, similar to this:

SPUser user = SPContext.Current.Web.CurrentUser;

foreach (SPGroup group in user.Groups)
{
  if (group.Name == [INSERT YOUR GROUP NAME HERE)
  {
  }
}

This worked when users were directly added to the SharePoint group. However, it did not work if they were part of an AD group, which was then added to the SharePoint group.

To fix the issue, I used code similar to this:

SPGroupCollection groups = SPContext.Current.Site.RootWeb.Groups;
bool inGroup = groups[INSERT YOUR GROUP NAME HERE].ContainsCurrentUser;

Friday, June 8, 2012

WMPLib Artist Blank

I'm using SharePoint to host music tracks, which I play with Windows Media Player (WMP).

The player is controlled via WMPLib - Microsoft's WMP API.

I found that I can add SharePoint music files to a WMP playlist via the API, and that the tracks are played. However, when I wanted to display information about the current track (track name, artist, duration, etc), I found that the artist property was empty.

I discovered the fix for this was to URL encode the URL of the track (as it exists in SharePoint) before adding it to the playlist. This was because the track URLs contained spaces (and potentially other odd characters).

Wednesday, June 6, 2012

SharePoint mp3 Headers

Music tracks include header information such as artist, album, duration, etc

In SharePoint Foundation 2010, I wanted to extract this header data and use it to populate library metadata.

To do this, I first downloaded TagLib.

The clever bit about TagLib is that you can create your own SharePoint-specific implementation of the TagLib.File.IFileAbstraction interface. I then used this in a SharePoint event receiver to extract the header data when a user uploads a music file.

Note that this works for other types of music tracks, not just mp3.

The following code shows the code I created for a class that implements IFileAbstraction:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using Microsoft.SharePoint;

namespace Redweb.Redio.Audio
{
public class SharePointMediaFile : TagLib.File.IFileAbstraction
{
private string _fileUrl = String.Empty;

public string FileUrl
{
get
{
return _fileUrl;
}
set
{
_fileUrl = value;
}
}

void TagLib.File.IFileAbstraction.CloseStream(System.IO.Stream stream)
{
if (stream == null)
throw new ArgumentNullException("stream");
stream.Close();
}

string TagLib.File.IFileAbstraction.Name
{
get { return _fileUrl; }
}

System.IO.Stream TagLib.File.IFileAbstraction.ReadStream
{
get
{
MemoryStream outputSteam = null;
using (SPSite site = new SPSite(_fileUrl))
{
using (SPWeb web = site.OpenWeb())
{
if ((web == null) || (web.Exists == false))
{
throw new Exception("No web exists.");
}
SPFile file = web.GetFile(_fileUrl);
if ((file == null) || (file.Exists == false))
{
throw new Exception("No file exists.");
}
byte[] bytes = file.OpenBinary();
outputSteam = new MemoryStream(bytes);
}
}

return outputSteam;
}
}

System.IO.Stream TagLib.File.IFileAbstraction.WriteStream
{
get { throw new NotImplementedException(); }
}
}
}

Friday, September 9, 2011

SharePoint 0x810200c6 List Validation Failed

We use custom code to create a new Publishing Page:

SPWeb currentWeb = SPContext.Current.Web;

PublishingWeb publishingWeb = PublishingWeb.GetPublishingWeb(currentWeb);

PublishingPage page = publishingWeb.GetPublishingPages().Add(newPageName, newPageLayout);

This was failing with the error:

<nativehr>0x810200c6</nativehr><nativestack></nativestack>List data validation failed.

The error was caused because the Pages library had validation applied to it, which validated two date fields. The formula for the validation was:

=[Start Date and Time]<[End Date and Time]

When the page was created in code, the validation was fired, which failed because the two date fields are initially empty.

The solution to the problem was to change the validation formula to handle the initial values e.g.

=[Start Date and Time]<=[End Date and Time]

Friday, May 27, 2011

SharePoint PublishingPageImage src

For an article page, SharePoint stores the value of the Publishing Image field in a particular format. Here is an example:

<img alt="" src="/SiteCollectionImages/home.jpg" style="BORDER: 1px solid; ">

I wanted to extract the value of the "src" attribute. Here is the code I used:

string fullValue = String.Format("{0}", file.Item[FieldNames.PUBLISHING_PAGE_IMAGE]);


string src = String.Empty;

if (String.IsNullOrEmpty(fullValue) == false)
{
string xml= String.Format("{0}</img>", fullValue);
string src = XElement.Parse(xml).Attribute("src").Value;
}

Wednesday, May 25, 2011

SharePoint "Object reference not set to an instance of an object" Page Save

I'd created a custom page layout, applied it to a page and then tried to edit the page. Everything was fine until I clicked on one of the save options for the page. At this point the site blew-up with the following stack trace:

NullReferenceException: Object reference not set to an instance of an object.]
Microsoft.SharePoint.WebPartPages.WikiPageWebPartSaver.SaveWebPartsInRichText(SPWebPartManager wpmgr) +230

It took a bit of work but I tracked-down the cause. Essentially the Designer had given me some HTML mark-up which I'd pasted into the page layout. This mark-up contained an additional "form" element. When this was removed, the page-save worked correctly.

UPDATE

I don't remember the exact code, but it was something like the code below. The first form element is generated by SharePoint. The second form element was pasted-in by mistake.

<form id="aspnetForm" method="post" name="aspnetForm" action="default.aspx">

...

<form action="destination_url" method="get">
</form>


...

</form>

Thursday, February 10, 2011

LoadControl() Request failed

I was tearing my hair out over an issue with using the UserControl.LoadControl method from within SharePoint, which kept throwing this exception:

Exception Details: System.Security.SecurityException: Request failed.

We run our code using the least priviledge possible, which means creating a custom CAS policy. However, no amount of tweaking the CAS policy seem to fix the issue.

It seems this is a common issue (see this blog post). People who have commented on that blog suggest a number of solutions. However, I found the only solution was to:
  1. Call the LoadControl method from an assembly located in the GAC.
  2. Use the PermissionSet Assert method to obtain sufficient privileges
Here is an example of the code:

public static Control LoadControl(UserControl parentControl, string controlName)
{
Control control = null;

System.Security.PermissionSet permissionSet = new System.Security.PermissionSet(System.Security.Permissions.PermissionState.Unrestricted);
permissionSet.Assert();

try
{
string controlPath = String.Format("~/_controltemplates/{0}", controlName);
control = parentControl.LoadControl(controlPath);
}
finally
{
System.Security.CodeAccessPermission.RevertAssert();
}

return control;
}


Friday, February 4, 2011

Content controls have to be top-level controls in a content page

I made a change to a page layout to include a CSS file and started seeing an error message which said "Content controls have to be top-level controls in a content page".

This is how the markup looked; can you see the error?

<asp:Content contentplaceholderid="PlaceHolderAdditionalPageHead" runat="server">

<ContentTemplate>

<SharePointWebControls:CssRegistration name="/_layouts/1033/styles/Themable/search.css" runat="server" >

</ContentTemplate>
</asp:Content>


Did you spot the error?

The CssRegistration declaration was missing its closing tag. Once fixed, the page worked correctly.

Friday, January 28, 2011

SharePoint Relative Url

We have a number of page layouts which allow the user to enter a URL. SharePoint stores the value as an absolute value, along with a description.

(BTW, you can use the SPFieldUrlValue class to extract the URL and description).

Anyhow, I wanted to generate a relative path from stored absolute value. I used the following code to do it.

Maybe you know a better way.....?


SPFieldUrlValue spFieldUrlValue = new SPFieldUrlValue(fieldValue);

string targetUrl = spFieldUrlValue.Url;

using(SPSite site = new SPSite(targetUrl))
{
  return targetUrl.Replace(site.Url, String.Empty);
}

Tuesday, January 25, 2011

2 Level Navigation

I wanted to create a mini-sitemap which had the following criteria:
  1. Only show sites
  2. Only show 2 levels of navigation

This article gave me an insight and I was then able to come up with this as the solution:

<SharePoint:AspMenu ID="FooterGlobalNav" runat="server" DataSourceID="GlobalNavDataSourceFooter"
Orientation="Vertical" StaticDisplayLevels="2" MaximumDynamicDisplayLevels="0" UseSimpleRendering="true" />

<publishingnavigation:portalsitemapdatasource id="GlobalNavDataSourceFooter" runat="server"
sitemapprovider="CombinedNavSiteMapProvider" showstartingnode="false" startfromcurrentnode="false"
startingnodeoffset="0" trimnoncurrenttypes="Heading" treatstartingnodeascurrent="true" />





Friday, January 21, 2011

The resource cannot be found - CONTROLTEMPLATES

While working in our team environment, a colleague stated that the SharePoint site had gone done. A screen shot is shown below:






The error only occurred on the root site.


It turns out this was because the home page of the root site (default.aspx) uses a page layout which references some custom user controls in the CONTROLTEMPLATES folder and my colleague's VM did not have these.