Monday, May 24, 2021

A summary of Modern Records Management in Office 365

 Record Management is a key pillar in any enterprise content management for an organization so that the organization can comply with their corporate policies, legal and regulatory. Microsoft 365 offer record management features including classifications, records and disposition. In this article, I am providing a high-level overview.

Microsoft 365 Compliance Center

An entry point for managing compliance needs using integrated solutions for classification, information governance, case management, and more.

Record Management Key Functionalities

Document ID

A unique identifier for the document is required for Record Management, so that document can be tracked when the document gets to move to a different location. We can configure in SharePoint Libraries to automatically assign Document IDs to documents.

Data classification

Organisations require to classify data to apply the policies to govern data. In Office 365 we can classify in few ways. Organizations can classify the data by applying Sensitivity labels, Retention labels and Sensitive info.

No alt text provided for this image


Sensitivity Labels

Sensitive labels are used to apply based on the sensitivity of the electronic contents, then the contents will be protected based on the labels such as different encryptions. We can assign a sensitive label to a document, site or email. The sensitive label is persistent, the label will be in tag with the document during the moves.

Creating and Publishing Sensitivity label

1.    Log in with your M365 account in

https://compliance.microsoft.com/informationprotection?viewid=sensitivitylabels

 

2.      Create a sensitivity label


No alt text provided for this image


Fill in the details such as Name, description, encryption, content marking, and then save.

3.    Publish a sensitivity label.

No alt text provided for this image



Retention labels


A document retention policy is required for every organization so that the organization can retain various documents based on the legal requirements.

Retention labels, we can classify data across all the information management system, and enforce retention rules based on that classification.

Create a Retention Label


1.    Log in with your M365 account in

https://compliance.microsoft.com/informationprotection?viewid=sensitivitylabels

2.      Create a new Retention Label


No alt text provided for this image



3.      Fill in the retention details.

No alt text provided for this image


4.      Publish the retention Label


To publish a label, we need to create a policy and publish it. Please take note, Label publishing will take some time to deploy all the locations.

No alt text provided for this image
No alt text provided for this image





Applying and using Retention Label.

1.      Go to SharePoint Library

2.      Choose the document that requires applying the label, and edit the properties.

No alt text provided for this image



Auto Applying Retention Label.

Record Managers can configure libraries to auto-apply the retention labels. Here are the steps to configure a library with automatic retention labels.

1.      Go to Library settings.

2.      Apply label to items in this list or library


No alt text provided for this image


Records


A retention label can be used to classify the document as Records. When you create a document, you can specify the label for Records.


No alt text provided for this image



File Plan


File Manager provides advanced Record Management capabilities to the files, from creation through collaboration and record declaration retention and finally its position

File plan enables us to  create, retention Label, File policies, disposition, record declaring

Disposition


M365 provides tools to manage the disposition of a document. At the end of a retention period, There are three options to be configured.

1.      Delete items automatically

2.      Trigger a disposition review

3.      Do Nothing.

For option 2,( Trigger a disposition review), we can configure further.

1.      Add disposition reviewers

2.      Add stages

The disposition reviewer, able to do the following during the review

1.      Approve disposal

2.      Relabel

3.      Extend

4.      Add reviewers

e-Discovery


eDiscovery is the process of identifying and delivering electronic information that can be used as evidence in legal cases by using a discovery begin search content in

·         Exchange Online mailbox

·         Microsoft 365 groups

·        Microsoft Teams

·        Microsoft SharePoint Online

·        One drive for business

Reporting


The intelligent reports on the “Overview” page are a quick window into discovering how and where sensitive info and labels are being used across Exchange, OneDrive, SharePoint and other locations. Each report includes links to investigate further

Explore classified content

Content Explorer is a powerful tool for reviewing all the classified content in your organization. Filter by sensitive info type or label to see how many items are classified, where they're stored, and drill down even more to inspect the actual email or document.

Monitor and review activity

Activity explorer rounds out our data classification capabilities by providing a detailed view into classification activities and trends across locations, such as when labels were applied, sensitive data modified, files printed, and much more


Friday, November 3, 2017

Events Summary listing for homepage- JavaScript-based





We often display key events in the Home page of SharePoint site. The source calendar may not from the same site and we may need to pull data from the different site. I wrote a simple JavaScript-based events summary listing below. It is a quick and easy solution.

OutPut




Script


<!--Author : Sutha Thavaratnarajah 01/09/2017
* You may need to refer jQuery reference if it is not exisit in your site.
 -->
<script  src="https://code.jquery.com/jquery-3.2.1.min.js"   integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4=" crossorigin="anonymous"></script>
 

<style>
.st-event-Day{
border: 1px solid black;
float:left;
margin:5px;
}

.st-event-month{
font-size:14px;
padding:5px 10px 2px 10px;
}
.st-event-singleDay{
font-size:16px;
font-weight: bold;
padding:2px 10px 2px 10px;
}
.st-event-details{
margin:5px;
}
.st-event-card-title{

}
.st-event-dard-datetime{

}
</style>
<script type="text/javascript">

var siteUrl = 'https://infoleadsolution.sharepoint.com/sites/demo2';

varItemViewURL = 'https://infoleadsolution.sharepoint.com/sites/demo2/Lists/MyCalander/DispForm.aspx?ID=';

var month_name = function(dt){
mlist = [ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" ];
  return mlist[dt.getMonth()];
};

$(document).ready(function() {

ExecuteOrDelayUntilScriptLoaded(retrieveListItems, "sp.js");
  
 } );
    
    
function retrieveListItems() {

    var clientContext = new SP.ClientContext(siteUrl);
    var oList = clientContext.get_web().get_lists().getByTitle('MyCalander');
        
    var camlQuery = new SP.CamlQuery();
    camlQuery.set_viewXml('<View><Query><OrderBy><FieldRef Name="Created" Ascending="false" /></OrderBy></Query><RowLimit>5</RowLimit></View>'); // change what ever filter you neeed.
    this.collListItem = oList.getItems(camlQuery);
        
     clientContext.load(collListItem, 'Include(ID,Title,EventDate)');
        
    clientContext.executeQueryAsync(Function.createDelegate(this, this.onQuerySucceeded), Function.createDelegate(this, this.onQueryFailed));        
        
}
function onQuerySucceeded(sender, args) {

    var listItemEnumerator = collListItem.getEnumerator();
         var Title = "";
   var eventDate = new Date();
   var eventID = 0;
   var itemURL ="";
   var content ="";
   eventMonth = "";
   eventDay = "";
    while (listItemEnumerator.moveNext()) {
        var oListItem = listItemEnumerator.get_current();
  
        eventID = oListItem.get_item('ID') ;
  Title = oListItem.get_item('Title') ;
  itemURL =varItemViewURL + eventID ;
  eventDate = (oListItem.get_item('EventDate')).toDateString('dd/MM/yyyy hh:mm'); //dateformating 
  //eventMonth=(oListItem.get_item('EventDate')).toDateString('MMM');
  eventMonth = month_name(new Date(oListItem.get_item('EventDate')));
  eventDay=oListItem.get_item('EventDate').getDate();
  //content += "<tr><td><b><a href='"+ itemURL + "'>" +  Title + "'</a></b><br>" +   eventDate + "<br><br></td></tr>";
  
  content +=  "<div style ='overflow: hidden;'>";
  content += "<div class='st-event-Day'>";
  content += "<div class='st-event-month'>"+ eventMonth +"</div>";
  content += "<div class='st-event-singleDay'>" + eventDay +"</div>";
  content += "</div>";
  content +=  "<div class='st-event-details'>";
  content += "<div  class='st-event-card-title'><a href='"+ itemURL + "'>" +  Title + "'</a></div>";
  content += "<div class='st-event-dard-datetime'>" +   eventDate + "</div>";
  content += "</div>";
  content += "</div>";
    
    }
     $(content).appendTo("#here_table");
}
function onQueryFailed(sender, args) {

    alert('Request failed. ' + args.get_message() + '\n' + args.get_stackTrace());
}
                       
</script>

<div>   
<div id="here_table"></div>
</div> 

Tuesday, September 19, 2017

Nintex Repeating Section Date/Time - Using in an email or a string builder





Problem

Date and Time often challenge in any development works, recently I experienced some challenges in Nintex repeating section.

Scenario:

I built a Nintex form with repeating sections which have a date and time field. The repeating section store the data in XML format.  A running workflow read the repeating section data to construct an email body.
 I queried the XML and try to store the values to workflow variables. Then I use a string builder to write the contents of my email. Everything went very well except the date. J

When I had a deeper look, I found-out Nintex write dates in MM/DD/YYYY format and my SharePoint environment in DD/MM/YYYY. Nintex has a detail explanation for that. You can find here. https://support.nintex.com/SharePoint/Forms/2016/Date_Format_in_XML_of_Repeating_Section_is_Always_MM%2F%2FDD%2F%2FYYYY_With_Query_XML_Workflow_Action

Solution

Query and store the date field value into text variable. Make sure return result as “Text”



Then, use “convert value” to another variable which is date time type variable.



You need to convert as follows.
Input-- > date-time variable – text type
Store in date time Variable – date time type
In advance – use the US culture. So that it will read the text field correctly. However, it will store as per your SharePoint regional settings.




Now, we can use the stored date time field in the string builder or in emails.

Thursday, July 27, 2017

locked for exclusive use- SharePoint file

In SharePoint Documents, sometimes we may experience the filed locked by error. In this post, I like to share my resolution for that.We might receive a message stating that file is locked for editing by another user, However, the locked by the user has no intention to lock the file. To release the document by scripting, I wrote the below script.

Please investigate the issue and then if it is appropriate, you shall use the script.

Script:

Release-FileLock


Wednesday, April 22, 2015

Timer Job to Start the workflow in a list

Recently, I had an interesting requirement. No of list contains items and they expire on certain dates and require to send reminders for each items.  Initially I thought to write a custom workflow and kick the workflow by the OOB expiration policy/information policy. However information policy check dates are above the specified date. Example: anything more than 7 years.  So if you set the workflow to kick, it’s going to kick again and again for any items which are more than 7 years. Especially you won’t prefer to send email more than once. So I come up with a timer job for that.
The timer job will read a central list, which has details of list that need to run the expire workflow, and then kick the workflow.
Here is the sample of that list.


Then I create a timer job.
Here is the code for that.

namespace SP.Sample.OnlineForms
{
    using System;
    using System.Collections.Generic;
    using System.Web;
    using Microsoft.SharePoint.Administration;
    using Microsoft.SharePoint;
    using Microsoft.SharePoint.Workflow;
    using Microsoft.SharePoint.WorkflowUtil;
    using Microsoft.SharePoint.Utilities;
    using System.Security.Permissions;
    using System.Text;
    using System.Collections.Specialized;

    public class SampleExpireAlertsTimerJob : SPJobDefinition
    {
        //SPWeb mySiteWeb;

        public SampleExpireAlertsTimerJob(SPWebApplication webApp)
            : base("Sample Expire Alerts", webApp, null, SPJobLockType.Job)
        {
            this.Title = "Sample Expire Alerts";
        }
        public SampleExpireAlertsTimerJob()
            : base()
        {
        }
        public override void Execute(Guid targetInstanceId)
        {
            string key = "TeamsRootSiteUrl";
            string TeamsRootSiteUrl = "";
            if (this.Properties.Contains(key))
            {
                TeamsRootSiteUrl = this.Properties[key].ToString();
               
            }

            if (!string.IsNullOrEmpty(TeamsRootSiteUrl))
            {

               

                using (SPSite site = new SPSite(TeamsRootSiteUrl))
                {
                    using (SPWeb web = site.RootWeb)
                    {
                        SPList list = web.Lists.TryGetList("SampleExpireAlerts");
                        SPQuery oQuery = new SPQuery();

                        oQuery.Query = string.Format(@"
                                                <OrderBy><FieldRef Name='ID' /></OrderBy>"
                                              );
                        SPListItemCollection collListItems = list.GetItems(oQuery);

                        foreach (SPListItem item in collListItems)
                        {
                            string weburl = item["WebURL"] as string;
                            string listName = item["ListName"] as string;
                            string dateFieldtoFilter = item["DateFieldtoFilter"] as string;
                            int daysToFilter = int.Parse(item["DaysToFilter"] as string);
                            string workflowName = item["WorkflowName"] as string;

                            StartAlertWorkFlow(weburl, listName, dateFieldtoFilter, daysToFilter, workflowName);

                        }
                    }
                }
            }
        }

        /// <summary>
        /// Send remainder and warning emails to the employees
        /// </summary>
        /// <param name="employeeName">Name of the employee ex: Sutha Thavaratnarajah</param>
        /// <param name="moduleName">Name of the module ex: conduct</param>
        /// <param name="TeamsRootSiteUrl"> Url retrived from the timer job property.</param>
        /// <param name="eid">email template ID.</param>
        public void StartAlertWorkFlow(string weburl, string listName, string dateFieldtoFilter, int daysToFilter, string workflowName)
        {
            using (SPSite SampleSite = new SPSite(weburl))
            {
                using (SPWeb SampleWeb = SampleSite.OpenWeb())
                {

                    SPList list = SampleWeb.Lists.TryGetList(listName);
                    SPQuery oQuery = new SPQuery();
                    //string userName = "Sutha Thavaratnarajah";
                    string datetofilter = System.DateTime.Today.AddDays(daysToFilter).ToString("yyyy-MM-dd");

                    oQuery.Query = string.Format(@"
                                                    <Where>
                                                      <Eq>
                                                         <FieldRef Name='{0}' />
                                                         <Value IncludeTimeValue='False' Type='DateTime'>{1}</Value>
                                                      </Eq>
                                                   </Where>", dateFieldtoFilter, datetofilter);
                    SPListItemCollection collListItems = list.GetItems(oQuery);
                    foreach (SPListItem item in collListItems)
                    {

                        item.Web.AllowUnsafeUpdates = true;
                        SPWorkflowManager workflowManager = item.Web.Site.WorkflowManager;
                        SPWorkflowAssociationCollection workflowAssociation = item.ContentType.WorkflowAssociations;
                        foreach (SPWorkflowAssociation Association in workflowAssociation)
                        {
                            if (Association.Name == workflowName)
                            {
                                workflowManager.StartWorkflow(item, Association, Association.AssociationData, true);
                                break;
                            }
                        }

                   

                    }
                  
                }
            }
        }
    }
}

So, this timer job will kick the specified workflow in specified List with the given criteria.


Note: you need to create the workflow in the target lists.