Wednesday, July 31, 2019

List, Add, Delete Custom Actions using JavaScript Object Model JSOM SharePoint Online (Classic)

The best way to add JS file to your SharePoint Online site is to use custom actions. Now, if you need to turn it on and off and need a page from where to be able to do so, keep reading below -

Throw the html code provided below on any page using content editor webpart after first updating the link to the js file you want to add and it would do the trick - 

Before adding custom action
After adding custom action

HTML Code - 

<div>
<div id="details">
<h2>Registered custom actions</h2>
</div>
<button id="addBOT" style="display:none" type="button">Add BOT User Action</button>
<button id="deleteBOT" style="display:none" type="button">Delete BOT User Action</button>

<script type="text/javascript">
    
    $(document).ready(function ()
{
    SP.SOD.executeFunc('sp.js', 'SP.ClientContext', retrieveListItems);
}); 
function retrieveListItems() {
    
    var clientContext = new SP.ClientContext.get_current();
    
    this.userCustomActions = clientContext.get_site().get_userCustomActions();
    clientContext.load(this.userCustomActions);
    
clientContext.executeQueryAsync(Function.createDelegate(this, listCustomActions), Function.createDelegate(this, onQueryFailed));
    
    console.log('Libraries Loaded');
}
function listCustomActions(){
var customActionEnumerator = this.userCustomActions.getEnumerator();
var BOTadded = false;

    while (customActionEnumerator.moveNext()) 
    {
        var oUserCustomAction = customActionEnumerator.get_current();
        if(oUserCustomAction.get_title()=="BOTAction"){BOTadded = true;}
        $("#details").append("<p>"+ oUserCustomAction.get_title() +"</p>");           
    }
    
    if(BOTadded){
    $("#deleteBOT").show();
    }else{
    $("#addBOT").show();
    }
    
    $("#addBOT").click(function(){
    var clientContext = new SP.ClientContext.get_current();
    var userActions = clientContext.get_site().get_userCustomActions();
    var newUserAction = userActions.add();
    newUserAction.set_name("BOTAction");
    newUserAction.set_title("BOTAction");
    newUserAction.set_location("ScriptLink");
    newUserAction.set_scriptSrc("<<LINK TO YOUR JS FILE LOCATION OR CDN>>");
    newUserAction.set_sequence(20000);
    newUserAction.update();
clientContext.load(userActions);
    clientContext.executeQueryAsync(Function.createDelegate(this, onQuerySucceeded), Function.createDelegate(this, onQueryFailed));
    });
    
    $("#deleteBOT").click(function(){
    this.clientContext = new SP.ClientContext.get_current();
    this.userActions = this.clientContext.get_site().get_userCustomActions();
   
this.clientContext.load(this.userActions);
    this.clientContext.executeQueryAsync(Function.createDelegate(this, deleteCustomActions), Function.createDelegate(this, onQueryFailed));
    });

};
function deleteCustomActions(){
var customActionEnumerator = this.userActions.getEnumerator();
while (customActionEnumerator.moveNext()) 
    {
        var oUserCustomAction = customActionEnumerator.get_current();
        if(oUserCustomAction.get_title()=="BOTAction"){
        oUserCustomAction.deleteObject();
        this.clientContext.load(oUserCustomAction);
        this.clientContext.executeQueryAsync(Function.createDelegate(this, onQuerySucceeded), Function.createDelegate(this, onQueryFailed));
        }
                  
    }

}
function onQuerySucceeded() {
    console.log('Action Completed');
    document.location.reload(true)
}
function onQueryFailed(sender, args) {
        console.log('Request failed. ' + args.get_message() + '\n' + args.get_stackTrace());
}
</script>

Monday, July 29, 2019

Minimalistic Chatbot Webchat Implementation with just JQuery

This assumes you already have a chat bot and know its secret key and bot id. Also, you want a chat bot button on right hand corner of your screen to open and close the chat window.
There is a git-hub implementation of that but it is using react and full fledged solution. If you want a quick implementation, continue reading on.

Here is what it looks like -

Here is the link to download the full code. (Please remember to update the secret key and bot id)

Html for implementation -

