How to use JSON to add a clickable button to a modern SharePoint list view

In the SharePoint modern list and libraries views, you can pretty easily add custom formatting and even custom buttons that, when clicked, take the user to other places or trigger other actions.

You can find all of the details about how to do this here. Here is a quick breakdown.

  1. Create a column or choose a column that will be used for your button.
  2. Open the button properties in the list settings or content type settings.
  3. Enter the JSON in the Custom Formatting box.

Here is an example snippet that you can start with.

{
   "elmType": "a",
   "txtContent": "",
   "attributes": {
      "target": "_blank",
    "iconName": "OpenInNewWindow",
        "class": "sp-field-quickAction",
        "href": {
         "operator": "+",
         "operands": [
            "{enter your site here}/Lists/{list name}/DispForm.aspx?ID=",
            "@currentField"
         ]
      }
   }
}

Of course, the URL can be anything you want. In the above example, I’m linking to the item display form of another list and passing in the ID of the item I want to view. In this case, the item in the field I am using contains the ID of the list item I want open. This can be helpful if you have multiple lists that reference each other or use a lookup. You can also use this approach to trigger Microsoft Flow or other workflows.

The “OpenInNewWindow” is the name of the icon I’m using. You can add your own icon or choose from any predefined icons from the Office UI Fabric site.

Again, you can find some great documentation with many examples provided by Microsoft here

Starting a SharePoint 2013 workflow programmatically using the JavaScript Client Object Model

The following code snippet shows how to start a SharePoint 2013 workflow using JavaScript. Continue reading Starting a SharePoint 2013 workflow programmatically using the JavaScript Client Object Model

Starting a SharePoint 2013 workflow programmatically using C#

The following code snippet shows how to create a SharePoint 2013 workflow using C#. While the code itself is pretty self explanatory, here are some hints:

  • SharePoint 2013 workflows are substantially different than previous SharePoint 2010 workflows. You can read about the SharePoint 2013 architecture here. Therefore, the code to start them is different as well. 
  • When starting the workflow, you must tap into the workflow subscription service, find the workflow you want to start and start it.
  • The sample code assumes you already have a workflow associated with the list.
  • The sample code does not contain any error handling. 
  • The sample code shows how to add input parameters. If your workflow has an initiation form that passes values into the workflow, you can create those values here.
/// <summary>
/// Starts the SharePoint 2013 workflow.
/// </summary>
/// <param name="item">The list item that we want to start the workflow against.</param>
/// <param name="workflowName">The name of the workflow that is associated with the list.</param>
private static void Start2013Workflow(SPListItem item, string workflowName)
{
    var workflowServiceManager = new WorkflowServicesManager(item.Web);
    var workflowSubscriptionService = workflowServiceManager.GetWorkflowSubscriptionService();

    // Get all workflows associated with the list
    var subscriptions = workflowSubscriptionService.EnumerateSubscriptionsByList(item.ParentList.ID);

    // Run all workflows associated with the list
    foreach (var workflowSubscription in subscriptions)
    {
        if (workflowSubscription.Name.Equals(workflowName))
        {
            // Pass in any initiation parameters
            var inputParameters = new Dictionary<string, object>();
            //inputParameters.Add("MyProperty", "MyValue");
            
            workflowServiceManager.GetWorkflowInstanceService().StartWorkflowOnListItem(workflowSubscription, item.ID, inputParameters);
        }
    }
}

All about SharePoint Work Item Timer Jobs

If you asked me how to use a Work Item Timer Job just 6 months ago I would have responded “What the heck is a Work Item Timer Job”? It turns out that Work Item Timer Jobs have existing since SharePoint 2010 (at least). I discovered them myself just a short time ago while doing research for a new business application I was implementing. Continue reading All about SharePoint Work Item Timer Jobs

Open a page in a SharePoint modal window

Ever have a need to open a page or external URL from your SharePoint site within a modal window? With the dialog framework provided by SharePoint, it is pretty easy to do so.

Here is an example. Add the following JavaScript to your page or a separate JavaScript file (and reference it from your page).

function OpenInModal(url) {
var options = {
url: url,
title: "Client Portal Site Request",
allowMaximize: false,
width: 1100,
height: 700,
dialogReturnValueCallback: refreshCallback
};

SP.UI.ModalDialog.showModalDialog(options);
};

function refreshCallback(dialogResult, returnValue) {
if (dialogResult = SP.UI.DialogResult.OK) {
SP.UI.ModalDialog.RefreshPage(SP.UI.DialogResult.OK);
}
};

The ShowRequestForm function accepts the URL which you want to open. There a few options that are set (see MSDN for details), including the width and height of the modal window. The dialogReturnValueCallback: refreshCallback tells it to call the refreshCallback function when the modal window is closed by the user. This is useful if you want to refresh the parent page or show a message to the user when the modal window is closed.

To call the OpenInModal function from your page, you can simply call the JavaScript function from a link or button.

<a href="javascript: ShowRequestForm('<<urltoopen>>');">Open Modal</a>

How to close the modal window programmatically

If you need to close the modal window programmatically you can do so with a couple of lines of code.

context.Response.Write("window.frameElement.commitPopup();");
context.Response.Flush();
context.Response.End();

 

 

Reorder SharePoint list columns to match it’s parent content type

I ran into an issue today. I have a site Content Type that defines several list fields and I have a specific order for those fields to be displayed. Today I added a new site column to this content type and I updated the order of my fields in the content type as usual. However, the lists that use this content type became all out of whack in regards to the order of the fields. I tried to update the order in the Content Type again and push down the changes, but never had any luck.

So, being a developer, I wrote up a simple console app that will iterate through my sites (about 40 webs) and find my particular list and content type and reset the order of the fields to match the parent content type.

There isn’t anything fancy about this, but I thought I would share anyway.

Since it is a console app, just copy the executable and the app.config file to your SharePoint server. Update the app.config with your own values.

This is only an example script, as with any script you run in your environment, ensure that you heavily test it in a test environment before running in production!

using System;
using System.Collections;
using System.Configuration;
using System.IO;
using System.Linq;
using Microsoft.Office.Server.Utilities;
using Microsoft.SharePoint;

namespace ReorderMeetingFields
{
    class Program
    {
        /// <summary>
        /// Logs a message to a text file that is located in the same directory as this console app. If the file doesn't exist, it will be created.
        /// </summary>
        /// <param name="message">The message to log.</param>
        /// <param name="isError">Is this an error or a normal message that we want to log?</param>
        internal static void LogMessage(string message, bool isError)
        {
            var fileName = String.Format("ErrorLog {0}.txt", DateTime.Now.ToLongDateString());

            if (!isError) fileName = String.Format("SuccessLog {0}.txt", DateTime.Now.ToLongDateString());

            StreamWriter stream = File.AppendText(fileName);
            TextWriter writer = stream;
            writer.WriteLine("{0}", message);
            writer.Flush();
            stream.Close();

            Console.WriteLine(message);
        }

        static void Main(string[] args)
        {
            try
            {
                // Retrieve some configuration values from our App.config file
                var siteUrl = ConfigurationSettings.AppSettings["SiteCollectionUrl"].ToString();
                var contentTypeName = ConfigurationSettings.AppSettings["ContentTypeName"].ToString();
                var listName = ConfigurationSettings.AppSettings["ListName"].ToString();
                Console.WriteLine("Configuration retrieved.");

                // Open the site collection
                using (SPSite site = new SPSite(siteUrl))
                {
                    // Retrieve the parent content type
                    SPContentType siteCt = site.OpenWeb().ContentTypes[contentTypeName];
                    Console.WriteLine("Retrieved the {0} site content type.", contentTypeName);

                    // Collect the field order of the parent content type and save it to a string array
                    ArrayList siteFieldOrder = new ArrayList();

                    foreach (SPFieldLink link in siteCt.FieldLinks)
                    {
                        siteFieldOrder.Add(link.Name);
                    }

                    string[] fieldStringArray = (string[])siteFieldOrder.ToArray(typeof(string));

                    // Create an instance of the Content Iterator so we can run through all of our existing sites.
                    var webIterator = new ContentIterator("WebIterator");

                    webIterator.ProcessSite(site,
                        new ContentIterator.WebProcessor((SPWeb web) =>
                        {
                            // Try to retrieve our list.
                            SPList list = web.Lists.TryGetList(listName);

                            if (list != null && !web.IsRootWeb) // In my case I don't want this to run on the root web, remove this condition if this doesn't apply
                            {
                                var listCT = list.ContentTypes[contentTypeName];
                                listCT.FieldLinks.Reorder(fieldStringArray);
                                listCT.Update();

                                // Write a message to the log
                                var message = String.Format("Reordered the fields in the '{0}' list on the '{1}' site.", list.Title, web.Url);
                                Console.WriteLine(message);
                                LogMessage(message, false);
                            }
                        }),
                        new ContentIterator.WebProcessorErrorCallout((SPWeb web, Exception ex) =>
                        {
                            var message = String.Format("Error in '{0}': {1}", web.Title, ex.ToString());
                            LogMessage(message, true);
                            return false;
                        }));
                }

                Console.WriteLine("Finished");
                Console.ReadLine();
            }
            catch (Exception ex)
            {
                LogMessage(ex.ToString(), true);
            }
        }
    }
}