src/value-added/SPScriptAudit.js
define([
'jquery',
"../utils/constants",
'../core/SPServices.utils',
//---------------------------
// We don't need local variables for these dependencies
// because they are added to the jQuery namespace.
'../core/SPServices.core'
], function (
$,
constants,
utils
) {
"use strict";
// Does an audit of a site's list forms to show where script is in use.
$.fn.SPServices.SPScriptAudit = function (options) {
var opt = $.extend({}, {
webURL: "", // [Optional] The name of the Web (site) to audit
listName: "", // [Optional] The name of a specific list to audit. If not present, all lists in the site are audited.
outputId: "", // The id of the DOM object for output
auditForms: true, // Audit the form pages
auditViews: true, // Audit the view pages
auditPages: true, // Audit the Pages Document Library
auditPagesListName: "Pages", // The Pages Document Library(ies), if desired. Either a single string or an array of strings.
showHiddenLists: false, // Show output for hidden lists
showNoScript: false, // Show output for lists with no scripts (effectively "verbose")
showSrc: true // Show the source location for included scripts
}, options);
var formTypes = [
["New", "NewForm.aspx", false],
["Display", "DispForm.aspx", false],
["Edit", "EditForm.aspx", false]
];
var listXml;
// Build the table to contain the results
$("#" + opt.outputId)
.append("<table id='SPScriptAudit' width='100%' style='border-collapse: collapse;' border=0 cellSpacing=0 cellPadding=1>" +
"<tr>" +
"<th></th>" +
"<th>List</th>" +
"<th>Page Class</th>" +
"<th>Page Type</th>" +
"<th>Page</th>" +
(opt.showSrc ? "<th>Script References</th>" : "") +
"</tr>" +
"</table>");
// Apply the CSS class to the headers
var scriptAuditContainer = $("#SPScriptAudit");
scriptAuditContainer.find("th").attr("class", "ms-vh2-nofilter");
// Don't bother with the lists if the options don't require them
if (opt.auditForms || opt.auditViews) {
// First, get all of the lists within the site
$().SPServices({
operation: "GetListCollection",
webURL: opt.webURL,
async: false, // Need this to be synchronous so we're assured of a valid value
completefunc: function (xData) {
$(xData.responseXML).find("List").each(function () {
listXml = $(this);
// If listName has been specified, then only return results for that list
if ((opt.listName.length === 0) || (listXml.attr("Title") === opt.listName)) {
// Don't work with hidden lists unless we're asked to
if ((opt.showHiddenLists && listXml.attr("Hidden") === "False") || !opt.showHiddenLists) {
// Audit the list's forms
if (opt.auditForms) {
// Get the list's Content Types, therefore the form pages
$().SPServices({
operation: "GetListContentTypes",
webURL: opt.webURL,
listName: listXml.attr("ID"),
async: false, // Need this to be synchronous so we're assured of a valid value
completefunc: function (xData) {
$(xData.responseXML).find("ContentType").each(function () {
// Don't deal with folders
if ($(this).attr("ID").substring(0, 6) !== "0x0120") {
var formUrls = $(this).find("FormUrls");
for (var i = 0; i < formTypes.length; i++) {
// Look for a customized form...
$(formUrls).find(formTypes[i][0]).each(function () {
SPScriptAuditPage(opt, listXml, "Form", this.nodeName, ((opt.webURL.length > 0) ? opt.webURL : $().SPServices.SPGetCurrentSite()) + constants.SLASH + $(this).text());
formTypes[i][2] = true;
});
// ...else the uncustomized form
if (!formTypes[i][2]) {
var defaultViewUrl = listXml.attr("DefaultViewUrl");
SPScriptAuditPage(opt, listXml, "Form", formTypes[i][0],
defaultViewUrl.substring(0, defaultViewUrl.lastIndexOf(constants.SLASH) + 1) + formTypes[i][1]);
}
}
// Reset the form types
for (i = 0; i < formTypes.length; i++) {
formTypes[i][2] = false;
}
}
});
}
});
}
// Audit the list's views
if (opt.auditViews) {
// Get the list's Views
$().SPServices({
operation: "GetViewCollection",
webURL: opt.webURL,
listName: listXml.attr("ID"),
async: false, // Need this to be synchronous so we're assured of a valid value
completefunc: function (xData) {
$(xData.responseXML).find("View").each(function () {
SPScriptAuditPage(opt, listXml, "View", $(this).attr("DisplayName"), $(this).attr("Url"));
});
}
});
}
}
}
});
}
});
}
// Don't bother with auditing pages if the options don't require it
var numLists = 0;
var listsArray = [];
if (typeof opt.auditPagesListName === "string") {
numLists = 1;
listsArray.push(opt.auditPagesListName);
} else {
numLists = opt.auditPagesListName.length;
listsArray = opt.auditPagesListName;
}
if (opt.auditPages) {
for (var i = 0; i < numLists; i++) {
$().SPServices({
operation: "GetList",
async: false,
cacheXML: true,
webURL: opt.webURL,
listName: listsArray[i],
completefunc: function (xData) {
$(xData.responseXML).find("List").each(function () {
listXml = $(this);
});
}
});
// Get all of the items from the Document Library
$().SPServices({
operation: "GetListItems",
async: false,
webURL: opt.webURL,
listName: listsArray[i],
CAMLQuery: "<Query><Where><Neq><FieldRef Name='ContentType'/><Value Type='Text'>Folder</Value></Neq></Where></Query>",
CAMLViewFields: "<ViewFields><FieldRef Name='Title'/><FieldRef Name='FileRef'/></ViewFields>",
CAMLRowLimit: 0,
completefunc: function (xData) {
$(xData.responseXML).SPFilterNode("z:row").each(function () {
var thisPageUrl = $(this).attr("ows_FileRef").split(constants.spDelim)[1];
var thisTitle = $(this).attr("ows_Title");
var thisPageType = (typeof thisTitle !== "undefined") ? thisTitle : "";
if (thisPageUrl.indexOf(".aspx") > 0) {
SPScriptAuditPage(opt, listXml, "Page", thisPageType, constants.SLASH + thisPageUrl);
}
});
}
});
}
}
// Remove progress indicator and make the output pretty by cleaning up the ms-alternating CSS class
scriptAuditContainer.find("tr[class='ms-alternating']:even").removeAttr("class");
}; // End $.fn.SPServices.SPScriptAudit
// Displays the usage of scripts in a site
function SPScriptAuditPage(opt, listXml, pageClass, pageType, pageUrl) {
var jQueryPage = 0;
var pageScriptSrc = {};
pageScriptSrc.type = [];
pageScriptSrc.src = [];
pageScriptSrc.script = [];
var scriptRegex = RegExp("<script[\\s\\S]*?/script>", "gi");
// Fetch the page
$.ajax({
type: "GET",
url: pageUrl,
dataType: "text",
async: false,
success: function (xData) {
var scriptMatch;
while (scriptMatch = scriptRegex.exec(xData)) {
var scriptLanguage = getScriptAttribute(scriptMatch, "language");
var scriptType = getScriptAttribute(scriptMatch, "type");
var scriptSrc = getScriptAttribute(scriptMatch, "src");
if (scriptSrc !== null && scriptSrc.length > 0 && !coreScript(scriptSrc)) {
pageScriptSrc.type.push((scriptLanguage !== null && scriptLanguage.length > 0) ? scriptLanguage : scriptType);
pageScriptSrc.src.push(scriptSrc);
jQueryPage++;
}
}
// Only show pages without script if we've been asked to do so.
if ((!opt.showNoScript && (pageScriptSrc.type.length > 0)) || opt.showNoScript) {
var pagePath = pageUrl.substring(0, pageUrl.lastIndexOf(constants.SLASH) + 1);
var out = "<tr class=ms-alternating>" +
"<td class=ms-vb-icon><a href='" + listXml.attr("DefaultViewUrl") + "'><IMG border=0 src='" + listXml.attr("ImageUrl") + "'width=16 height=16></A></TD>" +
"<td class=ms-vb2><a href='" + listXml.attr("DefaultViewUrl") + "'>" + listXml.attr("Title") + ((listXml.attr("Hidden") === "True") ? '(Hidden)' : '') + "</td>" +
"<td class=ms-vb2>" + pageClass + "</td>" +
"<td class=ms-vb2>" + pageType + "</td>" +
"<td class=ms-vb2><a href='" + pageUrl + "'>" + utils.fileName(pageUrl) + "</td>";
if (opt.showSrc) {
var thisSrcPath;
out += "<td valign='top'><table width='100%' style='border-collapse: collapse;' border=0 cellSpacing=0 cellPadding=1>";
for (var i = 0; i < pageScriptSrc.type.length; i++) {
thisSrcPath = (pageScriptSrc.src[i].substr(0, 1) !== constants.SLASH) ? pagePath + pageScriptSrc.src[i] : pageScriptSrc.src[i];
out += "<tr><td class=ms-vb2 width='30%'>" + pageScriptSrc.type[i] + "</td>";
out += "<td class=ms-vb2 width='70%'><a href='" + thisSrcPath + "'>" + utils.fileName(pageScriptSrc.src[i]) + "</td></tr>";
}
out += "</table></td>";
}
$("#SPScriptAudit").append(out);
}
}
});
} // End of function SPScriptAuditPage
function getScriptAttribute(source, attribute) {
var matches;
var regex = RegExp(attribute + "=(\"([^\"]*)\")|('([^']*)')", "gi");
if (matches = regex.exec(source)) {
return matches[2];
}
return null;
} // End of function getScriptAttribute
// Check to see if the script reference is part of SharePoint core so that we can ignore it
function coreScript(src) {
var coreScriptLocations = ["WebResource.axd", "_layouts"];
for (var i = 0; i < coreScriptLocations.length; i++) {
if (src.indexOf(coreScriptLocations[i]) > -1) {
return true;
}
}
return false;
} // End of function coreScript
return $;
});