Downloading SharePoint Attachments with PHP, a snippet

To use this code, please download the latest version of Camelot PHP Tools and the Camelot SharePoint Integration Toolkit

We have gotten a few questions how to download attachments from SharePoint List Items using Camelot PHP Tools. Originally we planned to implement a separate library doing this but recently came to the conclusion that it wasn’t really necessary. Instead we will rely on Camelot XML and the original SQL syntax.

$SharePointQuery = new SharePointQuery(array(
    'sql' => "SELECT FileName, Data FROM Testlist.Attachments WHERE ItemID = 1",
    'connection_name' => 'SharePointConnection1'
));

Snippet

The snippet below illustrates how to download the first attachment available with ItemId 1

/*
 * Camelot PHP Tools
 *
 * @version 1.5
 * @author Bendsoft
 * @package PHP Tools
 * @subpackage Camelot
 * @license FreeBSD License (www.bendsoft.com/licensing/)
 *
 * Note! 
 * Set up settings.php before using this file
 */
error_reporting(E_COMPILE_ERROR);
ini_set('display_errors', '1');


// Load all required files for Camelot PHP Tools
require_once '../loader.php';

try {
    $SharePointQuery = new SharePointQuery(array(
                'sql' => "SELECT FileName, Data FROM Testlist.Attachments WHERE ItemID = 1",
                'connection_name' => 'SharePointConnection1'
            ));

    // Store the file info, change the index [0] if you have multiple attachments
    $file = $SharePointQuery->CamelotSoap->_sorted->_content[0];
    
    // Send the file to the browser
    header("Content-type: application/data");
    header('Content-Disposition: attachment; filename="' . $file["FileName"] . '"');
    header("Content-Description: PHP Generated Data");
    
    echo base64_decode($file["Data"]);
    
    $file = null;
    
} catch (Exception $exc) {
    echo $exc->getTraceAsString();
}

It doesn’t really get harder than this, enjoy.
 

 

Posted in PHP | Leave a comment

New free web part, Introducing the Camelot SharePoint SQL Commander Web Part

The Camelot SharePoint SQL Commander Web Part is a simple yet powerful query browser for Microsoft SharePoint 2007 and 2010.

Note! This web part does not query the tables in the SQL server; instead it queries the lists and libraries that are enclosed within SharePoint. Read more about this in the Camelot .NET Connector section.

Official web page

http://www.bendsoft.com/downloads/sharepoint-web-parts/sharepoint-sql-commander/

FAQ

What does this web part do?

It give you an interface in SharePoint where you can query your SharePoint lists and libraries with sql. You can experiment with queries of modify your list items directly with SQL. This tool is great for for anyone who quickly want to change large amounts of list items or simply learn to know how the lists works in depth.

How do I install it?

Since this is a highly custom built web part you need to install it manually, you will find everything you need to know about the installation here.

What else do I need?

You must install the Camelot .NET Connector and the Camelot SharePoint Integration Toolkit on the same SharePoint server as the Web Part.

Are there any costs involved?

The web part and the integration toolkit is free to use, they are also open source so you will be able to modify them if you want to. The Camelot .NET Connector is licensed but you can use the trial license to try it, you will find everything you need to know here.

I’m a developer, do I need it?

This web part is great for developers, you can quickly have a look at the stuff under the hood and see how columns are named, what datatype they are using and so on.

I’m an SharePoint administrator, will it improve my life?

Yes, most likely. If you ever tried to delete or rename 10000 records in a list at once you will benefit from using this. Or if you simply want an inline CLI to view and modify your SharePoint Lists and Libraries, it’s very powerful.

 

Download Button

Use our contact form if you have any questions!

 

Posted in Web Parts | Leave a comment

Bendsoft announces Camelot .NET Connector 2.0 in May 2012!

Nyköping, Sweden, Jan 26, 2012

Bendsoft Corporation announced today that it will release version 2.0 of its core software product for accessing data in Microsoft SharePoint, the Camelot .NET Connector, in May of 2012. The Camelot .NET Connector 2.0 will include an extensive suite of new features including an updated SQL syntax with JOIN and UNION support. The Camelot .NET Connector 1.1 is the latest version of Bendsoft’s industry-leading ADO.NET driver for connecting to Microsoft SharePoint, designed and built on open standards, enabling developers to query data in Microsoft SharePoint using standard SQL language. In combination with open source toolkits provided by Bendsoft, such as the Camelot SharePoint Integration Toolkit and SharePoint PHP Tools, it is the best solution for integrating SharePoint from virtually any other platform or programming language like PHP, Java or Objective-C.

The Camelot .NET Connector 2.0 will dramatically extend the core capabilities of the connector. This release will offer the first implementation of JOIN and UNION SQL commands that will work between any lists and columns in your SharePoint site. Advantages of this feature will be simplified integration with external applications and extended possibilities for data synchronization, data reporting and analytics inside or outside SharePoint.

Eric Herlitz, Co-Founder and Sales Director commented, “We are very excited to make this announcement, introducing ground-breaking capabilities of the Camelot .NET Connector that will most definitely lower the costs of integration and maintenance in many of our customer’s SharePoint projects.”

Ulf Engstrand, Co-Founder and Systems Architect, adds, “The Camelot .NET Connector in combination with our open source toolkits provide a unique solution that developers need to efficiently integrate SharePoint with other applications. Many of our customers have specifically asked for JOIN support and we feel that this is a natural step to take.”

Existing customers will be offered a free upgrade to Camelot .NET Connector version 2.0 when it becomes available. The new version will not break feature compatibility with version 1.1, but recompilation may be required.

Posted in Camelot .NET Connector, General, SharePoint | Leave a comment

Camelot PHP Tools 1.4 for SharePoint released, now supporting NTLM Authentication!

When the latest version of the Camelot PHP Tools is paired with the Camelot SharePoint Integration Toolkit version 2.0.2 or newer it has full support for NTLM authentication.

Either static or personal credentials can be used. Read more about this in the documentation, http://docs.bendsoft.com/camelot-php-tools/latest/examples-and-tutorials/ntlm-authentication/

Setting up a NTLM Connection to SharePoint

// The settings are created in the settings.php file

// Creates a WcfSettings List Object
$WcfSettingsList = new WcfSettingsList();

// Add the connection "SharePointConnection1" enforcing NTLM authentication
$WcfSettingsList->append(new WcfSettingsObject("SharePointConnection1", "http://sharepointserver.com:8080/?wsdl", "MySharedKey", "sharepoint_connection", 0, "ntlm")); // server using ntlm
$WcfSettingsList->append(new WcfSettingsObject("SharePointConnection2", "http://anotherserver.com:8080/?wsdl", "MySharedKey", "sharepoint_connection")); // server not using ntlm

Static NTLM Credentials

Static credentials are used when you only want to authenticate the connection using a single account.

// Enter in settings.php

$GLOBALS['NTLM_CREDENTIALS'] = array(
    'ntlm_username' => 'domain user',
    'ntlm_password' => 'password'
);

Personal NTLM Credentials

When using personal NTLM Credentials each user/client will have to handle their own authentication, this is automatically handled by the class CamelotSoapClient that outputs a login form when required.


Download Button

Use our contact form if you have any questions!

Posted in PHP | Leave a comment

SharePoint surveys on external sites

Recently I discovered the SharePoint survey list type. It allows you to quickly create questions that you would like to have people answer and then view graphical summaries of the responses. It can for example be useful for companies to collect consumer opinions about their products or services.

 

In this article I will show how to integrate SharePoint surveys on your external sites using the Camelot .NET Connector. I will also try to briefly explain the structure of SharePoint survey lists. I have developed a simple survey control based on the ASP.NET Wizard control that walks the user through the questions. In the last step of the wizard, it completes the survey by inserting the results back into the SharePoint survey list. This is a great example of how to creatively use the Camelot .NET Connector and give you more time to work on what matters most – your business.

Requirements

This example project can be downloaded here!

Download example!

The structure of SharePoint survey lists

When you see a survey list in SharePoint for the first time, it may not be obvious how to work with its content. Each question is represented as a column in the list. The column display name (data column DisplayName) contains the actual question. The column type (data column Type) indicates the type of the answer, e.g. Text, Choice, MultiChoice, Boolean, etc. Depending on the type, the question needs to be presented differently. In addition, we should validate that required questions are answered by the user (data column Required). For simplicity in this example, we will only implement some of the types.

