Skip to content

Instantly share code, notes, and snippets.

@juntalis
Created May 11, 2012 07:50
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save juntalis/2658220 to your computer and use it in GitHub Desktop.
Save juntalis/2658220 to your computer and use it in GitHub Desktop.
Delete Sharepoint 2010 Field from Browser Console
// ==ClosureCompiler==
// @output_file_name delete-sp2010-field.js
// @compilation_level SIMPLE_OPTIMIZATIONS
// ==/ClosureCompiler==
/**
* @projectDescription An anonymous function, to be run from the browser's console on a
* Sharepoint 2010 List View page or Form. (For instance,
* http://site/Lists/MyList/AllItems.aspx) The script creates a UI
* that lists all Workflow Status fields and allows for the easy
* removal of these fields. (By removing the ReadOnly flag on the field,
* and then removing it) A bookmarklet version of this script can be found at:
*
* http://charles.grunwald.me/code/spfieldremove.html
*
* The core functionality was written during development on a Sharepoint Online
* project, and then extended on to provide a simple solution for following forum question:
*
* http://social.technet.microsoft.com/Forums/en-US/sharepoint2010customization/thread/613a9a71-7a8e-4769-9fe6-cbb65ac06b4a/#9e660621-ba12-4b3f-804f-139a57b1f1ae
*
* @author Charles Grunwald (Juntalis) ch@rles.grunwald.me
* @version 0.3
*/
(function (window) {
// High-level local variables.
var w = window,
doc = w.document,
body = (typeof doc['body'] === 'undefined') ? doc.getElementsByTagName('body')[0] : doc.body,
ctx = (typeof ctx === 'undefined') ? ((typeof _spPageContextInfo === 'undefined') ? null : _spPageContextInfo) : ctx,
/* Just realized the following two namespaces aren't always loaded, so I'll just substitute
my own definitions if they don't already exist. */
su = (typeof SP['ScriptUtility'] !== 'undefined') ? SP.ScriptUtility : (function (su) {
// To maintain compatibility with IE7, I need to declare it like this.
su.isUndefined = function (x) { return (typeof(x) === 'undefined') || (x == undefined); };
su.truncateToInt = function (x) { return new Number(Math.floor(x)); };
su.isNullOrUndefined = function (x) { return su.isUndefined(x) || (x == null); };
su.isNullOrEmptyString = function (x) { return su.isNullOrUndefined(x) || su.isUndefined(x.length) || (x.length === 0); };
return su;
})({}),
hu = (!su.isNullOrUndefined(SP['Utilities'])) && (!su.isNullOrUndefined(SP.Utilities['HttpUtility'])) ? SP.Utilities.HttpUtility : (function (hu) {
hu.htmlEncode = function (x) {
// Slightly modified from:
// http://stackoverflow.com/questions/4742600/what-is-the-most-simple-javascript-htmlencode-lib-function-implementation
// Not a real htmlEncode, but it'll work for our needs.
var r = '';
var allowedChars = '0123456789qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM';
for(var i = 0; i < x.length; i++) {
var ch = x.charAt(i);
var chCode = x.charCodeAt(i);
if(isNaN(chCode)) { continue; }
if(allowedChars.indexOf(ch) > -1) { r += ch; }
else { r += '&#' + chCode + ';'; }
}
return r;
};
// Since our htmlEncode function is actually an xmlEncode function.
hu.escapeXmlText = function (x) { return hu.htmlEncode(x); };
hu.urlKeyValueEncode = function (x) { return w.encodeURIComponent(x); };
hu.urlPathEncode = function (x) { return w.encodeURI(x); };
// Haven't really used this function and don't have time to reverse engineer it, so I'll just use this.
hu.ecmaScriptStringLiteralEncode = function (x) { x.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"); };
return hu;
})({}),
// Our console object.
console = (!su.isNullOrUndefined(w.console)) ? w.console : (function (console) {
console.log = function () { /* If we don't have a console, this is kind of pointless. */ };
// To avoid 20 alert boxes, I'm combining it into one message.
console.error = function () {
var m = "";
for(var i = 0; i < arguments.length; i++) { m += arguments[i]; }
alert(m);
};
console.dir = function () { /* If we don't have a console, this is kind of pointless. */ };
return console;
})({}),
// Simple class to allow shorter calls when using
// some of the Sharepoint Client Object stuff.
$P = (function ($P) {
$P.ui = {};
$P.err = function () {
var conErr = function() {
if(!$P.def(w.console)) {
if(!$P.def($P.ui['err'])) {
console.error.apply(this, arguments);
}
} else {
for(var i = 0; i < arguments.length; i++) {
console.error(arguments[i]);
}
}
}
if($P.def($P.ui['err'])) {
$P.ui.err.apply($P.ui, arguments);
}
conErr.apply($P, arguments);
};
$P.log = function () {
if(!$P.def(w.console)) { return; }
for(var i = 0; i < arguments.length; i++) {
var o = arguments[i];
if(typeof o === 'object') {
console.dir(o);
} else {
console.log(o);
}
}
};
$P.info = function () {
if($P.def($P.ui['info'])) {
$P.ui.info.apply($P.ui, arguments);
$P.log.apply($P, arguments);
} else {
$P.log.apply($P, arguments);
}
};
$P.noop = function () {};
$P.undef = function (o) { return su.isUndefined(o); };
$P.def = function (o) { return !su.isNullOrUndefined(o); };
$P.empty = function (s) { return su.isNullOrEmptyString(s); };
$P.floor = function (o) { return su.truncateToInt(o); };
$P.enc = {};
$P.enc.html = function (o) { return hu.htmlEncode(o); };
$P.enc.js = function (o) { return hu.ecmaScriptStringLiteralEncode(o); };
$P.enc.xml = function (o) { return hu.escapeXmlText(o); };
$P.enc.url = function (o) { return hu.urlKeyValueEncode(o); };
$P.enc.http = function (o) { return hu.urlPathEncode(o); };
// Again, not defined on every page, so..
$P.trim = ($P.def(w['TrimSpaces'])) ? w.TrimSpaces : function (s) { return s.replace(/^\s\s*/, '').replace(/\s\s*$/, ''); };
$P.list = null;
$P.fields = {};
$P.listTitle = null;
$P.listId = null;
$P.site = (function(){
if($P.def(L_Menu_BaseUrl)) {
return L_Menu_BaseUrl;
} else if($P.def(ctx)) {
if($P.def(ctx['HttpRoot'])) {
return ctx.HttpRoot.substr(w.location.protocol.length + w.location.hostname.length + 2);
} else if($P.def(ctx['webServerRelativeUrl'])) {
return ctx.webServerRelativeUrl;
}
} else {
var url = w.location.pathname;
url = url.substr(0, url.lastIndexOf('/'));
var idx = url.lastIndexOf('/Lists');
if(idx === -1) {
return url.substr(0, url.lastIndexOf('/'));
} else {
return url.substr(0, idx);
}
}
})();
$P.client = new SP.ClientContext($P.site);
$P.web = $P.client.web;
return $P;
})({});
// Add some extra utility functions to SPContext to make it
// easier to work with.
(function (client) {
client.lastResult = [];
client.q = function () {
var errFunc = function (sender, args) {
$P.err('An error occurred with executing a query.');
$P.log(args);
throw args;
};
var argLen = arguments.length;
var funcDel = Function.createDelegate;
if(argLen === 0) {
client.executeQueryAsync(funcDel($P, $P.noop), funcDel($P, errFunc));
} else if(argLen === 1) {
client.executeQueryAsync(funcDel($P, arguments[0]), funcDel($P, errFunc));
} else if(argLen === 2) {
client.executeQueryAsync(funcDel($P, arguments[0]), funcDel($P, arguments[1]));
} else {
client.executeQueryAsync(funcDel(arguments[0], arguments[1]), funcDel(arguments[0], arguments[2]));
}
return client;
};
client.l = function () {
var argLen = arguments.length;
var storeResults = function (args) {
client.lastResult = [];
for(var i = 0; i < args.length; i++) {
client.l(args[i]).lastResult.push(args[i]);
}
};
if(argLen > 1) {
storeResults(arguments);
} else if(argLen === 1) {
var arg = arguments[0];
if($P.def(arg)) {
if($P.def(arg['get_$h'])) {
client.load(arg);
client.lastResult = arg;
} else if($P.def(arg['getEnumerator'])) {
var enumerator = arg.getEnumerator();
client.lastResult = [];
while(enumerator.moveNext()) {
var i = enumerator.get_current();
client.lastResult.push(i);
client.load(i);
}
} else {
storeResults(arg);
}
}
}
return client;
};
client.lq = function (l, q) {
client.l.apply(client, l);
client.q.apply(client, q);
return client;
};
client.lists = (client.web = client.get_web()).get_lists();
client.list = (function (list) {
var idCache = {};
var titleCache = {};
list.byid = function (idVal, callback) {
var id, idStr;
if(!$P.def(idVal['ToSerialized'])) {
id = new SP.Guid(idVal);
} else {
id = idVal;
}
idStr = id.toString('D');
if(!$P.def(idCache[idStr])) {
idCache[idStr] = client.lists.getById(idVal);
}
$P.client.l(idCache[idStr]).q(function(){
titleCache[idCache[idStr].get_title()] = idCache[idStr];
if($P.def(callback)) {
callback.apply(this, arguments);
}
});
return idCache[idStr];
};
list.bytitle = function (title, callback) {
if(!$P.def(titleCache[title])) {
titleCache[title] = client.lists.getByTitle(title);
}
if($P.def(callback)) {
$P.client.l(titleCache[title]).q(function(){
idCache[titleCache[title].get_id().toString('D')] = titleCache[title];
if($P.def(callback)) {
callback.apply(this, arguments);
}
});
}
return titleCache[title];
};
return list;
})({});
})($P.client);
// Our entry point.
function main() {
// Before going any further, let's build our gui.
buildGui($P.ui).info('Resolving our list..');
// Resolve our list.
resolveList(function (list) {
if(!$P.def(list)) {
$P.err(
'Failed to resolve list title. You may want to try going to the default view of ' +
'the list you want to work on.'
);
throw new Error('Unknown List');
}
$P.info('Resolved current list to ' + $P.listTitle);
// Load our list and it's associated fields.
$P.info('Querying list information..');
loadListFields($P.listTitle);
});
return $P;
}
// Try to resolve our list name. Checks for different context variables present on forms, views, etc. If we can't
// find those, we'll use resolveByUrl
function resolveList(callback) {
var list;
if($P.def(ctx)) {
if($P.def(ctx['ListTitle'])) {
$P.listTitle = ctx.ListTitle;
list = $P.client.list.bytitle(ctx.ListTitle, function(){
$P.listId = list.get_id();
callback($P.list = list);
});
} else if($P.def(ctx['pageListId'])) {
$P.listId = new SP.Guid(ctx.pageListId);
list = $P.client.list.byid($P.listId, function(){
$P.listTitle = list.get_title();
callback($P.list = list);
});
}
} else {
resolveByUrl(callback);
}
}
// This function is pretty much guaranteed to find the list associated with the current URL. It does, however,
// assume that you're at a page in your list's root folder (ex: /Path/leading/up/to/Lists/MyList/Page.aspx)
function resolveByUrl(callback) {
var url = w.location.pathname;
url = url.substr(0, url.lastIndexOf('/'));
$P.client.l($P.client.lists).q(function () {
var lists = [];
var enumerator = $P.client.lists.getEnumerator();
while(enumerator.moveNext()) {
var list = enumerator.get_current();
lists.push(list);
$P.client.l(list);
}
$P.client.q(function () {
var folders = [];
for(var i = 0; i < lists.length; i++) {
var list = lists[i];
var folder = list.get_rootFolder();
folders.push(folder);
$P.client.l(folder);
}
$P.client.q(function () {
for(var i = 0; i < folders.length; i++) {
var folderUrl = folders[i].get_serverRelativeUrl();
if(url.toLowerCase() === folderUrl.toLowerCase()) {
$P.list = lists[i];
$P.listId = $P.list.get_id();
$P.listTitle = $P.list.get_title();
}
}
callback($P.list);
});
});
});
}
// Construct our UI
function buildGui(ui) {
if($P.def(ui['root'])) { return ui; }
// Based loosely on:
// http://stackoverflow.com/questions/592815/is-there-really-no-way-to-expose-the-prototype-of-a-html-element-in-ie-8
var DOMElement = (function(DOMElement){
// take a copy of
// document.createElement
var _createElement = doc.createElement;
// take copy of
// document.getElementsByTagName
var _getElementsByTagName = doc.getElementsByTagName;
// take copy of
// document.getElementById
var _getElementById = doc.getElementById;
// Our extensions.
var _funcs = {};
DOMElement.extend = function (name, fn) {
try {
HTMLElement.prototype[name] = fn;
} catch(e) {
_funcs[name] = fn;
}
return DOMElement;
};
try {
HTMLElement.prototype.extend = DOMElement.extend;
} catch(e) {
//
// IE doesn't allow access to HTMLElement
// so we need to override
// *document.createElement
// *document.getElementById
// *document.getElementsByTagName
//
// override document.createElement
doc.createElement = function (tag) {
var elem = _createElement(tag);
if($P.def(elem)) {
for(var name in _funcs) {
//noinspection JSUnfilteredForInLoop
elem[name] = _funcs[name];
}
}
return elem;
};
// override document.getElementById
doc.getElementById = function (id) {
var elem = _getElementById(id);
if($P.def(elem)) {
for(var name in _funcs) {
//noinspection JSUnfilteredForInLoop
elem[name] = _funcs[name];
}
}
return elem;
};
// override document.getElementsByTagName
doc.getElementsByTagName = function (tag) {
var elements = _getElementsByTagName(tag);
for(var i = 0; i < elements.length; i++) {
var elem = elements[i];
if($P.def(elem)) {
for(var name in _funcs) {
//noinspection JSUnfilteredForInLoop
elem[name] = _funcs[name];
}
}
}
return elements;
};
}
return DOMElement;
})({});
// Utility functions for our DOM element.
ui.e = function (n) { return doc.createElement(n); };
ui.byid = function(idVal) {
return doc.getElementById(idVal);
};
ui.bytag = function(tagName) {
return doc.getElementsByTagName(tagName);
};
DOMElement.extend('css', function(prop, val) {
if($P.def(val)) {
this.style[prop] = val;
} else if(typeof prop === 'object') {
if(typeof prop !== 'object') { return this; }
for(var cssProperty in prop) {
//noinspection JSUnfilteredForInLoop
this.style[cssProperty] = prop[cssProperty];
}
}
return this;
}).extend('cls', function (n) {
this.className = n;
return this;
}).extend('add', function(){
var self = this;
for(var i = 0; arguments.length < 0; i++) {
var child = arguments[i];
if($P.def(child)) {
self.appendChild(child);
}
}
return self;
});
// Add an element to the root element.
ui.root = ui.e('div');
ui.add = function (element) {
ui.root.appendChild(element);
return element;
};
/* Depending on what browser/version we're using.. */
if($P.def(w['innerWidth'])) {
ui.winWidth = window.innerWidth;
ui.winHeight = window.innerHeight;
} else if(
$P.def(doc['documentElement']) &&
$P.def(doc.documentElement['clientWidth']) &&
(doc.documentElement.clientWidth != 0)
) {
ui.winWidth = doc.documentElement.clientWidth;
ui.winHeight = doc.documentElement.clientHeight;
} else {
// Not sure why you're using such an older browser but..
ui.winWidth = body.clientWidth;
ui.winHeight = body.clientHeight;
}
/* Set up our ui root element and its main children */
(function (root) {
/* Set the root element's style. */
(function (s) {
s.position = 'absolute';
var w = $P.floor(ui.winWidth / 4),
h = $P.floor(ui.winHeight / 3);
if(w > 400) { w = 400; }
if(w < 250) { w = 250; }
if(h > 300) { h = 300; }
if(h < 200) { h = 200; }
ui.width = w;
ui.height = h;
s.width = w + 'px';
s.height = h + 'px';
s.left = $P.floor((ui.winWidth - w) / 2) + 'px';
s.top = $P.floor((ui.winHeight - h) / 2) + 'px';
s.border = '1px solid #bbb';
s.margin = s.padding = 0;
})(root.style);
root.id = 'SPDelField_Root';
/* The title area */
var title = ui.e('a'),
exit = ui.e('a');
ui.header = (function (header) {
title
.cls('ms-navitem')
.css({
'float':'left',
'display':'block',
'padding':'5px',
'margin':0,
'width': (ui.width - 37) + 'px'
});
exit
.cls('ms-navitem')
.css({
'float':'left',
'display':'block',
'padding':'5px',
'margin':0,
'width':'13px',
'textAlign':'center'
});
title.id = 'SPDelField_Title';
exit.id = 'SPDelField_Exit';
title.innerHTML = 'Workflow Field Remover';
exit.innerHTML = 'X';
exit.href = title.href = '#';
title.onclick = 'javascript:return false;';
exit.onclick = function (event) {
event.preventDefault();
body.removeChild(ui.root);
return false;
};
header.cls('ms-tvselected').css({ 'margin':0, 'padding':0, 'height':'25px', 'overflow':'hidden'});
header.appendChild(title);
header.appendChild(exit);
return header;
})(ui.e('div'));
ui.add(ui.header);
ui.body = (function (body) {
var h = (ui.height - 35);
body.css({
'background':'url("/_layouts/images/selbg.png") #f6f6f6 repeat-x left top',
'padding':'5px',
'margin':0,
'height':h + 'px'
});
// Getting lazy..
var mh = $P.floor(h / 2) - 15;
body.innerHTML =
'<div style="height:15px;" id="SPDelField_Desc">Check the fields you wish to remove &amp; click&nbsp;</div>' +
'<div style="height:' + mh + 'px;border:1px solid #bbb;margin-top:5px;background:#fff;overflow:auto;" id="SPDelField_Fields"><strong>Loading Fields..</strong></div>' +
'<div style="height:' + mh + 'px;border:1px solid #bbb;margin-top:5px;background:#fff;overflow:auto;" id="SPDelField_Messages">&nbsp;</div>';
return body;
})(ui.e('div'));
ui.add(ui.body);
})(ui.root);
body.appendChild(ui.root);
var description = ui.byid('SPDelField_Desc');
ui.fields = ui.byid('SPDelField_Fields');
var remove = ui.e('a');
remove.href = '#';
remove.innerHTML = 'Remove';
remove.onclick = function (event) {
event.preventDefault();
var nodes = ui.fields.childNodes;
for(var i = 0; i < nodes.length; i++) {
var node = nodes[i];
var val = node.childNodes[0];
if($P.def(val['checked']) && val.checked) {
deleteField(val.value);
}
}
return false;
};
description.appendChild(remove);
ui.info = function (m) {
var messages = ui.byid('SPDelField_Messages');
for(var i = 0; i < arguments.length; i++) {
var message = ui.e('p');
message.css({ borderBottom:'1px solid #e5e5e5', margin:'0px', padding:'2px' });
message.innerHTML = arguments[i];
messages.insertBefore(message, messages.firstChild);
}
};
ui.err = function (m) {
var messages = ui.byid('SPDelField_Messages');
for(var i = 0; i < arguments.length; i++) {
var message = ui.e('p');
message.css({ color:'#d00', fontWeight:'bold', borderBottom:'1px solid #e5e5e5', margin:'0px', padding:'2px' });
message.innerHTML = arguments[i];
messages.insertBefore(message, messages.firstChild);
}
};
ui.addField = function (fieldName, fieldObj) {
var wrapper = ui.e('p');
wrapper.css({ borderBottom:'1px solid #e5e5e5', margin:'0px', padding:'2px' });
var field = ui.e('input'),
label = ui.e('for'),
wn = 'spfieldwrapper_' + fieldName;
field.value = fieldName;
wrapper.id = wn;
fieldName = 'spfield_' + fieldName;
field.type = 'checkbox';
field.name = fieldName;
field.id = fieldName;
label.setAttribute('for', fieldName);
label.innerHTML = fieldObj.get_title();
wrapper.appendChild(field);
wrapper.appendChild(label);
ui.fields.appendChild(wrapper);
};
ui.addFields = function () {
ui.fields.innerHTML = '';
var fieldsLength = 0;
for(var fieldName in $P.fields) {
//noinspection JSUnfilteredForInLoop
ui.addField(fieldName, $P.fields[fieldName]);
fieldsLength += 1;
}
if(fieldsLength === 0) {
ui.fields.innerHTML = 'No Workflow Status fields detected.';
description.removeChild(remove);
}
};
return ui;
}
// Load our list and all of its associated fields.
function loadListFields(l) {
var fields = $P.list.get_fields();
$P.info('Loaded list.. Querying fields..');
$P.client.l(fields).q(function (sender, args) {
var enumerator = fields.getEnumerator();
while(enumerator.moveNext()) {
var field = enumerator.get_current();
if(!field.get_readOnlyField() || (field.get_typeDisplayName() !== "Workflow Status")) {continue;}
$P.fields[field.get_staticName()] = field;
$P.info('Resolved field ' + field.get_staticName());
}
$P._fields = fields;
$P.ui.addFields();
});
}
// Delete a field on our list.
function deleteField(fieldName) {
$P.info('Attempting to remove read-only property from field.');
if($P.undef($P.fields[fieldName])) {
$P.err('Field ' + fieldName + ' does not exist in this context.');
throw new Error('Invalid Field');
}
var field = $P.fields[fieldName];
field.set_readOnlyField(false);
field.update();
$P.client.l(field, $P._fields).q(function (s, a) {
// The executeQueryAsync will return an error, but that's fine. As long as the error matches
// what we expect, there shouldn't be an issue.
field.deleteObject();
field.update();
// If all goes as planned, we wont reach the $P.noop arg.
$P.client.l(field, $P._fields).q($P.noop, function (a, b) {
if(b.get_message() === 'One or more field types are not installed properly. Go to the list settings page to delete these fields.') {
$P.info('Field removed successfully');
$P.ui.fields.removeChild(doc.getElementById('spfieldwrapper_' + fieldName));
} else {
$P.err('An error occurred during the removal of this field.. This should not happen.');
$P.log(b);
}
});
});
}
return main();
})(window);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment