Showing posts with label DMS. Show all posts
Showing posts with label DMS. Show all posts

Friday, 6 January 2012

Adding a Custom Content Type to a document library with Powershell in Sharepoint 2010

Sometimes you will want to add your own Content Types (custom content types) to your list or document libraries (a list basically…). You can do it with the web interface, it is pretty easy, but what about if you need to perform this for 30,000 sites… you don’t want to spend the rest of the year clicking everywhere.

I have created a function , AddContentTypeToDocumentLibraryRemovingOtherContentType, which allows you to Add a Content Type (“DMSDocument”) and remove the default one (“Documents”). Be aware I will remove “Documents” from my list and add the content type from the site. Whart I am doing here is replacing the default “Document” content type with sophisticated Document content type I developed before called “DMSDocument”

You only need to pass:

  1. Name of the site URL
  2. Name of the Document Library
  3. Name of the Content Type to be added
  4. Name of the Content Type you want to remove from your Document Library.
 $_CurrentURL =  "http://sp_server"
 $_DocumentLibraryName = "Document Centre"
 $_ContentTypeToBeRemoved = "Document"
 $_ContentTypeToBeAdded = "DMSDocument"
 
 Function AddContentTypeToDocumentLibraryRemovingOtherContentType($_sCurrentURL,$_sDocumentLibraryName,$_sContentTypeToBeRemoved, $_sContentTypeToBeAdded)
 { 
 		## Getting the website...
  		$SPWeb = Get-SPWeb -Identity $_sCurrentURL		
		
		## Getting the Document Library (list) and enable Content Types
		$List = $SPWeb.Lists[$_sDocumentLibraryName]
		$List.ContentTypesEnabled = $true
		$List.update()
		
		## Getting the content types to be removed
		## NOTE: This particular one, "Document" lives in our list,
		##       so we remove it from there
		$DocumentCT = $List.ContentTypes[$_sContentTypeToBeRemoved]		
		## This Content type lives in the site, so we get it from there
    	         $CustomCT = $SPWeb.ContentTypes[$_sContentTypeToBeAdded]
    	         $List.ContentTypes.Delete($DocumentCT.Id)
		$List.ContentTypes.Add($CustomCT) 		
		$List.update()	
		
		## Disposing SPWEB object to avoid memory leaks.
		$SPWeb.Dispose()
}
AddContentTypeToDocumentLibraryRemovingOtherContentType $_CurrentURL $_DocumentLibraryName $_ContentTypeToBeRemoved $_ContentTypeToBeAdded

Conclusion: Powershell will bring you a high rate of productivity, but don’t forget to dispose the objects, otherwise you could cause memory leaks in your farm.

Thursday, 13 October 2011

Building a Document Management System with SharePoint 2010 - Part 11–Creating Content Types from the Client Model

This post is more about how to improve the performance of your DMS to the extreme using the client model:
Microsoft.SharePoint.Client.Runtime.dll
Microsoft.SharePoint.Client.dll
Well, what I mean with best performance is to be able to not just create Content Types with the Client Model (I don’t cover this part here because I assume you already have the content types), it is the fact we can assign Content Types, something that is not allowed in the client model unless you use some tricks.

What I am going to do is to assign two different Content Types (Client Folder, Matter Folder) so we can have a client/matter structure in our DMS. Both Content Types are already created and inherits from the native folder Content Type. So we will have something like this:


+Client1----+Matter1
                   |
                   +Matter2
                   |
                   +Matter3

Before going ahead wit the chunk of code, I will explain you how it works.
1- You create your folder.
2- You get the id from your content type.
3- You apply the id into this field: “ContentTypeId”. I update the client number and client description.

ListItem item = items[0];
item["ContentTypeId"] = _ctClientFolder.Id;
item["client"] = _sClientNumber;
item["clientdescription"] = _sClientDescription;
item.Update();
clientContext.ExecuteQuery();



