Take the 2-minute tour ×
SharePoint Stack Exchange is a question and answer site for SharePoint enthusiasts. It's 100% free, no registration required.

I was wondering if this is possible. The screenshot below somewhat illustrates what I'm searching for. I'd like to show a notification in a box whenever a new item has been added to a certain list or library.

enter image description here

Like when you're on Facebook and someone liked a picture from you or commented on a status you posted, you get a notification about that. I want to achieve the same in SharePoint, so that users will get a notification about a new item while browsing the site.

In what way might this be possible? Does this already exist? I didn't find anything about it on the internet. I know how to make a workflow that sends an e-mail whenever a new item is added to a certain list or library, but is there such a thing as a workflow that triggers a notification to be shown? If it is of any use, I use Nintex workflows.

Extra info
Ideally would be that a notification appears when a new item was added to a list or library anywhere on the intranet. So when someone is reading text on a page, they might suddenly see a notification in the top right saying a new item was added somewhere. I'm just not sure yet on how to detect when a new item was added, and how to make sure a script runs whenever the new item was added...

Also, I'd prefer a solution in Javascript (for now). Code in C# is welcome as well but I won't be able to use it for the next upcoming month since I'm currently limited to client side programming.

Final information
I actually managed to write a fully working script that gives live notifications when a new item was added to a list or library, based on whether or not the current user has "subscribed" to that list or library (I add the user to a SP list when he/she presses a subscribe button, and remove him/her from that said list when he/she presses an unsubscribe button).

Thoroughly tested for two days with three different SP libraries on different sub sites.

Most variables are dynamically fetched and I use multidimensional arrays to store the values in. I also use session variables to compare values with each other (to see if an array of list items from a SP list has gained in length compared to the length of an array in a session variable). I will be posting the code tomorrow morning, fully documented with comments.
Since the scripts I made contain many lines of code (roughly 220 lines for the notification script and 110 lines for the subscriber script), I'll post it on my blog with proper and clear markup and link to the blog post here (to avoid making this question look messy).

Please check back later to see if I have posted any updates, I think this solution might be interesting for many users here. :)

share|improve this question
    
Something to consider while designing this is whether or not this is unique per user (ie: tracking whether or not the user has visited the link) or if this is something that will have the same content globally. –  RJ Cuthbertson Feb 26 at 16:11
    
It is not supposed to be unique per user, but I'm planning on adding a button that will add the current user, the ID of the list on that page and the URL to that page in a separate list. Based on the users in that list, it will show only a notification to those users. –  Magali Feb 27 at 13:30

6 Answers 6

up vote 3 down vote accepted

Considered as answered. I've written two scripts for this: one to allow users to subscribe/unsubscribe to a list or library and one to show notifications whenever a new item was added to a list or library to which the user is subscribed to.

Create a list to store your subscribers:

You need to make a simple list at the top site level named "Subscribed users".

Your will then need to make three new columns for your list: ListLibID, PageURL and SubsiteURL.
I originally used to store the GUID of a list/library in the ListLibID column, but after several problems with that in different browsers I decided to just use the name of a list or library instead of the GUID. That was also the main reason on why I also needed a column to save the subsite in (required to call the list across sub sites).

Make sure your list named "Subscribed users" is allowed to be edited by everyone, meaning everyone should have edit permissions. You can change the settings of the view of the list so that it is only visible to you or not visible at all (use the filter for this, set some impossible filter so no item will ever be shown but will still be present in the list).That way you can avoid users sneaking around and trying to meddle with the list. I just used CSS to hide that particular list and the list itself from the site content, so nobody ever finds it.

Create buttons for your lists/libraries:

Now that you have your list ready, it is time to make some buttons. You'll only need to make these for list or libraries of which you want users to be able to subscribe to.

On the page that holds the web part of the list/library of which users should be able to subscribe to, place the following code directly above the code from the web part:

<div class="buttonContainer">
   <div class="subscrButton">
      <div class="subscrButtonTitle"></div>
      <div class="innerSubscr" style="display: none;">
         List or library name here​​
      </div>
   </div>
</div>

You can style your button with some CSS, but I'll leave that to you. ;)

Add references to your scripts:

Since you want to run the script on every page, you need to include both scripts on your master page. Make sure to change the path to the location where you stored the scripts and don't forget to change the id of the scriptlink (it has to be unique).

<!--SPM:<sharepoint:scriptlink id="scriptLink12" language="javascript" 
 localizable="false"  ondemand="false" runat="server"
 name="~sitecollection/Style Library/Scripts/subscribe-to-list-or-library.js">
-->
<!--SPM:<sharepoint:scriptlink id="scriptLink13" language="javascript" 
 localizable="false" ondemand="false" runat="server"
 name="~sitecollection/Style Library/Scripts/notification.js">
-->

Code for "subscribe-to-list-or-library.js":

SP.SOD.executeOrDelayUntilScriptLoaded( 'SP.UserProfiles.js', "~sitecollection/Style%20Library/Scripts/jquery.SPServices-2013.01.min.js");
SP.SOD.executeFunc('SP.js', 'SP.ClientContext');

var firstDiv;
var inList = new Boolean(); 
inList = false;
var listIDDel;
var SURL = $().SPServices.SPGetCurrentSite();
var PURL = window.location.pathname;
var LLID;
var USER = $().SPServices.SPGetCurrentUser({ 
    webURL: "", 
    fieldName: "Title", 
    fieldNames: {}, 
    debug: false
});

$("#content").find($("div.subscrButton")).each(function(){
    LLID = this.childNodes[1].innerHTML.replace(/[\u200B]/g, '');   
    firstDiv = this.childNodes[0].innerHTML.replace(/[\u200B]/g, '');
    $().SPServices({
        operation: "GetListItems",
        async: false,     
        webURL: 'https://your-site-here.com/',
        listName: 'Subscribed users',
        completefunc: function (xData, Status) {
            $(xData.responseXML).find("z\\:row, row").each(function() {
                if (($(this).attr("ows_Title") == USER) && ($(this).attr("ows_ListLibID") == LLID) ) {
                    //console.log('User: ' + $(this).attr("ows_Title") + '\tListLibId: ' + LLID);
                    inList = true;
                }
            });
        }
    });

    if (inList == false) {
        this.childNodes[0].innerHTML = 'SUBSCRIBE';
    }
    else if (inList == true) {
        this.childNodes[0].innerHTML = 'UNSUBSCRIBE';
        inList = false;
    }
});

$("div.subscrButton").click(function(){ 
    LLID = this.childNodes[1].innerHTML.replace(/[\u200B]/g, '');   
    firstDiv = this.childNodes[0].innerHTML.replace(/[\u200B]/g, '');
    console.log(firstDiv);
    if (firstDiv == "UNSUBSCRIBE") {
        this.childNodes[0].innerHTML = 'SUBSCRIBE';
        console.log('User "' + USER + '" wants to unsubscribe. ');

        $().SPServices({
            operation: "GetListItems",
            async: false,     
            webURL: 'https://your-site-here.com/',
            listName: 'Subscribed users',
            completefunc: function (xData, Status) {
                $(xData.responseXML).find("z\\:row, row").each(function() {
                    if (($(this).attr("ows_Title") == USER) && ($(this).attr("ows_ListLibID") == LLID) ) {
                        listIDDel = $(this).attr("ows_ID");
                    }
                });
            }
        });

        $().SPServices({
            operation: 'UpdateListItems',
            webURL: 'https://your-site-here.com/',
            listName: 'Subscribed users',
            updates: '<Batch OnError="Continue" PreCalc="True">' + 
                    '<Method ID="1" Cmd="Delete">' +
                    '<Field Name="ID">'+ listIDDel +'</Field>' +
                    '<Field Name="Title">'+ USER +'</Field>' +
                    '<Field Name="ListLibID">'+ LLID +'</Field>' +
                    '<Field Name="PageURL">'+ PURL +'</Field>' +
                    '<Field Name="SubsiteURL">'+ SURL +'</Field>' +
                    '</Method>' +
                 '</Batch>',
            completefunc: function(xData, Status) {
                console.log('User "'+ USER + '" is now removed from the subscribers list. ');   
            }
        });
        runInOtherFile();
        runDeleteInOtherFile(LLID);

    }
    else if (firstDiv == "SUBSCRIBE") {
        this.childNodes[0].innerHTML = 'UNSUBSCRIBE';
        console.log('User "' + USER + '" wants to subscribe. Adding user to subscribers list now. ');

        // You could add an optional check here, to see if there is already an item in the 'Subscribed users' list with the identical values. If there is, then do nothing. If there isn't, then add a new item.

        $().SPServices({
            operation: 'UpdateListItems',
            webURL: 'https://your-site-here.com/',
            listName: 'Subscribed users',
            updates: '<Batch OnError="Continue" PreCalc="True">' + 
                    '<Method ID="1" Cmd="New">' +
                    '<Field Name="Title">'+ USER +'</Field>' +
                    '<Field Name="ListLibID">'+ LLID +'</Field>' +
                    '<Field Name="PageURL">'+ PURL +'</Field>' +
                    '<Field Name="SubsiteURL">'+ SURL +'</Field>' +
                    '</Method>' +
                 '</Batch>',
            completefunc: function(xData, Status) {
                console.log('User "' + USER + '" has been added to the subscribers list. ');        
            }
        }); 
        runInOtherFile();
    }
});

Code for "notification.js":

// Get the name of the current user.
var USER = $().SPServices.SPGetCurrentUser({ 
    webURL: "", 
    fieldName: "Title", 
    fieldNames: {}, 
    debug: false
});

var currentViewUser;
var currentViewLLIB;
var currentViewPURL;
var currentViewSURL;
var currentViewCOUNT;
var counter = 0;
var tempArr;
var itemArray = new Array();
var itemArrayContainer = new Array();
var itemArrayReplaced = new Array();
var itemArrayContainerReplaced = new Array();


console.log('Notification script has been loaded! Starting script now...\n----------------------------------------------------------');
runOnLoad();

function runOnLoad() {
    $().SPServices({
        operation: "GetListItems",
        async: false,        
        webURL: 'https://your-site-here.com/',
        listName: 'Subscribed users',
        completefunc: function (xData, Status) {
            $(xData.responseXML).SPFilterNode("z:row").each(function() {
                if ($(this).attr("ows_Title") == USER) {
                    counter++;
                    itemArray[counter] = new Array($(this).attr("ows_Title"), unescape($(this).attr("ows_ListLibID")), $(this).attr("ows_PageURL"), $(this).attr("ows_SubsiteURL"), counter);
                    itemArrayContainer.push(itemArray[counter]);
                }
            });
        }
    });
}

if (counter != 0) {         
    for (var j = 0; j < itemArrayContainer.length; j++) {
        sessionItem = "sessionItem" + (j + 1);
        sessionStorage.setItem(sessionItem, "");
    }
    function repeatEvery5Seconds() {
        for (var i = 0; i < itemArrayContainer.length; i++) {
            currentViewUser = itemArrayContainer[i][0];
            currentViewLLIB = itemArrayContainer[i][1]; 
            currentViewPURL = itemArrayContainer[i][2];
            currentViewSURL = itemArrayContainer[i][3];
            currentViewCOUNT = "sessionItem" + itemArrayContainer[i][4];
            fetchCurrentListStatus(currentViewLLIB, currentViewCOUNT);
        }
    }
    var t = setInterval(repeatEvery5Seconds,5000);
}
else {
    console.log('User "' + currentViewUser + '" is not in the subscribers list. Notifications will not be shown.');
}

function fetchCurrentListStatus(currentViewLLIB, currentViewCOUNT) {
    var listItemArray = new Array();
    var listItemArrayBackup = new Array();

    $().SPServices({
        operation: "GetListItems",
        async: false,     
        crossDomain: true,
        webURL: currentViewSURL,
        listName: currentViewLLIB,
        completefunc: function (xData, Status) {
            $(xData.responseXML).find("z\\:row, row").each(function() {
                var rowData = $(this).attr("ows_ID");
                listItemArray.push(rowData);
                listItemArrayBackup.push(rowData);  
            });
        }
    });

    if (listItemArray.length == null || listItemArray.length == 0) {
        console.log('List is empty.');
        sessionStorage.setItem(currentViewCOUNT, listItemArray);
    }
    else if (listItemArray.length != null || listItemArray.length != 0) {
        console.log('Server list: \t\t"' + listItemArray + '"');
        if ( sessionStorage.getItem(currentViewCOUNT).length != 0 || sessionStorage.getItem(currentViewCOUNT) != 0 || sessionStorage.getItem(currentViewCOUNT) != "" ) {
            tempArr = sessionStorage.getItem(currentViewCOUNT).split(",");
            console.log('Session list: \t\t"' + tempArr + '"');
            itemChange();
        }
        else if (sessionStorage.getItem(currentViewCOUNT).length == 0 || sessionStorage.getItem(currentViewCOUNT) == 0 || sessionStorage.getItem(currentViewCOUNT) == "" ) {
        //Session was empty, so we'll set the value of the session item.
            sessionStorage.setItem(currentViewCOUNT, listItemArray);
            tempArr = sessionStorage.getItem(currentViewCOUNT).split(",");
            console.log('Session list: \t\t"' + tempArr + '"');
            itemChange();
        }
        if (tempArr.length == listItemArray.length) {
            console.log('No new item was added in the past 5 seconds. \n----------------------------------------------------------');   
        }
    }



    function itemChange() {
        if (listItemArray.length == tempArr.length) {
            //console.log('The array remains unchanged.');
        }
        else if (listItemArray.length < tempArr.length) {
            console.log('An item was deleted from a list. Now updating session storage. ');
            sessionStorage.setItem(currentViewCOUNT, listItemArray);        
        }
        else if (listItemArray.length > tempArr.length) {
            console.log('A new item has been added. Now updating session storage. '); 

            var array1 = listItemArray;
            var array2 = tempArr;
            var index;

            for (var i=0; i<array2.length; i++) {
                index = array1.indexOf(array2[i]);
                if (index > -1) {
                    array1.splice(index, 1);
                }
            }
            var currentItemName;
            var newListItemArray = new Array(); 
            for (var i = 0; i < array1.length; i++) {
                var currentItem = array1[i];
                 $().SPServices({
                    operation: "GetListItems",
                    ID: currentItem,
                    async: false,
                    crossDomain: true,
                    webURL: currentViewSURL,
                    listName: currentViewLLIB,
                    completefunc: function (xData, Status) {
                      $(xData.responseXML).SPFilterNode("z:row").each(function() {
                            if ($(this).attr("ows_ID") == currentItem) {
                                currentItemName = $(this).attr("ows_LinkFilename"); 
                            }
                            newListItemArray.push(currentItemName);
                      });
                   }
                });
            }

            var div = document.createElement("div");
            div.style.width = "auto";
            div.style.height = "21px";
            div.style.background = "#F7F7F7";
            div.style.color = "#3C82C7";
            div.style.border = "1px solid #d7d6d8";
            div.style.float = "right";
            div.style.padding = "5px";
            div.style.borderBottomLeftRadius = "4px";
            div.innerHTML = 'Een document genaamd "' + currentItemName + '" werd toegevoegd aan <a href="' + currentViewPURL + '">' + currentViewLLIB + '</a>.';
            div.className = "notification";
            document.getElementById("notificationArea").appendChild(div);
            $(document.getElementsByClassName("notification")).hide();
            $(document.getElementsByClassName("notification")).fadeIn(1500);
            setTimeout(function() { 
                $(document.getElementsByClassName("notification")).fadeOut(1500);
                document.getElementById("notificationArea").removeChild(div);
            }, 5000);


            if (tempArr != listItemArray) {
                sessionStorage.setItem(currentViewCOUNT, listItemArrayBackup);
            }
        }
    }
}

