5

Is there a way to reply to a comment on SharePoint Online blog post through customization? I don't think there is any OOB option available.

I don't have experience in creating remote event receivers or any custom jobs. Please provide a detailed description.

  • are you asking for blog site collection or blog feature under delve? – ThinkB4Code Jun 8 '17 at 13:14
  • I created a site using Blog template. For Post list, the comment filed is by default. I want to add reply to a comment feature. – AXA240778 Jun 8 '17 at 15:19
5
+25

The comments section on each post is a list view web part pulling from the Comments list. In order to get the reply feature, we need to create a custom Comments web part using HTML, and JavaScript.

Here is my analysis to finish this customization

New Column in Comments List

The reply is also a comment to another comment on a post. But it requires to know the parent comment. For this, we are adding a new column to the comments list.

Field Name = Parent Comment ID, Field Type = Number

JavaScript function to get posts, comment on a post and reply on a comment

//get comments
funciton getComments(postID) {
    var requestUrl = _spPageContextInfo.webAbsoluteUrl + "_api/web/Lists/getByTitle('Comments')/Items?$filter=PostTitleId eq " + postID;
    $.ajax({
            url: requestUrl,
            method: "GET",          
            headers: { "Accept": "application/json; odata=verbose" },
            success: function (data) {
                bindComments(data.d.results);               
            },
            error: function (data) {
                fail(data);
            }
        }); 
}
//bind comments
function bindComments(comments) {
    var container = $("#comments-container ul");
    foreach(var comment in comments) {
        container.append("<li comment-id='comment.Id' post-id='comment.PostTitleId'>\
                            <span>" + comment.Title  + "</span> \
                            <span>" + comment.Author.Title + "</span>\
                            <a href='javascript:' class='reply-comment'> \                          
                        </li>");

    }

    $(".reply-comment").on("click", function()) {
        $(this).closest("li").append("<div>\
                                        <input type='textbox'><input type='button' value='Reply' id='btnReply'>\
                                    </div>");

    }
    $("#btnReply").on("click", function(){
        var postId=$(this).closest("li").attr("post-id");
        var parentCommentId=$(this).closest("li").attr("comment-id");
        var comment = $(this).prev("input[type=text]").val();
        //Insert to Comments list using REST Api call
        var listItem = { Title: "your reply title", 
                        PostTitleId: postId, 
                        Body: comment, 
                        ParentCommentId: parentCommentId}; 
    });
}

//post a comment or reply
function postComment(comment) {

        var item = {
            "__metadata": { "type": "SP.Data.CommentsListItem" }
        };

        $.extend(item, comment);

        $.ajax({
            url: webUrl + "/_api/web/lists/getByTitle('" + listName + "')/items",
            type: "POST",
            contentType: "application/json;odata=verbose",
            data: JSON.stringify(item),
            headers: {
                "Accept": "application/json;odata=verbose",
                "X-RequestDigest": $("#__REQUESTDIGEST").val()
            },
            success: function (data) {
                alert('Success');
            },
            error: function (data) {
                alert('fail')
            }
        });

}

$(document).ready(function(){
    var postId = //get post id from url;
    $("#btnPostComment").ready(function() {
        var comment = $("#txtComment").val();       
        var listItem = { Title: "your reply title", 
                        PostTitleId: postId, 
                        Body: comment, 
                        ParentCommentId: 0};
        postComment(comment);
    });
    getComments(postId);
});

HTML

<div id="comments-container">
    <ul></ul>
    <div id="comment-box-container">
        <input type="text" id="txtComment">
        <input type="button" value="POST" id="btnPostComment">
    </div>
</div>

Use the component

  1. You can remove the old comments web part on the Post.aspx page
  2. You can add this component to the content editor web part. (Keep everything in a JavaScript and HTML files then refer in the content editor web part)

This is only prototype to build your reply feature along with exisiting features.

I feel this will give flexibity to extend your blog post even further.

  • Thanks so much @Venkat Konjeti. I really appreciate your assistance. I will apply this and will reach you back. – AXA240778 Jun 7 '17 at 13:38
2

Out of the box, I don't think this is not an option in SharePoint; therefore, it requires some custom work to be done.

However, I believe that a similar question has already been asked on here.

  • Hello Caldwell. I appreciate your reply. But that doesn't help. – AXA240778 Jun 6 '17 at 15:27
0

One such requirement was to have the ability to comment and reply on the comments on the news articles. Obviously, there was no out-of-the-box page layout in SharePoint online that gives this feature, so we had to do something custom.

<div class="container">  
    <div class="form-group col-md-6">  
        <hr> <label for="comment">Enter Comments:</label> <textarea class="form-control " rows="3" id="comment"></textarea> <a href="" ID="btn_CommentsSave" class="cbs-Line1Link ms-noWrap ms-displayBlock" data-bind="click: createListItem">  
<span> <i class="ms-Icon ms-Icon--post"></i> Post Comment</span>  
</a></div>  
</div>  
<div class="container">  
    <div class="row">  
        <div class="col-md-8">  
            <h2 class="page-header"><span data-bind="text: commentsCount"></span> Comments</h2>  
            <hr>  
            <section class="comment-list">  
                <!-- First Comment -->  
                <div class="row" data-bind="foreach: commentsfromUsers" style="padding: 5px !important">  
                    <div class="col-md-2 col-sm-2 hidden-xs" style="padding: 5px !important">  
                        <figure class="thumbnail"> <img class="img-responsive" data-bind="attr:{src: photo}" />  
                            <figcaption class="text-center" data-bind="text: userName"></figcaption>  
                        </figure>  
                    </div> 
<div class="col-md-10 col-sm-10">  
                        <div class="panel panel-default arrow left">  
                            <div class="panel-body" style="border:thin #C0C0C0 solid !important;">  
                                <header class="text-left"> <a><i class="ms-Icon ms-Icon--person" aria-hidden="true"></i></a> <span data-bind="text: userName"> </span> <a><i class="ms-Icon ms-Icon--event" aria-hidden="true"></i></a> <time datetime="16-12-2014 01:05" data-bind="text: Created"></time> </header>  
                                <div class="comment-post" data-bind="text: Comments"></div>  
                                <div class="text-right" align="right"><span data-bind="text: repliesCount"></span> reply(s) <a href="#" data-bind="click: $root.ShowHideItems"><i class="ms-Icon ms-Icon--reply"></i> Reply</a></div>  
                            </div>  
                        </div>  
                    </div>  
                    <div class="row" data-bind="attr:{id: 'divReply'+Id}" style="display:none !important;padding: 5px !important;" align="right">  
                        <div class="col-md-8 col-sm-8 col-md-offset-3 col-sm-offset-0 hidden-xs">  
                            <div class="form-group col-md-8"> <textarea class="form-control " rows="3" data-bind="attr:{id: 'addReply'+Id}"></textarea> <a ID="btn_addReplySave" href="" data-bind="click: $root.createReplyListItem">  
<i class="ms-Icon ms-Icon--post"></i> Post Reply  
</a></div>  
                        </div>  
                    </div>  
                    <div class="row"></div>  
                    <!-- Second Comment Reply -->  
                    <div data-bind="foreach: replies ">  
                        <div class="row">  
                            <div class="col-md-2 col-sm-2 col-md-offset-1 col-sm-offset-0 hidden-xs">  
                                <figure class="thumbnail"> <img class="img-responsive" data-bind="attr:{src: photo}" />  
                                    <figcaption class="text-center" data-bind="text: userName"></figcaption>  
                                </figure>  
                            </div>  
                            <div class="col-md-8 col-sm-8">  
                                <div class="panel panel-default arrow left">  
                                    <div class="panel-heading right"> Reply</div>  
                                    <div class="panel-body" style="border:thin #C0C0C0 solid !important;">  
                                        <header class="text-left"> <a><i class="ms-Icon ms-Icon--person" aria-hidden="true"></i></a> <span data-bind="text: userName"></span> <a><i class="ms-Icon ms-Icon--event" aria-hidden="true"></i></a> <time datetime="16-12-2014 01:05" data-bind="text: Created"> </time> </header>  
                                        <div class="comment-post" data-bind="text: reply"></div>  
                                    </div>  
                                </div>  
                            </div>  
                        </div>  
                    </div>  
                </div>  
            </section>  
        </div>  
    </div>  
</div>  `

The following JavaScript was added to the page layout.

    var siteCollectionURL = _spPageContextInfo.webAbsoluteUrl;  
var pageurl = window.location.href.replace("%20", "").replace("%27", "").replace(/[&$%‘’']/g, "");  

function ShowHideItems() {  
    document.getElementById('replyToComments').style.display = 'block';  
}  
var Comment = function(comment, Id, Created, CreatedById, replies) {  
    this.Comments = comment;  
    this.Id = Id;  
    var num = parseInt(Created.replace(/[^0-9]/g, ""));  
    var date = new Date(num);  
    this.Created = date.toLocaleString();  
    this.CreatedById = CreatedById;  
    this.replies = ko.observableArray(replies);  
    this.photo = ko.observable();  
    this.userName = ko.observable();  
    this.repliesCount = ko.observable(replies.length);  
    var self = this;  
    $.ajax({  
        url: _spPageContextInfo.webServerRelativeUrl + '/_vti_bin/listdata.svc/UserInformationList?$filter=Id%20eq%20' + CreatedById,  
        method: "GET",  
        headers: {  
            "Accept": "application/json; odata=verbose"  
        },  
        success: function(data) {  
            self.photo("https://<TenantURL>/_layouts/15/userphoto.aspx?size=S&accountname=" + data.d.results[0].WorkEmail.toString());  
            self.userName(data.d.results[0].Name);  
        },  
        error: function(data) {  
            alert('Error' + data.error);  
        }  
    });  
}  
var Reply = function(reply, Id, ParentId, Created, CreatedById) {  
    this.reply = reply;  
    this.Id = Id;  
    this.ParentId = ParentId;  
    var num = parseInt(Created.replace(/[^0-9]/g, ""));  
    var date = new Date(num);  
    this.Created = date.toLocaleString();  
    this.CreatedById = CreatedById;  
    this.photo = ko.observable();  
    this.userName = ko.observable();  
    var self = this;  
    $.ajax({  
        url: _spPageContextInfo.webServerRelativeUrl + '/_vti_bin/listdata.svc/UserInformationList?$filter=Id%20eq%20' + CreatedById,  
        method: "GET",  
        headers: {  
            "Accept": "application/json; odata=verbose"  
        },  
        success: function(data) {  
            self.photo("https://<TenantURL>/_layouts/15/userphoto.aspx?size=S&accountname=" + data.d.results[0].WorkEmail.toString());  
            self.userName(data.d.results[0].Name);  
        },  
        error: function(data) {  
            alert('Error' + data.error);  
        }  
    });  
}  

function getReplies(ID) {  
    var commentsurl = siteCollectionURL + '/_vti_bin/listdata.svc/Replies?$filter=%20ParentID%20eq%20%27' + ID + '%27';  
    var replies = new Array();  
    $.ajax({  
        url: siteCollectionURL + '/_vti_bin/listdata.svc/Replies?$filter=%20ParentID%20eq%20%27' + ID + '%27',  
        method: "GET",  
        headers: {  
            "Accept": "application/json; odata=verbose"  
        },  
        success: function(data2) {  
            for (var i = 0; i < data2.d.results.length; i++) {  
                replies.push(new Reply(data2.d.results[i].Reply, data2.d.results[i].Id, ID, data2.d.results[i].Created, data2.d.results[i].CreatedById));  
            }  
        },  
        error: function(data2) {  
            replies = null;  
        },  
        async: false  
    });  
    return replies;  
}  

function ClearFields() {  
    document.getElementById('comment').value = "";  
}  

function getParameterByName(name, url) {  
    if (!url) url = window.location.href;  
    name = name.replace(/[\[\]]/g, "\\$&");  
    var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"),  
        results = regex.exec(url);  
    if (!results) return null;  
    if (!results[2]) return '';  
    return decodeURIComponent(results[2].replace(/\+/g, " "));  
}  

function CommentReplyViewModel() {  
    var self = this;  
    self.userID = ko.observable();  
    self.commentsfromUsers = ko.observableArray();  
    self.RepliesfromUsers = ko.observableArray();  
    self.commentsCount = ko.observable();  
    self.ShowHideItems = function(data) {  
        $('#divReply' + data.Id).slideToggle();  
    }  
    self.getCommentsListItem = function() {  
        var commentsurl = siteCollectionURL + '/_vti_bin/listdata.svc/Comments?$filter=%20Title%20eq%20%27' + pageurl + '%27';  
        $.ajax({  
            url: siteCollectionURL + '/_vti_bin/listdata.svc/Comments?$filter=%20Title%20eq%20%27' + pageurl + '%27',  
            method: "GET",  
            headers: {  
                "Accept": "application/json; odata=verbose"  
            },  
            success: function(data2) {  
                self.commentsfromUsers.removeAll();  
                self.commentsCount(data2.d.results.length);  
                for (var i = 0; i < span data - mce - type = "bookmark"  
                    id = "mce_SELREST_start"  
                    data - mce - style = "overflow:hidden;line-height:0"  
                    style = "overflow:hidden;line-height:0" > < /span><span data-mce-type="bookmark" id="mce_SELREST_start" data-mce-style="overflow:hidden;line-height:0" style="overflow:hidden;line-height:0" ></span > < data2.d.results.length; i++) {  
                    self.commentsfromUsers.push(new Comment(data2.d.results[i].Comments, data2.d.results[i].Id, data2.d.results[i].Created, data2.d.results[i].CreatedById, getReplies(data2.d.results[i].Id)));  
                }  
            },  
            error: function(data2) {  
                alert('Error' + data.error);  
            }  
        });  
    }  
    self.createListItem = function() {  
        var commentsValue = document.getElementById('comment').value;  
        var url = siteCollectionURL + "/_vti_bin/ListData.svc/Comments";  
        var Comments = {  
            Comments: commentsValue,  
            SiteURL: pageurl,  
            Title: pageurl  
        };  
        var body = JSON.stringify(Comments);  
        $.ajax({  
            type: "POST",  
            url: url,  
            contentType: 'application/json',  
            processData: false,  
            data: body,  
            success: function() {  
                alert('Comments Saved Successfully.');  
                document.getElementById('comment').value = "";  
                self.getCommentsListItem();  
            }  
        });  
    }  
    self.createReplyListItem = function(data) {  
        var RepliesValue = document.getElementById('addReply' + data.Id).value;  
        var pageurl = window.location.href;  
        var allReplies;  
        var url = siteCollectionURL + "/_vti_bin/ListData.svc/Replies";  
        var Replies = {  
            Reply: RepliesValue,  
            ParentID: data.Id.toString(),  
            Title: pageurl  
        };  
        var body = JSON.stringify(Replies);  
        $.ajax({  
            type: "POST",  
            url: url,  
            contentType: 'application/json',  
            processData: false,  
            data: body,  
            success: function() {  
                alert('Reply Saved Successfully.');  
                document.getElementById('addReply' + data.Id).value = "";  
                $('#divReply' + data.Id).slideToggle();  
                allReplies = getReplies(data.Id);  
                data.replies(allReplies);  
                data.repliesCount(allReplies.length);  
            },  
            error: function(error) {  
                alert(error.error);  
            },  
            async: false  
        });  
    }  
    $.ajax({  
        url: url,  
        method: "GET",  
        headers: {  
            "Accept": "application/json; odata=verbose"  
        },  
        success: function(data) {  
            self.getCommentsListItem();  
        },  
        error: function(data) {  
            alert(data.error);  
        }  
    });  
}  
$(document).ready(function() {  
    ko.applyBindings(new CommentReplyViewModel());  
});  

Check-In and publish the page layout as a major version.

  • How can I remove the old comments web part on the Post.aspx page ? – Mohammad Saif Nov 23 '17 at 6:59

protected by Community Dec 11 '17 at 7:42

Thank you for your interest in this question. Because it has attracted low-quality or spam answers that had to be removed, posting an answer now requires 10 reputation on this site (the association bonus does not count).

Would you like to answer one of these unanswered questions instead?

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