Tuesday, February 19, 2013

Calling Winform function from SharePoint page.

Consider a scenario like passing a value from SharePoint page(ex. document library URL) or something to a windows form application function and execute a code based on the value passed. Web page and windows form may seem like two completely non-linked entities. But there is a way to establish connection to and fro.
 
Below are the steps to call a windows form function from a SharePoint page. 

1. Create a Win formand add a webbrowser control. Give the SharePoint site URL as the webbrowser control URL property.

2. Inside the Win formadd the ComVisibleAttribute to the class level.

 [System.Runtime.InteropServices.ComVisibleAttribute(true)]
    public partial class Save : System.Windows.Forms.Form
    {}

3. Add the ObjectForScripting property. This will act as a interface between the webpage and Win form

        [BrowsableAttribute(false)]
        public Object ObjectForScripting { get; set; }


4.  Add a function in Win form with a parameter just for test purpose. We will call this function from the SharePoint page.


        public void Test(String docurl)
        {
          MessageBox.Show(docurl);
        }

5. Now in the SharePoint page in the aspx page(if its an application page) call the Winform function by calling the below javascript code.

window.external.Test('url');

This will call the parent WinForm function and popup the message box.

6. To call this in .cs file code register with client script like below

this.Page.ClientScript.RegisterStartupScript(this.GetType(), "opendoc", "window.external.Test('" + locationpath + "/" + docname.Text);", true);


Tuesday, January 8, 2013

File not found exception in Office 2010 addin / ClientContext Code not executing

When we add any reference dlls(say in the example Microsoft.SharePoint.Client dll)  in Office 2010 addin and package it using Click once and install it in client machine it gives an


System.IO.FileNotFoundException: Could not load file or assembly 'Microsoft.SharePoint.Client, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c' or one of its dependencies. The system cannot find the file specified.

File name: 'Microsoft.SharePoint.Client, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c'

   at WordAddIn1.Form1.UploadDocument(Byte[] docbytes)

   at WordAddIn1.Form1.button1_Click(Object sender, EventArgs e)



Assembly manager loaded from:  C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\mscorwks.dll

Running under executable  C:\PROGRA~1\MICROS~2\Office14\WINWORD.EXE

Fix:


Add the reference dlls in the root of the project like below and publish again. This took me a day to figure out :(.

There were no good article to debug and fix this issue in msdn forums so thought this might be useful to someone

Upload a word document from Office 2010 plugin to SharePoint Doc library

When we create a Office 2010 addin  say for word and try to access SharePoint resources from it we know pretty much better option is 'Client Object model'.

1. Create a new Word 2010 addin and a button called 'Save'
2. Include a win form and add a button called "Upload doc"
3. Call this form on click of Save button.

4 Below code is to upload a current open word document in Office 2010 to a SharePoint site without saving locally on click of Upload Doc button.



 public void UploadDocument(byte[] docbytes)
        {

            ClientContext clientContext = new ClientContext("http://sharepointsiteurl/");

            List documentsList = clientContext.Web.Lists.GetByTitle("Events");

            var fileCreationInformation = new FileCreationInformation();

            fileCreationInformation.Content = docbytes;

            fileCreationInformation.Overwrite = true;

            fileCreationInformation.Url = "http://sharepointdoclibrarylocation/la.doc";
            Microsoft.SharePoint.Client.File uploadFile = documentsList.RootFolder.Files.Add(fileCreationInformation);
            uploadFile.ListItemAllFields.Update();
            clientContext.ExecuteQuery();
}

and on button click below code grabs the current document content as byte array and calls the above function to upload it to SharePoint

 private void button1_Click(object sender, EventArgs e)
        {

            Microsoft.Office.Interop.Word.Document doc = Globals.ThisAddIn.Application.ActiveDocument;

            doc.ActiveWindow.Selection.WholeStory();
            doc.ActiveWindow.Selection.Copy();

            IDataObject data = Clipboard.GetDataObject();

            byte[] bytes = System.Text.Encoding.UTF8.GetBytes(data.GetData(DataFormats.Html).ToString());


            MessageBox.Show("Before Upload");
            try
            {
                UploadDocument(bytes);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString());

            }
            MessageBox.Show("Uploaded to SharePoint Successfully");

        }

The difference from other solutions  is here we dont save the document anywhere locally instead directly grabbing the content as byte array and uploading it to SharePoint doc library which will improve the performance drastically.

Friday, June 29, 2012

Upload Document to a doc set using Managed Client Object Model

Below function uploads any file to a docset using Client object modeling.


 public void UploadDocument(string siteurl, string listname, string docsetname, string filename)
        {
           
            using (ClientContext clientContext = new ClientContext(siteurl))
            {
                //Get Document List
                List documentsList = clientContext.Web.Lists.GetByTitle(HttpUtility.UrlDecode(listname));


                var fileCreationInformation = new FileCreationInformation();
                //Assign to content byte[] i.e. documentStream


                fileCreationInformation.Content = System.IO.File.ReadAllBytes( filename )); ;
                //Allow owerwrite of document


                fileCreationInformation.Overwrite = true;
                //Upload URL


                fileCreationInformation.Url = siteurl + "/" + listname + "/" + docsetname + "/" +  filename";
                Microsoft.SharePoint.Client.File uploadFile = documentsList.RootFolder.Files.Add(fileCreationInformation);


                uploadFile.ListItemAllFields.Update();
                clientContext.ExecuteQuery();


            }
        }

Call the above function as below

UploadDocument("https://testsite", "test Library", "My Doc Set", "C:\test.ppt");

if you look at the code above we are reading the bytes from the file(which is in the client machine) and uploading it using the Microsoft.SharePoint.Client.File class. 


Get doc set id by doc set name

If you want to upload any file programmatically to a doc set you need the id of that doc set. In my last post I showed how to create a new doc set using Managed Client Object model. Here I will post the code snippet how to get a doc set id.

We need to CAML Query to get the doc set id by its name. Below function returns the docset id when the docset name is passed


   public int GetDocSetID(string siteurl,string docsetname,string listname)
        {
            ClientContext context = new ClientContext(siteurl);
            context.Credentials = System.Net.CredentialCache.DefaultNetworkCredentials;
            Web site = context.Web;
            var list = context.Web.Lists.GetByTitle(HttpUtility.UrlDecode(listname));
            CamlQuery query = new CamlQuery();
            query.ViewXml = "@<Where><Eq><FieldRef Name='FileLeafRef' /><Value Type='File'>" + docsetname + "</Value></Eq></Where>";
            var items = list.GetItems(query);
            context.Load(list);
            context.Load(items);
            context.ExecuteQuery();
            return items[0].Id;


        }


Remember doc set is just a list item and there cant be more than one docset with the same name. So the CAML query always returns one item.

Add a Document Set using Managed Client Object Model

I love Client Object Modeling. SharePoint 2010 has made easy how to talk to the SharePoint objects from Client machines.

Adding a docset pro grammatically using Object modeling should be easy. I was trying to add a new doc set using Managed Client Object Model. Remember docset is nothing but a item in a list. And document libraries are again nothing but just a list with different look and feel. Below function creates a new docset with the defined content type.



public int AddDocSet(string siteUrl, string listName, string docSetContentTypeName, string newDocSetName)
        {
            ClientContext clientContext = new ClientContext(siteUrl);
            clientContext.Credentials = System.Net.CredentialCache.DefaultNetworkCredentials;
            Web web = clientContext.Web;
            List list = clientContext.Web.Lists.GetByTitle(listName);


            clientContext.Load(clientContext.Site);


            ContentTypeCollection listContentTypes = list.ContentTypes;
            clientContext.Load(listContentTypes, types => types.Include
                                              (type => type.Id, type => type.Name,
                                              type => type.Parent));


            var result = clientContext.LoadQuery(listContentTypes.Where(c => c.Name == docSetContentTypeName));


            clientContext.ExecuteQuery();


            ContentType targetDocumentSetContentType = result.FirstOrDefault();


            ListItemCreationInformation newItemInfo = new ListItemCreationInformation();
            newItemInfo.UnderlyingObjectType = FileSystemObjectType.Folder;
            newItemInfo.LeafName = newDocSetName;
            ListItem newListItem = list.AddItem(newItemInfo);


            newListItem["ContentTypeId"] = targetDocumentSetContentType.Id.ToString();
            newListItem["Title"] = newDocSetName;
            newListItem.Update();


            clientContext.Load(list);
            clientContext.ExecuteQuery();
            return newListItem.Id;


        }


Call this function by passing the appropriate site url, list name , docsetcontent type, and the doc set name.

Copy Slides to Presentation outside of Slide Library Context

Slide Library is a powerful document management feature of SharePoint 2010 but still very much underutilized. You can publish slides from you local machine to this library directly. Slides are stored as .pptx files. To do this. right click the ppt and click "Publish slides" and enter the slide library URL in the address box. This is fairly easy to do and very useful for someone who wants to publish only parts of their ppt file.

The another important feature of Slide Library is the "Copy Slides to Presentation". You can select the slides from the library and on click of the "Copy Slides to Presentation" button it will create a new presentation with the selected slides or it will add to a existing presentation based on your selection.  

This is a very cool feature that I wanted to use in my custom webparts and in some other pages. "Copy Slides to Presentation" button is a activex control embedded in the slides library. I was digging into how this is working. I was looking at the sharepoint js files and found that 1033/SLDLIB.js is the file that is called when you click the button. If you look at the file they get the pptx file URLs and form a array and pass it to the active control. I tried to replicate the same in my custom webpart and it worked amazingly!

1. First add the below reference to the js file 
  <script type="text/javascript" src="/_layouts/1033/SLDLIB.js"></script>
2. Place the Activex Control in the page as below.

<object id="ppactivex" 
classid="clsid:99098758-CB85-4a90-924F-F21898796281" style="visibility:hidden" OnError="SetActiveXStatus(false);">
</object>

3.Copy the below vbscript function to the page.

<script type="text/vbscript">
function CallInsertSlides(Selected)
    arrSelected = Split(Selected, ",", -1, 0)
    set objx = Document.ppactivex
    objx.InsertSlidesFromSlideLibrary arrSelected
End function
</script>

4. Now just call the CallInsertSlides function from you javascript code as below

CallInsertSlides("/Slides%20Management/sureshtest.pptx");

In the above function I have passed only one URL. You can form a array of URLs from the items selected and pass it to the function as below. 

.S4-itm-cbx is the class of the checkboxes in the page.

$(".s4-itm-cbx:checked").each(function () {
            var parentcell = $(this).parent();
            var hyperlinkparent = $(parentcell).next().next().children();
            selItems[cSelectedSlides++] = GetEscapedUrl(portalurl + $(hyperlinkparent).attr("href"));           
        });

CallInsertSlides(selItems);
When the function CallInsertSlides is called it will open a dialog box to copy the presentation.
Happy coding !!!!.


Update: Below is the entire sample aspx page I have created as there were requests to provide the sample.




<%@ Assembly Name="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral,

PublicKeyToken=71e9bce111e9429c"%> <%@ Page Language="C#"

Inherits="Microsoft.SharePoint.WebPartPages.WikiEditPage"

MasterPageFile="~masterurl/default.master" meta:progid="SharePoint.WebPartPage.Document"

meta:webpartpageexpansion="full"       %>
<%@ Import Namespace="Microsoft.SharePoint.WebPartPages" %> <%@ Register

Tagprefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls"

Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral,

PublicKeyToken=71e9bce111e9429c" %> <%@ Register Tagprefix="Utilities"

Namespace="Microsoft.SharePoint.Utilities" Assembly="Microsoft.SharePoint, Version=14.0.0.0,

Culture=neutral, PublicKeyToken=71e9bce111e9429c" %> <%@ Import

Namespace="Microsoft.SharePoint" %> <%@ Assembly Name="Microsoft.Web.CommandUI,

Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register Tagprefix="WebPartPages" Namespace="Microsoft.SharePoint.WebPartPages"

Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral,

PublicKeyToken=71e9bce111e9429c" %>
<asp:Content ContentPlaceHolderId="PlaceHolderPageTitle" runat="server">
<SharePoint:ProjectProperty Property="Title" runat="server"/> -
<SharePoint:ListItemProperty runat="server"/>
</asp:Content>
<asp:Content ContentPlaceHolderId="PlaceHolderPageTitleInTitleArea" runat="server">
<span>
<SharePoint:DocumentFolderName runat="server" id="PageFolderName"

AppendSeparatorArrow = "true"/>
</span>
<span class="ms-WikiPageNameEditor-Display" id="wikiPageNameDisplay" runat="server">
<SharePoint:ListItemProperty runat="server"/>
</span>
<span class="ms-WikiPageNameEditor-Edit" style="display:none;" id="wikiPageNameEdit"

runat="server">
<asp:TextBox id="wikiPageNameEditTextBox" runat="server"/>
</span>
</asp:Content>
<asp:Content ContentPlaceHolderId="PlaceHolderPageDescription" runat="server">
<SharePoint:UIVersionedContent runat="server" UIVersion="4">
<ContentTemplate>
<SharePoint:ProjectProperty Property="Description" runat="server"/>
</ContentTemplate>
</SharePoint:UIVersionedContent>
</asp:Content>
<asp:Content ContentPlaceHolderId="PlaceHolderPageImage" runat="server">
<SharePoint:AlphaImage ID=onetidtpweb1 Src="/_layouts/images/wiki.png" Width=145

Height=54 Alt="" Runat="server"/></asp:Content>
<asp:Content ContentPlaceHolderId="PlaceHolderAdditionalPageHead" runat="server">


<script type="text/javascript"></script>

<meta name="CollaborationServer" content="SharePoint Team Web Site" />
<script type="text/javascript">
var navBarHelpOverrideKey = "WSSEndUser";
</script>
<SharePoint:RssLink runat="server"/>
<SharePoint:UIVersionedContent UIVersion="4" runat="server"><ContentTemplate>
<SharePoint:CssRegistration runat="server" Name="wiki.css" />
</ContentTemplate></SharePoint:UIVersionedContent>
</asp:Content>
<asp:Content ContentPlaceHolderId="PlaceHolderMiniConsole" runat="server">
<SharePoint:FormComponent TemplateName="WikiMiniConsole" ControlMode="Display"

runat="server" id="WikiMiniConsole"/>
</asp:Content>
<asp:Content ContentPlaceHolderId="PlaceHolderLeftActions" runat="server">
<SharePoint:RecentChangesMenu runat="server" id="RecentChanges"/>
</asp:Content>
<asp:Content ContentPlaceHolderId="PlaceHolderMain" runat="server">



<SharePoint:UIVersionedContent runat="server" UIVersion="3"

Id="PlaceHolderWebDescription">
<ContentTemplate>
<div class="ms-webpartpagedescription"><SharePoint:ProjectProperty

Property="Description" runat="server"/></div>
</ContentTemplate>
</SharePoint:UIVersionedContent>
<asp:UpdatePanel
  id="updatePanel"
  runat="server"
  UpdateMode="Conditional"
  ChildrenAsTriggers="false">
<ContentTemplate>
<SharePoint:VersionedPlaceHolder UIVersion="4" runat="server">
<SharePoint:SPRibbonButton
id="btnWikiEdit"


RibbonCommand="Ribbon.WikiPageTab.EditAndCheckout.SaveEdit.Menu.SaveEdit.Edit"
runat="server"
   Text="edit"/>
<SharePoint:SPRibbonButton
id="btnWikiSave"


RibbonCommand="Ribbon.WikiPageTab.EditAndCheckout.SaveEdit.Menu.SaveEdit.SaveAndStop"
runat="server"
   Text="edit"/>
<SharePoint:SPRibbonButton
id="btnWikiRevert"


RibbonCommand="Ribbon.WikiPageTab.EditAndCheckout.SaveEdit.Menu.SaveEdit.Revert"
   runat="server"
Text="Revert"/>
</SharePoint:VersionedPlaceHolder>
<SharePoint:EmbeddedFormField id="WikiField" FieldName="WikiField"

ControlMode="Display" runat="server"></SharePoint:EmbeddedFormField>
<WebPartPages:WebPartZone runat="server" ID="Bottom"

Title="loc:Bottom"><ZoneTemplate></ZoneTemplate></WebPartPages:WebPartZone>
</ContentTemplate>
<Triggers>
   <asp:PostBackTrigger ControlID="WikiField" />
   <asp:PostBackTrigger ControlID="btnWikiRevert" />
   <asp:PostBackTrigger ControlID="btnWikiSave" />
</Triggers>
 </asp:UpdatePanel>
 <script type="text/javascript" src="/_layouts/lazard/scripts/jquery-1.7.2.js"></script>
  <script type="text/javascript" src="/_layouts/lazard/1033/SLDLIB.js"></script>
<script type="text/javascript">

$(document).ready(function(){
        CallInsertSlides(serverurl+"/Slides%20Management/sureshtest.pptx");
});

   


</script>
<script type="text/vbscript">
function CallInsertSlides(Selected)
    arrSelected = Split(Selected, ",", -1, 0)
    set objx = Document.ppactivex
    objx.InsertSlidesFromSlideLibrary arrSelected
End function
</script>

<input type="button" value="Copy to presentation slides" onclick="javascript:CallPPT();">
<object id="ppactivex"
classid="clsid:99098758-CB85-4a90-924F-F21898796281" style="visibility:hidden"

OnError="SetActiveXStatus(false);">
</object>

</asp:Content>