function runInOtherFile() {
    console.log('Now running "runInOtherFile()". ');
    counter = 0;
    setTimeout(function() { 
    itemArrayContainerReplaced.length = 0;
        $().SPServices({
            operation: "GetListItems",
            async: false,        
            webURL: 'https://your-site-here.com/',
            listName: 'Subscribed users',
            completefunc: function (xData, Status) {
                $(xData.responseXML).SPFilterNode("z:row").each(function() {
                    if ($(this).attr("ows_Title") == USER) {
                        counter++;
                        itemArrayReplaced[counter] = new Array($(this).attr("ows_Title"), unescape($(this).attr("ows_ListLibID")), $(this).attr("ows_PageURL"), $(this).attr("ows_SubsiteURL"), counter);
                        itemArrayContainerReplaced.push(itemArrayReplaced[counter]);
                    }
                });
            }
        });
        console.log('List of subscribed users has been updated. \n----------------------------------------------------------');
        itemArrayContainer = itemArrayContainerReplaced;
    }, 1000);
}   

function runDeleteInOtherFile() {
    for (var k = 0; k < itemArrayContainer.length; k++) {
        if (itemArrayContainer[k][1] == LLID) {
            var tempCurrentViewCOUNT = "sessionItem" + itemArrayContainer[k][4];
            sessionStorage.setItem(tempCurrentViewCOUNT, "");    // 0);
            itemArrayContainer.splice(k, 1);
        }   
    }
    console.log('Session corresponding to the list/library from which user has unsubscribed is now empty.');
}



I strongly suggest you remove most console logs and minify the javascript. Also check on errors before you apply it to your site. Just because it does not give any errors in my browsers does not mean it will work flawless on your end. I did my best to remove all the errors and mistakes I encountered and made sure all validated well (for example, no double subscribing, no empty values,...).


Screenshots:

Code in action, here I am subscribed for two lists:
Code in action, here I am subscribed for two lists
Session item being updated when a new list/library item has been added:
Session item being updated when a new list/library item has been added
Notification (in Dutch) of a new item that was added, as well as a link to the page that holds a web part of that item:
Notification (in Dutch) of a new item that was added, as well as a link to the page that holds a web part of that item
Subscribing to a list:
Subscribing to a list
Unsubscribing from a list:
Unsubscribing from a list

I also posted about this on my blog here, with a link to here of course for credit.

Thank you everyone here who answered to my question, because of you I actually began searching the possibilities. I plan on making a C# solution when I'm able to, and will make an update when such a solution is available.

share|improve this answer
    
Please add more details here instead of linking to your private blog (it is not considered good practice)! –  Robert Lindgren Mar 5 at 14:24
    
I could add the code here, but it's incredibly long and I thought it would be bad if I made my post here too long. Would you consider it OK if I add the code here then? :) –  Magali Mar 5 at 14:26
    
Surely it is okay :D –  Robert Lindgren Mar 5 at 14:28
    
Consider it done! ;) Also, sorry for the images. I thought we could just click on them and see them bigger but that doesn't seem to be the case. –  Magali Mar 5 at 14:45

The best way would be to use WebSockets. With WebSockets the server can send the notifications to the browsers so the browsers don't have to poll the server for updates.

You will need some server side and some client side code. With SignalR the process is simplified and you'll see that very little code is needed to get things working.

Main steps:

  1. Setup SignalR for use in SP (for example in HttpHandlers init method)
  2. In ItemAdded event receiver broadcast the message with SignalR
  3. Write client side javascript code that will accept the message from the server and display it.

Here's one example http://blog-aspc.azurewebsites.net/sharepoint-with-signalr/

With only client side code you can't get the real-time behaviour. The closest you can get to real-time behaviour without WebSockets is by periodically running a script to check for any changes (Brent Ellis's answer).

share|improve this answer
    
I would indeed like to use your answer but can't try it out just yet. I'm very close to a solution in JavaScript (currently I can send live notifications about new items in one library) but I want to improve the code a bit more before I put it online. Anyway, as soon as I am allowed to do server-side scripting I'll try and apply your answer. ;) –  Magali Feb 27 at 13:36

Here's a post that demonstrate how to create a notification list which sends notifications to users as notification box, post is for SharePoint 2010 but it can used in SharePoint 2013,

SharePoint Notifications Facebook / Google style