I am going to add the code below, to use it, be sure you add:
Microsoft.SharePoint.Client.Runtime.dll
Microsoft.SharePoint.Client.dll
Into your references and :

using Microsoft.SharePoint.Client;
To call the method just try this:
CreateClientMatter(@"http://mysharepointserver", "Documents", "00000272", "my client", "00000018", "my matter");
Just copy and paste this code into your project, if the client/matter is not there it will created and add the properties:
        private List ListCheck(ClientContext clientContext, Web _wListWeb, string _sListName)
        {
            List _lExistingList = null;
            try
            {
                //###############################
                //## We check if the file exists
                //###############################
                _wListWeb = clientContext.Web;
                ListCollection _lCollectionOfLists = _wListWeb.Lists;
                IEnumerable<List> existingLists = clientContext.LoadQuery(
                        _lCollectionOfLists.Where(
                        list => list.Title == _sListName)
                        );
                clientContext.ExecuteQuery(); 
                _lExistingList = existingLists.FirstOrDefault();
                //############################### 
            }
            catch (Exception ex)
            {
                throw new ArgumentNullException();
            }
            return _lExistingList;
        }
        private void CreateClientFolder(string _sServerURL, ClientContext clientContext, Web _wListWeb, List _lList, string _sListName, string _sClientNumber, string _sClientDescription)
        {
            //## Variables.
            Folder _fExistingFolder = null;
            ContentType _ctClientFolder = null; 
            //## We get all the folders
            FolderCollection _afolders = _lList.RootFolder.Folders; 
            //## Building the URL (folder)
            String _sClientfolderUrl = String.Format("/{0}/{1}", _sListName, _sClientNumber);
            //## We check if the URL exists
            IEnumerable<Folder> _iClientExistingFolders = clientContext.LoadQuery<Folder>(
                _afolders.Where(
                folder => folder.ServerRelativeUrl == _sClientfolderUrl)
                );
            clientContext.ExecuteQuery();
            //## Getting the result
            _fExistingFolder = _iClientExistingFolders.FirstOrDefault();
            if (_fExistingFolder == null)                       
            {
                //## Adding the folder
                _wListWeb.Folders.Add(_sServerURL + @"/" + _sListName + @"/" + _sClientNumber);
                clientContext.ExecuteQuery();
                //## Quering our folder
                CamlQuery query = new CamlQuery();
                query.ViewXml = @"       <View>
                                             <Query>
                                                <Where>
                                                   <Eq>
                                                      <FieldRef Name='FileLeafRef'/>
                                                      <Value Type='Text'>"+_sClientNumber+@"</Value>
                                                   </Eq>
                                                </Where>
                                             </Query>
                                             <RowLimit>1</RowLimit>
                                          </View>"; 
                ListItemCollection items = _lList.GetItems(query);
                clientContext.Load(items);
                //## Getting the content types
                ContentTypeCollection _ctListOfContentTypes = clientContext.Web.AvailableContentTypes;
                clientContext.Load(_ctListOfContentTypes);
                clientContext.ExecuteQuery();
                //## Getting the Content Type Id
                foreach (ContentType _ctItem in _ctListOfContentTypes)
                {
                    if (_ctItem.Name == "Client Folder")
                    {
                        _ctClientFolder = _ctItem;
                        break;
                    }
                }
                //## This is the tricky part, where we convert our folder
                //## Into a content type. We add some metadata as well               
                if (items.Count > 0)
                {
                    ListItem item = items[0];
                    item["ContentTypeId"] = _ctClientFolder.Id;
                    item["client"] = _sClientNumber;
                    item["clientdescription"] = _sClientDescription;
                    item.Update();
                    clientContext.ExecuteQuery();
                }
            }
        }
 
        private void CreateMatterFolder(string _sServerURL, ClientContext clientContext, Web _wListWeb, List _lList, string _sListName,string _sClientNumber, string _sMatterNumber,string _sMatterDescription)
        {
            Folder _fExistingFolder = null;
            ContentType _ctMatterFolder = null; 
            FolderCollection _afolders = _lList.RootFolder.Folders; 
            String _sMatterfolderUrl = String.Format("/{0}/{1}/{2}", _sListName, _sClientNumber, _sMatterNumber); 
            IEnumerable<Folder> _iMatterExistingFolders = clientContext.LoadQuery<Folder>(
                _afolders.Where(
                folder => folder.ServerRelativeUrl == _sMatterfolderUrl)
                );
            clientContext.ExecuteQuery();
            _fExistingFolder = _iMatterExistingFolders.FirstOrDefault(); 
            if (_fExistingFolder == null)
            {
                _wListWeb.Folders.Add(_sServerURL + @"/" + _sListName + @"/" + _sClientNumber + @"/" + _sMatterNumber);
                clientContext.ExecuteQuery();
                //## Quering our folder
                CamlQuery query = new CamlQuery();
                query.ViewXml = @"       <View Scope='RecursiveAll'>
                                             <Query>
                                                <Where>
                                                   <Eq>
                                                      <FieldRef Name='FileLeafRef'/>
                                                      <Value Type='Text'>" + _sMatterNumber + @"</Value>
                                                   </Eq>
                                                </Where>
                                             </Query>
                                             <RowLimit>1</RowLimit>
                                          </View>";
 
                ListItemCollection items = _lList.GetItems(query);
                clientContext.Load(items); 
                //## Getting the content types
                ContentTypeCollection _ctListOfContentTypes = clientContext.Web.AvailableContentTypes;
                clientContext.Load(_ctListOfContentTypes);
                clientContext.ExecuteQuery(); 
                //## Getting the Content Type Id
                foreach (ContentType _ctItem in _ctListOfContentTypes)
                {
                    if (_ctItem.Name == "Matter Folder")
                    {
                        _ctMatterFolder = _ctItem;
                        break;
                    }
                }
                //## This is the tricky part, where we convert our folder
                //## Into a content type. We add some metadata as well               
                if (items.Count > 0)
                {
                    //## This is just in case the client number
                    //## has the same name than the new matter
                    ListItem item = items.Count == 2 ? items[1] : items[0];
                    item["ContentTypeId"] = _ctMatterFolder.Id;
                    item["matter"] = _sMatterNumber;
                    item["matterdescription"] = _sMatterDescription;
                    item.Update();
                    clientContext.ExecuteQuery();
                }
            }
        }
 
        public bool CreateClientMatter(string _sServerURL, string _sListName, string _sClientNumber, string _sClientName, string _sMatterNumber, string _sMatterName)
        {
            bool _bResult = false;
            List _lList = null;           
            try
            {
                using (var clientContext = new ClientContext(_sServerURL))
                {
                    Web _wListWeb = clientContext.Web;
                    //###############################
                    //## We check if the list exists
                    //###############################                   
                    _lList = ListCheck(clientContext, _wListWeb, _sListName);
                    //###############################                  
                    if (_lList != null)
                    {                       
                        //#################################
                        //## CREATE FOLDER CONTENT TYPES
                        //## IF THEY DON'T EXIST
                        //##################################
                        CreateClientFolder(_sServerURL, clientContext, _wListWeb, _lList, _sListName, _sClientNumber, _sClientName);
                        CreateMatterFolder(_sServerURL, clientContext, _wListWeb, _lList, _sListName, _sClientNumber,_sMatterNumber,_sMatterName);                    
                        //##################################
                        _bResult = true;
                    }
                }
            }
            catch (Exception ex)
            {
                string _sError = ex.ToString();               
            }
            return _bResult;
        }


