Sharepoint: How to show AppendOnlyHistory on a display template in a cross-publishing scenario - sharepoint-2013

The overarching requirement I am trying to implement is to show comments (made on a list, item by item basis).
I added the feature on the authoring side by enabling versioning on the list and adding a text field with the option "Append Changes to Existing Text" set to true.
This indeed allows me to comment on items and displays them chronologically, but on the authoring side only.
The issue is that the UI part will be done on another site collection and I can't find a straightforward way to get all comments there.
So far, every resource I have found points to
<SharePoint:AppendOnlyHistory runat="server" FieldName="YourCommentsFieldName" ControlMode="Display"/>
The thing is, I can't (don't know how to) use this inside a display template.
So far, I am getting all my data using the REST API, via
var siteUrl=_spPageContextInfo.webAbsoluteUrl.replace("publishing","authoring");
$.ajax({
url: siteUrl + "/_api/web/lists/getbytitle('" + listname + "')/items(" + id + ")",
type: 'GET',
async:false,
headers: {"accept": "application/json;odata=verbose",},
dataType: 'JSON',
success: function(json) {
console.log(json);
//var obj = $.parseJSON(JSON.stringify(json.d.results));
//alert(obj);
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
alert("error :"+XMLHttpRequest.responseText);
}
});
What this gives me is the latest comment only. I need a simple way to get a hold of the entire thread.

I ended up using javascript object model to get them like so:
function GetComments(listname, itemId) {
var siteUrl = _spPageContextInfo.webAbsoluteUrl.replace("publishing", "authoring");
if ($(".comments-history").length) {
$().SPServices({
operation: "GetVersionCollection",
async: false,
webURL: siteUrl,
strlistID: listname,
strlistItemID: itemId,
strFieldName: "Comments",
completefunc: function (xData, Status) {
$(xData.responseText).find("Version").each(function (data, i) {
var xmlComment = $(this)[0].outerHTML;
var arr = xmlComment.split(/comments|modified|editor/g);
var comment = arr[1].trim().substring(2, arr[1].length-2);
var dateSt = Date.parse((arr[2].substring(1, arr[2].length)).replace('/"', ''));
var user = getUsername(arr[3]);
var st = "<div class='comment-item'><div class='comment-user'>" + user + "(" + FormatDate(dateSt) + ")</div>";
st += "<div class='comment-text'>" + comment + "</div></div>";
$(".comments-history").append(st);
});
}
});
}
}
the parsing could be better, but this is just an initial working idea

Related

jqGrid addRowData does not fire afterInsertRow

I'm upgrading my jqGrid from 4.7.1 to 4.14
This is my script for initializing the grid
jQuery("#detFlex62_1").jqGrid({
url: root + mod + '/detaillistview',
datatype: "local",
colNames:[' ', '<?=lang("users_company_code")?>', '<?=lang("users_company_name")?>', ' ', ' '],
colModel:[
{name:'myac', width:50, fixed:true, sortable:false, resize:false, formatter:'actions', formatoptions:{keys:true, editbutton : false, delbutton : false, delOptions: {reloadAfterSubmit:false},editOptions: {reloadAfterSubmit:false}}},
{name:'company_code',index:'company_code', width:100},
{name:'company_name',index:'company_name', width:100},
{name:'company_id',index:'company_id', width:100,hidden:true},
{name:'company_access_id',index:'company_access_id', width:100,hidden:true}
],
width: $('.body').width()-40,
height: 120,
pager: '#pagerFlex62_1',
sortname: 'user_id',
sortorder: "desc",
editurl: root + mod + '/detailpost',
caption:"<?=lang("users_title")?>",
onSelectRow: function(id){
activedf = "#detFlex62_1";
},
afterInsertRow: function (rowid) {
var grid = $(this),
iCol = getColumnIndexByName(grid,'myac'); // 'act' - name of the actions column
grid.find(">tbody>tr[id=" + rowid + "].jqgrow>td:nth-child(" + (iCol + 1) + ")")
.each(function() {
$("<div>",
{
title: "Edit",
mouseover: function() {
$(this).addClass('ui-state-hover');
},
mouseout: function() {
$(this).removeClass('ui-state-hover');
},
click: function(e) {
df_edit_1($(e.target).closest("tr.jqgrow").attr("id"));
/*alert("'Custom' button is clicked in the rowis="+
$(e.target).closest("tr.jqgrow").attr("id") +" !");*/
}
}
).css({float:"left"})
.addClass("ui-pg-div ui-inline-edit")
.append('<span class="ui-icon ui-icon-pencil"></span>')
.prependTo($(this).children("div"));
$("<div>",
{
title: "Delete",
mouseover: function() {
$(this).addClass('ui-state-hover');
},
mouseout: function() {
$(this).removeClass('ui-state-hover');
},
click: function(e) {
df_delete_1($(e.target).closest("tr.jqgrow").attr("id"));
/*alert("'Custom' button is clicked in the rowis="+
$(e.target).closest("tr.jqgrow").attr("id") +" !");*/
}
}
).css({float:"left"})
.addClass("ui-pg-div ui-inline-edit")
.append('<span class="ui-icon ui-icon-trash"></span>')
.prependTo($(this).children("div"));
});
}
});
jQuery("#detFlex62_1").jqGrid('navGrid','#pagerFlex62_1',{edit:false,del:false,search:false, addfunc: df_add_1, editfunc: df_edit_1});
And here is where I add new row into the grid
function df_addToJSON_1(form)
{
var idx = $('input[name=index]',form).val();
var id = $('input[name=id]',form).val();
var totalRows = jQuery("#detFlex62_1").jqGrid('getGridParam', 'records');
var data = {
company_code: $('input[name=company_code]',form).val(),
company_name: $('input[name=company_name]',form).val(),
company_id: $('input[name=company_id]',form).val(),
company_access_id: $('input[name=company_access_id]',form).val()
};
if (idx=='')
{
idx = getMaxRowId($('#detFlex62_1'));
$('#detFlex62_1').jqGrid("addRowData", idx + 1, data, "last");
}
else
{
$('#detFlex62_1').jqGrid("setRowData", idx, data);
}
//pCheckShow();
return false;
}
The new row is added, but without triggering the afterInsertRow event. Why is this happening? Is there a mistake in my code?
Free jqGrid supports afterAddRow, afterSetRow and afterDelRow callbacks, which will be called at the end of addRowData, setRowData and delRowData methods. The most new callbacks introduced by free jqGrid has one parameter (options, for example), which properties contains additional information. It allows to use only the properties, which one need, without require to insert unneeded first parameters if you need to use only the last parameter of old-style callback. Additionally, the new-style of callback parameters allows easy to extend the options in the future release of free jqGrid.
Thus you can, for example, change afterInsertRow: function (rowid) { to afterAddRow: function (options) { var rowid = options.rowid;
In general I would recommend you to make more changes in your code. What you do inside of afterInsertRow is creating custom action button. Your code is long and slow because jqGrid 4.7 had no simple way to create custom action button. Free jqGrid do supports the feature. I recommend you to read the wiki article, to examine the code of the demo, this one and the demos included in the answer. You will see that creating action buttons is very easy. You will have full control over onClick callback.

How to fetch more than 5000 item from SharePoint list?

I am trying to get more than 5000 items form SharePoint list. I am using below code:
var Url = appurl +"/_api/SP.AppContextSite(#target)/web/lists/getbytitle('" + listName + "')/items?$select=ID,UserCountry,UserName/Name,UserName/Title,ModuleID/ModuleName,TopicID/TopicName,VisitCount,PolicyName,PolicyVisitCount,FAQName,FAQVisitCount,ReferenceName,ReferenceVisitCount&$top=5100&$orderby=VisitCount desc,UserName/Title asc &$expand=UserName/Title,ModuleID,TopicID&#target='" + hostWebUrl+ "'"
It works fine for less than 5000 records. But as soon as the number of records increase (more than 5000) it gives internal server error. Why is it doing this?
function fetchStatistics() {
var executor;
var appurl = appWebUrl.replace('#', '');
appurl = appWebUrl.replace('#wrapper', '');
var url = appurl + "/_api/SP.AppContextSite(#target)/web/lists/getbytitle('" + listName + "')/items?$select=ID,UserCountry,UserName/Name,UserName/Title,ModuleID/ModuleName,TopicID/TopicName,VisitCount,PolicyName,PolicyVisitCount,FAQName,FAQVisitCount,ReferenceName,ReferenceVisitCount&$orderby=VisitCount desc,UserName/Title asc &$expand=UserName/Title,ModuleID,TopicID&#target='" + hostWebUrl + "'",
jQuery.getScript("../_layouts/15/SP.RequestExecutor.js", getStatisticsDetails);
function getStatisticsDetails()
{
executor = new SP.RequestExecutor(appurl);
executor.executeAsync(
{
url: url,
method: "GET",
dataType: "json",
headers: { "Accept": "application/json; odata=verbose" },
success: function (data) {
var response = JSON.parse(data.body);
if (response.d.__next) {
url = response.d.__next;
getStatisticsDetails();
}
bindDataGrid();
},
error:errorHandlerFetchStatistics
}
);
}
}
Check https://msdn.microsoft.com/en-us/library/ff798465.aspx
You need to index your list or make repeated calls to get more then 5000 item.
Also you can use $filter or $top in your api call to stay below 5000.

Jquery Tool: Keep selected tab on refresh or save data

I am using jquery tool for tab Ui,
Now I want to keep tab selected on page reload. Is there any way to do that? below is my code
$(function() {
// setup ul.tabs to work as tabs for each div directly under div.panes
$("ul.tabs").tabs("div.panes > div");
});
Here is a simple implementation of storing the cookie and retrieving it:
function getCookie(c_name) {
var i, x, y, ARRcookies = document.cookie.split(";");
for (i = 0; i < ARRcookies.length; i++) {
x = ARRcookies[i].substr(0, ARRcookies[i].indexOf("="));
y = ARRcookies[i].substr(ARRcookies[i].indexOf("=") + 1);
x = x.replace(/^\s+|\s+$/g, "");
if (x == c_name) {
return unescape(y);
}
}
}
function setCookie(c_name, value, exdays) {
var exdate = new Date();
exdate.setDate(exdate.getDate() + exdays);
var c_value = escape(value) + ((exdays == null) ? "" : "; expires=" + exdate.toUTCString());
document.cookie = c_name + "=" + c_value;
}
Then, to save/retrieve cookie data with jQuery UI Tabs:
$(function() {
// retrieve cookie value on page load
var $tabs = $('ul.tabs').tabs();
$tabs.tabs('select', getCookie("selectedtab"));
// set cookie on tab select
$("ul.tabs").bind('tabsselect', function (event, ui) {
setCookie("selectedtab", ui.index + 1, 365);
});
});
Of course, you'll probably want to test if the cookie is set and return 0 or something so that getCookie doesn't return undefined.
On a side note, your selector of ul.tabs may be improved by specifying the tabs by id instead. If you truly have a collection of tabs on the page, you will need a better way of storing the cookie by name - something more specific for which tab collection has been selected/saved.
UPDATE
Ok, I fixed the ui.index usage, it now saves with a +1 increment to the tab index.
Here is a working example of this in action: http://jsbin.com/esukop/7/edit#preview
UPDATE for use with jQuery Tools
According the jQuery Tools API, it should work like this:
$(function() {
//instantiate tabs object
$("ul.tabs").tabs("div.panes > div");
// get handle to the api (must have been constructed before this call)
var api = $("ul.tabs").data("tabs");
// set cookie when tabs are clicked
api.onClick(function(e, index) {
setCookie("selectedtab", index + 1, 365);
});
// retrieve cookie value on page load
var selectedTab = getCookie("selectedtab");
if (selectedTab != "undefined") {
api.click( parseInt(selectedTab) ); // must parse string to int for api to work
}
});
function getCookie(c_name) {
var i, x, y, ARRcookies = document.cookie.split(";");
for (i = 0; i < ARRcookies.length; i++) {
x = ARRcookies[i].substr(0, ARRcookies[i].indexOf("="));
y = ARRcookies[i].substr(ARRcookies[i].indexOf("=") + 1);
x = x.replace(/^\s+|\s+$/g, "");
if (x == c_name) {
return unescape(y);
}
}
}
function setCookie(c_name, value, exdays) {
var exdate = new Date();
exdate.setDate(exdate.getDate() + exdays);
var c_value = escape(value) + ((exdays === null) ? "" : "; expires=" + exdate.toUTCString());
document.cookie = c_name + "=" + c_value;
}
Here is a working (unstyled) example: http://jsbin.com/ixamig/12/edit#preview
Here is what I see in Firefox when inspecting the cookie from the jsbin.com example:
This is what worked for me... at least I haven't run into any issues yet:
$('#tabs').tabs({
select: function (event, ui)
{
$.cookie('active_tab', ui.index, { path: '/' });
}
});
$('#tabs').tabs("option", "active", $.cookie('active_tab'));
I'm using: jQuery 1.8.2, jQuery UI 1.9.1, jQuery Cookie Plugin.
I set the "path" because in C# I set this value in a mvc controller which defaults to "/". If the path doesn't match, it wont overwrite the existing cookie. Here is my C# code to set the value of the same cookie used above:
Response.Cookies["active_tab"].Value = "myTabIndex";
Edit:
As of jQuery UI 1.10.2 (I just tried this version, not sure if it's broken in previous versions), my method doesnt work. This new code will set the cookie using jQuery UI 1.10.2
$('#tabs').tabs({
activate: function (event, ui) {
$.cookie('active_tab', ui.newTab.index(), { path: '/' });
}
});
The easiest way to survive between page refresh is to store the selected tab id in session or through any server-side script.
Only methods to store data on client side are: Cookies or localStorage.
Refer to thread: Store Javascript variable client side

Refresh a webpage just once after 5 seconds

I'm looking for a JavaScript solution (or whatever else) that will refresh a webpage ONLY once, after 5 seconds it has been opened. Is this possible without being stuck in a refresh loop?
try this:
setTimeout(function ()
{
if (self.name != '_refreshed_'){
self.name = '_refreshed_';
self.location.reload(true);
} else {
self.name = '';
}
}, 5000);
You could do this in many different ways, but I think the easiest would be to add a query string to the url after the refresh, allowing us to tell if the refresh has already occurred:
//Function to get query string value. Source: http://www.bloggingdeveloper.com/post/JavaScript-QueryString-ParseGet-QueryString-with-Client-Side-JavaScript.aspx
function getQuerystring(key, default_){
if (default_==null) default_="";
key = key.replace(/[\[]/,"\\\[").replace(/[\]]/,"\\\]");
var regex = new RegExp("[\\?&]"+key+"=([^&#]*)");
var qs = regex.exec(window.location.href);
if(qs == null)
return default_;
else
return qs[1];
}
//check if our query string is already set:
if(getQuerystring(r) !== 1){
setTimeout(function(){window.location.href = window.location.href + '?r=1'},5000)
}
If there is the possibility that a query string is already present, you will have to account for that and change the '?' to an '&'.
Sure, if you don't mind using jquery you can do it via an ajax call after waiting 5 seconds. Just throwing you some sample code:
How to wait 5 seconds with jQuery?
$(document).ready(function() {
// Get data
$.ajax({
url : '/tommyStockExchange/Data',
dataType : 'html',
data : {
'format' : 'H',
'type' : 'E'
},
success : function(data) {
$("#executions").html(data);
},
statusCode : {
404 : function() {
alert('executions url 404 :(');
}
}
});
});
Make it redirect to the same page with a different #hash and in JS only register the redirect if the hash isn't set.
You just need to pass some sort of data between page loads. This can be done in a multitude of ways — use a cookie, a URL query parameter, or something on the server side. Query parameter example:
if (!location.search.match(/(\?|&|^)stopRefreshing(=|&|$)/))
{
setTimeout(function ()
{
var search = location.search;
location.search = search ? search + '&stopRefreshing' : 'stopRefreshing';
}, 5000);
}
Demo: http://jsbin.com/ofawuz/edit

AS3 API: Deleting App Invites

I'm using the ActionScript-Facebook API for my project. Facebook now leaves it up to us to delete app invites once they are used.
In their documentation, they have a JavaScript snippet to do what I need to do:
FB.api(requestId, 'delete', function(response) {console.log(response);});
Cool. The AS3 API call is like such:
Facebook.api(referID, callback, "POST");
For the life of me, I'm not sure how to work this. I've tried:
Facebook.api(referID, function(){trace("callback");}, "delete");
Facebook.api(referID, function(){trace("callback");});
Facebook.api(referID, {access_token:accessTokenString}, "delete");
Here's the documentation:
https://developers.facebook.com/docs/reference/dialogs/requests/#deleting
The following worked for removal of application requests:
var full_request_id : String = request_id + "_" + user_id;
var method : String = "/" + full_request_id;
Facebook.deleteObject(method, callback);
#see AbstractFacebook.as
The actionscript-api will then add the property 'method' with value 'delete' to the parameters of your call:
protected function deleteObject(method:String, callback:Function = null):void {
var params:Object = {method:'delete'};
api(method, callback, params, URLRequestMethod.POST);
}
if (params.access_token == null) { params.access_token = accessToken; }