If not then there's something new in SharePoint 2013 "Pushed Notification" but it has been built to be used with windows 8 phones, however I believe you can change it.. You can look at the code in post below, as it's creating a notification list using Visual Studio, might be something that can help.

How to: Configure and use push notifications in SharePoint 2013 apps for Windows Phone

Last but not least, you can create an event receiver for all lists within a site collection by following this post,

How to add event receivers to all the lists in a site collection?

On trigger, add a notification to a notification list (site collection based list), display notifications using rest api to a custom delegate control.

However Newsfeed is actually what you really be looking at for notifications,

share|improve this answer
1  
Downvote just for the Push Notifications part, you really should know what are Push Notifications before saying "however I believe you can change it" –  Arsalan Adam Khatri Feb 26 at 12:00
    
@ArsalanAdamKhatri Thanks, I definitely going to read about it, meantime, I edited my answer :) –  Muhammad Raja Feb 26 at 12:04
    
The newsfeed on our intranet site redirects to a mywap page that is not linked to our intranet. When I click on newsfeed, I end on a site in SharePoint that is completely different in layout and does not contain the term driven navigation from our intranet. It was set up like this (not by me) and I cannot change it unfortunately. So unless I can replace that newsfeed with a custom one, I can't use this. :( –  Magali Feb 26 at 12:25
    
@Magali The site you get redirected to is "My Host Site Collection", because site collection isn't publishing site, it will not display managed metadata navigation, it can be done by custom master page, but that's as you said out of scope here –  Muhammad Raja Feb 26 at 12:51

what about some custom javascript to do an asynchronous REST query into the library, make it show new items posted or modified in the last 7 days, or whatever your timeframe is?

Here is an example I use:

Must include "SP.REQUESTEXECUTOR" script

<script src="/_layouts/15/sp.requestexecutor.js" type="text/javascript"></script>

function getMyIcons(myLocation) {   

    var executor5 = new SP.RequestExecutor(_spPageContextInfo.webAbsoluteUrl);
    executor5.executeAsync({
        url: mySite + "/_api/Web/Lists/GetByTitle('Icons')/items?$Title,URL,OneWordID,ImageURL&$orderby=Priority&$filter=substringof('"+myLocation+"',Office)%20or%20(Office eq 'All')",
        method: "GET",
        headers: { "Accept": "application/json; odata=verbose" },
        success: function(data) {

            var jsonObject = JSON.parse(data.body);
            var results = jsonObject.d.results;

            //define variables

            //loop through results
            for(i=0; i < results.length; i++){
                // build HTML strings based on results.variableName
            }           
            // inject HTML strings into DOM
            $("#myLinksPlaceholder").html(str);     

        },
        error: function(data, errorCode, errorMessage) {
            // do something
        }
    });
}
share|improve this answer
    
Would there be a way to show a notification on the very moment that a new item was added? Since the way I imagine it, it should run a script as soon as there is a new item, rather than checking which items were added in a certain timeframe. –  Magali Feb 26 at 12:09
2  
it runs on page load based on criteria in your URL query, you could potentially set up some kind of timer to re run it periodically –  Brent Ellis Feb 26 at 12:15
1  
Didn't think about the timer, great suggestion. If the script isn't too heavy then I can re-run it every 30 seconds or so. –  Magali Feb 26 at 12:20

The hard bit is trying to define what you mean by new. Is this new to the library or new to the user?

SharePoint automatically adds little icons to new items in lists and libraries so an easy option could be to just get a count of these?

In 2013 they appear as:

<span class='ms-newdocument-iconouter'>...</span>

So, the following would give you a count using jQuery as an example:

$('span.ms-newdocument-iconouter').length;
share|improve this answer
    
Not really searching for a way to count new items. I genuinely need a way to show new items in a notification, so that a user can eventually click the notification if they want to and then be redirected to the list or library containing the new item. I'd even be satisfied by just a text saying that a new item was added to [list/library name], just to let the user know. –  Magali Feb 26 at 12:12

SharePoint 2010/2013 has a notification support SP.UI.Notify.addNotification(strHtml, bSticky);You can use them. More info here:

http://msdn.microsoft.com/en-us/library/office/ff407632(v=office.14).aspx

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.