Friday, August 15, 2014

Creating two different default views for SharePoint List - part 1

Problem:


Let's say you have one Document Library in SharePoint and in this library you have created custom Content Type called "DocCase", which is derived from Content Type "Folder" and it has its own custom metadata columns. "DocCase" is a container for documents, i.e. every document that is uploaded to this library is uploaded to one "DocCase". It is actually a Folder for my documents, but with my custom columns (metadata).

But, Content Type "DocCase" and documents that are uploaded to that "DocCase" have different metadata. I would like to set two different views (to show different metadata) for each of these Content Types.


NOTE: I am using "Doc Case" Content Type just as an example. This process of creating two default views can be applied to ordinary "Folder" Content Type, or any other Content Type that inherits "Folder" Content Type.



Solution:

Soloution is to create two new views in SharePoint list. First view will be default, but only in top-level of our list, and the second will be default for "DocCase" Content Type in that list. This means that when you go to your list, view will show columns of "DocCase", and when you click on one of "DocCase", it will show columns of these documents (I hope you understand what I am trying to do here).

I am going to describe three ways of doing this:
1. In SharePoint list (GUI)
2. Using C# code in Visual Studio (following post)
3. Using List Definition in Visual Studio (following post 2)


1. In SharePoint list:

Go to your list and create new "Standard view". For a name of this view I wrote "DocCase view", but it can be anything you want. Check option "Make this view default view".


Choose the fields you would like to see in your view (this is just an example of my fields):




Go to section "Folders" and select option "In the top-level folder".




Now click Save. Our first view is done.


Now, you need to create second "Standard view" for documents. Name it as you wish and check the "Make this the default view" option.



Select desired fields (example of my fields):




