Create search scope for searching inside a site

Creating search scope for a site in PowerShell is basically a two steps operation. First step consists in creating a search scope inside a search application, while second step consists in creating a search rule and making this rule to search inside a site. There is nothing complicated in this, as you will see in description below.

First thing you need to do is to make PowerShell prompt for the folder (URL) where site is located.

Write-Host "Enter Site URL:"
$url = Read-Host

Once I created a variable $url with the value assigned dynamically by the user, I can proceed to create the search scope.

$searchapp = Get-SPEnterpriseSearchServiceApplication
$src = Get-SPEnterpriseSearchQueryScope  -Identity "Search scope name" -SearchApplication $searchapp
if($src -ne $null) {
	Remove-SPEnterpriseSearchQueryScope -Identity  "Search scope name"  -SearchApplication $searchapp
}
$src = New-SPEnterpriseSearchQueryScope -Name "Search scope name" -SearchApplication $searchapp -Description "Search scope name" -DisplayInAdminUI $true

Scope name of course is going to be changed to fit your needs. Or you can assign the scope name dynamically by applying the same technique as I applied for site URL.

The last step is to add search rule for making new search scope pointing to site URL.

$rule = New-SPEnterpriseSearchQueryScopeRule -Scope $src -RuleType "Url" -Url $url -FilterBehavior "Require" -MatchingString $url -UrlScopeRuleType "Folder"

I hope everything is clear. For a complete view, see full code below.

Write-Host "Enter Site URL:"
$url = Read-Host

$searchapp = Get-SPEnterpriseSearchServiceApplication
$src = Get-SPEnterpriseSearchQueryScope  -Identity "Search scope name" -SearchApplication $searchapp
if($src -ne $null) {
	Remove-SPEnterpriseSearchQueryScope -Identity  "Search scope name"  -SearchApplication $searchapp
}
$src = New-SPEnterpriseSearchQueryScope -Name "Search scope name" -SearchApplication $searchapp -Description "Search scope name" -DisplayInAdminUI $true

$rule = New-SPEnterpriseSearchQueryScopeRule -Scope $src -RuleType "Url" -Url $url -FilterBehavior "Require" -MatchingString $url -UrlScopeRuleType "Folder"
Advertisements

Remove content type from a SharePoint list

I am not going to reinvent the wheel. Samples of code for removing a content type from a list can be found all over the internet. What I want to do is to present a way to organize the code and make it reusable. And with a little help from extension methods, this can be achieved very easy.

Before deleting the content type, I need to be sure it is attached to the list first, otherwise code will end up with an error. For this I have created the following extension method:

        /// <summary>
        /// Get content type id (SPList extension method)
        /// </summary>
        /// <param name="lst"></param>
        /// <param name="contentTypeName"></param>
        /// <returns></returns>
        public static Nullable<SPContentTypeId> GetContentTypeId(this SPList lst, string contentTypeName)
        {
            for (int i = 0; i < lst.ContentTypes.Count; i++)
            {
                if (lst.ContentTypes[i].Name == contentTypeName)
                {
                    return lst.ContentTypes[i].Id;
                }
            }
            return null;
        }

This methods is supposed to return an SPContentTypeId if content type with specified name is attached to the list, or null if not. For sure you can avoid iteration, but I prefer it because it might be possible to adapt the code later for identifying the content type by other properties convertible to string.

The second method is based on the first one and is deleting the content type if it is attached to the list. Again, iteration is not the representing the optimized code, but is easy for me to change this way.

        /// <summary>
        /// Remove content type (SPList extension method)
        /// </summary>
        /// <param name="lst"></param>
        /// <param name="contentTypeName"></param>
        public static void RemoveContentType(this SPList lst, string contentTypeName)
        {
            Nullable<SPContentTypeId> cTypeId = lst.GetContentTypeId(contentTypeName);
            if (cTypeId != null)
            {
                lst.ContentTypes.Delete((SPContentTypeId)cTypeId);
                lst.Update();
            }
        }

Assuming you have a variable(SPList type) called “mylist”, code to delete the content type become simple:

mylist.RemoveContentType("Content type name");

Move files in a document library with JavaScript

Moving files inside a document library can be a requirements coming from the customer. Luckily, JavaScript is offering a simple solution, without going into server side development. I will describe below how this can be done and try to explain my code as much as possible.

First thing I did was to create a sort of reusable code and as always I created a namespace and add reference for Visual Studio auto-completion.

/// <Reference Name="MicrosoftAjax.js" />
/// <Reference Name="MicrosoftAjaxWebForms" /> 
Type.registerNamespace("Shp");

