Skip navigation
All Places > Getting Started > Blog > 2016 > June

We recently ran into a request to "archive project sites after the project has been completed" and this started to make me think of how we can automate as much as possible behind the scenes. Before we could do anything, we first had to define what "archive" meant to the end users, site owners, and us (the SharePoint team). After a few conversations, we all landed on the following:


  1. An archived site retains current permission structure, but is reduced down to read at the site level.
  2. All users will have access to the site, for historical reference, at a read level.
  3. The site will be "stamped" with an expiration date, upon which the site will be reviewed and deleted.
  4. Any content on the site may need to be migrated to team sites (for operational guides).


With these key points in mind, we can begin to really understand how to approach this. I am going to go over the first two steps here, and the others at a later time.

I do want to say that this will perform the actions at the top level of the site. Meaning, that if a user or group is applied directly to a library or a list, this process will not change the permissions that were applied through breaking inheritance.


Let's dive in!


The Setup

Before we get to the good stuff, let us go over what we need.

I did this in a UDA because we have heard similar asks from other departments, so in preparation I went ahead and did it this way. You can easily do this in a site workflow and feed it from a list.


UDA Parameters


Workflow Variables


Retaining Current Structure Reduced to Read

Honestly, at first we suggested to remove all user permissions and then grant everyone read via AD. This accomplishes the same thing, but we lose the permissions structure that was in-place. We also avoid any issues with orphaned user groups, or even removing user groups that were applied on other project sites.


First thing to do is set the permission mask variable to read. This is done by setting out variable ReadPermissionMask to 138612833.

There are a lot of articles out there regarding permission masks and what each of them are.


Next, we need to get the applied groups and update their permissions. We will use Web Service calls to get the group XML so that we can iterate through the data and perform our actions.


We can use the GetGroupCollectionFromWeb service call from usergroup.asmx to set our varGroupXML. We will go after the data from the site that is provided by the user or workflow that we require as a parameter (inputSiteURL). You will have to put in an actual site URL to generate a list of web methods, but you can then replace the URL with your variable once you are setup.



Now that we have the data, we need only to get the specific user group names. So of course we will throw a Query XML action in there and query our variable (varGroupXML). I would recommend testing the web service and seeing the XML so that you understand the structure. It helps determine the XPath needed to get the desired elements. For this, we will use the following for the XPath to get to the Name element: /defaultNS:GetGroupCollectionFromWeb/defaultNS:Groups/defaultNS:Group/@Name


It is time to do the actual updates! We will use a For each loop, and look at the varPermissionIDColl. For each varPermissionID in that collection, we want to call a web service to update the permissions. We will use the UpdatePermission web service in permissions.asmx. Just as before, we will use the inputSiteURL variable to target the specific site. We will need a bit more information on this step:


objectType: Web

permissionIdentifier: varPermissionID

permissionType: Group

permissionMask: ReadPermissionMask



That is it for the groups. If you were to run this now, all user groups on the target site would be updated to Read permissions.

While we updated all the groups, what about the users? In a perfect world, everyone would be in a group so we would not need to worry about random users being applied outside of a group...but alas we do not live there and random users are granted access outside of user groups. So to accomplish the same thing for users, you will run through the same process, but this time go after users, not groups.


Just like in the first web service call, you will want to use a method from usergroup.asmx, but this time you will want to call the GetUserCollectionFromWeb method to set our varUserXML.


Again, using a Query XML action, get a collection of names to perform the updates on. This time we will want to get login names since we are dealing with users. The XPath for this will be something like: /defaultNS:GetUserCollectionFromWeb/defaultNS:Users/defaultNS:User/@LoginName


I stored it into the varPermissionIDColl variable (you could create another variable if wanted or needed for debugging) and looped through it in the same way. This time, in the web service call, we need to make a slight change since we are dealing with users:


objectType: Web

permissionIdentifier: varPermissionID

permissionType: User

permissionMask: ReadPermissionMask


That covers all user groups and users that are applied to the site level. Last piece is to apply all users to the site with Read permissions.

We accomplish this with a simple web service call using the method AddPermission from permissions.asmx. Here is what it will look like:


Final Thoughts

This process is by no means one-size-fits-all, but it does accomplish some common asks (at least from what we have encountered). If you need to reduce all users and groups down to read, this is a straightforward way of doing things. Also, you could elevate permissions using the same process; simply change the permission mask. I have been toying with the idea of adding some logic to this to allow for permission level selection and evaluate the desired level within the workflow. This would allow for both reducing permissions as well as elevating them all within one UDA.


Also, as I said at the beginning, this only updates users and groups at the top site level. Any areas with broken inheritance will not be affected by this. I would like to explore how to "re-inherit" throughout the site, or possibly, find the areas with broken inheritance. This way no one slips through the cracks with different permissions.


Let me know what you have done or how you would approach/improve this!


Until next time.


I posted the UDA in Nintex Xchange™ titled Remove Permissions UDA  if you want to grab a copy of it.

Converting a Word Document to PDF

I am often asked about seperation of permissions with working and published documents, and a way of doing this nicely with workflow is to use the Convert document action.  We have many instances within the business where a small team have access to a "working" document library but push finalised and approved documents to an open document library for all authenticated users to consume.  We will use this scenario for the working example below.  We will have our "working" document library called Read Write PDF and have our "published" document library called Shared Documents.  All users will have access to read the content of Shared Documents but only team members will have access to Read Write PDF.

  • Navigate to the Read Write PDF document library and select Library tab in the ribbon, Workflow Settings and Create a Workflow in Nintex Workflow.
  • Select the blank template.
  • We need to change the file name of the document we are working on to remove the .docx extension and replace it with .pdf (as when we configure the Convert document action later we will need the full name including extension).  We can do this by adding a Regular expression action to the workflow.
  • The Regular expression action requires a variable to put the output value into, so we need to create a variable.
  • Drag the Regular expression action to the first design node on the workflow and double click to open the configuration options.
  • To create a variable for the output of this action, select Variables in the ribbon of the Regular expression action to open up the Workflow Variables window.  Select New in the ribbon and create a variable as below:


  • Select Save and then close the Workflow Variables window to return to the configuration of the Regular expression action.
  • Configure it as follows:


  • So now we have the name and extension of our newly created pdf, we need to actually convert the document into a PDF and place it into the Shared Documents library.  Add the Convert document action to the next design node on the workflow and double click it to open the configuration settings.
  • Configure the item as follows, ensuring that the Output URL is https://xxx/sites/busitc/Nintex/Shared%20Documents/{vTextFileName} - replacing the bold area with the variable created earlier by using the insert reference book and selecting Workflow Variables.


In this example I have decided to catch the errors (if any occur) in the above action and store the occurrence and text in variables called vBoolError (did the error occur) and vTextError (what was the error).

I will use a Run if logic action to log the details of the error to the history list if an error occurred.

  • Add the Run if action to the next design node on the workflow and double click it to open the configuration window.


The idea behind this is that we want to check if our newly created variable vBoolError is equal to yes, which would imply that the Convert document action errored.

The configuration above checks if the variable vBoolError = yes

Inside the Run if action, I use Log in history list action to log the reason for the error in the message body by referencing the variable vTextError


Add any additional actions as required to your workflow.  Change the start up options as required (in this example we were happy with a manual start but this could be done after multiple approvals for example) and Publish.

Hello again!

I wanted to take some time and go over the Query List action as there has been a rise in questions from the community on how to leverage it. Not only the query, but what to do with the data after we query it. Let's dive right in.


Query List Action

The action is straightforward enough; query a list for specific results. An example could be all items where [Created] = {varDate} or [ID] = {varLookupID} or even just return all items (I would advise against this if possible). But how do we store the resulting data? What about if we get multiple results? If you know that the query is going to return only one item every time, then a normal variable (based on the data type it will be storing) will suffice. Otherwise, you are going to want to store your results in a Collection variable for every piece of data you want to do something with. So, if you are querying a list with 10 columns, and you only need 4 of them (to send a notification), you will need to create 4 collection variables to store the resulting data.



Collection Variables

A collection variable allows us to store a grouping of similar data types (a set of start dates, or item IDs) in an array so that we can iterate through it and utilize the data in some way. We may want to use the data to do calculations, or perhaps build a string and populate an email. So how do we do that? We can use a Loop, more specifically, a For Each loop.




We can use a For Each loop to iterate through our collection and get the corresponding data singled out. We want to start with one of our collections and store the first item in a more manageable variable. Below I used the titleColl and stored the result in itemTitle starting at the position designated by the workflow variable index. By default, you do not have to use an index, but I find it easier to manage in the event that I want to target a specific location within my collection. Also, it keeps my logic streamlined and easier to follow by someone else.

Next we need to get the corresponding data based on the where we are at in the loop. We can run these in parallel and use Collection operations to GET our variables.

Within each of our Collection operation actions, we want to get the data at the same index as the others and then store it in an appropriate variable. For example, if we want to get the item's Start Date, End Date, and ID, we would target each of those collections at the specific index and store that data in a workflow variable like so:

I am not going to go into details about the other operations that you can do with collection variables, but I highly recommend the blog posts that Paul Crawford put together on Collection Operations  and more specifically Collection Operation - Get .


Ok, so now we have the specific item details that we wanted, but what can we do with it? Short answer, anything! You now have the most granular data on that item, so here is where the "I need it to do [this]" happens. For example, if you needed to send an email notification to the [Created By] for each item, you can now do that and populate the item with specific information. We see a lot of this for reminder emails based on a specific date. Query a list based on "status" and then see if any item(s) need to trigger a reminder based on a due date.


In this example, I am building a string to put in an email. I grab the itemTitle and add it to the emailString each iteration:


Here is the list (feeling strange today ):

And here is the resulting email:


Final thoughts

One thing that I did not mention is to increment the index. This allows you to keep track of where you are in the loop and if needed, you can move back and forth using that variable. To increment index, simply add a Math operation action and increase it by 1.


I do want to mention that I have been exposed to a different way of looping through collections by Cassy Freeman. I have been experimenting with this new method and while it will provide the exact same results, it does perform a bit better with larger queries. Perhaps we can dive into that next time and shed some light on the differences and benefits!


Until next time!


P.S. - I attached the .nwf file for anyone that wants to see it in action. I am more "hands on" learner and like to see things in action and tinker to learn

Filter Blog

By date: By tag: