The Only Thing You Can’t Do With the SharePoint Object Model

Can you guess what item I found that you can't do with the SharePoint Object Model?  I have been through the entire API from top to bottom and there seems to be one thing missing!

 A way to add authoratative URLs to your SSP Search Configuration!  In my latest course, SharePoint Search Administration, you explore the Search API looking to perform every task that is available through the web UI.  I was successful in doing everything but add those nice relevance algorithm changes authoritative URLs

 If you have successfully done this, ping me back!  Otherwise, I have to think that it was the ONE AND ONLY THING that got left out of the Object Model (oh, let's not talk about the web services interfaces shall we…).

Create a BDC Application Definition for a Web Service

In working with a large Canadian Law firm this week, I built this lab to help them learn to build Web Services that cater to BDC Web Service Application Definition files.  Pretty neat stuff…keep in mind that you have  to setup the BDC Editor before performing these steps.

Exercise 1 – Create a BDC App Def File (Web Service)

Purpose:
        Create
a BDC Application Definition File with the Microsoft BDC tool.  Note that this tool is a basic editor, it
doesn’t implement any advanced functionality. 
To do advanced things, you will need to reference the schema file and
build your own xml.

Result:           
An Application Definition File

Task 1 –Create a Web
Service

  1. In Visual Studio, create a
    new web service project
    • Click
      “File->New->Web Site”
    • Select “ASP.NET Web
      Service”
    • For location, type
      “D:lab workBDCWebService”
    • Click “Ok”

Task 2 –Create a
return class

  1. Create a new class to act as
    our return type
    • Right click the
      project, select “Add New Item”
    • Select “Class”
    • For name, type
      “Product.cs”
    • Click “Add”
  2. Click “Yes” to the “App_Code”
    directory
  3. Add the following variable to
    the class (copy under the “public class Product” line):



protected int m_ProductID;

    protected
string m_ProductName;

    protected
int m_SupplierID;

    protected
int m_CategoryID;

    protected
string m_QuantityPerUnit;

    protected
double m_UnitPrice;

    protected
short m_UnitsInStock;

    protected
short m_UnitsOnOrder;

    protected
short m_ReorderLevel;

    protected
bool m_Discontinued;

 

  1. Add the following properties
    to the class:


public int ProductID

    {

        get {
return (m_ProductID); }

        set {
m_ProductID = value; }

    }

    public
string ProductName

    {

        get {
return (m_ProductName); }

        set {
m_ProductName = value; }

    }

    public int
SupplierID

    {

        get {
return (m_SupplierID); }

        set {
m_SupplierID = value; }

    }

    public int
CategoryID

    {

        get {
return (m_CategoryID); }

        set {
m_CategoryID = value; }

    }

    public
string QuantityPerUnit

    {

        get {
return (m_QuantityPerUnit); }

        set {
m_QuantityPerUnit = value; }

    }

    public
double UnitPrice

    {

        get {
return (m_UnitPrice); }

        set {
m_UnitPrice = value; }

    }

    public
short UnitsInStock

    {

        get {
return (m_UnitsInStock); }

        set { m_UnitsInStock
= value; }

    }

    public
short UnitsOnOrder

    {

        get {
return (m_UnitsOnOrder); }

        set {
m_UnitsOnOrder = value; }

    }

    public
short ReorderLevel

    {

        get {
return (m_ReorderLevel); }

        set {
m_ReorderLevel = value; }

    }

    public bool
Discontinued

    {

        get {
return (m_Discontinued); }

        set {
m_Discontinued = value; }

    }

 

 

  1. Add the following methods to
    the class:



public Product(IDataRecord record)

    {

       
this.Fill(record);

    }

 

    internal
void Fill(IDataRecord record)

    {

       
m_ProductID = (int)record["ProductID"];

       
m_ProductName = (string)record["ProductName"];

 

        if
(record["SupplierID"] != DBNull.Value)

        {

            m_SupplierID =
(int)record["SupplierID"];

        }

        if
(record["CategoryID"] != DBNull.Value)

        {

           
m_CategoryID = (int)record["CategoryID"];

        }

        if
(record["QuantityPerUnit"] != DBNull.Value)

        {

            m_QuantityPerUnit
= (string)record["QuantityPerUnit"];

        }

        if
(record["UnitPrice"] != DBNull.Value)

        {

           
m_UnitPrice = Convert.ToDouble(record["UnitPrice"]);

        }

        if
(record["UnitsInStock"] != DBNull.Value)

        {

           
m_UnitsInStock = Convert.ToInt16(record["UnitsInStock"]);

        }

        if
(record["UnitsOnOrder"] != DBNull.Value)

        {

           
m_UnitsOnOrder = Convert.ToInt16(record["UnitsOnOrder"]);

        }

        if
(record["ReorderLevel"] != DBNull.Value)

        {

           
m_ReorderLevel = Convert.ToInt16(record["ReorderLevel"]);

        }

       
m_Discontinued = (bool)record["Discontinued"];

 

    }

 

  1. Add the following attribute
    to the class:



[Serializable]

 

  1. Compile the project, fix any
    errors

Task 3 –Implement the
web methods

  1. Open the Service.cs file
  2. Add the following using
    statements to the file:


using System.Data;

using System.Data.SqlClient;

using System.Collections.Generic;

using System.ComponentModel;

using Microsoft.ApplicationBlocks.Data;

using System.Configuration;

 

  1. Add a property to get a
    connection string from the configuration file:


private string ConnectionString

    {

        get

        {

           
AppSettingsReader reader = new AppSettingsReader();

           
return (string)reader.GetValue("ConnectionString",
typeof(string));

        }

    }

 

  1. Open the web.config file
  2. Add the following line to the
    <Configuraton><appSettings> element:


    <add
key="ConnectionString"
value="server=.;database=northwind;uid=sa;pwd=Pa$$w0rd"/>

 

  1. Save the file
  2. Add a method to get a list of
    ids:



[WebMethod(Description = "Returns a List of
Products IDs")]

    public
List<int> GetProductEnumeratorIDs()

    {

       
List<int> retVal = new List<int>();

 

        string
sql = "SELECT [ProductID] FROM [Products]";

 

       
SqlDataReader reader = null;

 

        try

        {

 

           
reader = SqlHelper.ExecuteReader(this.ConnectionString,
CommandType.Text, sql);

 

            if
(reader.HasRows)

            {

 

               
while (reader.Read())

               
{

                   
retVal.Add(reader.GetInt32(0));

                }

 

            }

 

        }

        catch

        {

           
throw;

        }

        finally

        {

            if
(reader != null && !reader.IsClosed)

            {

               
reader.Close();

            }

        }

 

        return
retVal;

    }

 

  1. Add a method to get a single
    Product from the database by ProductId:


[WebMethod(Description = "Returns a single Product Entity by ID")]

    public
Product GetProduct(int productID)

    {

        string
sql = "SELECT [ProductID], [ProductName], [SupplierID], [CategoryID],
[QuantityPerUnit], [UnitPrice], [UnitsInStock], [UnitsOnOrder],
[ReorderLevel], [Discontinued] FROM [Products] WHERE [ProductID] =
@ProductID";

 

       
SqlDataReader reader = null;

 

        try

        {

 

           
reader = SqlHelper.ExecuteReader(this.ConnectionString,

                            CommandType.Text,
sql,

                            new
SqlParameter[] {

                                                    new SqlParameter("@ProductID",
productID) });

 

            if
(reader.HasRows)

            {

               
reader.Read();

               
return new Product(reader);

 

            }

 

        }

        catch

        {

           
throw;

        }

        finally

        {

            if
(reader != null && !reader.IsClosed)

            {

               
reader.Close();

            }

        }

 

        return
null;

    }

 

  1. Add a method to get a single
    Product from the database by ProductName:



[WebMethod(Description = "Returns a List of
Products Filtered By Name")]

    public
List<Product> GetProductsByName(string productName)

    {

       
List<Product> retVal = new List<Product>();

 

        string
sql = "SELECT [ProductID], [ProductName], [SupplierID], [CategoryID],
[QuantityPerUnit], [UnitPrice], [UnitsInStock], [UnitsOnOrder], [ReorderLevel],
[Discontinued] FROM [Products] WHERE [ProductName] Like @ProductName";

 

       
SqlDataReader reader = null;

 

        try

        {

 

           
reader = SqlHelper.ExecuteReader(this.ConnectionString,

                            CommandType.Text,
sql,

                            new
SqlParameter[] {

                                                    new
SqlParameter("@ProductName", string.Format("%{0}%",
productName)) });

 

            if
(reader.HasRows)

            {

 

               
while (reader.Read())

               
{

                   
retVal.Add(new Product(reader));

               
}

 

            }

 

        }

        catch

        {

           
throw;

        }

        finally

        {

            if
(reader != null && !reader.IsClosed)

            {

               
reader.Close();

            }

        }

 

        return
retVal;

    }

  1. Add a method to get all the
    Products from the database:


[WebMethod(Description = "Returns a List of Products")]

    public
List<Product> GetProducts()

    {

       
List<Product> retVal = new List<Product>();

 

        string
sql = "SELECT [ProductID], [ProductName], [SupplierID], [CategoryID],
[QuantityPerUnit], [UnitPrice], [UnitsInStock], [UnitsOnOrder],
[ReorderLevel], [Discontinued] FROM [Products]";

 

       
SqlDataReader reader = null;

 

        try

        {

 

           
reader = SqlHelper.ExecuteReader(this.ConnectionString,

                            CommandType.Text,
sql);

 

            if
(reader.HasRows)

            {

 

               
while (reader.Read())

               
{

                   
retVal.Add(new Product(reader));

               
}

 

            }

 

        }

        catch

        {

           
throw;

        }

        finally

        {

            if
(reader != null && !reader.IsClosed)

            {

               
reader.Close();

            }

        }

 

        return
retVal;

    }

 

  1. Add a helper class called
    SqlHelper:
    • Right click the
      project, select “Add New Item”
    • Select “Class”
    • For name, type
      “SqlHelper”
    • Click “Add”
  2. Paste the code in the 07_Lab07.extra.txt
    code snippet file into the file
  3. Compile the project, fix any
    errors
  4. Set the web service port to
    200
    • Click the project in
      the solution explorer
    • In the “Properties”
      window, set Dynamic Ports to “false”
    • Set the Port number to
      “2000”
  5. Run the web service, press F5
  6. Click “Ok” to create the
    web.config file
  7. You should see your web
    service running on port 2000


Task 4 –Create a BDC
Application Definition file

  1. Click “Start->All
    Programs->Microsoft Business Data Catalog Definition Editor”
  2. The BDC editor will start:


  1. Click “Add LOB System”
  2. Click “Connect to Web Service”
  3. Type the URL of the web
    service “http://localhost:2000/BDCWebService/Service.asmx”
  4. Click “Connect”
  5. Click “Add Web Method” (on
    the right side)
  6. Drag all the methods to the design surface (make sure they are all
    added to the same Entity)!
    • GetProductEnumeratorIDs
    • GetProduct
    • GetProductsByName
    • GetProducts


  1. Click “Ok”
  2. For the name, type “Products”
  3. Click “Ok”
  4. Note how our Instances and
    Entities are populated


  1. Also note how the
    Identifiers, Methods and Actions are populated
  2. Right Click on any node,
    notice how you get the ability to add a new item that is appropriate for
    whatever level you are on in the tree view


  1. Select the “Enitiy1” node
  2. In the Property editor,
    change the name to “Products”


  1. Expand the
    Products->Methods->GetProductByName->Parameters->Return->Return->Item
    nodes


  1. You will see the properties
    of the Product class exposed as fields that will be returned in your BDC
    Application!
  2. Right click “Identifier”,
    select “Add Identifier”
    • For Name, type
      “ProductId”
    • For the Type, select
      “System.Int32”
  3. Setup an Enumerator method
    • Expand GetProductEnumeratorIDs
    • Right click
      “Instances”, select “Add Method Instance”
    • Click the “Id
      Enumerator” method type
    • For the name, type
      “EnumId”
    • Click “Ok”
  4. Setup a SpecificFinder method
    • Expand GetProduct
    • Right click “Filters”,
      select “Add Filter”
    • For FilterType, select
      “Equals”
    • For Name, type
      “ProductId”
    • Expand
      “Parameters->Return->Return”
    • Select “ProductID”
    • Set the Identifier to
      “ProductId[Product]”
    • Right click
      “Instances”, select “Add Method Instance”
    • Click the “SpecificFinder”
      method type
    • For the name, type
      “ProductSpecificFinder”
    • Click “Ok”
  5. Setup a Finder method
    • Expand GetProducts
    • Expand
      “Parameters->Return->Return->Item”
    • Select “ProductID”
    • Set the Identifier to
      “ProductId[Product]”
    • Right click
      “Instances”, select “Add Method Instance”
    • Click the “Finder”
      method type
    • For the name, type
      “ProductFinder”
    • Click “Ok”
  6. Right click the “Products”
    LobSystem and select “Export”


  1. Save to your desktop as Lists.xml
  2. Open the file, review its
    contents

Task 5 –Upload your
new BDC Application

  1. Open the Central
    Administration site
  2. Click “SharedServices1”
  3. Click “Import Application
    Definition”
  4. Select your “products.xml”
    file
  5. Click “Import”

Task 7 –Create BDC
Web Part

  1. Open your team site
  2. Click “Site Actions->Edit
    Page”
  3. Select the “Business Data
    List” web part
  4. Click “Add”
  5. Click “Open the tool pane”
    link
  6. For Type, click the browse
    button
  7. Select the “Products”
    business Data Type
  8. Click “Ok”
  9. Click “Ok”
  10. You should see a listing of
    all the products from the web service!


 

Use InfoPath to create your BDC Application Definitions

I found the ability to point InfoPath at XSD's useful in working with the BDC xsd and building application definitions.  Check it out, think about your other XSDs and how you might create forms around them!

 

Exercise 3 – Create An InfoPath Form (Xml)

Purpose:
        Learn
to create an InfoPath Form from an Xml document!

Result:           
An InfoPath Form

Task 1 – Create A Xml
Based Form

  1. Open InfoPath 2007 (if open,
    click “File->Design a Form Template”)
  2. Click “Design A Form
    Template”
  3. Select the “Xml or Schema”
    icon
  4. Click the “Enable
    browser-compatible features only”


  1. Click “Ok”
  2. Browse to the “C:Program
    FilesMicrosoft Office Servers12.0Bindcmetadata.xsd” file
  3. Click “Open”
  4. Click “Next”
  5. Click “Finish”
  6. From the Data Source view, drag
    the “LobSystem” node to the work area
  7. Select “Section with
    Controls”


  1. Click “Preview”
  2. Notice how you can now create
    a BDC application definition file using InfoPath!


  1. Close the preview
  2. Save the template as “MyXsdForm”

 

Building Custom SharePoint 2007 Document Converters

Module #09: Custom
Document Converter Lab #
02
(Optional)

 

 

Course:           Programming
Microsoft Office SharePoint Server

Estimated Time to Complete:  45 minutes

Objectives:

·        
Create/Deploy a
Custom Converter

Operating
Notes:
 

 

·        
Document Converters do not run on Domain Controllers!
Therefore, you will be able to create and install the converter, but not test
it with this image

Deliverables:

·        
None

 

Overview:         Learn
to create your own Document Converter

Exercise 1 – Create the Converter

Purpose:
        Create
a converter to transform a document

Result:           
A document converter

Task 1 – Create a Console
Application

  1. Open Visual Studio
  2. Create a new project
    • Click “File->New->Project”
    • Select “Console
      Application”
    • For name, type “MyConverter”
    • For location, type “D:Lab
      Work”

    • Click “Ok”
  1. Add a reference to
    WindowsBase
    • Right click the
      project, select “Add reference”
    • On the .NET tab,
      select “WindowsBase”
    • Click “Ok”

Task 2 – Update the
code

  1. Update the program.cs file to
    the following:


using System;

using System.Collections.Generic;

using System.Text;

using System.IO;

using System.IO.Packaging;

using System.Xml;

 

namespace MyConverter

{

    class
ConverterShell

    {

        #region
Private Members

 

        private
string[] inputArgs = null;

        private
string localPathtoFile = null;

        private
string localSettings = null;

        private
string localLogPath = null;

        private
string localOutputFullPath = null;

        private
const string ConverterName = "Xml to Xml";

        private
const string InputExtension = ".xml";

        private
const string OutputExtension = ".xml";

        private
const string InputArgInText = "-in";

        private
const string InputArgSettingsText = "-settings";

        private
const string InputArgOutText = "-out";

        private
const string InputArgLogText = "-log";

        private
StreamWriter streamLog = null;

 

       
#endregion

        #region
main

        static
void Main(string[] args)

        {

            //
Create New Converter

           
ConverterShell localConverter = new ConverterShell();

           
localConverter.inputArgs = args;

            //
Get command line

            localConverter.GetCommandLine(localConverter.inputArgs);

            //
Verify the files and copy

            if
(localConverter.VerifyFilesExist())

            {

XmlToXml myConverter = new
XmlToXml(localConverter.localOutputFullPath);

            }

           
return;

        }

       
#endregion

 

        #region
support routines

        bool
VerifyFilesExist()

        {

           
StreamReader streamIn = null;

           
bool disableCopy = false;

 

            if
(this.localPathtoFile.Equals(this.localOutputFullPath))

            {

               
disableCopy = true;

            }

            if
(this.localOutputFullPath.Length.Equals(0))

            {

               
LogResult(" Output file " + this.localOutputFullPath +
"is null ");

               
LogResult(" Please use a different file for the output file
");

               
return false;

            }

 

            try

            {

               
Stream streamInTmp = File.OpenRead(this.localPathtoFile);

               
streamIn = new StreamReader(streamInTmp);

               
streamIn.Close();

            }

           
catch

            {

               
LogResult("Error Input File for reading" +
this.localPathtoFile);

               
return false;

            }

           
LogResult(" Converting file " + this.localPathtoFile +
" to " + this.localOutputFullPath);

            //
Copy the file to the output path

            if
(!disableCopy)

            {

               
try

               
{

                   
File.Copy(this.localPathtoFile, this.localOutputFullPath, true);

               
}

               
catch

               
{

                   
LogResult("Error copying file to " +
this.localOutputFullPath);

                   
return false;

               
}

            }

           
return true;

        }

        // Get
Command Line Parameters

        public
void GetCommandLine(string[] args)

        {

            int
i = 0;

            //
Get all of the command line arguments

           
//           
Console.WriteLine(" args " + args[0] + " " +
args[1] +

           
//                " "
+ args[2] + " " + args[3] + " " + args[4] + " "
+ args[5]);

            try

            {

               
while (i < 8 && args[i] != null)

                {

                   
if (args[i] == InputArgInText && args[i + 1] != null)

                   
{

                        this.localPathtoFile
= args[i + 1];

                        i += 2;

                        continue;

                 
  }

                   
if (args[i] == InputArgSettingsText && args[i + 1] != null)

                   
{

                        this.localSettings =
args[i + 1];

                        i += 2;

                        continue;

                   
}

                    if (args[i] ==
InputArgOutText && args[i + 1] != null)

                   
{

                       
this.localOutputFullPath = args[i + 1];

                        i += 2;

                        continue;

                   
}

                    if (args[i] == InputArgLogText
&& args[i + 1] != null)

                   
{

                        this.localLogPath =
args[i + 1];

                        i += 2;

                        continue;

                   
}

                   
i += 2;

               
}

            }

           
catch (IndexOutOfRangeException e)

            {

               
// Need to eat the exception since we do not want to end because of
insufficient args

               
return;

            }

           
return;

        }

        void
LogResult(string value)

        {

            try

            {

               
Stream streamLogTmp = File.Open(this.localLogPath, FileMode.Append);

               
this.streamLog = new StreamWriter(streamLogTmp);

               
this.streamLog.WriteLine(value);

               
this.streamLog.Close();

            }

           
catch

            {

               
Console.WriteLine(value);

            }

        }

       
#endregion

    }

}

 

  1. Add a new class called
    XmlToXml.cs
    • Right click the
      project, click “New->Item”
    • Select “Class”
    • For Name, type
      “XmlToXml.cs”

    • Click “Add”
  1. Modify the class code to the
    following:


using System;

using System.Collections.Generic;

using System.Text;

using System.IO;

using System.Xml;

 

namespace MyConverter

{

    class
XmlToXml

    {

        private
string filePath = null;

       

        public
XmlToXml(string pathName)

        {

           
filePath = pathName;

        }

 

        public
void AddNode()

        {

            try

            {

               
XmlDocument doc = new XmlDocument();

               
doc.Load(filePath);

               
doc.LastChild.AppendChild(doc.CreateNode(XmlNodeType.Element, "NewNode",
"A new node"));

               
doc.Save(filePath);

            }

           
catch (Exception ex)

            {

               
throw ex;

            }

           
finally

           
{               

            }

        }       

    }

}

 

  1. Review the code you just
    pasted in, mainly review the “Main” method
  2. Right click the project, select
    “Properties”
  3. Click the “Debug” tab
  4. For Command line arguments,
    copy the following:


-in C: arget.xml –out c:output.xml –log c:converter.log

  1. Compile the code, fix any
    errors
  2. Copy the executable (“D:Lab WorkMyConverterMyConverterinDebugMyConverter.exe”)
    to the “C:program filesMicrosoft office servers12.0TransformApps”
    directory.
    • NOTE: this is where
      your document converters should reside, notice the other converters that
      reside here:
      1. DocXPageConverter
      2. InfoPathPageConverter
      3. XslApplicatorConverter

Task 3 – Set
permission on HtLauncher directory

 

1.      Open
the “c:Program FilesMicrosoft office Servers12.0Bin” directory

2.      Right
click the HtmlTrLauncher directory, select “Properties”

3.      Click
“Security”

4.      Add
everyone with Full Control

5.      Click
“Ok”

 

Task 4 – Create a
feature and elements file

  1. Create a new directory called
    MyConverter in the /template/features directory of the 12 hive
  2. Create a file called
    feature.xml, paste the following into it:


<?xml version="1.0" encoding="utf-8" ?>

<Feature 
Id="[new guid]"

         
Title="My Converter"

         
Description="Converts xml to my own format"

         
Version="1.0.0.0"

         
Scope="WebApplication"

         
xmlns="http://schemas.microsoft.com/sharepoint/">

   
<ElementManifests>

       
<ElementManifest Location="MyConverter.xml"/>

   
</ElementManifests>

</Feature>

 

  1. Also note that you need to
    replace the [new guid] with a new GUID
    • Run the D:Lab Files09_Lab02guidgen.exe
      tool
    • Click “Radio #4”
    • Click “Copy”
    • Paste the GUID into
      the feature.xml file
    • Remove the curly
      braces ({})
  2. Create another file called
    MyConverter.xml, paste the following into it:


<?xml version="1.0" encoding="utf-8" ?>

<Elements
xmlns="http://schemas.microsoft.com/sharepoint/">

   
<DocumentConverter

       
Id="[new guid]"

       Name="Xml
to Xml"

       App="MyConverter.exe"

       From="xml"

       To="xml"
/>

</Elements>

 

  1. Be sure you create another
    guid id for the converter!
  2. Run the following commands:


stsadm –o installfeature –filename MyConverterfeature.xml

stsadm –o activatefeature –filename
MyConverterfeature.xml –url
http://servername:100

 

    • NOTE: if you copy and paste
      and get an error when running the command, replace the dashes!
  1. Perform an IISRESET
  2. Your new document converter
    is now installed!

Task 5 –Enable
Document Converters

  1. Open the Central
    Administration Site
  2. Click “Application management”
  3. Click “Manage Web Application
    features”
  4. For the “My Converter”
    feature, click “Activate”
    • NOTE: Ensure you are
      in context of web application on port 100
  5. Click “Document Conversions”
  6. Ensure that the web
    application is set to your team site web app
  7. Check the “Yes” radio button
  8. Select a load balancer server
    (you likely only have one to pick from)
  9. Notice your new converter is
    listed (“Customize “Xml to Xml” (xml into xml))
  10. Open in a new tab/windows the
    link for the converter, note the settings you can apply
  11. In the original central
    administration window, click Apply

Task 6 – Test the
converter

  1. Open your team site (http://servername:100)
  2. Add an xml document to the
    “Shared Documents” document library called TheirXml.xml with the following
    text inside the file:

 

<?xml version="1.0"
encoding="utf-8" ?>

<WatchThis>

</WatchThis>

 

  1. In the JavaScript dropdown
    for the item, select “Convert Document->Xml To Xml”

  1. For document name, type
    “OurXml”

  1. Click “Ok”
  2. A timer job will pick up the
    conversion request and put the converted document in the Pages document
    library.  As we mentioned previously
    before, a document converter will not run on a domain controller properly.