<!DOCTYPE html>
<html>
<head>
    <style>
        #botDiv {
            height:500px;
        }


        #dialogContainer {
            right: 20px;
            background-color: hsla(0,0%,100%,.8);
            -webkit-backdrop-filter: blur(10px);
            backdrop-filter: blur(10px);
            border-radius: 4px;
            box-shadow: 0 0 20px rgba(0,0,0,.2);
            border: 4px solid #39c;
            bottom: 80px;
            display: flex;
            flex-direction: column;
            max-width: 400px;
            min-width: 320px;
            position: absolute;
            /* top: 20px; */
            width: 30%;
            height: 500px;
        }


        #chatButton {
            position: fixed;
            bottom: 20px;
            right: 20px;
            z-index: 2001;
        }

        .chat {
            height: 52px;
            width: 52px;
        }

        .chat {
            background-color: #414244 !important;
            box-shadow: 0 4px 6px 0 rgba(0, 0, 0, 0.2);
            color: white !important;
            height: 50px;
            width: 50px;
            border-radius: 50%;
            font-family: "TW Cen MT", "Segoe UI", Arial, sans-serif !important;
            text-decoration: none;
            border: 1px solid #ffffff;
            display: block;
        }

        .chat img {
            position: relative;
        }

        .chat img {
            display: inline-block;
            padding-bottom: 1px;
            background: transparent;
            width: 1.5em;
            left: 50%;
            top: 45%;
            -webkit-transform: translateX(-50%) translatey(-50%);
            -moz-transform: translateX(-50%) translatey(-50%);
            transform: translateX(-50%) translatey(-50%);
        }
    </style>
</head>
<body>
    <div id="webchat" role="main"></div>
    <div id="chatButton">
        <span class="bot-notification" style="display:none">1</span>
        <a class="chat" href="#" id="openDialog">
            <img alt="Chat Bubble" src="img/chat-bubble.svg">
        </a>
    </div>
    <div id="dialogContainer" style="display:none;"><div id="botDiv" role="main" /></div>


    <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
    <script src="https://cdn.botframework.com/botframework-webchat/latest/webchat.js"></script>
    <script>
        var INPEXBot = (function () {
            var _botOpened = false;

            $("#openDialog").click(function () {
                if (!_botOpened) {
                    openBotDialog();
                }
                else {
                    closeBotDialog();
                }
                return false;
            });

            function closeBotDialog() {
                _botOpened = false;
                $('#dialogContainer').hide();
            }

            function openBotDialog() {
                _botOpened = true;
                $('#dialogContainer').show();
                activateBot();
            }

            function activateBot() {
                window.WebChat.renderWebChat(
                   {
                      directLine: window.WebChat.createDirectLine({
                         token: 'YOUR SECRET KEY'
                      }),
                      userID: 'YOUR BOT ID'
                   },
                    document.getElementById('botDiv')
                );
            };

        })();
    </script>
</body>
</html>

Thursday, August 30, 2018

Fixing Network Adapter Issue in Laptops

I woke up this morning to find my laptop was not displaying wifi. Under network adapters, I could not find any wireless adapters.

These are the steps I took to fix it -

0. Before you troubleshoot, try windows troubleshooting.

1. Explore device manager. Scan for hardware changes, reinstall drivers if you wireless network adapter has a yellow question mark on it.

2. Restore computer to an old restore point. (This is in case you have created a restore point)

3. Find updated wireless driver on the support website for your laptop. Try installing that.

4. And this is what fixed it for me - as shown in the screenshot below, go to Control Panel > Uninstall a program. Find your wireless adapter software and hit change. Then do a repair.


5. Last option would be to reset your windows (if you are on Windows 10)

One of the above should hopefully work for you.
Best of luck 

Wednesday, January 24, 2018

Site Icon click to home page of root site - Best method

There are a few alternatives to how one can get SharePoint Icon to always go to the home page of the root site. (By default, it goes to the home page of current site, so it is an issue if you are in a sub-site)

I found the following JavaScript method best and most robust among all the alternatives. The alternatives were updating the link on document load etc. but all those assume that SP will not update that link after your script has run (which it does in Search Center)

var hijackSiteIconLinkClick = function () {
        $('.ms-siteicon-a').click(function (event) {
            event.preventDefault();
            event.stopPropagation();
            window.location.href = window.location.protocol + "//" + window.location.host + _spPageContextInfo.siteServerRelativeUrl;
        });
    };

The above works for every type of subsite as it runs on anchor click event and not when JS thinks that SharePoint page is loaded.

PS: The code above assumes that you have JQuery loaded on the page prior to running this script

Sunday, October 22, 2017

O365 - Workflows sending mails

SharePoint Online Workflow in Office 365 cannot send emails to Distribution Lists. A few of my colleges have come across this issue when dealing with workflows in O365.

This information is widely enabled and there are a few blogs about it like - https://askmanisha.wordpress.com/2016/01/19/send-emails-to-distribution-lists-using-sharepoint/

So what you need is a mail enabled security group.

But wait, there is another piece to the puzzle which often stumps people. This mail enabled security group needs at least read-only access to the site where workflow is.

There you go, now you can successfully have workflows mailing stuff.

To summarize, two things required for workflow to send mail -
1. Mail enabled security group
2. Mail enabled security group having at least read-only permissions to the site.



