Category Archives: Utilities

Create a folder in a custom list with REST

Here is a snippet that helps create a folder in a custom list (not a document library) using the SharePoint REST API (and jQuery).

This is the main function – no need to change anything in this one:

function createFolderInList(arg) {
    var deferred = jQuery.Deferred();
    var folder = {
        "__metadata": { "type": "SP.ListItem" },
        "ContentTypeId": "0x0120",
        "Title": arg.folderName
    };
    var useGUID = arg.listId.charAt(0) === "{";
    jQuery.ajax({
        "url": _spPageContextInfo.webServerRelativeUrl + "/_api/web/lists/" + (useGUID ? "getById('" + arg.listId + "')" : "getByTitle('" + arg.listId + "')") + "/items",
        "type": "POST",
        "contentType": "application/json;odata=verbose",
        "data": JSON.stringify(folder),
        "headers": {
            "Accept": "application/json;odata=verbose",
            "X-RequestDigest": jQuery("#__REQUESTDIGEST").val()
        },
        "success": function (data) {
            // Successfully created folder, but needs to update FileLeafRef
            var updData = { "__metadata": { "type": data.d.__metadata.type }, "Title": arg.folderName.split("/").pop(), "FileLeafRef": arg.folderName };
            jQuery.ajax({
                "url": _spPageContextInfo.webServerRelativeUrl + "/_api/web/lists/" + (useGUID ? "getById('" + arg.listId + "')" : "getByTitle('" + arg.listId + "')") + "/items(" + data.d.Id + ")",
                "type": "POST",
                "data": JSON.stringify(updData),
                "headers": {
                    "IF-MATCH": "*",
                    "X-HTTP-Method": "MERGE",
                    "accept": "application/json;odata=verbose",
                    "content-type": "application/json;odata=verbose",
                    "X-RequestDigest": jQuery("#__REQUESTDIGEST").val(),
                },
                "success": function () {
                    // Done creating and renaming folder
                    deferred.resolve();
                },
                "error": function (err) {
                    deferred.reject(err);
                }
            });
        },
        "error": function (err) {
            deferred.reject(err);
        }
    });
    return deferred.promise();
}

This is how you call it from your code:

createFolderInList({
    "folderName": "Created from code",
    "listId": "MyTestList", // List GUID or display name - if you use GUID it must include the curly braces around the GUID like this: {c5c44b98-34f1-4ade-87d3-ed292eee0d84}
}).done(function () {
    alert("Folder created");
}).fail(function (err) {
    alert("Failed to create folder:\n" + JSON.stringify(err));
});

You can create a subfolder by specifying the folderName like this:

"folderName": "Created from code/subfolder 1"

Alexander

Fix classic form fallback on modern lists

This will fix the problem where the form is opened in the sidepanel without DFFS when you click to view, edit or add a new item from a modern list view.

ChangeLog

December 12, 2020 – v1.0.0.3

Changes how the site URL is written in the content type form URL to ensure it still works if you save the site as a template after installing DFFS. In v1.0.0.2 the site URL was hard coded and resulted in a broken link to the forms if it was restored on another site URL. To ensure you don’t have to reinstall DFFS to fix this I have now changed it in both the normal DFFS installer (published November 25, 2020) and in this tool.

July 31, 2020 – v1.0.0.2

Fixed a bug where only the first 100 DFFS configurations in the SPJS-DynamicFormsForSharePoint list were checked. Also changed a jQspjs (jQuery noConflict namespace in DFFS) reference that made it only work when loaded in a DFFS form and not in a standalone page with plain jQuery loaded.


Microsoft is investigating the ticket i submitted yesterday regarding the breaking change or bug that was released for first release customers on Office 365link to my post from yesterday.

I have been able to create a script that fixes this problem on all DFFS enabled lists within a site (it must be run once for each site) by writing the NewFormUrl, DispForUrl and EditFormUrl to the content types for these lists. A big thanks to Earl Libby from SPMarketplace for helping me figure out this workaround!

I will release a new version of DFFS this weekend (August 8-9) where the DFFS installer is updated to include this fix.

This is what it does

  1. Gets all lists in current site to use in step 2
  2. Gets all stored configurations from the DFFS config list “SPJS-DynamicFormsForSharePoint” and gets all unique lists – finding the list GUID from the lists gathered in step one.
  3. Looping over all content types in all the DFFS enabled lists and checks if the NewFormUrl, DisplayFormUrl and EditFormUrl matches the current content type scope (the current list path) and if not update it.

How to use it

This script is designed to be dropped directly in the console of any page within a SharePoint site, but it needs jQuery to run so it may be best to open a list NewForm by typing in this in the browser address bar:

https://contoso.sharepoint.com/sites/YourSite/Lists/YourList/NewForm.aspx

The open the console and paste the script there.

I recommend using Google Chrome or the new Edge (chromium) as these have better console than Internet Explorer and the old Edge.

The script will log to the console so you know what is going on.

Please note that you need at least manage lists permissions to use this script. Also note that this only fixes lists that already have DFFS installed. This means that any new lists you install DFFS in might still fail unless you re-run this script. This will be fixed when I release a new version of the DFFS installer sometime later this week.

Get the script here

Link to script file (zipped).

Please let me know if you have any question in the comments below.

Best regards,
Alexander

Toggle list visibility (hide from all site content)

Updated August 05, 2019 to fix an error when used on the root site and the BaseUrl field was set to /. To load the lists on the root site you must leave the field empty.

Here is a tool to toggle the visibility of all custom lists (BaseTemplate 100) and document libraries (BaseTemplate 101) – and also the Site Pages library (BaseTemplate 119). It is used to toggle the Hidden property on the list – which determines whether or not to show the list in Site Contents.

Please note that you must have the necessary rights (manage lists) to use it, and if you don’t you will get an error message. Also note that this snippet loads jQuery from code.jquery.com – if your site does not have Internet access you must change this script tag to refer a local copy of jQuery.

Add this snippet to a Script Editor Web Part in a Classic Web Part Page to use it.

<div class="pageHeader">BaseUrl to the site</div>
<input type="text" style="width:200px;" id="baseUrlInput" onkeydown="if(event.keyCode === 13){getAllLists()}"><input
    type="button" onclick="getAllLists()" value="Load all lists" />
<div id="allListsTable"></div>
<style type="text/css">
    .pageHeader {
        font-size: 1.5em;
        font-weight: 300;
    }

    .headerRow td {
        font-size: 1.5em;
        border-bottom: 1px silver solid;
    }

    .bodyRow td {
        padding: 3px 5px;
    }

    .bodyRow:hover td {
        background-color: #00ad56;
        color: #ffffff;
    }
</style>
<script src="https://code.jquery.com/jquery-3.4.1.min.js"
    integrity="sha256-CSXorXvZcTkaix6Yvo6HppcZGetbYMGWSFlBw8HfCJo=" crossorigin="anonymous"></script>
