Commit 4aa41f44 authored by Roisin Pearson's avatar Roisin Pearson Committed by Cecilia Vela Gurovic
Browse files

Bug #1742347 Next and Previous arrows added to the timeline viewport

Change-Id: Ia5e26f7791a1aec90cd26d3092f7542881456255
parent 6e25089e
......@@ -69,6 +69,11 @@ if ($viewid && $fileid) {
}
}
// If the artefact exists in a previous version of the view we can display it
if (!$artefactok && artefact_in_view_version($file, $viewid)) {
$artefactok = true;
}
// The user may be trying to download a file that's not in the page, but which has
// been attached to a public comment on the page
if ($commentid = param_integer('comment', null)) {
......
This diff is collapsed.
......@@ -10,7 +10,7 @@
structureObj: [{}],
distanceMode: 'auto', // predefinedDistance , fixDistance
fixDistanceValue: 2,
firstPointMargin: 2,
firstPointMargin: 1,
map: {
"dataRoot": "dataArray",
"isSelected": "isSelected",
......@@ -18,7 +18,8 @@
"subTitle": "subTitle",
"dateValue": "dateValue",
"pointCnt": "pointCnt",
"bodyCnt": 'bodyCnt'
"bodyCnt": 'bodyCnt',
"idValue": "idValue"
},
isSelectedAttrProvided: false,
formatTitle: function (title, obj) { },
......@@ -35,6 +36,8 @@
"mappedJsonObject": "mappedJsonObject"
}
var timelineCurrentIndex;
var _initTimeLine = function (timeline) {
var timelineComponents = {};
......@@ -52,8 +55,15 @@
setDatePosition(timelineComponents, options.eventsMinDistance);
//assign a width to the timeline
var timelineTotWidth = setTimelineWidth(timelineComponents, options.eventsMinDistance);
// We don't need the timeline prev/next buttons to be active if our timeline is shorter than viewport
if (timelineTotWidth < timelineComponents['timelineWrapper'].width()) {
timelineComponents['timelineNavigation'].find('.next').addClass('inactive');
}
//the timeline has been initialize - show it
timeline.addClass('loaded');
//the currently selected item
var timelineCurrentItem = timelineComponents['eventsContent'].find('.selected');
timelineCurrentIndex = timelineComponents['eventsContent'].find('li').index(timelineCurrentItem);
//detect click on the next arrow
timelineComponents['timelineNavigation'].on('click', '.next', function (event) {
......@@ -70,46 +80,22 @@
event.preventDefault();
timelineComponents['timelineEvents'].removeClass('selected');
$(this).addClass('selected');
timelineCurrentIndex = $(this).closest('li').index();
updateViewportButtons(timelineComponents);
updateOlderEvents($(this));
updateFilling($(this), timelineComponents['fillingLine'], timelineTotWidth);
updateVisibleContent($(this), timelineComponents['eventsContent']);
});
//detect click on the next viewport arrow
timelineComponents['timelineNavigationViewport'].on('click', '.next', function (event) {
event.preventDefault();
// $().addClass('selected');
var currentSelected = $('#jtlinesection .events a.selected');
currentSelected.removeClass('selected');
console.log(currentSelected);
var nextSelected = currentSelected.closest('li').next().find('a');
nextSelected.addClass('selected');
console.log(nextSelected);
updateOlderEvents(nextSelected);
updateFilling(nextSelected, timelineComponents['fillingLine'], timelineTotWidth);
updateVisibleContent(nextSelected, timelineComponents['eventsContent']);
updateSlide(timelineComponents, timelineTotWidth, 'next');
});
//detect click on the prev viewport arrow
timelineComponents['timelineNavigationViewport'].on('click', '.prev', function (event) {
event.preventDefault();
var currentSelected = $('#jtlinesection .events ol li a.selected');
currentSelected.removeClass('selected');
console.log(currentSelected);
var nextSelected = currentSelected.closest('li').next().find('a');
nextSelected.removeClass('selected');
var prevSelected = currentSelected.closest('li').prev().find('a');
prevSelected.addClass('selected');
updateOlderEvents(prevSelected);
updateFilling(prevSelected, timelineComponents['fillingLine'], timelineTotWidth);
updateVisibleContent(prevSelected, timelineComponents['eventsContent']);
updateSlide(timelineComponents, timelineTotWidth, 'prev');
// prevSelected.removeClass('older-event');
console.log(prevSelected);
showNewContent(timelineComponents, timelineTotWidth, 'prev');
});
//detect click on the next viewport arrow
timelineComponents['timelineNavigationViewport'].on('click', '.next', function (event) {
event.preventDefault();
showNewContent(timelineComponents, timelineTotWidth, 'next');
});
//on swipe, show next/prev event content
timelineComponents['eventsContent'].on('swipeleft', function () {
var mq = checkMQ();
......@@ -121,14 +107,13 @@
});
//keyboard navigation
$(document).keyup(function (event) {
$(document).on('keyup', function (event) {
if (event.which == '37' && elementInViewport(timeline.get(0))) {
showNewContent(timelineComponents, timelineTotWidth, 'prev');
} else if (event.which == '39' && elementInViewport(timeline.get(0))) {
showNewContent(timelineComponents, timelineTotWidth, 'next');
}
});
//});
}
var updateSlide = function (timelineComponents, timelineTotWidth, string) {
......@@ -146,11 +131,8 @@
var visibleContent = timelineComponents['eventsContent'].find('.selected'),
newContent = (string == 'next') ? visibleContent.next() : visibleContent.prev();
var showOldContent = function (timelineComponents, timelineTotWidth, string) {
var visibleContent = timelineComponents['eventsContent'].find('.selected'),
oldContent = (string == 'prev') ? visibleContent.prev() : visibleContent.next();
if (newContent.length > 0) { //if there's a next/prev event - show it
timelineCurrentIndex = (string == 'next') ? timelineCurrentIndex + 1 : timelineCurrentIndex - 1;
var selectedDate = timelineComponents['eventsWrapper'].find('.selected'),
newEvent = (string == 'next') ? selectedDate.parent('li').next('li').children('a') : selectedDate.parent('li').prev('li').children('a');
......@@ -161,6 +143,17 @@
updateOlderEvents(newEvent);
updateTimelinePosition(string, newEvent, timelineComponents);
}
updateViewportButtons(timelineComponents);
}
var updateViewportButtons = function (timelineComponents) {
timelineComponents['timelineNavigationViewport'].find('.next').removeClass('inactive');
timelineComponents['timelineNavigationViewport'].find('.prev').removeClass('inactive');
if (timelineCurrentIndex == 0) {
timelineComponents['timelineNavigationViewport'].find('.prev').addClass('inactive');
}
if (timelineCurrentIndex == (fetchedData.length - 1)) {
timelineComponents['timelineNavigationViewport'].find('.next').addClass('inactive');
}
}
......@@ -171,8 +164,16 @@
timelineWidth = Number(timelineComponents['timelineWrapper'].css('width').replace('px', '')),
timelineTotWidth = Number(timelineComponents['eventsWrapper'].css('width').replace('px', ''));
var timelineTranslate = getTranslateValue(timelineComponents['eventsWrapper']);
if ((string == 'next' && eventLeft > timelineWidth - timelineTranslate) || (string == 'prev' && eventLeft < -timelineTranslate)) {
if (
(string == 'next' && (
(eventLeft > timelineWidth - timelineTranslate) ||
((timelineWidth - timelineTranslate) - timelineWidth > eventLeft)
)) ||
(string == 'prev' && (
(eventLeft < -timelineTranslate) ||
((timelineWidth - timelineTranslate) < eventLeft)
))
) {
translateTimeline(timelineComponents, -eventLeft + timelineWidth / 2, timelineWidth - timelineTotWidth);
}
}
......@@ -185,8 +186,6 @@
//update navigation arrows visibility
(value == 0) ? timelineComponents['timelineNavigation'].find('.prev').addClass('inactive') : timelineComponents['timelineNavigation'].find('.prev').removeClass('inactive');
(value == totWidth) ? timelineComponents['timelineNavigation'].find('.next').addClass('inactive') : timelineComponents['timelineNavigation'].find('.next').removeClass('inactive');
(value == 0) ? timelineComponents['timelineNavigationViewport'].find('.prev').addClass('inactive') : timelineComponents['timelineNavigationViewport'].find('.prev').removeClass('inactive');
(value == totWidth) ? timelineComponents['timelineNavigationViewport'].find('.next').addClass('inactive') : timelineComponents['timelineNavigationViewport'].find('.next').removeClass('inactive');
}
var updateFilling = function (selectedEvent, filling, totWidth) {
......@@ -232,7 +231,6 @@
totalItemsWidth += objPredefinedDistance;
}
timelineComponents['timelineEvents'].eq(i).css('left', distanceNorm * min + 'px');
}
}
......@@ -242,6 +240,7 @@
var timeSpanNorm;
var totalWidth;
var objPredefinedDistance;
var nextfixDistanceValue = options.firstPointMargin;// For First Point Margin
if (options.distanceMode == 'auto') {
timeSpan = daydiff(timelineComponents['timelineDates'][0], timelineComponents['timelineDates'][timelineComponents['timelineDates'].length - 1]);
......@@ -251,7 +250,7 @@
}
else if (options.distanceMode == 'fixDistance') { // Define Fixed Distance
var fixedDistByPixl = (options.eventsMinDistance * options.fixDistanceValue);
var allPointsWidth = (fixedDistByPixl * fetchedData.length); //- 60; //+ (options.firstPointMargin * options.eventsMinDistance);
var allPointsWidth = (fixedDistByPixl * fetchedData.length); // - 30;
totalWidth = allPointsWidth;
}
else if (options.distanceMode == 'predefinedDistance') { // predefined Distance inside Json Object for each object
......@@ -268,29 +267,40 @@
// -----------------------------------------------------------
var updateVisibleContent = function (event, eventsContent) {
var eventDate = event.data('date'),
eventID = event.data('id'),
visibleContent = eventsContent.find('.selected'),
selectedContent = eventsContent.find('[data-date="' + eventDate + '"]'),
selectedContent = eventsContent.find('[data-id="' + eventID + '"]'),
selectedContentHeight = selectedContent.height();
var classEntering = 'selected enter-left',
classLeaving = 'leave-right';
if (selectedContent.index() > visibleContent.index()) {
var classEnetering = 'selected enter-right',
classEntering = 'selected enter-right',
classLeaving = 'leave-left';
} else {
var classEnetering = 'selected enter-left',
classLeaving = 'leave-right';
}
selectedContent.addClass(classEnetering);
visibleContent.addClass(classLeaving).one('webkitAnimationEnd oanimationend msAnimationEnd animationend', function () {
selectedContent.addClass(classEntering);
var evdone = false;
visibleContent.addClass(classLeaving).animate({
opacity: 1,
},
200,
function () {
if (!evdone) {
// we only want to attach this once
visibleContent.removeClass('leave-right leave-left selected');
selectedContent.removeClass('enter-left enter-right');
selectedContent.addClass('selected');
evdone = true;
}
});
eventsContent.css('height', selectedContentHeight + 'px');
}
var updateOlderEvents = function (event) {
event.parent('li').prevAll('li').children('a').addClass('older-event').end().end().nextAll('li').children('a').removeClass('older-event');
event.removeClass('older-event');
}
var getTranslateValue = function (timeline) {
......@@ -401,7 +411,6 @@
$.getJSON(options.url, options.params)
.done(function (data) {
//----------------------
if (data.error==false) {
_constructDom(data.message.data, obj);
}
......@@ -415,8 +424,6 @@
var statusText = err.statusText;
var errd = statusText + status;
$(obj).html(errd);
// alert('error');
//console.log(err);
});
}
if (options.callType == callTypes.jsonObject) {
......@@ -463,14 +470,13 @@
"title": dataCollection[i][options.map.title],
"subTitle": dataCollection[i][options.map.subTitle],
"dateValue": dataCollection[i][options.map.dateValue],
"idValue": dataCollection[i][options.map.idValue],
"pointCnt": dataCollection[i][options.map.pointCnt],
"bodyCnt": dataCollection[i][options.map.bodyCnt]
}
$liTimeLineCollection += '<li><a href="#0" data-date="' + point.dateValue + '" ';
// if (point.isSelected) $liTimeLineCollection += 'class="selected"';
$liTimeLineCollection += '<li><a href="#0" data-id="' + point.idValue + '" data-date="' + point.dateValue + '" ';
$liTimeLineCollection += 'class="tlnode ' + (isSelectedPoint ? 'selected' : '') + '"';
$liTimeLineCollection += '>';
$liTimeLineCollection += point.pointCnt + '</a></li>';
// -----------------
......@@ -481,7 +487,7 @@
else {
$liEventContentCollection += 'class="lineli"';
}
$liEventContentCollection += ' data-date="' + point.dateValue + '">' + formatTitle(point.title, dataCollection[i]) + formatSubTitle(point.subTitle, dataCollection[i]) + formatBodyContent(point.bodyCnt, dataCollection[i]) + '</li>';
$liEventContentCollection += ' data-id="' + point.idValue + '" data-date="' + point.dateValue + '">' + formatTitle(point.title, dataCollection[i]) + formatSubTitle(point.subTitle, dataCollection[i]) + formatBodyContent(point.bodyCnt, dataCollection[i]) + '</li>';
}
//-----------------------------------------
......@@ -490,8 +496,8 @@
var $container = $(mainObject);
var $sectionStart = '<section id="jtlinesection" class="jtline">';
var $timeline = '<div class="timeline"><div class="events-wrapper"><div class="events"><ol>' + $liTimeLineCollection + '</ol><span class="filling-line" aria-hidden="true"></span></div></div><ul class="cd-timeline-navigation"><li class="lineli"><a href="#0" class="prev inactive">Prev</a></li><li lineli><a href="#0" class="next">Next</a></li></ul></div>';
var $eventsContent = '<div class="events-content"><ol>' + $liEventContentCollection + '</ol><ul class="cd-timeline-navigation-second"><li><a href="#0" class="prev inactive">Prev</a></li><li><a href="#0" class="next">Next</a></li></ul></div>';
var $timeline = '<div class="timeline"><div class="events-wrapper"><div class="events"><ol>' + $liTimeLineCollection + '</ol><span class="filling-line" aria-hidden="true"></span></div></div><ul class="cd-timeline-navigation"><li class="lineli"><a href="#0" class="prev inactive">' + get_string_ajax('previous', 'mahara') + '</a></li><li class="lineli"><a href="#0" class="next">' + get_string_ajax('next', 'mahara') + '</a></li></ul></div>';
var $eventsContent = '<div class="events-content"><ol>' + $liEventContentCollection + '</ol><ul class="cd-timeline-navigation-second"><li><a href="#0" class="prev inactive">' + get_string_ajax('previousversion', 'view') + '</a></li><li><a href="#0" class="next">' + get_string_ajax('nextversion', 'view') + '</a></li></ul></div>';
var $sectionEnd = '</section>';
var allHtml = $sectionStart + $timeline + $eventsContent + $sectionEnd;
......
(function(a){a.fn.extend({jTLine:function(b){var b=a.extend({eventsMinDistance:60,callType:'ajax',url:'',structureObj:[{}],distanceMode:'auto',fixDistanceValue:2,firstPointMargin:2,map:{dataRoot:'dataArray',isSelected:'isSelected',title:'title',subTitle:'subTitle',dateValue:'dateValue',pointCnt:'pointCnt',bodyCnt:'bodyCnt'},isSelectedAttrProvided:!1,formatTitle:function(){},formatSubTitle:function(){},formatBodyContent:function(){}},b),d={jsonObject:'jsonObject',ajax:'ajax',mappedJsonObject:'mappedJsonObject'},e=function(G){var H={};H.timelineWrapper=G.find('.events-wrapper'),H.eventsWrapper=H.timelineWrapper.children('.events'),H.fillingLine=H.eventsWrapper.children('.filling-line'),H.timelineEvents=H.eventsWrapper.find('a'),H.timelineDates=s(H.timelineEvents),H.eventsMinLapse=u(H.timelineDates),H.timelineNavigation=G.find('.cd-timeline-navigation'),H.eventsContent=G.children('.events-content'),m(H,b.eventsMinDistance);var I=n(H,b.eventsMinDistance);G.addClass('loaded'),H.timelineNavigation.on('click','.next',function(J){J.preventDefault(),f(H,I,'next')}),H.timelineNavigation.on('click','.prev',function(J){J.preventDefault(),f(H,I,'prev')}),H.eventsWrapper.on('click','a',function(J){J.preventDefault(),H.timelineEvents.removeClass('selected'),a(this).addClass('selected'),p(a(this)),k(a(this),H.fillingLine,I),o(a(this),H.eventsContent)}),H.eventsContent.on('swipeleft',function(){var J=w();'mobile'==J&&g(H,I,'next')}),H.eventsContent.on('swiperight',function(){var J=w();'mobile'==J&&g(H,I,'prev')}),a(document).keyup(function(J){'37'==J.which&&v(G.get(0))?g(H,I,'prev'):'39'==J.which&&v(G.get(0))&&g(H,I,'next')})},f=function(G,H,I){var J=q(G.eventsWrapper),K=+G.timelineWrapper.css('width').replace('px','');'next'==I?j(G,J-K+b.eventsMinDistance,K-H):j(G,J+K-b.eventsMinDistance)},g=function(G,H,I){var J=G.eventsContent.find('.selected'),K='next'==I?J.next():J.prev();if(0<K.length){var L=G.eventsWrapper.find('.selected'),M='next'==I?L.parent('li').next('li').children('a'):L.parent('li').prev('li').children('a');k(M,G.fillingLine,H),o(M,G.eventsContent),M.addClass('selected'),L.removeClass('selected'),p(M),h(I,M,G)}},h=function(G,H,I){var J=window.getComputedStyle(H.get(0),null),K=+J.getPropertyValue('left').replace('px',''),L=+I.timelineWrapper.css('width').replace('px',''),M=+I.eventsWrapper.css('width').replace('px',''),N=q(I.eventsWrapper);('next'==G&&K>L-N||'prev'==G&&K<-N)&&j(I,-K+L/2,L-M)},j=function(G,H,I){var J=G.eventsWrapper.get(0);H=0<H?0:H,H='undefined'!=typeof I&&H<I?I:H,r(J,'translateX',H+'px'),0==H?G.timelineNavigation.find('.prev').addClass('inactive'):G.timelineNavigation.find('.prev').removeClass('inactive'),H==I?G.timelineNavigation.find('.next').addClass('inactive'):G.timelineNavigation.find('.next').removeClass('inactive')},k=function(G,H,I){var J=window.getComputedStyle(G.get(0),null),K=J.getPropertyValue('left'),L=J.getPropertyValue('width');K=+K.replace('px','')+ +L.replace('px','')/2;var M=K/I;r(H.get(0),'scaleX',M)},l,m=function(G,H){var I,J,K,L=b.firstPointMargin;for(l=b.firstPointMargin,i=0;i<G.timelineDates.length;i++)'auto'==b.distanceMode?(I=t(G.timelineDates[0],G.timelineDates[i]),J=Math.round(I/G.eventsMinLapse)+2):'fixDistance'==b.distanceMode?0==i?J=b.firstPointMargin:(L+=b.fixDistanceValue,J=L):'predefinedDistance'==b.distanceMode&&(K=y.dataArray[i].nextDistance,0==i?J=b.firstPointMargin:(L+=K,J=L),l+=K),G.timelineEvents.eq(i).css('left',J*H+'px')},n=function(G,H){var I,J,K,M=b.firstPointMargin;if('auto'==b.distanceMode)I=t(G.timelineDates[0],G.timelineDates[G.timelineDates.length-1]),J=I/G.eventsMinLapse,J=Math.round(J)+4,K=J*H;else if('fixDistance'==b.distanceMode){var N=b.eventsMinDistance*b.fixDistanceValue,O=N*y.length;K=O}else'predefinedDistance'==b.distanceMode&&(K=l*b.eventsMinDistance);return G.eventsWrapper.css('width',K+'px'),p(G.eventsWrapper.find('a.selected')),k(G.eventsWrapper.find('a.selected'),G.fillingLine,K),h('next',G.eventsWrapper.find('a.selected'),G),K},o=function(G,H){var I=G.data('date'),J=H.find('.selected'),K=H.find('[data-date="'+I+'"]'),L=K.height();if(K.index()>J.index())var M='selected enter-right',N='leave-left';else var M='selected enter-left',N='leave-right';K.attr('class',M),J.attr('class',N).one('webkitAnimationEnd oanimationend msAnimationEnd animationend',function(){J.removeClass('leave-right leave-left'),K.removeClass('enter-left enter-right')}),H.css('height',L+'px')},p=function(G){G.parent('li').prevAll('li').children('a').addClass('older-event').end().end().nextAll('li').children('a').removeClass('older-event')},q=function(G){var H=window.getComputedStyle(G.get(0),null),I=H.getPropertyValue('-webkit-transform')||H.getPropertyValue('-moz-transform')||H.getPropertyValue('-ms-transform')||H.getPropertyValue('-o-transform')||H.getPropertyValue('transform');if(0<=I.indexOf('(')){var I=I.split('(')[1];I=I.split(')')[0],I=I.split(',');var J=I[4]}else var J=0;return+J},r=function(G,H,I){G.style['-webkit-transform']=H+'('+I+')',G.style['-moz-transform']=H+'('+I+')',G.style['-ms-transform']=H+'('+I+')',G.style['-o-transform']=H+'('+I+')',G.style.transform=H+'('+I+')'},s=function(G){var H=[];return G.each(function(){var I=a(this),J=I.data('date').split('T');if(1<J.length)var K=J[0].split('/'),L=J[1].split(':');else if(0<=J[0].indexOf(':'))var K=['2000','0','0'],L=J[0].split(':');else var K=J[0].split('/'),L=['0','0'];var M=new Date(K[2],K[1]-1,K[0],L[0],L[1]);H.push(M)}),H},t=function(G,H){var I=Math.round(H-G);return I},u=function(G){var H=[];for(i=1;i<G.length;i++){var I=t(G[i-1],G[i]);H.push(I)}return Math.min.apply(null,H)},v=function(G){for(var H=G.offsetTop,I=G.offsetLeft,J=G.offsetWidth,K=G.offsetHeight;G.offsetParent;)G=G.offsetParent,H+=G.offsetTop,I+=G.offsetLeft;return H<window.pageYOffset+window.innerHeight&&I<window.pageXOffset+window.innerWidth&&H+K>window.pageYOffset&&I+J>window.pageXOffset},w=function(){return window.getComputedStyle(document.querySelector('.jtline'),'::before').getPropertyValue('content').replace(/'/g,'').replace(/"/g,'')},x=function(G){A(G)},y,z=function(G){y=G},A=function(G){b.callType==d.ajax&&a.getJSON(b.url,function(){}).done(function(H){H?B(H,G):a(G).html('Error fetching data')}).fail(function(H){var I=H.status,J=H.statusText;a(G).html(J+I)}),b.callType==d.jsonObject&&(b.structureObj?B(b.structureObj,G):a(G).html('Error fetching data'))},B=function(G,H){var I='',J='',K;K='/'==b.map.dataRoot?G:G[b.map.dataRoot],z(K);var L,M=K.find(V=>'true'===V[b.map.isSelected]);M||(K[0][b.map.isSelected]='true');for(var N=0;N<K.length;N++)L=K[N][b.map.isSelected],point={isSelected:L,title:K[N][b.map.title],subTitle:K[N][b.map.subTitle],dateValue:K[N][b.map.dateValue],pointCnt:K[N][b.map.pointCnt],bodyCnt:K[N][b.map.bodyCnt]},I+='<li><a href="#0" data-date="'+point.dateValue+'" ',I+='class="tlnode '+(L?'selected':'')+'"',I+='>',I+=point.pointCnt+'</a></li>',J+='<li ',L&&(J+='class="selected"'),J+=' data-date="'+point.dateValue+'">'+C(point.title,K[N])+D(point.subTitle,K[N])+E(point.bodyCnt,K[N])+'</li>';var O=a(H),Q='<div class="timeline"><div class="events-wrapper"><div class="events"><ol>'+I+'</ol><span class="filling-line" aria-hidden="true"></span></div></div><ul class="cd-timeline-navigation"><li><a href="#0" class="prev inactive">Prev</a></li><li><a href="#0" class="next">Next</a></li></ul></div>',R='<div class="events-content"><ol>'+J+'</ol></div>';O.html('<section id="jtlinesection" class="jtline">'+Q+R+'</section>');var U=a('#jtlinesection',O);e(U)},C=function(G,H){var I=b.formatTitle(G,H);return I?b.formatTitle(G,H):'<h2>'+G+'</h2>'},D=function(G,H){var I=b.formatSubTitle(G,H);return I?b.formatSubTitle(G,H):'<em>'+point.subTitle+'</em>'},E=function(G,H){var I=b.formatBodyContent(G,H);return I?b.formatBodyContent(G,H):'<p>'+G+'</p>'},F=function(){a('.tlnode').on('click',function(G){b.onPointClick!==void 0&&b.onPointClick(G)})};return this.each(function(){var G=a(this);x(G),F()})}})})(jQuery);
\ No newline at end of file
......@@ -524,3 +524,6 @@ $string['timeline'] = 'Timeline';
$string['timelinespecific'] = 'Timeline for %s';
$string['savetimeline'] = 'Save to timeline';
$string['savetimelinespecific'] = 'Save timeline for %s';
$string['noversionsexist'] = 'There are no saved versions to display for the page "%s"';
$string['previousversion'] = 'Previous version';
$string['nextversion'] = 'Next version';
......@@ -3355,6 +3355,64 @@ function is_view_suspended($view, $artefacts=true) {
", array($viewid));
}
}
/**
* Checks if artefact was in a previous version of the view
*
* @param int|object $artefact ID of an artefact or object itself.
* Will load object if ID is supplied.
* @param int $view ID of a page that contains artefact.
*
* @return boolean True if artefact is in previous version of view, False otherwise.
*/
function artefact_in_view_version($artefact, $view) {
if (!is_object($artefact)) {
require_once(get_config('docroot') . 'artefact/lib.php');
$artefact = artefact_instance_from_id($artefact);
}
// First check if in current view
if (artefact_in_view($artefact, $view)) {
return true;
}
// If not in current view then lets check the older versions
$db_version = get_db_version();
if (is_postgres() && version_compare($db_version, '9.2.0', '>=')) {
// We can check direct on the json data
return get_records_sql_array("SELECT id FROM {view_versioning} v, JSON_ARRAY_ELEMENTS(CAST(v.blockdata AS JSON)->'blocks') obj
WHERE (? = ANY(TRANSLATE(obj->'configdata'->>'artefactids','[]','{}')::int[]) OR
obj->'configdata'->>'artefactid' = ?)
AND view = ?", array($artefact->get('id'), $artefact->get('id'), $view));
}
else if (is_mysql() && mysql_get_type() == 'mysql' && version_compare($db_version, '8.0.0', '>=')) {
// Note: we can't translate the array string to an array yet so we need to do a regexp match instead
$mysqlregex = '\\\[' . $artefact->get('id') . ',|\\\s' . $artefact->get('id') . ',|\\\s' . $artefact->get('id') . '\\\]';
return get_records_Sql_array("SELECT id FROM {view_versioning} v WHERE view = ?
AND (REGEXP_LIKE(JSON_EXTRACT( CAST(v.blockdata AS JSON), '$.blocks[*].configdata.artefactid'), '" . $mysqlregex . "')
OR REGEXP_LIKE(JSON_EXTRACT( CAST(v.blockdata AS JSON), '$.blocks[*].configdata.artefactids'), '" . $mysqlregex . "')
)", array($view));
}
// If we can't check direct on the json data We'll need to limit the results to those that possibly
// contain the blockid and work back from most recent to make things faster
if ($versions = get_records_sql_array("SELECT id, view, blockdata
FROM {view_versioning}
WHERE view = ? AND blockdata LIKE '%' || ? || '%'
ORDER BY ctime DESC", array($view, $artefact->get('id')))) {
foreach ($versions as $version) {
$blockdata = json_decode($version->blockdata);
if (isset($blockdata->blocks) && is_array($blockdata->blocks)) {
foreach ($blockdata->blocks as $block) {
if (isset($block->configdata) && isset($block->configdata->artefactid) && $block->configdata->artefactid == $artefact->get('id')) {
return true;
}
if (isset($block->configdata) && isset($block->configdata->artefactids) && in_array($artefact->get('id'), $block->configdata->artefactids)) {
return true;
}
}
}
}
}
return false;
}
/**
* Checks if artefact or at least one of its ancestors is in view
......
......@@ -986,6 +986,7 @@ class View {
delete_records('view_autocreate_grouptype', 'view', $this->id);
delete_records('tag', 'resourcetype', 'view', 'resourceid', $this->id);
delete_records('view_visit','view',$this->id);
delete_records('view_versioning', 'view', $this->id);
delete_records('existingcopy', 'view', $this->id);
$eventdata = array('id' => $this->id, 'eventfor' => 'view');
if ($collection = $this->get_collection()) {
......@@ -7069,51 +7070,78 @@ class View {
* Fetch a list of versions for the particular view
*
* @param string $view the ID of the view we wish to retrieve versioning information from
* @param $fromdate date of the oldest version we want to retrieve
* @param $todate date of the newest version we want to retrieve
* @return object $views an object containing the count and data of the versions
*/
public function get_versions($view, $fromdate=NULL, $todate = NULL) {
public function get_versions($view, $fromdate = null, $todate = null) {
if (!is_numeric($view)) {
throw new InvalidArgumentException(get_string('noaccesstoview', 'view'));
}
if (is_numeric($fromdate)) {
$fromdate = db_format_timestamp($fromdate);
}
else {
$fromdate = db_format_timestamp(strtotime($fromdate));
}
if (is_numeric($todate)) {
$todate = db_format_timestamp($todate);
}
else {
$todate = db_format_timestamp(strtotime($todate));
}
$versions = new stdClass();
$versions->count = 0;
$versions->total = 0;
$versions->data = array();
if ($fromdate && $todate) {
if ($records = get_records_sql_array("SELECT vv.*, v.title AS viewname, v.owner, v.institution
$sql = "SELECT vv.*, v.title AS viewname, v.owner, v.institution
FROM {view_versioning} vv
JOIN {view} v ON v.id = vv.view
WHERE vv.ctime < ? AND vv.ctime > ? AND vv.view = ?
ORDER BY vv.ctime ASC", array($todate, $fromdate, $view))) {
$versions->count = count($records);
$versions->data = $records;
WHERE vv.view = ?";
$values = array($view);
if ($fromdate) {
$sql .= " AND vv.ctime >= ?";
$values[] = $fromdate;
}
if ($todate) {
$sql .= " AND vv.ctime <= ?";
$values[] = $todate;
}
else {
if ($records = get_records_sql_array("SELECT vv.*,v.title AS viewname, v.owner, v.institution
FROM {view_versioning} vv
JOIN {view} v ON v.id = vv.view
WHERE vv.view = ?
ORDER BY vv.ctime ASC", array($view))) {
$sql .= " ORDER BY vv.ctime ASC";
if ($records = get_records_sql_array($sql, $values)) {
$versions->count = count($records);
$versions->data = $records;
}
$versions->total = count_records('view_versioning', 'view', $view);
return $versions;
}
return $versions;
public function get_timeline_form($view, $from = null, $to = null) {
if (is_numeric($from)) {
$from = db_format_timestamp($from);
}
if (is_numeric($to)) {
$to = db_format_timestamp($to);
}
public function get_timeline_form($view, $from = '-3 months', $to = 'now') {
require_once('pieforms/pieform/elements/calendar.php');
$elements = array(
'from' => array(
'title' => get_string('From'),
'type' => 'calendar',
'defaultvalue' => strtotime($from),
'caloptions' => array(
'showsTime' => false,
),
),
'to' => array(
'title' => get_string('To'),
'type' => 'calendar',
'defaultvalue' => strtotime($to),
'caloptions' => array(
'showsTime' => false,
),
),
'viewid' => array(
'type' => 'hidden',
......@@ -7129,6 +7157,7 @@ class View {
$form = array(
'name' => 'timeline',
'elements' => $elements,
'autofocus' => false,
);
return pieform($form);
}
......@@ -7136,18 +7165,20 @@ class View {
public function build_timeline_results($search, $offset, $limit) {
return false;
}
public function format_versioning_data($data) {
if (empty($data)) {
return $data;
}
$data=json_decode($data);
$data = json_decode($data);
$this->numrows = isset($data->numrows) ? $data->numrows : $this->numrows;
$this->layout = isset($data->layout) ? $data->layout : $this->layout;
$this->description = isset($data->description) ? $data->description : '';
$this->tags = isset($data->tags) && is_array($data->tags) ? $data->tags : array();
$colsperrow = array();
if (isset($data->columnsperrow)) {
foreach ($data->columnsperrow as $k=>$v){
foreach ($data->columnsperrow as $k => $v) {
$colsperrow[$k] = $v;
}
}
......@@ -7163,7 +7194,6 @@ class View {
if (!empty($data->blocks)) {
require_once(get_config('docroot') . 'blocktype/lib.php');
foreach ($data->blocks as $k => $v) {
// log_debug($k . ': ' . $v->blocktype);
safe_require('blocktype', $v->blocktype);
$bi = new BlockInstance(0,
array(
......@@ -7177,19 +7207,15 @@ class View {
'configdata' => serialize((array)$v->configdata),
)
);
// $html .= call_static_method(generate_class_name("blocktype", $v->blocktype), "render_instance", $bi);
$this->columns[$v->row][$v->column]['blockinstances'][] = $bi;
}
}
$html = $this->build_rows(true);
// log_debug($html);
$html = $this->build_rows();
$data->html = $html;
return $data;
}
}