Conclusion: In order to create a decent DMS be always sure you keep your server clean of web services and workflows, anything it can be done in the client side, it will leave the server free to do what it has to do, SERVE!

Monday, 19 September 2011

Creating a Sequential WorkFlow in Visual Studio with some extra bits

In this article I am going to post how to create a simple sequential workflow with Visual Studio 2010 for Sharepoint 2010. The goal of this project is to change the Title of the documents when someone inserts a bad word. It is basically a parent control for our document library [“Shared Documents”].

What do we need?

  1. Visual Studio 2010.
  2. Sharepoint 2010.
  3. A list called Shared Documents (it comes by default).
  4. 20 minutes of your time.

Step 1
Go so Visual Studio 2010->New Project->Sharepoint->2010->Empty Sharepoint Project, call it netsourcecodeWorkflows, click ok and deploy as farm solution. Go to your project right click->Add->New Item…->Sharepoint->2010->Sequential Workflow-> call it NSCParentalWorkflow.
image

Step 2
Select List Workflow because we will be working with one list. Click Next
image

Step 3
Select Shared Documents. Click Next
image

Step 4
Leave the default values are they are. Click Finish.image

Step 5
Now go your workflow (NSCParentalWorkflow.cs) double click and open the designer. Drag and drop an activity “While” just below OnWorkFlowActivated. Double click in the While activity->Properties->Condition->select Code Condition->Condition—>Type isWorkflowFinish and return.
image

