By default, SharePoint Document Libraries enable a person to e-mail a link to a document in a document library. This makes sense, because from a security perspective, you want to make sure that whomever clicks on the link actually has access to view the document. SharePoint will take care of authentication, ensuring that person is allowed to view the document. However, someone on Microsoft's SharePoint discussion board presented an interesting scenario: their users don't have direct access to their SharePoint site over their network, so they wouldn't be able to open a link from an e-mail they sent to themselves, to get to that document, because they wouldn't have access to the SharePoint server URL. Intead, they wanted to be able to actually e-mail the document itself from the document library to themselves, so they could read it apart from being on the SharePoint site. I have written the code neceesary to install a Feature that will do just that. (Please note, I have not added extensive error handling, validation, multi-lingual resources, web.config values, etc., for the sake of brevity.)

The first step is to create a new project in Visual Studio. (I have used C# for this example.) I have named the project MailDocument. I will create a folder structure that contains a DeploymentFiles folder and a TEMPLATE folder. Inside the TEMPLATE folder I have two more folders: a FEATURES folder and a LAYOUTS folder, (mirroring the structure of the folders in the C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\TEMPLATES folder). Create a folder called MailDocument inside the FEATURES folder. Inside your project, add a reference to Microsoft.SharePoint. (You can browse to this dll, located in the ISAPI folder of the "12" directory.)

First, I want to create an ASPX page that will contain just a few controls: a TextBox for the user to enter the e-mail address they want to send the e-mail to, a Button control they can click to send the e-mail, and a Label control that will display a nice message to the user when it's finished sending, as well as Panel control that will hide the TextBox and the Button when the e-mail has been sent. Create a page called MailDocument.aspx inside the LAYOUTS folder.

This page is going to be an application page in SharePoint, (i.e. will live in the LAYOUTS directory in the C:\Program Files\Common Files\Microsoft Shared\web server extensions\12 folder), so it will use the application.master Master Page. I will fill two content placeholders on the page: one for the page title, and another for the page content.

<asp:Content ID="Title" runat="server" ContentPlaceHolderID="PlaceHolderPageTitleInTitleArea">
E-mail a Document
</asp:Content>

<asp:Content ID="ContentMain" runat="server" ContentPlaceHolderID="PlaceHolderMain">
<asp:Panel ID="MailDocumentPanel" runat="server">
Please enter the e-mail address you would like to send the document to:<br />
<asp:TextBox ID="EmailAddress" runat="server" Width="300"/>
<asp:Button ID="GoButton" runat="server" Text="Go" OnClick="GoButton_Click" />
</asp:Panel>
<asp:Label ID="SuccessMessage" runat="server"/>
</asp:Content>

The next step is to create a code-behind page for this page. Create a file called MailDocument.aspx.cs inside the FEATURES folder. Add the following "using" directives and add a namespace and class name as follows:

using System;
using System.IO;
using System.Net.Mail;
using System.Net.Mime;
using System.Web.UI;
using System.Web.UI.WebControls;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Administration;
using Microsoft.SharePoint.WebControls;

namespace MailDocument
{
    public class MailDocument : LayoutsPageBase
    {
    }
}

Now that we have a code-behind page, let's wire up the ASPX page to it. SharePoint can't reference code-behind pages the way a normal ASP.NET web applicatin does. In order for SharePoint to reference that code-behind page it will reference the class inside a compiled dll, and in order for the dll to be used by SharePoint, in needs to be trusted. One way to do that is to deploy it to the GAC. In order for a dll to be in the GAC, it needs to get signed. So the next step you'll want to take is to right click on the project properties, click on the "Signing" tab, and create a new key. After that, we'll want to extract the public key. (For a quick way to do this in Visual Studio, visit Andrew Connell's blog entry on the topic.) Now that we've done all that, we can wire the two pages together by adding the following line to the top of the ASPX page:

<%@ Page Language="C#" MasterPageFile="~/_layouts/application.master" Inherits="MailDocument.MailDocument, MailDocument, Version=1.0.0.0, Culture=neutral, PublicKeyToken=[Your token goes here]" %>

OK, now that we have front-end page built, let's write the code that will get the document and send the e-mail.

The page itself is going to live in the LAYOUTS directory, which all lists will reference. That being the case, we need to know which list, and which list item, we're going to be using. To do this, we'll pass the List GUID and the Item ID as parameters in the query string to the page. Once we've figured out which list item we're dealing with, we can grab the document file itself. The code looks as follows:

protected void GoButton_Click(object sender, EventArgs e)
{
    SPList list = SPContext.Current.Web.Lists[new Guid(Request.QueryString["ListId"])];
    SPDocumentLibrary docLib = (SPDocumentLibrary)list;
    int id = System.Convert.ToInt32(Request.QueryString["ItemId"]);
    SPListItem listItem = docLib.Items.GetItemById(id);
    if (listItem.File != null)
    {
        ContentPlaceHolder mainContentPlaceholder = this.Master.FindControl("PlaceHolderMain") as ContentPlaceHolder;
        string toEmailAddress = ((TextBox)mainContentPlaceholder.FindControl("EmailAddress")).Text.Trim();
        SendEmail(toEmailAddress, listItem.Title, listItem.File, list);
    }
}

The next thing we need to do is actually send the e-mail. We'll want to use the e-mail server that was configured in the SharePoint Central Administration. We'll find the name of the SMTP server, create an e-mail message, attach the file to the e-mail, and send it. Here's the method:

private void SendEmail(string toAddress, string subject, SPFile file, SPList list)
{
    SPWebApplication webApp = SPContext.Current.Site.WebApplication;
    SPOutboundMailServiceInstance mailServer = webApp.OutboundMailServiceInstance;
    string smtpServer = mailServer.Server.Address;

    string listUrl = SPContext.Current.Web.Url + list.DefaultViewUrl;

    MailMessage message = new MailMessage("administrator@linwareinc.com", toAddress);
    message.Subject = subject;
    message.IsBodyHtml = true;
    message.Body = "This file was sent from the following SharePoint list: " +
    "<a href=\"" + listUrl + "\">" + listUrl + "</a>.";

    Stream stream = file.OpenBinaryStream();
    //You can add code to figure out which MIME type to use based on the type of attachment.
    Attachment attachment = new Attachment(stream, @"application/msword");
    attachment.Name = file.Name;
    message.Attachments.Add(attachment);

    try
    {
        SmtpClient client = new SmtpClient(smtpServer);
        client.Send(message);
    }
    catch (Exception e)
    {
        throw e;
    }
    finally
    {
        stream.Close();
    }

    ContentPlaceHolder mainContentPlaceholder = this.Master.FindControl("PlaceHolderMain") as ContentPlaceHolder;
    ((Panel)mainContentPlaceholder.FindControl("MailDocumentPanel")).Visible = false;
    ((Label)mainContentPlaceholder.FindControl("SuccessMessage")).Text = "Your message has been sent.";
}

Now that we have the ASPX page put together, we need to start building the Solution package that contains the feature. The first step is to create a manifest file. Create a new XML file in the root of your project and call it manifest.xml. Add the following XML to it:

<Solution xmlns="http://schemas.microsoft.com/sharepoint/"
    SolutionId="72CFD5DA-10F0-499b-B4E9-863B6DB87717"
    ResetWebServer="TRUE">
    <FeatureManifests>
        <FeatureManifest Location="MailDocument\feature.xml"/>
    </FeatureManifests>
    <TemplateFiles>
        <TemplateFile Location="LAYOUTS\MailDocument.aspx"/>
    </TemplateFiles>
    <Assemblies>
        <Assembly DeploymentTarget="GlobalAssemblyCache" Location="MailDocument.dll" />
    </Assemblies>
</Solution>

This code is saying three things about the solution: 1. The feature manifest can be found inside the MailDocument folder in the FEATURES folder. 2. The MailDocument.aspx page is in the LAYOUTS folder. 3. Deploy the dll to the GAC.

The next step is to create the feature manifest. To do this, create an XML file called feature.xml inside your TEMPLATE/FEATURES/MailDocument folder in your project. Add the following XML to the file:

<Feature xmlns="http://schemas.microsoft.com/sharepoint/"
    Id="5785441E-DD86-4df5-A867-D7AC37712A27"
    Title="Mail Document"
    Scope="Web" Hidden="false" Version="1.0.0.0"
    Description="Allows you to e-mail documents from your Document Libraries">
    <ElementManifests>
        <ElementManifest Location="elements.xml"/>
    </ElementManifests>
</Feature>

Notice that the Feature's Scope is "Web". That means this feature can be deployed for a particular subsite, meaning all the document libraries in a particular site will have this new functionality when the feature is deployed. The ElementManifest node is telling the feature where to find the element manifest file. So, last but not least, let's add the elements.xml file. To do this, create a file called elements.xml in the same directory you added the feature.xml file.

Although the elements manifest can be used to deploy files to a particular SharePoint library, we've already used the Manifest to instruct the solution package to deploy the ASPX page to the file system, and the dll to the GAC, so we don't need to deploy any more files. However, we do want our users to be able to get to the page they created. We do this by adding a "custom action", which in this case, will add an item to the context menu when a user right clicks on a list item in the document library. Add the following XML to the elements.xml file:

<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
    <CustomAction
        Id="MailDocument.Link"
        RegistrationType="List"
        RegistrationId="101"
        Location="EditControlBlock"
        Sequence="350"
        Title="E-mail Document"
        ImageUrl="~site/_layouts/IMAGES/EML16.GIF"
        Description="Allows a user to e-mail a document in a document library to themselves." >
        <UrlAction Url="/_layouts/MailDocument.aspx?ItemId={ItemId}&ListId={ListId}"/>
    </CustomAction>
</Elements>

Let's look at the XML. The RegistrationType attribute is telling us to add this action to a particular kind of list. The RegistrationId attribute is the Id for a Document library. Those two properties tell the action to be available on all lists that are Document Libraries. The Location attribute of "EditControlBlock" indicates the context menu that appears when the user right clicks an item in the library. The Title is what will appear in the context menu, and the ImageUrl indicates the location of the icon that will appear next to the item in the context menu (if you so choose to add one. In our case, we're using an image from the default SharePoint installation.) The UrlAction node indicates the URL the user should be taken to when the click the item in the context menu. Notice that the item id and list id can be passed using {ItemId} and {ListId} in the query string.

You've done it! You've created all the files necessary for your feature. The last thing we need to do is to compile the solution. In order to this, we'll need to take a few steps.

1.) Download the MAKECAB.EXE tool from Microsoft and install it in your WINDOWS\system32 directory. You can download the SDK at http://support.microsoft.com/default.aspx/kb/310618.
2.) Create a file inside the DeploymentFiles folder of your project and call it BuildSharePointPackage.Targets. Copy the following XML into that file:

<Project DefaultTargets="BuildSharePointPackage" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
    <PropertyGroup>
        <MakeCabPath>"C:\Windows\System32\MAKECAB.EXE"</MakeCabPath>
    </PropertyGroup>
    <Target Name="BuildSharePointPackage">
        <Exec Command="$(MakeCabPath) /F DeploymentFiles\BuildSharePointPackage.ddf /D CabinetNameTemplate=$(MSBuildProjectName).wsp /D DiskDirectory1=wsp\$(Configuration) "/>
        <Exec Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU'"
Command="$(MakeCabPath) /F DeploymentFiles\BuildSharePointPackage.ddf /D CabinetNameTemplate=$(MSBuildProjectName).cab /D DiskDirectory1=wsp\$(Configuration)"/>
    </Target>
</Project>

Now we need to create a DDF file, which tells MAKECAB.EXE how to package the files we created. Create a new file called BuildSharePointPackage.ddf inside the DeploymentFiles folder. Add the following code to it:

.OPTION Explicit
.Set DiskDirectoryTemplate=CDROM
.Set CompressionType=MSZIP
.Set UniqueFiles=Off
.Set Cabinet=On

manifest.xml
bin\debug\MailDocument.dll MailDocument.dll
TEMPLATE\FEATURES\MailDocument\feature.xml MailDocument\feature.xml
TEMPLATE\FEATURES\MailDocument\elements.xml MailDocument\elements.xml
TEMPLATE\LAYOUTS\MailDocument.aspx LAYOUTS\MailDocument.aspx

