New to TeamSystem & need to share a document library to another site

Good morning all. This may be more of a Sharepoint related question, but I couldn't find any MSDN forums related to Sharepoint problems. The question I have does relate to Team System so I thought I'd start by posting here. If anyone can help with this problem I'm facing or knows of a great Sharepoint forum I'd appreciate it!

First, some background information...My company supports multiple Sharepoint servers. Some of these servers are on different domains. One server, for instance, and all the WSS sites hosted on it, are dedicated to our clients. Whenever we create a new client project, a WSS site on this server (I'll call it ServerA for here) is created. ServerA is on Windows Active Directory DomainA. Each of our clients get a unique account created for them in DomainA and are assigned permissions in their WSS site on ServerA. ServerA also participates in DomainB. DomainB contains all of the Active Directory accounts for our employees. All of the employees in my division are given permission to the client's WSS site on ServerA. This has worked fine for us and isn't the problem.

The problem that I'm facing is that we are now going to begin using Visual Studio Team System to store certain documents for our new projects. When you create a new project in Team System, it generates its own Sharepoint site on the Team System application server (I'll call it ServerB). ServerB participates in DomainB only and is only accessible to company employees on the internal network. It doesn't have an IP that is accessible via the Internet. What my company wants me to do is enable the document libraries stored on the project's WSS site on ServerB to be accessible by the clients on their WSS site on ServerA. The clients will only need read access to these documents, although I'm sure that the ability to save them will be a feature requested in the future. I've scoured MSN, Google, etc. to find answers on how to do this. One was to add a page viewer web part to the client's WSS site on ServerA and point it back to the document library on ServerB. This works fine if I'm the person viewing the client's WSS site. I can see the documents on the client's WSS site since I have permission to their WSS site on ServerA, permission to the TeamSystem site on ServerB, and because my PC is on the company internal network. This won't work for our clients, however.

One solution that we thought of would be to create a web service that would serve as an intermediary between the WSS site on ServerA and the WSS site on ServerB. The web service would have to retrieve a list of documents from the document library on ServerB. The client's WSS site on ServerA would contain a custom built web part that would use the web service to retrieve the documents and present them to the clients. What I'm asking here is A) is this really a feasible solution and B) how could I go about starting to implement this Another related request is that they want our clients to also be able to enter new Issues into TeamSystem for their project from their WSS site on ServerA. I was thinking that a similar web service would also have to be created to do this too. I am a Sharepoint newbie, so I don't know enough yet to tell people here that this can definitely work or no that it can't. I've began reading about Sharepoint's API and web services that come with it, but haven't gotten far. If anyone knows of a solution or of some resources that I could use to figure one out that would be great. Any advice is greatly appreciated!

Thanks
- B



Answer this question

New to TeamSystem & need to share a document library to another site

  • PradeepTR

  • djs1

    Hi Nodnarb501;

    Thankyou very much for the coding, I have followed per to your instructions and having slight problems. I get the following error :

    MirrorEventSink.ds(41,36): error CS0246: The type or namespace name 'BaseEventSink' could not be found <are you missing a using directive or an assembly reference >

    Would you or Know how to solve this problem; regards back to the coding the changes I made from your example where the User ID, Password and Domain.

    Regards,

    TC



  • Discofunk

    Hello all. I've solved the problem on my own.

    Please refer to the following links:
    http://www.microsoft.com/downloads/details.aspx FamilyId=4B2C2F1B-D74A-482A-903A-45BB44C5DEC4&displaylang=en

    and

    http://www.gotdotnet.com/Community/UserSamples/Details.aspx SampleGuid=5451793d-288c-4fed-8e79-6c44ab0550e2

    The solution I decided on was to use document library event handlers that update remote document libraries using WSS/FrontPage RPC commands. I've tested this out in a live environment and it works great. Basically, I modifed the event handler code in the event handler toolkit by inserting some code I found in the gotodotnet example that uploaded documents to a doc library by using FrontPage RPC commands. To test, I installed the event handler DLL on our internal Team System WSS server and binded it to a document library on a test site, setting a new property I created that points to the destination doc library in the Properties section of the event handler setup. I then created a test site on ServerA on the same level as the rest of our client sites and created a test document library in that site. I gave an account in DomainB Contributor access on both sites since both servers/sites participate in DomainB. Now, when I insert, modify, or delete documents in Team System, the same happens to the document on the remote server/library. The code takes the inserted file from the Team System SharePoint site, puts it into a memory stream, and sends it to the author.dll on the remote server. If a file is deleted from the Team System SharePoint site, the deleted filename is sent along with a delete command. No dlls or web services need to be installed on the remote server.

    To build a project similar to mine, all you need to do is combine the two examples in the URLs above. Use the EventHandler Toolkit's MirrorEventSink example. Take out the code in each of the 'handle' events that does the work of copying files. All you really need there are the files you get from URLBefore or URLAfter. Next, take the code for PutDocument, etc. into the event handler. Copy the EventHandler Toolkit's file object into a byte[] that you can pass into PutDocument. Also, read in a Property from the EventHandler's configuration that points to your destination library (replace the ListData xml tag from the EventHandler Toolkit example with a full URL to the desination doc lib). Use this in PutDocument to tell it the URL to act upon. You'll need to specify credentials for the code from the toolkit along with the code from the rpc publish. That is a very basic rundown, but I hope it helps someone out.

    - B


  • s_ursan

    Hi TC,

    Here's my code that replaced MirrorEventSink.cs in the Microsoft SP event handler toolkit. It's a lot easier to just post this instead of explain each change in detail. Basically, I left the "shell" of the MirrorEventSink.cs file mostly in tact. I replaced the code in the methods, such as HandleInsert, with code to call methods that are close to the methods from gotodotnet example. What made me realize the answer was to combine the two was that I saw how both used files. I could get the inserted/deleted/etc file from an SPFile and put it into a byte array that the gotodotnet example could use. See the code in HandleInsert().

    To use my example, use the following in the Event Handler settings of the source document library:

    Assembly Name

    “DocLibEventHndlr, Version=1.0.0.1, Culture=neutral, PublicKeyToken=9e26394354629b57”

    Class Name

    DocLibEventHndlr.DocMirrorEventSink

    Properties

    <Data>

    <SiteURL>http://destinationsharepointserver.xxx/sitename/DocLibraryName</SiteURL>

    </Data>

     

    Here is the code:

    using System;
    using System.Net;
    using System.Xml;
    using System.Web;
    using System.IO;
    using System.Text;
    using System.Security.Principal;
    using Microsoft.SharePoint;

    namespace DocLibEventHndlr
    {
     /// <summary>
     /// This is a Sharepoint Document Library Event handler used to publish documents on
     /// one server to another document library (destination). The destination doc library
     /// can be on the same site, same server, or an entirely different document library on
     /// a remote server. This program uses FrontPage/Sharepoint "RPC" calls to the
     /// author.dll located in Sharepoint sites to do the work. The credentials used
     /// here must have at least read access in the source doc library and at least
     /// contributor/write access in the destination doc library. Different credentials for
     /// the source and destination doc libraries can be used if required.
     /// </summary>
     public class DocMirrorEventSink : BaseEventSink
     {
      private Guid m_mirrorListId = Guid.Empty;
      private WindowsIdentity m_Identity = null;

      const string SourceUserName = "Username";
      const string SourcePassword = "password";
      const string SourceDomain   = "domainname";
      
      // If necessary, customize the Dest username, password, domain, else, just use the source.
      const string DestUserName   = SourceUserName;
      const string DestPassword   = SourcePassword;
      const string DestDomain     = SourceDomain;
        
      protected override WindowsIdentity HandlerIdentity
      {
       get
       {
        if (m_Identity == null)
        {
         // Get the Credentials (username / password) that this event sink instance should run as
         // and Initialize the m_identity object by calling BaseEventSink.CreateIdentity
         //
         // m_Identity = CreateIdentity(username, domain, password);
         m_Identity = CreateIdentity(SourceUserName, SourceDomain, SourcePassword);
        }
        return m_Identity;
       }
      }

      protected override void HandleEvent()
      {
       switch (EventInfo.Type)
       {
        case SPListEventType.Copy:
         HandleCopy();
         break;
        case SPListEventType.Delete:
         HandleDelete();
         break;
        case SPListEventType.Insert:
         HandleInsert();
         break;
        case SPListEventType.Move:
         HandleMove();
         break;
        case SPListEventType.Update:
         HandleUpdate();
         break;
       }
      }


      private void HandleCopy()
      {
       // Don't do anything, for now.
      }

      private void HandleDelete()
      {
       if (EventInfo.UrlBefore != null)
       {
        SPFile file;
       
        try
        {
         // Retrieve a string of the destination document librarie's URL
         string destsitestr = GetDestSiteURL();

         // Create a Sharepoint File (SPFile) object so we can get the filename of the
         // deleted document. Use UrlBefore because Sharepoint event handlers fire after
         // the document has been delted from the Sharepoint site.
         file = EventWeb.GetFile(EventInfo.UrlBefore);

         DeleteDocument(destsitestr, file.Name);
        }
        finally
        {
         // Free our objects in a finally clause to ensure they are freed
         file = null;
        }
       }
      }

      private void HandleMove()
      {
       // If moving a document out of the source library, delete the remote copy since we have
       // no way to tell where we should put the remote document.
       HandleDelete();
      }

      private void HandleInsert()
      {
       if (EventInfo.UrlAfter != null)
       {
        SPFile file;
        byte[] Destfile;
        
        try
        {
        
         // Retrieve a string of the destination document librarie's URL
         string destsitestr = GetDestSiteURL();    
         
         // Create a Sharepoint File (SPFile) object so we can get an instance of the
         // inserted document. Use UrlAfter because Sharepoint event handlers fire after
         // the document has been inserted into the Sharepoint site.
         file = EventWeb.GetFile(EventInfo.UrlAfter);

         // Create a byte array to hold the contents of the inserted file.
         Destfile = file.OpenBinary();

         PutDocument(destsitestr, file.Name, Destfile,null);

        }
        finally
        {
         // Free our objects in a finally clause to ensure they are freed
         Destfile = null;
         file = null;
        }
       }
      }

      private void HandleUpdate()
      {
       // The PutDocument method used by HandleInsert() specifies overwrite to be true,
       // so just call HandleInsert() rather than re-code it here.
       HandleInsert();
      }

      #region Modified FPSEPublish example code
      // The following code was modified from the FPSEPublish example found at
      //
    http://www.gotdotnet.com/Community/UserSamples/Details.aspx SampleGuid=5451793d-288c-4fed-8e79-6c44ab0550e2

            /// <summary>
            /// Parses the string of a URL (stored in a URI object), returns the site and the folder of the remote
            /// document library
            /// </summary>
            /// <param name="uri">The entire URL to the destination document library</param>
            /// <param name="webUrl">Site path to the destination document library</param>
            /// <param name="fileUrl">Folder name of the destination document library</param>
      public void UrlToWebUrl(string uri, out string webUrl, out string fileUrl)
      {
       Uri myUri = new Uri(uri);

       string postBody = String.Format("method=url+to+web+url&url={0}&flags=0", myUri.AbsolutePath); 
       string response = SendRequest(myUri.GetLeftPart(UriPartial.Authority) + "/_vti_bin/shtml.dll/_vti_rpc",postBody);
       
       webUrl = GetReturnValue(ref response, "webUrl");
       fileUrl = GetReturnValue(ref response, "fileUrl");
      }

      /// <summary>
      /// Removes a document in the destination document library
      /// </summary>
      /// <param name="uri">The entire URL to the document</param>
      /// <param name="fileName">Name of the file to delete</param>
      public void DeleteDocument(string uri, string fileName)
      {
       Uri myUri = new Uri(uri);
       string webUrl, fileUrl;
       UrlToWebUrl(uri, out webUrl, out fileUrl); 

       string postBody = String.Format(
        "method=remove+documents&service_name=&url_list=[{0}]\n",
        HttpUtility.UrlEncode(fileUrl) + @"/" + fileName);  // <-- Add a "/" and the filename here
       
       ASCIIEncoding encoding = new ASCIIEncoding();
       MemoryStream stream = new MemoryStream();
       stream.Write(encoding.GetBytes(postBody), 0, postBody.Length);

       SendRequest(myUri.GetLeftPart(UriPartial.Authority) + webUrl + "/_vti_bin/_vti_aut/author.dll", stream.GetBuffer(), stream.Length);
       
       stream.Close();

      }

      /// <summary>
      /// Inserts or replaces a file in the destination document library
      /// </summary>
      /// <param name="uri">The entire URL to the document</param>
      /// <param name="fileName">Name of the inserted/updated file</param>
      /// <param name="binfile">Binary bye array containing the file to insert/update into the destination library</param>
      /// <param name="metaInfo">Not used here (for now). Pass in null.</param>
      public void PutDocument(string uri, string fileName, byte[] binfile, string metaInfo)
      {
       Uri myUri = new Uri(uri);
       string webUrl, fileUrl;
       UrlToWebUrl(uri, out webUrl, out fileUrl);

       if (null == metaInfo)
        metaInfo = "";

       string postBody = String.Format(
        "method=put+document&service_name=&document=[document_name={0};meta_info=[{1}]]&put_option=overwrite&comment=&keep_checked_out=false\n",
        HttpUtility.UrlEncode(fileUrl) + @"/" + fileName, // <-- Add a "/" and the filename here
        metaInfo);

       ASCIIEncoding encoding = new ASCIIEncoding();
       MemoryStream stream = new MemoryStream();
       stream.Write(encoding.GetBytes(postBody), 0, postBody.Length);

       // Write the contents of binfile to the memory stream
       stream.Write(binfile, 0, binfile.Length);

       SendRequest(myUri.GetLeftPart(UriPartial.Authority) + webUrl + "/_vti_bin/_vti_aut/author.dll", stream.GetBuffer(), stream.Length);
       
       stream.Close();
      }

      /// <summary>
      /// Sends the stream to the remote Sharepoint server and receives a response.
      /// </summary>
      /// <param name="uri">URL to send the request to</param>
      /// <param name="postBody">Bye array containing the request</param>
      /// <param name="postLength">Length of request</param>
      /// <returns></returns>
      private string SendRequest(string uri, byte[] postBody, long postLength)
      {
       string responseText = null;

       try
       {
        WebRequest request = WebRequest.Create(uri);
        request.Method = "POST";
        request.ContentType = "application/x-www-form-urlencoded";
        request.Headers.Add("X-Vermeer-Content-Type", "application/x-www-form-urlencoded");
        
        // Override the default credentials. The credentials used here must have access to
        // both the origin Sharepoint site and destination Sharepoint site. The user account
        // on the destination site must have the ability to create & delete files in the
        // destination document library.
        //request.Credentials = CredentialCache.DefaultCredentials;
        request.Credentials = new NetworkCredential(DestUserName,DestPassword,DestDomain);
        
        Stream newStream = request.GetRequestStream();

        int offset = 0;
        while(postLength > 0)
        {
         if (postLength > 4096)
         {
          newStream.Write(postBody, offset, 4096);
          postLength -= 4096;
          offset += 4096;
         }
         else
         {
          newStream.Write(postBody, offset, Convert.ToInt32(postLength));
          break;
         }
        }

        newStream.Close();

        HttpWebResponse response = (HttpWebResponse)request.GetResponse();
        Stream receiveStream = response.GetResponseStream();
        StreamReader readStream = new StreamReader(receiveStream, Encoding.UTF8);

        responseText = readStream.ReadToEnd();

        response.Close();
        readStream.Close();
       }
       catch (Exception e)
       {
        throw new Exception(e.Message);
       }

       return responseText;
      }
      
      /// <summary>
      /// Overloaded version of SendRequest that converts the postBody into an encoding
      /// </summary>
      /// <param name="uri">URL to send the request to</param>
      /// <param name="postBody">String to convert into a request</param>
      /// <returns></returns>
      private string SendRequest(string uri, string postBody)
      {
       ASCIIEncoding encoding = new ASCIIEncoding();
       return SendRequest(uri, encoding.GetBytes(postBody), postBody.Length);
      }

      private string GetReturnValue(ref string response, string key)
      {
       int start = response.IndexOf(key + "=");
       if (-1 == start)
        return null;
       else
        start += key.Length + 1;

       int end = response.IndexOf("\n", start);
       return response.Substring(start, end - start);
      }

      /// <summary>
      /// Gets the URL of the destination Sharepoint site from the SiteURL tag in the event
      /// handler properties set for this list in Sharepoint.
      /// </summary>
      /// <returns>A string of the destination URL</returns>
      private string GetDestSiteURL()
      {
       SPListEvent evt = EventInfo;
       string siteURL = "";
       if (evt.SinkData != null && evt.SinkData.Length > 0)
       {
        try
        {
         XmlDocument xd = new XmlDocument();
         xd.LoadXml(evt.SinkData);
         XmlNode xn = xd.SelectSingleNode("//SiteURL");
         if (xn != null)
         {
          siteURL = xn.InnerText;
         }
        }
        catch(Exception e) {throw new Exception("Cannot parse mirror SiteURL tag. Check event handler properties of source document library.", e);}
       }
               
       return siteURL;

      }
      #endregion


     //public class DocMirrorEventSink : BaseEventSink
     }
    }


     


  • iPlexor

    Hi,

    I am very interested how you shared a document library to another site, but I have some questions to ask you if possible.....

    1. You mention that you modifed the event handler code in the event handler toolkit by inserting some code you found in the gotodotnet example that uploaded documents to a dic library by using FrontPage RPC Commands. I have downloaded the code from the link that you gave and extracted the file, etc.

    I do not seem to understand how you modified the code and how you combine the two examples in both URL's given.

    Would you please be able to provide on how you done this in more detail, thank you........

    Regards,

    TC



  • New to TeamSystem & need to share a document library to another site