Step 6
Double Click on “onWorkflowActivated1”, that will create an event for you.
image 

Step 7
Now get one of the events from the tool bar menu, for example “onWorkflowItemChanged”, drag and drop inside the while. Single click in the event and go to Properties->Correlation Token->select workflowToken. Now do a double click to create the event. You should see something like this:
image

Step 8
It is time to add the logic behind, but before that, we should find a bunch of bad words for our parental control. Because in our “virtual” company we have English people and Spaniards, we should check both languages. Anyway this is a list of bad words, combined.

  • Fuck (en)
  • Cunt (en)
  • Mother Fucker (en)
  • Piece of Shit (en)
  • Bastard (en)
  • Joder (es)
  • Gilipollas (es)
  • Puta (es)
  • Mierda (es)
  • Cabron (es)

Our workflow will check the Title, if some one tries tries to change it with a bad word it will set the filename and it will send and email to the administrator.

Step 9
Go to NSCParentalWorkflow.cs right click –> View code. Remove all the code inside and paste this one:

using System;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Collections;
using System.Drawing;
using System.Linq;
using System.Workflow.ComponentModel.Compiler;
using System.Workflow.ComponentModel.Serialization;
using System.Workflow.ComponentModel;
using System.Workflow.ComponentModel.Design;
using System.Workflow.Runtime;
using System.Workflow.Activities;
using System.Workflow.Activities.Rules;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Workflow;
using Microsoft.SharePoint.WorkflowActions;
namespace netsourcecodeWorkFlows.NSCParentalWorkFlow
{
    public sealed partial class NSCParentalWorkFlow : SequentialWorkflowActivity
    {
        public NSCParentalWorkFlow()
        {
            InitializeComponent();
        }
        public Guid workflowId = default(System.Guid);
        public SPWorkflowActivationProperties workflowProperties = new SPWorkflowActivationProperties();
        private bool isWorkFlowActive;
        private void OnWhile(object sender, ConditionalEventArgs e)
        {
            e.Result = isWorkFlowActive;
        }
        private void onWorkflowActivated1_Invoked(object sender, ExternalDataEventArgs e)
        {
            isWorkFlowActive = true;
           
            CheckTitle();
        }
        private void onWorkflowItemChanged1_Invoked(object sender, ExternalDataEventArgs e)
        {
            CheckTitle();
        }
        private void CheckTitle()
        {
            string _sTitle = workflowProperties.Item["Title"]!=null?workflowProperties.Item["Title"].ToString():"";
            string _sFileRef = workflowProperties.Item["FileLeafRef"].ToString();
            switch (_sTitle.ToLower())
            {
                case "fuck":
                case "bastard":
                case "cunt":
                case "mother Fucker":
                case "piece of Shit":
                case "joder":
                case "gilipollas":
                case "puta":
                case "mierda":
                case "cabron":
                    {
                        workflowProperties.Item["Title"] = _sFileRef;
                        workflowProperties.Item.Update();
                        isWorkFlowActive = false;
                        SendEmailToAdministrator();
                        break;
                    }
                default: break;
            }          
        }
        public void SendEmailToAdministrator()
        {
            System.Collections.Specialized.StringDictionary _aHeaders = new System.Collections.Specialized.StringDictionary();
            _aHeaders.Add("to", "admin@netsourcecode.com");
            _aHeaders.Add("from", "admin@netsourcecode.com");
            _aHeaders.Add("subject", "This guy is inserting bad words into our DMS");
            string _sBody = SPContext.Current.Web.CurrentUser.Email;
            Microsoft.SharePoint.Utilities.SPUtility.SendEmail(workflowProperties.Web, _aHeaders, _sBody);
        }
    }
}