Each inserted item in a survey list represents one participation in the survey. By default, the same SharePoint user can only participate in a survey once. If you want to publish your survey externally and allow non-sharepoint users to participate, you must allow multiple responses. This setting can be changed under survey options.

The order of the questions is another difficult issue when selecting. I found that every survey has a content type with the same name as the list itself. By querying the list with this content type, the columns always come out in the same order as arranged in SharePoint. For example, to select all the answers in correct order from the survey list “Customer Survey”, you should use “SELECT * FROM `Customer Survey`.`Customer Survey`“.

Preparation

In SharePoint, create a new survey list in one of your sites. Add a number of questions to the survey. Make sure to enable multiple responses under survey options as shown below. In this example, I have created a silly “Cats” survey, that evaluates what type of cat person you are.

 

 

Building a web user control for presenting surveys

My aim was to create a common control that can be used on several sites so I decided to go with a ASP.NET web user control. This control takes two properties; ListName and ConnectionString. The first gives the name of a survey list that should be displayed and the second the connection string used for connecting to SharePoint.

In Visual Studio 2010, add a new Web User Control (language C#) to your web site and call it SurveyControl.ascx. Ensure that “Place code in separate file” is checked.

SurveyControl.ascx

Replace the content of the markup file (SurveyControl.ascx) with the following lines. It shows a standard ASP.NET Wizard control with a start step that should present the survey and an end step that completes the survey. All the questions will be added as steps in between the start and the end step. The OnFinishButtonClick and OnNextButtonClick events are handled by us. We have also added some css styling and graphics to the control to improve the visual impression. You can of course make the control more general by adding all static texts as inputs to the control.

<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="SurveyControl.ascx.cs" Inherits="SurveyWebSiteExample.SurveyControl" %>

<style type="text/css">
    .survey
    {
        font-family:trebuchet ms;        
        padding:10px;           
    }
</style>

<div style="margin-left:auto; margin-right:auto; margin-top:100px; width:700px; overflow:hidden; border:solid black 1px;">
    <div><img src="images/cat.jpg" width="100%" alt="" /></div>
    <div class="survey">
        <asp:Wizard ID="SurveyWizard" runat="server" DisplaySideBar="false" OnFinishButtonClick="FinishButtonClicked" OnNextButtonClick="NextButtonClicked">
        <WizardSteps>
            <asp:WizardStep ID="StartStep" runat="server">
                <p>We would like to ask you a few questions about cats. This survey will help you find what temperament of cat is a good match for you. Please click next to continue!</p>
            </asp:WizardStep>
            <asp:WizardStep ID="EndStep" runat="server" StepType="Complete" AllowReturn="false" >
                <p>Thank you for participating!</p>
            </asp:WizardStep>
        </WizardSteps>
        </asp:Wizard>
    </div>
</div>

SurveyControl.ascx.cs

Now its time to implement the code behind our control. In Page_Init we will add the questions to the survey. To get all the questions, we simply execute a SHOW FIELDS query on our list. The columns returned are listed here. We have added a public property named ListName, that should give us the name of the survey list. Using some additional LINQ on our results, we sort out all the questions in the list and iterate over the result set. Each question will be added as new wizard step in our wizard control. The Type column is used to determine in what way the question will be presented. For example, Boolean is represented by a RadioButton control showing YES/NO. For Choice and MultiChoice fields we can use the EnumValues column to retrieve the alternatives. The question itself is shown at the top of the page.

The Camelot .NET Connector requires a connection string for connecting to SharePoint so we need to provide a property for that as well. The connection string is assumed to be stored in the web.config. The following connection string template can be used in most cases but all options are descibed in the connector documentation.

<add name="sharepoint_sales" connectionString="Server=my.sharepointserver.com;Database=customers;User=myuser;Password=mypass;Authentication=Ntlm;TimeOut=60;" />

So, altogether, the code in Page_Init looks as follows.

        protected void Page_Init(object sender, EventArgs e)
        {
            _questions = Helper.ExecuteDataTable("SHOW FIELDS FROM `" + ListName + "`.`" + ListName + "`", ConfigurationManager.ConnectionStrings[this.ConnectionString].ConnectionString);

            foreach (DataRow q in _questions.AsEnumerable().Where(r => (bool)r["VisibleInView"] == true && (bool)r["ReadOnly"] == false && (string)r["Name"] != "ContentType"))
            {
                var step = new WizardStep();

                if ((bool)q["Required"])
                    step.Controls.Add(new LiteralControl("<h2>" + q["DisplayName"] + "<font color="red"> *</font></h2>"));
                else
                    step.Controls.Add(new LiteralControl("<h2>" + q["DisplayName"] + "</h2>"));

                SurveyWizard.WizardSteps.Insert(SurveyWizard.WizardSteps.Count - 1, step);

                switch ((string)q["Type"])
                {
                    case "Choice":
                        var singleChoice = new RadioButtonList();
                        singleChoice.ID = ((Guid)q["ID"]).ToString();

                        foreach (Match choice in Regex.Matches((string)q["EnumValues"], @"[(.*?)]"))
                            singleChoice.Items.Add(choice.Groups[1].Value);

                        step.Controls.Add(singleChoice);
                        break;
                    case "MultiChoice":
                        var multiChoice = new CheckBoxList();
                        multiChoice.ID = ((Guid)q["ID"]).ToString();

                        foreach (Match choice in Regex.Matches((string)q["EnumValues"], @"[(.*?)]"))
                            multiChoice.Items.Add(choice.Groups[1].Value);

                        step.Controls.Add(multiChoice);
                        break;
                    case "Text":
                        var singleLineText = new TextBox();
                        singleLineText.ID = ((Guid)q["ID"]).ToString();

                        step.Controls.Add(singleLineText);
                        break;
                    case "Boolean":
                        var yesNo = new RadioButtonList();
                        yesNo.ID = ((Guid)q["ID"]).ToString();

                        yesNo.Items.Add("Yes");
                        yesNo.Items.Add("No");

                        step.Controls.Add(yesNo);
                        break;
                    default:
                        break;
                }
            }
        }

We also need to implement event handlers for the OnFinishButtonClick and OnNextButtonClick events. In NextButtonClicked method we will do some validation of the answers before going to the next question. In this example, we will only validate required questions. However, it would be possible to also validate the actual data entered by the user, such as text length or number range. If a required question is not answered by the user, this method will return a cancel notification to the wizard control.

The code in NextButtonClicked looks as follows.

        protected void NextButtonClicked(object sender, WizardNavigationEventArgs e)
        {
            if (SurveyWizard.ActiveStepIndex == 0) return;
                 
            var index = 0;
            foreach (DataRow q in _questions.AsEnumerable().Where(r => (bool)r["VisibleInView"] == true && (bool)r["ReadOnly"] == false && (string)r["Name"] != "ContentType"))
            {
                if (index == SurveyWizard.ActiveStepIndex - 1)
                {
                    var control = SurveyWizard.ActiveStep.FindControl(((Guid)q["ID"]).ToString());

                    if (control != null)
                    {
                        string value = null;

                        switch ((string)q["Type"])
                        {
                            case "Choice":
                                value = ((RadioButtonList)control).SelectedValue;
                                break;
                            case "MultiChoice":
                                value = ((CheckBoxList)control).Items.Cast<ListItem>().Where(m => m.Selected == true).Select(m => m.Value).FirstOrDefault();
                                break;
                            case "Text":
                                value = ((TextBox)control).Text;
                                break;
                            case "Boolean":
                                value = ((RadioButtonList)control).SelectedValue == "Yes" ? "true" : "false";
                                break;
                            default:
                                break;
                        }

                        if ((bool)q["Required"] && string.IsNullOrEmpty(value))
                        {
                            e.Cancel = true;
                            return;
                        }
                    }

                    return;
                }
                index++;
            }
        }

The FinishButtonClicked method is called when the user finishes the survey. It will gather all the answers and form the INSERT query. Again, each type of question needs to be handled differently. The answers are first added to a dictionary and then the insert statement is built using LINQ.

The code in FinishButtonClicked looks as follows.

        protected void FinishButtonClicked(object sender, WizardNavigationEventArgs e)
        {
            NextButtonClicked(sender, e);
            if (e.Cancel) return;

            var answers = new Dictionary<string, object>();

            foreach (DataRow q in _questions.AsEnumerable().Where(r => (bool)r["VisibleInView"] == true && (bool)r["ReadOnly"] == false && (string)r["Name"] != "ContentType"))
            {
                var control = SurveyWizard.FindControl(((Guid)q["ID"]).ToString());

                switch ((string)q["Type"])
                {
                    case "Choice":
                        string singleChoiceAnswer = ((RadioButtonList)control).SelectedValue;
                        answers.Add(((Guid)q["ID"]).ToString("N"), singleChoiceAnswer);
                        break;
                    case "MultiChoice":
                        string[] multiChoiceAnswer = ((CheckBoxList)control).Items.Cast<ListItem>().Where(m => m.Selected == true).Select(m => m.Value).ToArray();
                        answers.Add(((Guid)q["ID"]).ToString("N"), multiChoiceAnswer);
                        break;
                    case "Text":
                        string singleLineTextAnswer = ((TextBox)control).Text;
                        answers.Add(((Guid)q["ID"]).ToString("N"), singleLineTextAnswer);
                        break;
                    case "Boolean":
                        bool yesNoAnswer = ((RadioButtonList)control).SelectedValue == "Yes" ? true : false;
                        answers.Add(((Guid)q["ID"]).ToString("N"), yesNoAnswer);
                        break;
                    default:
                        break;
                }
            }

            var insertCommand = "INSERT INTO `" + ListName + "` SET " + _questions.AsEnumerable().Where(r => (bool)r["VisibleInView"] == true && (bool)r["ReadOnly"] == false && (string)r["Name"] != "ContentType").Select(m => "`" + (string)m["Name"] + "`" + " = @" + ((Guid)m["ID"]).ToString("N")).Aggregate((a, b) => a + ", " + b);
            using (var connection = new SharePointConnection(ConfigurationManager.ConnectionStrings[this.ConnectionString].ConnectionString))
            {
                connection.Open();

                using (var command = new SharePointCommand(insertCommand, connection))
                {
                    foreach (var answer in answers)
                        command.Parameters.Add("@" + answer.Key, answer.Value);

                    command.ExecuteNonQuery();
                }
            }
        }

Creating a sample page

Add a new web page to your project (we are using default.aspx in this example) and replaced the content with the following markup code. On the top of the page, we register our new survey control and later down we add an instance of this control to the page. Remember that we need to set the ListName and ConnectionString properties to initialize the control. The named ConnectionString must also be added to the web.config.

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="SurveyWebSiteExample._Default" UICulture="en-US" %>

<%@ Register TagName="Survey" TagPrefix="camelot" Src="~/SurveyControl.ascx" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <camelot:Survey ID="Survey" runat="server" ListName="Cats" ConnectionString="sharepoint_connection"></camelot:Survey>
    </div>
    </form>
</body>
</html>

Testing the page

Now, start your web site and browse to your page. If everything goes fine, your survey should show up.

 

 

Posted in Camelot .NET Connector | Leave a comment

New syntax in PHP Tools 1.3 Connections

Since we simplified the connection handling in PHP Tools 1.3 and store all connections in the settings file we have also changed the connection syntax for PHP Tools.

The connection settings are now stored in the WcfSettingsObject object set up in settings.php. We removed the compression, connString and sharedKey parameters from the argument array and moved this into the WcfSettingsObject.

// Excerpt from settings.php
$WcfSettingsList = new WcfSettingsList();
$WcfSettingsList->append(new WcfSettingsObject("SharePointConnection1", "http://sharepointserver.com:8080/?wsdl", "MySharedKey", "sharepoint_connection", 0));
$WcfSettingsList->append(new WcfSettingsObject("SharePointConnection2", "http://sharepointserver.com:8080/?wsdl", "MySharedKey", "sharepoint_customerssite", 0));
$WcfSettingsList->append(new WcfSettingsObject("SharePointConnection3", "http://sharepointserver.com:8080/?wsdl", "MySharedKey", "sharepoint_testsite", 0));

Snippets

Selecting with SELECT

$SharePointQuery = new SharePointQuery(array(
    'sql' => "SELECT * FROM Tasks WHERE ID > 10",
    'connection_name' => 'SharePointConnection1'
));

Selecting by list and view name

$SharePointQuery = new SharePointQuery(
    array(
        'listName' => 'Tasks',
        'viewName' => 'All Tasks',
        'includeAttachements' => false,
        'connection_name' => 'SharePointConnection1',
        'columns' => ''
    )
);

Inserting

$SharePointNonQuery = new SharePointNonQuery(array(
    'sql' => "INSERT INTO Tasks (Title,AssignedTo,Status,Priority,DueDate,PercentComplete) VALUES ('Test task from PHP',1,'In Progress','(1) High', '".  date('Y-m-d H:i:s') ."',0.95)",
    'method' => 'ExecuteNonQuery',
    'connection_name' => 'SharePointConnection1'
));

Updating

$SharePointNonQuery = new SharePointNonQuery(array(
    'sql' => "UPDATE Tasks SET Title = 'Test task from PHP (updated)' WHERE PercentComplete = 0.95 AND TITLE = 'Test task from PHP'",
    'method' => 'ExecuteNonQuery',
    'connection_name' => 'SharePointConnection1'
));

Deleting

$SharePointNonQuery = new SharePointNonQuery(array(
    'sql' => "DELETE FROM Tasks WHERE Title = 'Test task from PHP (updated)' OR Title = 'Test task from PHP'",
    'method' => 'ExecuteNonQuery',
    'connection_name' => 'SharePointConnection1'
));

Using with stored procedures

There is a complete list with available procedures at the documentation site, http://docs.bendsoft.com/camelot-net-connector/latest/procedures/

Downloading a file

$download = new CamelotDownloadFile(array(
    "file" => $_GET["file"],
    "listName" => 'Shared Documents',
    "connection_name" => 'SharePointConnection1'
));

$download->download_file();

Uploading a file

$args = array(  "file" => $_FILES,
    "listName" => 'Shared Documents',
    "folder" => 'Folder/',
    "connection_name" => 'SharePointConnection2'
); 

$UploadFile = new CamelotUploadFile($args);

PHP Tools official documentation: http://docs.bendsoft.com/camelot-php-tools/latest/

Contact us if you have any questions regarding the PHP Tools.

Posted in PHP | Leave a comment

Installing the Camelot SharePoint Integration Toolkit

Camelot SharePoint Integration Toolkit simplifies integrations between SharePoint and other solutions, platforms and languages, e.g. PHP, Java, Objective-C and so on. It provides a set of standards for querying and transferring list and library data using standard SQL syntax and Camelot XML via self-hosted WCF services.

To better explain how to set up the Camelot SharePoint Integration Toolkit we’ve recorded this short screencast.

You can read more about the Integration Toolkit at http://www.bendsoft.com/downloads/camelot-sharepoint-integration-toolkit/, download and further instructions is available at Codeplex http://camelottoolkit.codeplex.com.

 

 

Posted in Integration Toolkit, Videos | Leave a comment

Quick look at the upcoming update of Camelot Joomla Tools for SharePoint

It’s nearly a year ago since we released the last version of the Joomla Tools for SharePoint, our apologies. We have however focused a bit on enabling possibilities like direct transmit of data from SharePoint to the PHP world, including Joomla, instead of distributing the data to the local database.

So, here is a preview of what to come, if you are interested in becoming a beta tester please contact us at http://www.bendsoft.com/contact-us/

Posted in Joomla, Videos | Leave a comment

Programmatically download a SharePoint document library in .NET

In this post I will show you a simple, but yet very useful piece of code that downloads an entire SharePoint document library to local disk drive. Many of our customers need to export and publish SharePoint documents externally, sometimes involving programmatic processing and manipulation. Others just do this regularly for keeping backups. This example helps you getting started and also show how to use the Modified date of documents to sharpen the performance of your application.

Requirements

This example project can be downloaded here!

Download example!

Building the application

In this example, we will create a simple console application that queries a SharePoint document library for all its documents and folders and then iterates through the results. The entire structure will then be reconstructed on local disk, including empty folders. We will also compare the Modified date of each document with the last write time of the corresponding local file to decided whether a document needs to be updated. This may increase the performance significantly when doing large backups.

In Visual Studio 2010, create a new Console Application (C#) and call it DownloadDocumentLibraryExample.

  

Open up Program.cs and replace the content with the following lines. You will also need to add a reference to the Camelot.SharePointConnector.dll, found in the global assembly cache.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Camelot.SharePointConnector.Data;
using System.IO;

namespace DownloadDocumentLibraryExample
{
    class Program
    {
        static void Main(string[] args)
        {
            var connectionString = "Server=mysharepointserver.com;Database=;User=user;Password=password;Authentication=Ntlm;TimeOut=60;";
            var rootFolder = @"c:documents";

            var documents = Helper.ExecuteDataTable("SELECT ServerUrl, Modified, ContentType FROM `Shared Documents`", connectionString);
            if (documents.Rows.Count > 0)
            {
                foreach (System.Data.DataRow doc in documents.Rows)
                {
                    Console.WriteLine(doc["ContentType"] + "  " + doc["ServerUrl"]);

                    using (var connection = new SharePointConnection(connectionString))
                    {
                        connection.Open();

                        switch ((string)doc["ContentType"])
                        {
                            case "Folder":
                                if (!Directory.Exists(rootFolder + doc["ServerUrl"]))
                                    Directory.CreateDirectory(rootFolder + doc["ServerUrl"]);

                                break;
                            case "Document":
                                var parentFolder = Directory.GetParent(rootFolder + doc["ServerUrl"]).ToString();

                                if (!Directory.Exists(parentFolder))
                                    Directory.CreateDirectory(parentFolder);

                                if (!File.Exists(rootFolder + doc["ServerUrl"]) || File.GetLastWriteTime(rootFolder + doc["ServerUrl"]) != (DateTime)doc["Modified"])
                                {
                                    using (var command = new SharePointCommand("CALL DOWNLOAD('Shared Documents', '" + doc["ServerUrl"] + "')", connection))
                                    {
                                        var bytes = (byte[])command.ExecuteScalar();
                                        File.WriteAllBytes(rootFolder + doc["ServerUrl"], bytes);
                                        File.SetLastWriteTime(rootFolder + doc["ServerUrl"], (DateTime)doc["Modified"]);
                                    }
                                }

                                break;
                        }

                    }
                }
            }
            Console.WriteLine("Complete!");
            Console.ReadKey();
        }
    }
}

Firstly, we declare our SharePoint connection string required for connecting to the SharePoint server. You will need to modify the connection string in the example according to your configuration. The template in the example should work fine in most cases but all options are described in the connector documentation. Secondly, we declare a root folder where all documents will be downloaded, e.g. “c:\documents”.

Now we are ready to query the document library. For simplicity in this example, we choose to download the Shared Documents library. Our query selects all items in this library returning the ServerUrl, Modified and ContentType columns. The first column indicates the relative path of a document or folder, e.g. “textfile.txt” or “folder1/folder2/textfile.txt”, giving us the structure of the library. It will be printed to the console window. The ContentType can be either “Folder” or “Document”. Note that we use the ExecuteDataTable method in the Helper class to select directly into a DataTable.

In the next step, we iterate through the items returned by the select query. If the item is of type “Folder”, we simply create the folder under the root folder. If the item is of type “Document”, we first compare the date of any existing file with the SharePoint Modified date to determine if the document has changed since it was last downloaded. If it has, we use the DOWNLOAD procedure of the Camelot .NET Connector to fetch the file and then write it to disk.

Running the application

Now run the application and see what happens. If everything goes fine and there are documents and/or folders in the library, you should see a list of items as they are being processed. When the application is done you should find the documents in your root folder.

  

If you run the application a second time, you probably notice that documents are not being downloaded again until they have been modified.

Posted in Camelot .NET Connector, General, Integrations, SharePoint | Leave a comment

Howto install and activate the Camelot .NET Connector for SharePoint

To better support the users who aren’t developers we decided to record a few screencasts describing some of the basics and howto’s of our products.

This describes how to install and activate the Camelot .NET Connector for SharePoint which you can download from http://www.bendsoft.com/net-sharepoint-connector/

Posted in Camelot .NET Connector, Videos | Leave a comment