<script type="text/javascript">
    jQuery("#baseUrlInput").val(_spPageContextInfo.webServerRelativeUrl !== "/" ? _spPageContextInfo.webServerRelativeUrl : "");
    function getAllLists() {
        var baseUrl = jQuery("#baseUrlInput").val();
        jQuery.ajax({
            "url": baseUrl + "/_api/web/lists?$select=Id,Title,BaseTemplate,RootFolder,Hidden&$expand=RootFolder",
            "method": "GET",
            "headers": {
                "accept": "application/json; odata=verbose",
                "content-type": "application/json;odata=verbose",
                "X-RequestDigest": getFormDigest()
            },
            "success": function (data) {
                var arr = [], b = [];
                jQuery.each(data.d.results, function (i, list) {
                    if (list.BaseTemplate !== 100 && list.BaseTemplate !== 101 && list.BaseTemplate !== 119) {
                        return;
                    }
                    arr.push({
                        "title": list.Title,
                        "rootfolder": list.RootFolder !== undefined ? list.RootFolder.Name : "",
                        "id": list.Id,
                        "hidden": list.Hidden
                    });
                });
                arr.sort(function (a, b) {
                    if (a.title < b.title) {
                        return -1;
                    }
                    if (a.title > b.title) {
                        return 1;
                    }
                    return 0;
                });
                b.push("<table>");
                b.push("<tr class='headerRow'>");
                b.push("<td>");
                b.push("Title");
                b.push("</td>");
                b.push("<td>");
                b.push("URL-name");
                b.push("</td>");
                b.push("<td>");
                b.push("Hidden");
                b.push("</td>");
                b.push("</tr>");
                jQuery.each(arr, function (i, list) {
                    if (list.rootfolder === "") {
                        return;
                    }
                    b.push("<tr class='bodyRow'>");
                    b.push("<td>");
                    b.push(list.title);
                    b.push("</td>");
                    b.push("<td>");
                    b.push(list.rootfolder);
                    b.push("</td>");
                    b.push("<td>");
                    b.push("<input listguid='" + list.id + "' type='checkbox'" + (list.hidden ? " checked='checked'" : "") + " onclick='toggleVisible(this);'/>");
                    b.push("</td>");
                    b.push("</tr>");
                });
                b.push("</table>");
                jQuery("#allListsTable").html(b.join(""));
            },
            "error": function (err) {
                jQuery("#allListsTable").html(err.statusText + ": " + err.status);
            }
        });
    }

    function toggleVisible(elm) {
        var baseUrl = jQuery("#baseUrlInput").val();
        var hidden = jQuery(elm).prop("checked");
        var guid = jQuery(elm).attr("listguid");
        jQuery("#status_" + guid).remove();
        var itemPayload = {
            "__metadata": { "type": "SP.List" },
            "Hidden": hidden,
        };
        jQuery.ajax({
            "url": baseUrl + "/_api/Web/Lists/GetById('" + guid + "')",
            "type": "POST",
            "data": JSON.stringify(itemPayload),
            "headers": {
                "accept": "application/json; odata=verbose",
                "content-type": "application/json;odata=verbose",
                "IF-MATCH": "*",
                "X-HTTP-Method": "MERGE",
                "X-RequestDigest": getFormDigest()
            },
            success: function (data) {
                // Success
                jQuery(elm).after("<span id='status_" + guid + "' style='margin-left:4px;'>OK</span>");
            },
            error: function (err) {
                jQuery(elm).after("<span id='status_" + guid + "' style='margin-left:4px;color:red;'>Error! - Open the console for details (F12 > Console).</span>");
                console.log(err);
            }
        });
    }

    function getFormDigest() {
        var r = _spPageContextInfo.formDigestValue;
        if (r === undefined) {
            r = jQuery("#__REQUESTDIGEST").val();
        }
        return r;
    }
</script> 

Hope you find this tool useful.

Alexander

SPJS-Utility updated with support for querying large lists

By request I have added support for querying large lists (over 5000 items) using “spjs_QueryItems” or “spjs.utility.queryItems” in SPJS-Utility.js

If you use this for example with the DFFS Plugin “Autocomplete”, and have hit this error:

The attempted operation is prohibited because it exceeds the list view threshold enforced by the administrator

You can now update SPJS-Utility.js to fix this problem.

Please note that these functions are undocumented and mostly used internally from the different SPJS functions and solutions. If you are interested in using these functions in your own code, dig into the code to discover how it works, or ask a question in the forum.

Download

You find SPJS-utility.js here.

Alexander

Approve or reject list items

I have previously posted a JavaScript solution for approving multiple documents or list items. This is an updated version that adds two buttons in the ribbon: one for “Approve” and one for “Reject”. Refer the original solution for details, but please note that the variables for controlling the text values have changed.

Use this code to load the solution in a list view
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<script type="text/javascript" src="/Scripts/ApproveOrRejectSelected.js"></script>
Change the default text values

If you are happy with the default, English text, you can skip this next step.

To override the default text, add this to the CEWP below the script reference to “ApproveOrRejectSelected.js”,and change the text as you like:

<script type="text/javascript">
spjs.contentApproval.text = {
	"approveBtn":"Approve selected",
	"rejectBtn":"Reject selected",
	"groupLabel":"Approve or Reject",
	"working":"Processing items...",
	"done":"{0} items processed"
};
</script>

You can wrap it like this to switch based on the logged in users language (in MUI environments):

switch(_spPageContextInfo.currentLanguage){
	case 1044:
		// Add Norwegian values
		spjs.contentApproval.text = {
			"approveBtn":"Approve selected",
			"rejectBtn":"Reject selected",
			"groupLabel":"Approve or Reject",
			"working":"Processing items...",
			"done":"{0} items processed"
		};
	break;
	case 1031:
		// Add German values
		spjs.contentApproval.text = {
			"approveBtn":"Approve selected",
			"rejectBtn":"Reject selected",
			"groupLabel":"Approve or Reject",
			"working":"Processing items...",
			"done":"{0} items processed"
		};
	break;
}
Download ApproveOrRejectSelected.js

Get the file here.

Alexander

SPJS Simple tooltip

By request I have “ripped” the DFFS tooltip from DFFS. This code can use to create a custom tooltip with the same style and function as in DFFS. This means that you can have a hover-over tooltip that sticks if you click the hover-image.

Take a look at it, and familiarize with the code before asking questions.

The tooltip text or HTML is added to the variable “SPJS_SimpleTooltipContents”, and the “key” is used in the image “onmouseover” to pull in the correct tooltip.

The code
<style type="text/css">
div.SPJS_SimpleTooltipPlaceholder img{
	cursor:pointer;
}
#SPJS_SimpleTooltipPlaceholder{
	/*width:350px;*/
	white-space:normal;
	background-color:#E7ECEF;
	border:1px #9F9E9C solid;
	display:none;
	font-size:11px;
	font-weight:normal;
	max-width:500px;
	z-index:1001;
}
div.SPJS_SimpleTooltipHolderHead{
	height: 16px;
	padding:1px;
	border-bottom:1px silver solid;
	background-color:#0072C6;
}	
div.SPJS_SimpleTooltipHolderClose{
	float:right;
	width: 16px;
	height: 16px;
	cursor:pointer;
	background-repeat: no-repeat;
	background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAYdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjAuM4zml1AAAAB5SURBVDhPzZJBDoAgEAN5CG/jF74fGewigSUcOOgkTaTtrhoN/yHnHIuSFGU3djkFAuMqaiWu5RlJ0QvmkzXqEqkfBneBV+TsefMrAIEKK9bDBgUVR/bDQEnlkaO7G+slBCr0cPa8eUkxjz/j8Y9EiSVoesRd/hUh3IlkrlzxjRMpAAAAAElFTkSuQmCC);
}
</style>

<script type="text/javascript">
var SPJS_SimpleTooltipContents = {
	"helpText1":"<div style='width:200px;'>This is a custom tooltip for field 1<div style='color:red'>You can use HTML</div></div>",
	"helpText2":"<div style='width:200px;'>This is a custom tooltip for field 2<div style='color:red'>You can use HTML</div></div>"
};