Go to section "Folders" and select option "In folder of content type" and select your content type (in this case it is "DocCase".




Hit Save button, and that is it.

Now, when you go to your list (Documents in my case), default view "DocCase view" opens and shows our "DocCases" with their columns (Client Address, Client ID...).




But, when you click on one of the "DocCases" ("My New Doc Case"), it switches to the second view - "Docs" - and you can see that different columns appear (Created By, Created, Modified, Modified By...).





In my next posts (post2, post3), I am showing how to do the exact same thing programmatically.

Wednesday, August 6, 2014

SharePoint 2010 - Problems after December 2013 CU install

Recently I installed December 2013 Cumulative Update for SharePoint 2010 on one of my dev machines. CU is described in this post:

http://blogs.technet.com/b/stefan_gossner/archive/2013/12/12/december-2013-cu-for-sharepoint-2010-has-been-released.aspx

But, after installation I began experiencing some problems on my default and custom forms (NewForm.aspx, EditForm.aspx) and some web parts. These pages had custom javascript and were using jQuery and SPServices libraries.

After some investigation, I realized that my scripts were not the problem. Problem was in field names, i.e. CU changed field names on every form (but only for required fields). It added text „Required Field“ in the title of every required field on my form. But real name of that field in Sharepoint won't change, this new name is generated only on *.aspx pages.



For example, if you have a field named "Address" on your NewForm.aspx page (and it is a required field), then this is how HTML of that field looks like on your NewForm.aspx before CU installation:

<input name="ctl00$m$g_7954fc2a_0743_4337_838a_2ccb20bcf887$ctl00$ctl05$ctl00$ctl00$ctl00$ctl04$ctl00$ctl00$TextField"
type="text"
maxlength="255" id="ctl00_m_g_7954fc2a_0743_4337_838a_2ccb20bcf887_ctl00_ctl05_ctl00_ctl00_ctl00_ctl04_ctl00_ctl00_TextField"
title="Address"
class="ms-long ms-spellcheck-true" />


And this is how it looks like after CU installation (notice „title“ attribute):

<input name="ctl00$m$g_7954fc2a_0743_4337_838a_2ccb20bcf887$ctl00$ctl05$ctl00$ctl00$ctl00$ctl04$ctl00$ctl00$TextField"
type="text"
maxlength="255" id="ctl00_m_g_7954fc2a_0743_4337_838a_2ccb20bcf887_ctl00_ctl05_ctl00_ctl00_ctl00_ctl04_ctl00_ctl00_TextField"
title="Address Required Field"
class="ms-long ms-spellcheck-true" />


So, names of my fields on NewForm.aspx page are changed. What does that mean? This means that, if you are trying to get values of your “Address” field with jQuery like this:

$('input[title="Address"]').val()

…it isn’t possible anymore. You will get ‘undefined’.

You need to change the name of your field:

$('input[title="Address Required Field"]').val()

The same goes if you are using following methods:

Before:

getTagFromIdentifierAndTitle("select", "Lookup", "Address")

After:

getTagFromIdentifierAndTitle("select", "Lookup", "Address Required Field")


If you are using SPServices, then you also need to check field names in these methods, for example, if you are using $().SPServices.SPCascadeDropdowns
This problem is described in the following link:

http://spservices.codeplex.com/discussions/538561

NOTE: This newly added text „Required Field“ is depended on your site's language. „Required Field“ is added only if default language on your site is English. For other languages text „Required Field“ will be localized.

Tuesday, August 5, 2014

SharePoint 2013 - Update list items with client object model

Here is a very simple example of using SharePoint Client Object Model. In this example I will update every item in my list, i.e. I will update field "Address" with value "my new value". 

1. Open Visual Studio and go to File --> New Project
2. Select Windows --> Console Application (or SharePoint Console Application)
3. Add Microsoft.SharePoint.Client and Microsoft.SharePoint.Client.Runtime references to your project:



4. Paste this code in you new project:




using Microsoft.SharePoint.Client;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApplication1 {
    class Program {
        static void Main(string[] args) {

            // Here you need to enter url of your site
            ClientContext ctx = new ClientContext("http://yourSiteUrl");

            // Get the list by its name - here you enter your list title
            List list = ctx.Web.Lists.GetByTitle("ListTitle");

            ListItemCollection coll = list.GetItems(CamlQuery.CreateAllItemsQuery());
            // we need to load all fields
            ctx.Load(coll);
            ctx.ExecuteQuery();

            // change value of one field in every list item
     // NOTE: this field must exist in your list
            foreach (var item in coll) {
                item["Address"] = "my new value";
                item.Update();
                ctx.ExecuteQuery();               
            }
        }
    }
}

Wednesday, June 25, 2014

SharePoint 2013 - how to move files and folders with versions programmatically

This is a demonstration how you can copy files, folders and Document Set programmatically. 

If you want to move just files (documents) inside same SharePoint site and keep all versions of that file, an example is shown in the method MoveFileWithVersions.

If you have folders that contain multiple files and you want to move one folder with all files that are contained in that folder and all versions of files, then look at method MoveFolderWithVersions.

But, if you have Document Set (and files inside) and you want to copy it to a different location and keep all versions of these files, it is not possible. You can copy Document Set with all files inside, but you cannot copy versions of these files, you will get only last versions of documents. This is method CopyDocumentSet.

In first two methods (MoveFileWithVersions and MoveFolderWithVersions) you actually move files and folders to a new location, you do not copy it - file (or folder) is only on the new location, but in the last method (CopyDocumentSet), you just copy Document Set - you get same Document Set on new and old location (but on the new location there is only one version of files).

If you want to move Document Set and its files with versions to a new location, you can do that if you recreate same Document Set on the new location and copy each individual file from that Document Set with MoveFileWithVersions method.

Here is the code created in simple Console Application:


static void Main(string[] args)
{
     using (SPSite site = new SPSite("http://dev/sites/test"))
     {
          using (SPWeb web = site.OpenWeb())
          {
               SPList oldLib = web.Lists["sourceDocLib"];
               SPList newLib = web.Lists["destinationDocLib"];
              
               // move file with all versions
               MoveFileWithVersions(oldLib, newLib);

               // move folder with its files and their versions
               MoveFolderWithVersions(web, oldLib, newLib);
                    
               // copy Document Set - DOES NOT INCLUDE OLD VERSIONS OF THE FILES
               CopyDocumentSet(web, oldLib, newLib);
          }
     }
}


private static void MoveFileWithVersions(SPList oldLib, SPList newLib)
{
     SPListItem item = oldLib.GetItemById(1); 
     SPFile file = item.File;
     file.MoveTo(newLib.RootFolder.Url + "/" + item.File.Name, true);
}

private static void MoveFolderWithVersions(SPWeb web, SPList oldLib, SPList newLib)
{
     SPFolder oldFolder = web.GetFolder(oldLib.RootFolder.Url + "/myFolder");
     oldFolder.MoveTo(newLib.RootFolder.Url + "/myFolder");
}

private static void CopyDocumentSet(SPWeb web, SPList oldLib, SPList newLib)
{
     SPFolder oldFolder = web.GetFolder(oldLib.RootFolder.Url + "/myDS");
     DocumentSet documentSet = DocumentSet.GetDocumentSet(oldFolder);
     SPContentTypeId contentTypeId = newLib.ContentTypes["Document Set"].Id;
     byte[] documentSetData = documentSet.Export();
     string documentSetName = documentSet.Item.Name;
     SPFolder targetFolder = newLib.RootFolder;
     Hashtable properties = oldFolder.Properties;
     DocumentSet.Import(documentSetData, documentSetName, targetFolder, contentTypeId, properties, web.CurrentUser);
}