Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
What's new
7
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Open sidebar
mahara
mahara
Commits
4aa41f44
Commit
4aa41f44
authored
Feb 05, 2018
by
Roisin Pearson
Committed by
Cecilia Vela Gurovic
Oct 01, 2018
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Bug #1742347 Next and Previous arrows added to the timeline viewport
Change-Id: Ia5e26f7791a1aec90cd26d3092f7542881456255
parent
6e25089e
Changes
15
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
15 changed files
with
393 additions
and
253 deletions
+393
-253
htdocs/artefact/file/download.php
htdocs/artefact/file/download.php
+5
-0
htdocs/js/jTLine/js/jquery-2.1.4.js
htdocs/js/jTLine/js/jquery-2.1.4.js
+0
-4
htdocs/js/jTLine/js/jtline.js
htdocs/js/jTLine/js/jtline.js
+79
-73
htdocs/js/jTLine/js/jtline.min.js
htdocs/js/jTLine/js/jtline.min.js
+0
-1
htdocs/lang/en.utf8/view.php
htdocs/lang/en.utf8/view.php
+3
-0
htdocs/lib/mahara.php
htdocs/lib/mahara.php
+58
-0
htdocs/lib/view.php
htdocs/lib/view.php
+99
-73
htdocs/theme/raw/sass/features/_timeline.scss
htdocs/theme/raw/sass/features/_timeline.scss
+19
-30
htdocs/theme/raw/templates/view/versioning.tpl
htdocs/theme/raw/templates/view/versioning.tpl
+40
-34
htdocs/theme/raw/templates/view/view.tpl
htdocs/theme/raw/templates/view/view.tpl
+17
-15
htdocs/view/createversion.php
htdocs/view/createversion.php
+1
-0
htdocs/view/versioning.json.php
htdocs/view/versioning.json.php
+11
-11
htdocs/view/versioning.php
htdocs/view/versioning.php
+13
-11
htdocs/view/view.php
htdocs/view/view.php
+4
-1
test/behat/features/user_content/versioning.feature
test/behat/features/user_content/versioning.feature
+44
-0
No files found.
htdocs/artefact/file/download.php
View file @
4aa41f44
...
...
@@ -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
))
{
...
...
htdocs/js/jTLine/js/jquery-2.1.4.js
deleted
100644 → 0
View file @
6e25089e
This diff is collapsed.
Click to expand it.
htdocs/js/jTLine/js/jtline.js
View file @
4aa41f44
...
...
@@ -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 prev viewport arrow
timelineComponents
[
'
timelineNavigationViewport
'
].
on
(
'
click
'
,
'
.prev
'
,
function
(
event
)
{
event
.
preventDefault
();
showNewContent
(
timelineComponents
,
timelineTotWidth
,
'
prev
'
);
});
//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
);
});
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,8 +143,19 @@
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
'
);
}
}
}
var
updateTimelinePosition
=
function
(
string
,
event
,
timelineComponents
)
{
//translate timeline to the left/right according to the position of the selected event
...
...
@@ -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
);
//-
6
0;
//+ (options.firstPointMargin * options.eventsMinDistance);
var
allPointsWidth
=
(
fixedDistByPixl
*
fetchedData
.
length
);
//
-
3
0;
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-d
ate
="
'
+
eventD
ate
+
'
"]
'
),
selectedContent
=
eventsContent
.
find
(
'
[data-
i
d="
'
+
event
I
D
+
'
"]
'
),
selectedContentHeight
=
selectedContent
.
height
();
var
classEntering
=
'
selected enter-left
'
,
classLeaving
=
'
leave-right
'
;
if
(
selectedContent
.
index
()
>
visibleContent
.
index
())
{
var
classEnetering
=
'
selected enter-right
'
,
classLeaving
=
'
leave-left
'
;
}
else
{
var
classEnetering
=
'
selected enter-left
'
,
classLeaving
=
'
leave-right
'
;
classEntering
=
'
selected enter-right
'
,
classLeaving
=
'
leave-left
'
;
}
selectedContent
.
addClass
(
classEnetering
);
visibleContent
.
addClass
(
classLeaving
).
one
(
'
webkitAnimationEnd oanimationend msAnimationEnd animationend
'
,
function
()
{
visibleContent
.
removeClass
(
'
leave-right leave-left selected
'
);
selectedContent
.
removeClass
(
'
enter-left enter-right
'
);
selectedContent
.
addClass
(
'
selected
'
);
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,25 +470,24 @@
"
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>
'
;
// -----------------
$liEventContentCollection
+=
'
<li
'
;
if
(
isSelectedPoint
)
{
$liEventContentCollection
+=
'
class="lineli selected"
'
;
}
else
{
}
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
;
...
...
htdocs/js/jTLine/js/jtline.min.js
deleted
100644 → 0
View file @
6e25089e
(
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
htdocs/lang/en.utf8/view.php
View file @
4aa41f44
...
...
@@ -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'
;
htdocs/lib/mahara.php
View file @
4aa41f44
...
...
@@ -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
...
...
htdocs/lib/view.php
View file @
4aa41f44
...
...
@@ -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'
));
}
$versions
=
new
stdClass
();
$versions
->
count
=
0
;
$versions
->
data
=
array
();
if
(
$fromdate
&&
$todate
)
{
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.ctime < ? AND vv.ctime > ? AND vv.view = ?
ORDER BY vv.ctime ASC"
,
array
(
$todate
,
$fromdate
,
$view
)))
{
$versions
->
count
=
count
(
$records
);
$versions
->
data
=
$records
;
}
if
(
is_numeric
(
$fromdate
))
{
$fromdate
=
db_format_timestamp
(
$fromdate
);
}
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
)))
{
$versions
->
count
=
count
(
$records
);
$versions
->
data
=
$records
;
}
$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
();
$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.view = ?"
;
$values
=
array
(
$view
);
if
(
$fromdate
)
{
$sql
.
=
" AND vv.ctime >= ?"
;
$values
[]
=
$fromdate
;
}
if
(
$todate
)
{
$sql
.
=
" AND vv.ctime <= ?"
;
$values
[]
=
$todate
;
}
$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
;
}
public
function
get_timeline_form
(
$view
,
$from
=
'-3 months'
,
$to
=
'now'
)
{
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
);
}
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,59 +7165,56 @@ 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
);
$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
){