Step 10
Deploy the project, add an item, and try to edit a document and insert a bad word. The Title will revert itself to the file name.


Click the Icon to download the Icon.
image

Monday, 21 February 2011

Building a Document Management System with Sharepoint 2010 - Part 9 - UI Office 2010 (Adding Ribbon to the Fluent UI)

We are going to explore how to write a Ribbon-Tab in MS Word. What I am going to do is basically clone an excelent article wrote by Ken Getz, MCW Technologies, LLC in 2006 (http://msdn.microsoft.com/en-us/library/aa338202.aspx#OfficeCustomizingRibbonUIforDevelopers_Customizing) and extend it, bringing even more resources. This article has been for a long time the only source for developers of how to customize Ribbons in MS Word.

I am not going to change the design of the tab because it contains all the basic controls. It looks messy but I think, that is exactly what we need.

1) Open Microsoft Word 2010
2) Go to the Developer Tab.
3) If you do not see the Developer tab, you must identify yourself as a developer. To do this in your application, click the Microsoft Office Button, click Application Options, click Customize Ribbon, and then select Show Developer Tab in the Ribbon. This is a global setting that identifies you as a developer in all Office applications that implement the Fluent UI.
4) Click on the Visual Basic button.

5) Click on ThisDocument.
6) Copy and paste this code (VBA function) into the box:

Sub justamacro(ByVal control As IRibbonControl)

MsgBox ("I have done something!")
End Sub

7) Close this window.

8) Now we have to save this file as a document template, to do that just go to File->Save As and type c:\productivetemplate.docm (if you use Excel or PowerPoint you can do exactly the same thing but saving the document with .xlsm, and .pptm.

9) Exit from Microsoft Word.
10) Now! We are moving to the “funny stuff” . Go to C:\ , create a folder called customUI , create a new xml file inside called customUI.XML, then copy and paste this code, save the file and close it.





































11) Go to your c:\productivetemplate.docm and rename it with c:\productivetemplate.zip, drag and drop your folder CustomUI and open the zip file.


12) Drag the _rels folder to the desktop. A folder named _rels containing the .rels file appears on the desktop.

13) Open the new folder, and then open the .rels file in a text editor.

14) Between the final element and the closing element, add a line that creates a relationship between the document file and the customization file. Ensure that you specify the folder and file names correctly (the Id attribute supplies a unique relationship ID for the customUI—its value is arbitrary).

15) Save the .rels file.

16) Drag the .rels file from the desktop to the _rels folder in the compressed file, replacing the existing .rels file.

17) Remove the .zip extension from the container file and rename it with c:\productivetemplate.docm.

18) Now! Open the file… you should see something like this:


