SharePoint Online - Last Modified

Infostruction banner: a person holding a mug stands in a dim home office lit by server racks and monitors, with the Infostruction logo and wordmark across the centre.

Issue

By default, SharePoint Online document libraries don’t float up the ‘Last Modified’ time when you make changes within a folder hierarchy. This will bring hate mail from your users, especially if they’ve moved from Box.com, where it works this way for helping to identify and sort by recent changes deep in the structure. Many old online articles try to cover this, but the solution has been evasive until I spent the better half of a day trying to figure it out in PowerApp. *flips Cray hat backward*

Solution

Screenshots of the PowerApp are below. The one major limitation of this solution is that if you put an empty folder within a folder structure, it doesn’t trigger the update but instead shows a failed run, which isn’t actually a problem. Only files within folders will successfully start and run the job. That applies to folder -> folder -> folder  -> folder -> folder -> file or as deep as you need to go, and it works very quickly, usually within a minute.

1. Create a new automated app named ‘
FolderModified.‘


The finished Power App showing a folder's last modified time

When a file is created or modified (properties only)
 
The flow trigger, "When a file is created or modified (properties only)"
 

2. ‘CFileName‘

Step 2, the CFileName compose action

{
    "inputs": "@{triggerOutputs()?['body/{FilenameWithExtension}']}-@{equals(triggerOutputs()?['body/IsFolder'],false)}",
    "metadata": {
        "operationMetadataId": "c697bc2a-8bcd-44ad-80bf-87f29e9b4455"
    }

 

3. ‘CFolderPath‘


Step 3, the CFolderPath compose action

{
    "inputs": "@triggerOutputs()?['body/{Path}']",
    "metadata": {
        "operationMetadataId": "ebace1b1-1fa1-4f76-8e97-4344ffe8c11b"
    }
}
 

 
4. ‘VArrFolderPath‘
 
Step 4, the VArrFolderPath variable
 
{
    "inputs": {
        "variables": [
            {
                "name": "VArrFolderPath",
                "type": "array",
                "value": "@split(outputs('CFolderPath'),'/')"
            }
        ]
    },
    "metadata": {
        "operationMetadataId": "c4aa6183-5a4c-4451-b045-97a8c020a83f"
    }
}
 

 
5. ‘VPath‘
 
 
Step 5, the VPath variable
 
{
    "inputs": {
        "variables": [
            {
                "name": "VPath",
                "type": "string"
            }
        ]
    },
    "metadata": {
        "operationMetadataId": "9f50d26d-5624-4ef0-b02a-b98a41957d54"
    }
}
 

 
6. ‘CUser‘
 
 
Step 6, the CUser compose action capturing who modified the file
{
    "inputs": [
        {
            "Key": "@{triggerOutputs()?['body/Editor/Claims']}"
        }
    ],
    "metadata": {
        "operationMetadataId": "e80df3fb-feec-4ea5-a683-ad7a5d9b5c65"
    }
}
 

 
7. ‘CModified‘
 
 
Step 7, the CModified compose action
 
{
    "inputs": "@formatDateTime(triggerOutputs()?['body/Modified'],'g')",
    "metadata": {
        "operationMetadataId": "beb03772-baf0-4615-bc04-7fd5650f46f9"
    }
}
 

 
8. ‘CProperties’
 
 
Step 8, the CProperties compose action
 
{
    "inputs": [
        {
            "FieldName": "Editor",
            "FieldValue": "@{string(outputs('CUser'))}"
        }
    ],
    "metadata": {
        "operationMetadataId": "178a3d12-1338-456d-ab40-c9d1f4214bea"
    }
}

'

 
9. ‘VFilterFolderPath‘
 
 
Step 9, the VFilterFolderPath variable
 
{
    "inputs": {
        "from": "@variables('VArrFolderPath')",
        "where": "@equals(empty(item()), false)"
    },
    "metadata": {
        "operationMetadataId": "4a001a83-8073-4ff1-9e81-5e3a7073b027"
    }
}
 

 
10. ‘IFolders‘

 
Step 10, the IFolders loop
 

10.1. ‘Append to VPath’


Step 10.1, appending to VPath inside that loop
 

10.2. ‘IGFPath’


Step 10.2, the IGFPath action
 

10.3. ‘CValid’

 
Step 10.3, the CValid check
 

Now edit the ‘HTTP‘ request to SharePoint:
 
The HTTP request to SharePoint, edited as described
 

{
    "inputs": {
        "host": {
            "connectionName": "shared_sharepointonline_1",
            "operationId": "HttpRequest",
            "apiId": "/providers/Microsoft.PowerApps/apis/shared_sharepointonline"
        },
        "parameters": {
            "dataset": "https://yoursite.sharepoint.com/sites/SITE",
            "parameters/method": "POST",
            "parameters/uri": "_api/web/lists/getbytitle('Documents')/items(@{outputs('IGFPath')?['body/ItemId']})/validateUpdateListItem",
            "parameters/body": "{\n\"formValues\": @{outputs('CProperties')},\n\"bNewDocumentUpdate\": false\n}"
        },
        "authentication": "@parameters('$authentication')"
    },
    "metadata": {
        "operationMetadataId": "5c59f1f4-cd00-4bd9-aa59-bde4deaa317d"
    }
}
 

*** Note: getbytitle('Documents') refers to the name of the Document Library. '/Shared Documents/ (Default) = Documents', or your 'Custom Name' for it.***

Success!

  • Top-level Folder will show activity within, not just when it was created:
 
A top-level folder now showing activity from an hour ago rather than its creation date
 

  • Objects within or within the sub-folders shown in the Last Modified above:

The folders and documents inside, whose activity drives that timestamp
 

  • Short runtimes:

The completed Last Modified view in SharePoint Online, with short runtimes

Just so you know, you should DISABLE this before migrating large amounts of data. The jobs tend to trigger 422 throttling errors in bulk and jam up with performance issues in the queue that hang up jobs. This forces you to recreate the object or copy it to a new version and delete the old one to flush the job queue.
 

Note #2: I’ve also noticed that copying files as the same user that the PowerApp is running under can create a loop that says the account keeps touching files/folders long after you’ve completed the job. I keep this account stable because activating MFA breaks all those permissions, and you have to go back into each Flow to repair it.

Note #3: The easiest way to duplicate this is by creating a template and using the ‘Save As’ feature to clone another copy. You’ll have to run these on each Document Library, and if you’ve got a large Intranet, that can be quite a few jobs. Just so you know, you have to edit 2-3 locations within the job to tune it for the new destination.

 
Note #4: You may receive ‘Failed’ notifications about these Apps from Microsoft. These are from folders being created and expected behavior, not indicating that the app itself is broken or not functioning correctly. I have it running on 35+ sites with minimal overhead or issues, other than a job that doesn’t run for a certain period can be automatically disabled by Microsoft. Still, you get an email warning about it.

Have a better way to do this? Let me know!

Share your thoughts, leave a comment

Your email address will not be published. Required fields are marked *

You may also like these

INFOSTRUCTION

You're about to become an infostruction VIP!

By subscribing, you’ll receive a monthly round-up of the latest news. There’ll be no spam, I promise :)