Programatically enable Content Approval (Moderation) in SharePoint List - sharepoint

I am writing a app that creates a custom events list.
I'd like to enable content approval (also referred to Moderation on the back end) upon creation of the list.
Here's what my list-creation code looks like.
function createList(listToCreate)
{
// Create a SharePoint list with the name that the user specifies.
var hostUrl = decodeURIComponent(getQueryStringParameter("SPHostUrl"));
var hostContext = new SP.AppContextSite(currentContext, hostUrl);
var hostweb = hostContext.get_web();
var listCreationInfo = new SP.ListCreationInformation();
//title the list
listCreationInfo.set_title(listToCreate);
//set the base type of the list
listCreationInfo.set_templateType(SP.ListTemplateType.events);
var lists = hostweb.get_lists();
//use the creation info to create the list
var newList = lists.add(listCreationInfo);
var fieldCollection = newList.get_fields();
//add extra fields (columns) to the list & any other info needed.
fieldCollection.addFieldAsXml('<Field Type="User" DisplayName="Requester" Name="Requester" />', true, SP.AddFieldOptions.AddToDefaultContentType);
fieldCollection.addFieldAsXml('<Field Type="User" DisplayName="Manager" Name="Manager" />', true, SP.AddFieldOptions.AddToDefaultContentType);
fieldCollection.addFieldAsXml('<Field Type="Boolean" DisplayName="Approved" Name="Approved" />', true, SP.AddFieldOptions.AddToDefaultContentType);
//Attempting to enable moderation. This doesn't seem to have any effect.
newList.set_enableModeration(true);
currentContext.load(fieldCollection);
currentContext.load(newList);
currentContext.executeQueryAsync(onListCreationSuccess, onListCreationFail);
}
function onListCreationSuccess() {
alert("We've created a list since one didn't exist yet. Look in the site that hosts this app for the list." );
}
function onListCreationFail(sender, args) {
//alert("We didn't create the list. Here's why: " + args.get_message());
}
Unfortunately it seems that .set_enableModeration(true); has no effect. I don't get any errors, but when I look at the settings for the list I created using this code I see this:
So Content Approval clearly was not enabled via the method I'm using.

In order to set Content Approval using SP.List.enableModeration Property, the SP.List.update() Method have to be called as demonstrated below:
list.set_enableModeration(true);
list.update();

Related

SharePoint 2016 Custom Action to trigger a workflow

I am trying to create a custom action that can be chosen by right-clicking on a file in a any folder of a particular SharePoint Library. This custom action would copy the file into the same folder with the user's login name appended to the end of the file name.
I currently have an event receiver that will perform the custom action when a file is being updated but that's not when I want it to happen. I was able to add a custom action to the right-click file menu using SharePoint Designer but SharePoint Designer only lets the custom action trigger special SharePoint 2010 compatible workflows or load a web page. I need to make it so the event handler (or possibly a workflow) fires when the user chooses the custom action after right-clicking on the file. I'm not sure what approach or what kind of project or app I need to create in Visual Studio 2017 to get this functionality.
Your custom action should call javascript function or perform GET request to your SharePoint hosted WCF or ASMX WebService.
ASMX
Official MSDN Walktrought: Creating a Custom ASP.NET Web Service
For additional resources with more screenshots check this blog post: Walkthrough: Creating a Custom ASP.NET (ASMX) Web Service in SharePoint 2010
WCF
Official Technet Walkthrough: SharePoint 2013: Create a Custom WCF REST Service Hosted in SharePoint and Deployed in a WSP
Note: with GET request you will need web.AllowUnsafeUpdate = true
With javascript u need AJAX call ie jQuery.ajax()
/edit
To hook up web service and your custom action use SharePoint Desinger, delete or change your existing custom action, change type to Navigate to URL and in the textbox type:
javascript: (function() { console.log('Testing...' + {ItemId}); /* your web service call */ })();
Use {ItemId} alias to pass proper item id to your AJAX call.
On the other hand, on web service side use SPWorkflowManager class to start a workflow on item. Check the code belowe (link):
public void StartWorkflow(SPListItem listItem, SPSite spSite, string wfName) {
SPList parentList = listItem.ParentList;
SPWorkflowAssociationCollection associationCollection = parentList.WorkflowAssociations;
foreach (SPWorkflowAssociation association in associationCollection) {
if (association.Name == wfName){
association.AutoStartChange = true;
association.AutoStartCreate = false;
association.AssociationData = string.Empty;
spSite.WorkflowManager.StartWorkflow(listItem, association, association.AssociationData);
}
}
}
I found a way to do this using JavaScript, without SharePoint Designer. I put the following script in a Content Editor web part on the page where the listview webpart is and now I can right click on a file and get the option to "Get My Copy". If you have a Comments sub folder, the renamed copy will get put there.
<script type="text/javascript">
// adds the menu option to Get My Copy
function Custom_AddDocLibMenuItems(m, ctx)
{
var strDisplayText = "Get My Copy"; //Menu Item Text
var strAction = "copyFile()";
var strImagePath = ""; //Menu item Image path
CAMOpt(m, strDisplayText, strAction, strImagePath); // Add our new menu item
CAMSep(m); // add a separator to the menu
return false; // false means standard menu items should also be rendered
}
// append current user account to filename and copy to subfolder named Comments
function copyFile()
{
// get web and current user from context
var context = new SP.ClientContext.get_current();
var web = context.get_web();
this.currentUser = web.get_currentUser();
context.load(currentUser);
// load the folder
var currentFolder = decodeURIComponent(ctx.rootFolder);
var folderSrc = web.getFolderByServerRelativeUrl(currentFolder);
context.load(folderSrc,'Files');
context.executeQueryAsync(
function() {
// get the first (and hopefully only) file in the folder
var files = folderSrc.get_files();
var e = files.getEnumerator();
e.moveNext()
var file = e.get_current();
// get user account
var curUserAcct = currentUser.get_loginName();
curUserAcct = curUserAcct.substring(curUserAcct.indexOf("\\") + 1);
// get file without extension
var file_with_ext = file.get_name();
var name_without_ext = file_with_ext.substr(0, file_with_ext.lastIndexOf("."));
var destLibUrl = currentFolder + "/Comments/" + name_without_ext + " " + curUserAcct + ".docx";
file.copyTo(destLibUrl, true);
context.executeQueryAsync(
function() { alert("Success! File File successfully copied to: " + destLibUrl); },
function(sender, args) { alert("error: " + args.get_message()) }
);
},
function(sender, args){ alert("Something went wrong with getting current user or getting current folder '" + currentFolder + "'. " + args.get_message()); }
);
}
</script>

sharepoint list item click event and value change

So my goal is to allow the user to click the item on the list and it automatically changes a value on the item. Mostly I want a easy to allow the user to mark the item as being read. This list will be updated every week, so it will allow the user to know what is new easier. I have already set up the views so the users can only see their own data on the personal view.
I have looked online and I'm not having great luck. This is what I have so far.
<script src="https://code.jquery.com/jquery-1.11.2.min.js" type="text/javascript"></script><script type="text/javascript">
$(document).ready(function() {
$(".ms-listviewtable > tbody > tr").click(function(){ //only works before ajax runs
setTimeout( function() {
//alert("Hello! I am an alert box!!");
var ctx = SP.ClientContext.get_current("https://...");
var items = SP.ListOperation.Selection.getSelectedItems(ctx);
var clientContext = new SP.ClientContext();
var oList = clientContext.get_web().get_lists().getByTitle('Ticket');
this.oListItem = oList.getItemById(items[0].id);
this.oListItem.set_item('read','yes');
this.oListItem.update();
clientContext.executeQueryAsync(Function.createDelegate(this, this.onQuerySucceeded), Function.createDelegate(this, this.onQueryFailed));
}, 200); //had to be put in place to give time for the list to be selected
});
});
These are the issues I have.
the first time I click a item it tells be SP.ListOperation.Selection.getSelectedItems is null. However if I click the same item it will give me the ID number. Then if I click it a third time it will tell me its null again.
It does not change the vaule, but it does give me the right ID number after I click it two times.
EDIT,
see comments below
Using this code:
$("table.ms-listviewtable").on("mouseup", "tr.ms-itmhover", function() {
//your code
});

Creating list content type using csom

I am not able to create a list content type using the below snippet. It throws a ServerException with additional information - "The site content type has already been added to this list."
var list = clientContext.Web.Lists.GetByTitle("sometitle");
var documentCT = clientContext.Web.ContentTypes.GetById("0x0101");
clientContext.Load(list,l=> l.ContentTypes);
clientContext.Load(documentCT);
clientContext.ExecuteQuery();
var test = new ContentTypeCreationInformation(){
Name = "TestCT", ParentContentType =documentCT };
list.ContentTypes.Add(test);
list.Update();
clientContext.ExecuteQuery();
Basically, I want to create a list content type whose parent is the "Document" CT.
I encountered this same problem.
What is happening here is that you have added the content type to the list successfully but you haven't turned on "allow management of content types" in Library settings > Advanced Settings > First setting. You will not see the content type via the UI.
Once you turn on this setting you will see your content type was in fact added.
Here's how I create a library
public static List CreateLibrary(ClientContext context, string title, bool allowContentTypes)
{
ListCreationInformation lci = new ListCreationInformation
{
Description = "Library used to hold Dynamics CRM documents",
Title = title,
TemplateType = 101,
};
List lib = context.Web.Lists.Add(lci);
lib.ContentTypesEnabled = allowContentTypes ? true : false;
lib.Update();
context.Load(lib);
context.ExecuteQuery();
return lib;
}
For your case just add in the line:
list.ContentTypesEnabled = true;
Don't forget the list.Update(), I see you have it in your code but for anyone else, this part is essential before you use ExecuteQuery()

Customizing the sharepoint document set

I have to customize sharepoint 2013 document set welcome page(document set properties web part) and
I am not able to find the document set home aspx page
Can I use any client context model to fetch the data and display doc set properties.
Just create a new content type inheriting from Document Set, add the extra columns and set which ones need to be shown on the welcome page via the Document Set settings link when editing the Content Type. Also under the Document Set settings page you can click on 'Customize the Welcome Page' which allows you to edit the page just like any other web part page.
On the second point the client context will need to be connected to the web that contains the list and specific document set you're after, the identity used to connect will need permissions to the document set.
Edit:
To customize the look and feel by injecting JavaScript/CSS you'll need to make use of the ScriptLink custom action.
This lets you inject a custom piece of JavaScript into all pages. In the script you'll need logic to determine if the custom CSS should be applied, and if so Inject it.
The C# for injecting a script block via ScriptLink Custom Action:
public void AddJsLink(ClientContext ctx, Web web)
{
string scenarioUrl = String.Format("{0}://{1}:{2}/Scripts", this.Request.Url.Scheme,
this.Request.Url.DnsSafeHost, this.Request.Url.Port);
string revision = Guid.NewGuid().ToString().Replace("-", "");
string jsLink = string.Format("{0}/{1}?rev={2}", scenarioUrl, "injectStyles.js", revision);
StringBuilder scripts = new StringBuilder(#"
var headID = document.getElementsByTagName('head')[0];
var");
scripts.AppendFormat(#"
newScript = document.createElement('script');
newScript.type = 'text/javascript';
newScript.src = '{0}';
headID.appendChild(newScript);", jsLink);
string scriptBlock = scripts.ToString();
var existingActions = web.UserCustomActions;
ctx.Load(existingActions);
ctx.ExecuteQuery();
var actions = existingActions.ToArray();
foreach (var action in actions)
{
if (action.Description == "injectnavigation" &&
action.Location == "ScriptLink")
{
action.DeleteObject();
ctx.ExecuteQuery();
}
}
var newAction = existingActions.Add();
newAction.Description = "injectnavigation";
newAction.Location = "ScriptLink";
newAction.ScriptBlock = scriptBlock;
newAction.Update();
ctx.Load(web, s => s.UserCustomActions);
ctx.ExecuteQuery();
}
Then your JavaScript would have something like:
if(window.location.href.indexOf(patternToMatchToDocSetpage)>-1) {
var link = document.createElement("link");
link.href = "http://example.com/mystyle.css";
link.type = "text/css";
link.rel = "stylesheet";
document.getElementsByTagName("head")[0].appendChild(link);
}
I'd advise you to take a look at the relevant PnP sample on script link injection

Get Current Users Group using the SP 2010 javascript Client Side Object Model

I am trying to obtain the current user's SharePoint group name that they belong to. I haven't been able to find a method/property that provides that information. I've only been able to get the current user's username. Is there a property that provides me this information that I am not seeing?
There is no direct method for returning the groups for the current user through javascript.
Here is a post to an MSDN discussion group that describes a work around to return this information.
If you want to know the group Name for checking permissions, a workaround is here.
So basically:
context = new SP.ClientContext.get_current();
web = context.get_web();
var value = web.get_effectiveBasePermissions();
If you need the Group Name, unfortunately there is no direct way to do that. But we can get the current user and get user collection for one group. Then you can check the user collection from one group to see whether it contains the current user.
Get current user: example
Get group collection for the current web: example
Get specified group
var groupCollection = clientContext.get_web().get_siteGroups();
// Get the visitors group, assuming its ID is 4.
visitorsGroup = groupCollection.getById(4);
Get users for the group
var userCollection = visitorsGroup.get_users();
Check the user collection to see whether it contains the specified user.
For a simple demo you can see the following document.
As indicated by Vadim Gremyachev here you can get the current user var currentUser = currentContext.get_web().get_currentUser() then get all the groups var allGroups = currentWeb.get_siteGroups();
From here you can loop through the group to see if your user is in the current group. So if you have a list of groups you want to check, Members, Owners, Viewers, then just use this method to detect if they are in each group.
function IsCurrentUserMemberOfGroup(groupName, OnComplete) {
var currentContext = new SP.ClientContext.get_current();
var currentWeb = currentContext.get_web();
var currentUser = currentContext.get_web().get_currentUser();
currentContext.load(currentUser);
var allGroups = currentWeb.get_siteGroups();
currentContext.load(allGroups);
var group = allGroups.getByName(groupName);
currentContext.load(group);
var groupUsers = group.get_users();
currentContext.load(groupUsers);
currentContext.executeQueryAsync(OnSuccess,OnFailure);
function OnSuccess(sender, args) {
var userInGroup = false;
var groupUserEnumerator = groupUsers.getEnumerator();
while (groupUserEnumerator.moveNext()) {
var groupUser = groupUserEnumerator.get_current();
if (groupUser.get_id() == currentUser.get_id()) {
userInGroup = true;
break;
}
}
OnComplete(userInGroup);
}
function OnFailure(sender, args) {
OnComplete(false);
}
}
// example use
window.IsCurrentUserMemberOfGroup("Members", function (isCurrentUserInGroup){
if(isCurrentUserInGroup){
console.log('yep he is');
} else {
console.log('nope he aint');
}
});

Resources