Tuesday, February 21, 2017

Setting SharePoint SEO (Search Engine Optimization) settings using CSOM

I wanted to add viewport meta to my SharePoint page to make it responsive. The best way to do that I found and agreed with was to add it to Search Engine Optimization settings. Stefan Bauer has an excellent blog on it: http://www.n8d.at/blog/how-to-add-viewport-meta-without-editing-the-master-page/

So, next step was to be able to provision this via CSOM. So I wrote the following code does that:

public void SetSearchEngineOptimizationSettings(ClientContext ctx)
{
  var web = ctx.Web;
  web.AllProperties["seoincludecustommetatagpropertyname"] = "true";
  web.AllProperties["seocustommetatagpropertyname"] = "<meta name=\"viewport\" content=\"width=device-width,initial-scale=1,maximum-scale=1\" />";
  web.Update();
  ctx.ExecuteQuery();
}


Monday, February 20, 2017

SharePoint Search URLs and how to use them in Webpart TitleUrl property

I recently came across a flaw/bug/defect with SharePoint that made me spend a lot of time on a simple thing, as simple as adding a TitleUrl to a content editor webpart.

The url I wanted my content editor webpart title to navigate to was a search result page:
/search/Pages/resultseverything.aspx#Default={"k":"","o":[{"p":"ViewsRecent","d":1}],"l":3081}

The mumbo-jumbo after # in the url instructs search result webpart to render the result ordered in the decreasing order of recent views. This is new Sharepoint way sorting using JSON format.

However, the pain point is that when you try and add this url to the TitleUrl property of the web part, SharePoint does not allow you to do that and throws an error.

I tried the encoded form of the above url: #Default=%7B%22k%22%3A%22%22%2C%22o%22%3A%5B%7B%22p%22%3A%22ViewsRecent%22%2C%22d%22%3A1%7D%5D%2C%22l%22%3A3081%7D

This did not work either, because when you now click on the link, SharePoint tries to encode this encoded string including '#' in the url and results in unexpected behavior.

This lead me to building my own custom solution to get around the issue (jQuery to the rescue).
(Assuming you have already added jQuery to your sharepoint site as custom action or otherwise)

First step:
I added the following url to the titleurl:
/Pages/resultseverything.aspx?TA

Second step:
Then added script editor on the page with following javascript:
$('h2.ms-webpart-titleText > a').click(function (e) {
                var redirectUrl = $(e.target.parentElement.parentElement).href;
                var i = redirectUrl.indexOf('?');
                var redirectUrlNew = redirectUrl;
                if (i >= 0) {
                    switch (redirectUrl.substring(i + 1)) {
                        case "TA":
                            redirectUrlNew = _spPageContextInfo.siteAbsoluteUrl + '/search/Pages/resultseverything.aspx#Default={"k":"","o":[{"p":"ViewsRecent","d":1}],"l":3081}';
                            break;
                    }
                }
                window.location.href = redirectUrlNew;
                e.preventDefault();
            });

So what the above code does is finds all the webpart title links and grabs their href element. Based on the token in front of '?', it redirects the page to the that location.

This was all hunky-dory except this does not prevent users from right-clicking on the link and going 'open in new tab/window' to end up going to the page - /Pages/resultseverything.aspx?TA and of-course, TA as a token does not do the sorting unless replaced by #Default =....

So another jQuery trick:

Third step:
$.each($('h2.ms-webpart-titleText > a'), function (i, value) {
                var hrefValue = $(value).attr('href');
                $(value).attr('data-redirect', hrefValue);
                $(value).removeAttr('href');
            });

$('h2.ms-webpart-titleText > a').click(function (e) {
                var redirectUrl = $(e.target.parentElement.parentElement).attr("data-redirect");
                var i = redirectUrl.indexOf('?');
                var redirectUrlNew = redirectUrl;
                if (i >= 0) {
                    switch (redirectUrl.substring(i + 1)) {
                        case "TA":
                            redirectUrlNew = _spPageContextInfo.siteAbsoluteUrl + '/search/Pages/resultseverything.aspx#Default={"k":"","o":[{"p":"ViewsRecent","d":1}],"l":3081}';
                            break;
                    }
                }
                window.location.href = redirectUrlNew;
                e.preventDefault();
            });

So the first part of the code above loops through all the webpart title links and removes their href attributes with 'data-redirect' attribute. By removing href attribute, one ensures that the browser no longer provides that 'right-click and open in new window' option. Also, as you may notice, I had to update the code of second step to use value of 'data-redirect' attribute rather than href.

This worked like a charm but took me way more time in accomplishing what I expected to have worked out-of-the-box for SharePoint.

I hope this saves someone's time.