There are some icons we can customize, so if for example we want to display a happy face we only need to do this: imageMso="HappyFace" . This property is inside the control. To have a full list of images we will need to go to this website and download the plugin: http://www.microsoft.com/downloads/en/confirmation.aspx?FamilyID=12b99325-93e8-4ed4-8385-74d0f7661318&displaylang=en

These are some of the images you can get:




As soon as you install the plugin you can go to the developers Tab and you will see this new dialog where you click in teh image and brings back the code.



Conclusion:
We have learnt how to create Ribbons for Office 2007/2010 in a easy way, next time we will start the hard way, Visual Studio 2010, where the posibilities are completly endless. The integration between Sharepoint, Office 2010 and your systems will be eseential. That will be the time where you will notice the huge gap between MOSS and the new Sharepoint 2010.

Building a Document Management System with Sharepoint 2010 - Part 8 - UI Office 2010 (Adding Document-Based Add-ins to the Fluent UI)

We are going to explore how to deliver a custom template for Office 2010 (This can be used for Office 2007 as well).

The whole point of this article, it is to create a unique template where, our secretaries are going to type some text  and send the text to a remote location (ie: a solicitor) minimizing the number of clicks to one. This is just an example, but when you are focus in a DMS, you have to think about different ways of delivering speed, just because that speed will be translated in productivity and money.

So what we want really, it is a simple button. In that way they will avoid: 1) going to the tab File, 2) click on save as, 3) type a name, 4) click on save, 5) open Outlook, 6) create a new email, 7) type the name of the lawyer, 8) send an email to the lawyer, saying that the document is ready in the DMS and 9) close Microsoft Word.

In theory we will save eight steps and more than 120 seconds per document.

As I said this is just an example, and, it is in your hands to develop the backend of this application. In our case we will just display a message box saying, “Document Sent”.

There are many ways of doing this, but we are going to concentrate in Ribbons. Ribbons can be modified by:

a) a MS Word Template
b) Visual Studio 2010 Add-in.

In this chapter we will look to the first part: MS Word Templates.

1) Open Microsoft Word 2010
2) Go to the Developer Tab.
3) If you do not see the Developer tab, you must identify yourself as a developer. To do this in your application, click the Microsoft Office Button, click Application Options, click Customize Ribbon, and then select Show Developer Tab in the Ribbon. This is a global setting that identifies you as a developer in all Office applications that implement the Fluent UI.

4) Click on the Visual Basic button.
5) Click on ThisDocument.
6) Copy and paste this code (VBA function) into the box:



Sub SendDocumentMacro(ByVal control As IRibbonControl)
MsgBox ("The document has been sent!")
End Sub



7) Close this window.
8) Now we have to save this file as a document template, to do that just go to File->Save As and type c:\productivetemplate.docm (if you use Excel or PowerPoint you can do exactly the same thing but saving the document with .xlsm, and .pptm.
9) Exit from Microsoft Word.
10) Now! We are moving to the “funny stuff” . Go to C:\ , create a folder called customUI , create a new xml file inside called customUI.XML, then copy and paste this code, save the file and close it.

11) Go to your c:\productivetemplate.docm and rename it with c:\productivetemplate.zip, drag and drop your folder CustomUI and open the zip file.
12) Drag the _rels folder to the desktop. A folder named _rels containing the .rels file appears on the desktop.
13) Open the new folder, and then open the .rels file in a text editor.

14) Between the final element and the closing element, add a line that creates a relationship between the document file and the customization file. Ensure that you specify the folder and file names correctly (the Id attribute supplies a unique relationship ID for the customUI—its value is arbitrary).
15) Save the .rels file.
16) Drag the .rels file from the desktop to the _rels folder in the compressed file, replacing the existing .rels file.
17) Remove the .zip extension from the container file and rename it with c:\productivetemplate.docm.
18) Now! Open the file… you should see something like this:

19) Click on the button to see the message.

Conclusion:
We have developed a complete new template, removing all the default options, in the next chapter we will check how to add different controls and tabs to a normal MS Word instance.