One of the limitations of JavaScript client object model is it cannot be synchronous. This means I need to create a lot of callback functions for complex operations. But my idea was to load main information on start-up and store it in a configuration class to be used later.

Shp.Info = function () {
    /// <summary>Static class which contains information about web and lists.</summary>
}

Shp.Info.context = null;
Shp.Info.web = null;
Shp.Info.lists = null;

Shp.Info.Initialize = function (callback, arguments) {
    /// <summary>Initialize Shp.Info class.</summary>
    var _cArgs = (typeof (arguments) === "undefined") ? ([]) : (arguments);
    Shp.Info.context = SP.ClientContext.get_current();
    Shp.Info.web = Shp.Info.context.get_web();
    var webLists = Shp.Info.web.get_lists();
    Shp.Info.context.load(webLists);

    var fail = function (args, message) {
        alert('Cannot get web information: ' + args.get_message() + '\n' + args.get_stackTrace());
    };

    var success = function () {
        Shp.Info.lists = webLists.$2_1;
        if ((typeof (callback) !== "undefined") && (callback !== null)) callback.apply(null, _cArgs);
    };

    Shp.Info.context.executeQueryAsync(success, fail);
}

Shp.Info._get_documentLibrary = function (docLibName) {
    /// <summary>Get document library from initalized web (internal use).</summary>

    for (var i = 0; i < Shp.Info.lists.length; i++) {
        if ((Shp.Info.lists[i].get_baseType() === 1) && (Shp.Info.lists[i].get_title() === docLibName)) {
            return Shp.Info.lists[i];
        }
    }
    return null;
}

Shp.Info.registerClass("Shp.Info");

What I am suggesting is to initialize configuration class on page load event, before making any other operation on SharePoint.

function pageLoad() {
    var loadNotification = SP.UI.Notify.addNotification("Please wait until system is fully loaded.", true);
    Shp.Info.Initialize(function () {
        SP.UI.Notify.removeNotification(loadNotification);
    });
}

For move files operation I have created a separate class:

Shp.ListItemsOps = function () {
    /// <summary>SharePoint ListItemsOps custom static class.</summary>
}


Shp.ListItemsOps.moveFileById = function (sourceDocLibName, fileid, destinationurl, callback) {
    /// <summary>Get file by id and copy to the destination URL</summary>
    /// <param name="sourceDocLibName" type="String" mayBeNull="false" optional="false"></param>
    /// <param name="fileid" type="Number" mayBeNull="false" optional="false"></param>
    /// <param name="destinationurl" type="String" mayBeNull="false" optional="false"></param>
    /// <param name="callback" type="Function" mayBeNull="true" optional="true"></param>
    var e = Function._validateParams(arguments, [{ name: 'sourceDocLibName', type: String, mayBeNull: false, optional: false },
    { name: 'fileid', type: Number, integer: true, mayBeNull: false, optional: false },
    { name: 'destinationurl', type: String, mayBeNull: false, optional: false },
    { name: 'callback', type: Function, mayBeNull: true, optional: true }], false);
    if (e) throw Error.create("Shp.ListItemsOps.copyFileById method was called with invalid arguments");

    var source = Shp.Info._get_documentLibrary(sourceDocLibName);
    if (source === null) {
        alert("Cannot find " + sourceDocLibName + " in web document libraries");
        return;
    }
    var spItem = source.getItemById(fileid);
    Shp.Info.context.load(spItem);

    var fail = function (source, args) {
        alert('Cannot move file: ' + args.get_message() + '\n' + args.get_stackTrace());
    }

    var copyItemSuccess = function (spFile) {
        if ((typeof (callback) !== "undefined") && (callback !== null)) {
            callback();
        }
    }

    var getItemSuccess = function () {
        if (spItem.get_fileSystemObjectType() !== 0) {
            alert("Items was found in the document library but is not a file type");
            return;
        }

        var spFile = spItem.get_file();
        Shp.Info.context.load(spFile);
        spFile.moveTo(destinationurl + '/' + spItem.get_item('FileLeafRef'), 1);
        Shp.Info.context.load(spFile);

        Shp.Info.context.executeQueryAsync(function () { copyItemSuccess(spFile); }, fail);
    }
    Shp.Info.context.executeQueryAsync(getItemSuccess, fail);
}

Shp.ListItemsOps.registerClass("Shp.ListItemsOps");

Shp.ListItemsOps.moveFileById is static function which should be called with 3 parameters: document library name (as string), file id (as integer) and destination url (as string).

If you have suggestions how to improve this code in any sense, please let me know. My intention is to create a reusable code easy to use for everyone.