var SPJS_SimpleTooltip = {
	"data":{
		"isSP13":typeof _spPageContextInfo !== "undefined" && _spPageContextInfo.webUIVersion === 15 ? true : false
	},
	"show":function(elm,key){
		var p = $(elm).position(), l = {};
		if($("#SPJS_SimpleTooltipPlaceholder").length === 0){
			$("body").append("<div id='SPJS_SimpleTooltipPlaceholder'></div>");
		}
		$(elm).after($("#SPJS_SimpleTooltipPlaceholder"));
		$("#SPJS_SimpleTooltipPlaceholder").html("<div style='padding:3px;'>"+(SPJS_SimpleTooltipContents[key] !== undefined ? SPJS_SimpleTooltipContents[key] : "The tooltip for key \""+key+"\" was not found.")+"</div>").attr("pin","0").css({"position":"absolute","left":p.left+15});
		// Check left pos
		l.l = p.left+30;
		l.tw = $("#SPJS_SimpleTooltipPlaceholder").width();
		l.ww = $(window).width();
		if(l.l + l.tw > l.ww){
			$("#SPJS_SimpleTooltipPlaceholder").css("left",(p.left - (l.l + l.tw - l.ww)));
		}
		$("#SPJS_SimpleTooltipPlaceholder").stop(true,true).fadeIn(200);
	},
	"click":function(elm){
		if($("#SPJS_SimpleTooltipPlaceholder").find("div.SPJS_SimpleTooltipHolderClose").length === 0){
			$("#SPJS_SimpleTooltipPlaceholder").attr("pin","1").prepend("<div class='SPJS_SimpleTooltipHolderHead'><div class='SPJS_SimpleTooltipHolderClose' onclick='SPJS_SimpleTooltip.hide(true);'></div></div>");
		}
	},
	"hide":function(c){
		if($("#SPJS_SimpleTooltipPlaceholder").attr("pin") !== "1" || c){
			$("#SPJS_SimpleTooltipPlaceholder").attr("pin","0").stop(true,true).fadeOut(100);
		}
	}
};

</script>
This HTML code shows you how to use the tooltip
<!-- Example HTML -->
<table cellpadding="0" cellspacing="0">
	<tr>
		<td valign="top">Field 1: </td>
		<td valign="top"><input style="width:300px;" type="text">
			<img onmouseover="SPJS_SimpleTooltip.show(this,'helpText1')" onmouseout="SPJS_SimpleTooltip.hide()" onclick="SPJS_SimpleTooltip.click(this)" src="/_layouts/images/hhelp.gif" border="0">
		</td>
	</tr>
		<tr>
		<td valign="top">Field 2: </td>
		<td valign="top"><input style="width:300px;" type="text">
			<img onmouseover="SPJS_SimpleTooltip.show(this,'helpText2')" onmouseout="SPJS_SimpleTooltip.hide()" onclick="SPJS_SimpleTooltip.click(this)" src="/_layouts/images/hhelp.gif" border="0">
		</td>
	</tr>
</table>
<!-- Example HTML -->

PS: It’s the <img> tags that calls the tooltip, the other HTML is for demonstration only.

I hope you can make use of this code.

Alexander

SPJS Poll for SharePoint v2.0

I have brushed up the 5 year old solution Poll for SharePoint, to change the deprecated “Google Image Charts to use Google Charts (AKA Google Visualization). The Image Charts are officially deprecated, but will probably continue working for some time, but to ensure you have a working solution, you should upgrade to this new version.

Please note that there are some changes to the Content Editor Web Part code, so existing users must not only update the script file, but also look over the CEWP code and make some small changes.

This code lets you generate polls without the need for server side installed WebParts.
Change log
March 15, 2015
v2.0 released. This one has no new functionality, but the code has been brushed up, and now the charts are generated using “Google Charts” rather than “Image Charts”.
Poll

IMG

Result with column chart

IMG

How to set it up
Create a custom list with the following fields
  • Answer: Single line of text
  • Question: Single line of text

Name it anything you like, but keep the display name fairly simple (no special characters) as you will use the display name in the CEWP code.

CEWP code

The CEWP code below refers jQuery from Google. If you have a local copy of jQuery you can change the script src. You find the code for the file “SPJS-Poll.js” at the bottom of the page.

NOTE: You must change the script src for the file “SPJS-Poll.js” and “spjs-utility.js” to point your instance of the files – the CEWP code will not work unless you do this.
Place this code where you want the poll to appear:
<div id="SPJS_Poll"></div>
<link type="text/css" href="/Scripts/Poll/spjs_poll.css" rel="stylesheet">
<script type="text/javascript" src="https://www.google.com/jsapi"></script>
<script type="text/javascript" src="https://code.jquery.com/jquery-1.11.2.min.js"></script>
<script type="text/javascript" src="/Scripts/spjs-utility/spjs-utility.js"></script>
<script type="text/javascript" src="/Scripts/Poll/SPJS-Poll.js"></script>
<script type="text/javascript">

// Preload the chart solution
google.load("visualization", "1", {"packages":["corechart","table"]});

// If you want to override these texts, uncomment the object and localize as you like.
/* 
	spjs.poll.text = {
		"submitLinkText":"Submit",
		"backLinkText":"Back",
		"showResultBtnText":"Show result",
		"pollNotActiveText":"The poll is not active prior to {0}",
		"pollEndedText":"The poll ended on {0}",
		"alreadyRespondedText":"You answered: ",
		"totalResponsesLabel":"Total responses: ",
		"chartLegendLabels":["Answer","Count"]

	};
*/

spjs.poll.init({pollAnswerListName:'Poll',
				listBaseUrl:L_Menu_BaseUrl,				
				id:"Poll_3", // Allowed characters id a-z, 0-9 - and _
				start:"01/02/2015", // format: mm/dd/yyyy
				end:"03/15/2015", // format: mm/dd/yyyy
				singleResponse:false,
				q:"What is your favorite junk food?",
				qStyle:"font-size:1.5em;color:#444;",
				aStyle:"font-size:xx-small",
				a:["Pizza","Hot dog","Hamburger","Neither of them"], // Leave empty for free input				
				color:["red","green","blue","orange"],
				forceLowerCaseAnswer:false, // Group result by lowercase				
				chart:"col", // table, bar, col or pie
				height:200,
				width:500});

</script>

Object attributes explained

  • pollAnswerListName: DisplayName or GUID of the list that stores the answers
  • listBaseUrl: The baseUrl of the site. This is like “/sites/hr” when the list is located in the site “hr” under “/sites”. Use L_Menu_BaseUrl (or omit the property) for current site.
  • id: The unique id of the poll. All poll answers are stored in a list and this id is used to separate each poll
  • start: Start date in the format mm/dd/yyyy
  • end: End date in the format mm/dd/yyyy
  • singleResponse: true for one reply per user, false for unlimited number of replies
  • q: Poll question. To have a linefeed in the question, use <br>
  • qStyle: CSS syntax style
  • aStyle: CSS syntax style
  • a: Answers in an array format. To use free input and not predefined answers, leave the array empty.
  • color: Colors for the chart in an array format. This must have the same length as the previous parameter – one color for each answer
  • forceLowerCaseAnswer: Primarily for use with free input to avoid getting two “series” when the only difference are uppercase characters.
  • chart: “bar” for bar chart, “col” for column chart, “pie” for pie chart or “table” for a plain table.
  • height: Height in pixels
  • width: Width in pixels
Regarding free input

If you leave the attribute “a” as an empty array, the user can supply free text as “answer”. When using free input, the result are automatically presented as a table.

Download code

The code for the file SPJS-Poll.js. You find spjs-utility.js here.

Questions or feedback

Post any questions in the forum

Alexander

DFFS v4.250 released

This release has multiple changes and additions from the previous production release (v4.200). You find the complete changelog her: https://spjsblog.com/dffs/dffs-change-log/

I will show you one enhancement in particular – the new options for side-by-side headings and labels:
IMG

This layout is achieved with this configuration
In the “Tabs” tab I have added this configuration:

IMG
Please note how the headers are moved to the side-by-side column using the “Header ID”.

In the Side-by-side tab I have added these labels:

IMG

In the Custom CSS section in the Misc tab I have added this code:
.dffs-vertical-text{
text-align:center;
transform: rotate(-90deg);
font-size:22px;
}
td.sbs_tdIndex_1, td.sbs_tdIndex_2, td.sbs_tdIndex_3, td.sbs_tdIndex_4{
	width:350px !important;
}
td.sbs_Field input.ms-long, td.sbs_Field div.sp-peoplepicker-topLevel{
	width:250px !important;
}
Comments and feedback

Please add any comments, questions or feedback in the forum: https://spjsblog.com/forums/forum/dynamic-forms-for-sharepoint/

Alexander