The very last thing we need to do is to tell Visual Studio to create the CAB and WSP files (the WSP being what will ultimately be deployed to SharePoint) after the project compiles. To do this, you'll need to right click on the project and say "Unload project". (If you don't see this option, you'll need to change your Visual Studio settings so that you can see the VS Solution file.) After you have unloaded the project, right click the project and say "Edit MailDocument.csproj". This will pull up the XML file. Go to the very bottom of the file and look for the node that says "Import Project". Right after that node, and a second node that looks like this:

<Import Project="DeploymentFiles\BuildSharePointPackage.targets" />

At the very bottom of the page you'll see some node commented out. Replace the "AfterBuild" target with the following code (and uncomment it out):

<Target Name="AfterBuild">
    <CallTarget Targets="BuildSharePointPackage" />
</Target>

When you're done, your solution should look like this:

Now reload your project in Visual Studio and compile it. And voila! You should not only get your dll in the Debug directory, but you'll get a folder called wsp in your project root. (It's a hidden folder, so you'll have to toggle the Show All Files icon in your Solution Explorer pane.)

You can download a zip file of the code here.

After you have deployed the solution and activated the feature, let's take a look at the feature in action. Below we have a document library with a Word document in it. Below, you can see the context menu:

MailDocument context menu

When the user clicks on the link, they go to the .aspx page. Notice the URL in the browser, and how it has the list's guid and the item's id:

And here's the e-mail the person gets, including the attachment:

Parts of this code are based on code from a class I took last week by Andrew Connell, SharePoint MVP. You can view his blog at http://www.andrewconnell.com/blog/. The class was taken through the Ted Pattison Group. I highly recomment Andrew's course.

If you'd like to read more about how to do the kinds of things I've mentioned in this article, visit the following How To's on Microsoft MSDN site:
How to: Add Actions to the User Interface
Creating a Feature for an Entry Control Block Item in Windows SharePoint Services 3.0
Creating a Solution Package in Windows SharePoint Services 3.0

35 comments

# Sharath on 10/06/07 at 23:57
Good post on explaning the whole process end-to-end



# Muhanad Omar Email on 10/09/07 at 06:44
What a great post!

Thanks for going through the entire process step-by-step. I'll definitely be recommending this article to other SP Developers!
# mike Email on 10/16/07 at 08:57
a bit useful, but very unhelpful around the fact that it will also be applied to folders and the user will experience a crash if this is attempted.

it would be helpful to provide information about this
# Tom Vervoort Email on 10/23/07 at 11:00
I think this code is posted without trying first, because the textbox and the label in the aspx page don't have a closing tag. ;-)

But anyway... I'm not here to complain off course. I have some improvements I wanted to share.

In elements.xml, it's better to change the ampersand (&) in the urlaction to & for not getting an error when installing the feature.

Also in elements.xml, if you change registrationtype and registrationid like this:

RegistrationType="ContentType"
RegistrationId="0x0101"

... you would link the action to the content type 'document' and all its children. This way, you won't have the action on folders anymore.
# Jayanth Nadig on 10/24/07 at 07:17


Hi Becky,



I saw Ur article it was really a nice. Right now I have different requirement.



I want to add a menu item in sharepoint 2007. If you open the sharepoint home page you can see the webparts available there. If you click document library link it will take you to the respective page. There you can see list of documents under related documents. If you click any document it will show a combobox with options as view properties, edit properties, manage permissions, edit in Microsoft office word, delete, sent to option as other location, e-mail a link, create document workspace, and download a copy. Here in this send to menu I want to add another item called Fax. I want to do this in C# 2.0. Should I create web application or website for this? How to do this? Please help me.

Expecting your reply at the earliest
# Becky [Member] Email on 10/24/07 at 09:38
My apologies. My blog software doesn't like displaying XML markup, so I have to translate various tags into their escape characters so that they render correctly in the browser. I must have accidentally deleted some important characters during that process! If you donwload the sample code in the project, it should have the closing tags, etc.

Thanks also for sharing how to register just for certain content types.
# Ali Email on 10/29/07 at 07:54
Thanks for doing this Becky. The part that describes the feature deployment process (makecab, ddf file) etc. is very helpful. This feature deployment comes in extremely handy when dealing with many features in a SharePoint project.
# Jay K Email on 10/29/07 at 13:45
Hi Becky,

Thanks for taking time to publish such a wonderful article. It was of great help to me. Appreciate it.
# Rob Pearmain Email on 11/14/07 at 07:34
Hi Becky,

This is a great post, thanks.

Is it possible to allow Security Trimming on a context menu item, so that Readers cannot email but everyone else can. Better still, can you security trim it so the option does not appear if you are a reader, but will if an admin.

Many thanks
# Ame Email on 11/27/07 at 00:07
Hi,
Its a great post.Thanks.
I have one more problem. I added a context menu as shown above and then added one more list template in the same feature.xml. Now i am not able to see my context menu on the list. but i am getting my list template in the Lists. I want to have both the custom menu as well as the list template by using same feature.xml. Is it possible? Can anybody help?
# Raja Email on 11/28/07 at 13:28
Hi,
Nice article. I had to do something similar..your post was a very good starting point for me. I used JavaScript to mail the attachment...you can find it here:
https://blogs.pointbridge.com/Blogs/ayyapusetty_raja/Lists/Posts/Post.aspx?ID=11

As I mentioned, in this approach, you should be ready to lower your IE security settings..

Thanks again..
# Racheal Email on 11/28/07 at 18:18
This is a great article and of good use. Do you or anyone else know of any code
that can check-out and download multiple files from SharePoint. It is the
opposite from multiple file upload.

Thanks
# JohnG Email on 12/04/07 at 09:56
If I want to have an script send a Sharepoint document as an attachment on a regular basis, using Windows Task Scheduler, could that be done?
# Himadrish Laha Email on 12/27/07 at 01:53
Hi Becky,

This is very nice one and helpful for thinking more innovative ideas.

Thanks for sharing the information.

I really appreciate your hard work.

Thanks,
Himadrish
# spyral Email on 03/26/08 at 05:20
hmmm, this post is exactly what I needed, though after I deployed the solution I could not see it as a feature to activate for the web application and also the option does not appear in the drop down on the doc library in my MOSS 2007 instance. Any suggestions?
# Becky [Member] Email on 03/26/08 at 08:54
The feature's scope is "Site", which means the feature will show up under "Site Collection Features", not "Site Features". Also, the option will not be a drop down menu in the document library, so much as an item in the context menu when you right click on a document in the library.
# spyral Email on 03/26/08 at 10:47
Becky, thanks. I was able to get it working. Great post....you solved the issue I have been fighting with for about a week now :)
# Daniel Email on 03/31/08 at 22:44
Hi, this is exactly what i need, but i keep getting this error.

System.NullReferenceException: Object reference not set to an instance of an object. at MailDocument.MailDocument.SendEmail(String toAddress, String subject, SPFile file, SPList list) at MailDocument.MailDocument.GoButton_Click(Object sender, EventArgs e)
# Becky [Member] Email on 04/01/08 at 11:05
It sounds like you have an object whose value is Null, that's throwing the error. You can debug the SharePoint feature by setting a break point in your code in Visual Studio, then going to the Debug menu, and selecting the "Attach to process" menu item. Attach to the w3wp.exe process that your SharePoint site is using. Next time you activate the feature, you should be able to break into your code and see which value isn't getting set.
# Daniel Email on 04/01/08 at 17:51
Hi Becky, Thanks for your help. i got it working.
# Chad Clarke Email on 04/04/08 at 15:13
By way of introduction, I'd like to let you know that I'm very impressed with this article. I haven't been posting much to my blog or been in the online community much lately, but am looking to get back into it. I would really like to be associated with MCS someday. Currently I am a Senior Consultant with Sogeti USA. Thank you so much for this article.
# Becky [Member] Email on 04/07/08 at 11:06
Thanks, Chad!
# manikandan Email on 04/09/08 at 01:56
It is working fine with the document library. but I want to send a e Mail from the Form Library which are having Infopath forms. Is it possible to activate the E-Mail a form option in the Form library with Infopath forms.
# Becky [Member] Email on 04/10/08 at 10:16
In response to Manikandan, if you look at the CustomAction element, there's an attribute called "RegistrationId". This number points to the kind of list you're referencing. Try changing the RegistrationId from "101" to "115". (If you're pointing to a custom library, you would change it to the ListTemplate id of your custom list.)

Take a look at the "Type" element to see the Id's for different list types: http://msdn2.microsoft.com/en-us/library/ms462947.aspx
# guigoss Email on 04/15/08 at 09:47
Hi,
Is it possible to send an e-mail to a contact list or a group?
Thanks a lot for this blog.
# Manikandan Email on 04/23/08 at 23:50
Hi Becky !
thanks for ur reply.After I had changed Registrationid="115" its working fine.Now am able to send infopath form through email.only problem on this is,its not working for internet ..(ie,within the domain its ok..what can i do for the different domain usage? ).And one more thing the aspx file in read only format..how do i make a change in that?
am waiting for ur reply..
# Mithri Email on 04/28/08 at 12:33
Hey becky
Very Nice Post.

Thanks
# hopsuisse Email on 05/19/08 at 08:51
Hi, this is just wonderful ! I'm having an issue though with the following line : SPList list = SPContext.Current.Web.Lists[new Guid(Request.QueryString["ListId"])]; I come to an error saying the list doesn't exist anymore. Seems to be some persistance issue or I've missed something. Any ideas? Thx.
# Becky [Member] Email on 05/19/08 at 16:15
hosuisse,
Sounds like you're getting a null value somewhere. You can actually step through your code in Visual Studio like you would other code. See my response from Daniel above to see how to do it. That will most likely tell you what value is missing.
# hopsuisse Email on 05/20/08 at 06:25
Thx Becky.
I was having trouble with site and subsites. Instead of this :
------------------
protected void GoButton_Click(object sender, EventArgs e)
{
SPList list = SPContext.Current.Web.Lists[new Guid(Request.QueryString["ListId"])];
------------------

I work with this :

protected void GoButton_Click(object sender, EventArgs e)
{
//Get the current site or subsite
SPSite site = new SPSite(Request.QueryString["SiteUrl"]);
SPWeb web = site.OpenWeb();
//
SPList list = web.Lists[new Guid(Request.QueryString["ListId"])];

Of course you have to add the SiteUrl paramater in the UrlAction tag of elements.xml
# Gilly Email on 05/25/08 at 03:21
It sounds really simple, I just havea funny basic question because something here doesn't seem to work for me...

What kind of VS project did you choose when you started this project???
I just didn't seem to get the .snk file you see in the picture...
# Becky [Member] Email on 05/27/08 at 14:56
I just used a regular C# project for the template. (I wrote the article before the days of some of these nice tools like STSDEV released by Ted Pattison and Andrew Connell on Codeplex: http://www.codeplex.com/stsdev) You can add a strong key to a project by right clicking on the project in Visual Studio, and selecting "Properties", then clicking on the "Signing" tab. Select the "Sign the Assembly" checkbox, and then you can add a key or create a new one.
# Vidya Email on 06/10/08 at 05:17
In Microsoft Office Sharepoint Server(MOSS)

I wanna customize the context menu for document library items. If i click on
the newly created context menu option that particular folder's content or
document should move to a local folder like to C or D drive. I have done the customizing part of the context menu using ows.js file and using content editor webpart
till now but now i want to add the functionality to that option.
# Prashant Email on 06/20/08 at 11:58
Hi Becky,
Excellent article, explained in detail.

I have a question similar to the previous users post. If i want to develop a custom context right click menu which would download the file to the local desktops hard drive is there a way to do it.

Thanks.
# Becky [Member] Email on 06/24/08 at 11:15
Prashant,
A user could just right click on the document and say "Save as" and save it to their hard drive, without having to have a context menu. However, if you did want to create a context menu, at the end of the day, the page you navigate to in this article is really just a regular ASPX page. You can take this example, but replace the code in the .aspx page with your code that would stream the document to the user's desktop instead of attaching it to an e-nmail (using the System.IO namespace).
Best wishes,
Becky

This post has 5 feedbacks awaiting moderation...

Leave a comment


Your email address will not be revealed on this site.

Your URL will be displayed.
(Line breaks become <br />)
(Name, email & website)
(Allow users to contact you through a message form (your email will not be revealed.)
September 2010
Sun Mon Tue Wed Thu Fri Sat
 << <   > >>
      1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30    

Search

powered by b2evolution