Add a revision history sidebar

pull/1/head
Pranav Kant 8 years ago
parent eb79866efd
commit 3235b886ad

@ -305,6 +305,14 @@
<length>4</length>
<comments>The unique ID of the file authorized</comments>
</field>
<field>
<name>version</name>
<type>integer</type>
<notnull>true</notnull>
<default>0</default>
<length>4</length>
<comments>Authorized version, if any, of given fileid</comments>
</field>
<field>
<name>path</name>
<type>text</type>

@ -346,10 +346,19 @@ class DocumentController extends Controller {
* Only for authenticated users!
*/
public function wopiGetToken($fileId){
\OC::$server->getLogger()->debug('Generating WOPI Token for file {fileId}.', [ 'app' => $this->appName, 'fileId' => $fileId ]);
$arr = explode('_', $fileId, 2);
$version = '0';
if (count($arr) == 2) {
$fileId = $arr[0];
$version = $arr[1];
}
\OC::$server->getLogger()->debug('Generating WOPI Token for file {fileId}, version {version}.', [ 'app' => $this->appName, 'fileId' => $fileId, 'version' => $version ]);
$row = new Db\Wopi();
$token = $row->generateFileToken($fileId);
$token = $row->generateFileToken($fileId, $version);
// Return the token.
return array(
@ -367,12 +376,19 @@ class DocumentController extends Controller {
public function wopiCheckFileInfo($fileId){
$token = $this->request->getParam('access_token');
\OC::$server->getLogger()->debug('Getting info about file {fileId} by token {token}.', [ 'app' => $this->appName, 'fileId' => $fileId, 'token' => $token ]);
$arr = explode('_', $fileId, 2);
$version = '0';
if (count($arr) == 2) {
$fileId = $arr[0];
$version = $arr[1];
}
\OC::$server->getLogger()->debug('Getting info about file {fileId}, version {version} by token {token}.', [ 'app' => $this->appName, 'fileId' => $fileId, 'version' => $version, 'token' => $token ]);
$row = new Db\Wopi();
$row->loadBy('token', $token);
$res = $row->getPathForToken($fileId, $token);
$res = $row->getPathForToken($fileId, $version, $token);
if ($res == false || http_response_code() != 200)
{
return false;
@ -389,6 +405,7 @@ class DocumentController extends Controller {
return array(
'BaseFileName' => $baseFileName,
'Size' => $size,
'Version' => $version
//'DownloadUrl' => '',
//'FileUrl' => '',
);
@ -404,13 +421,51 @@ class DocumentController extends Controller {
public function wopiGetFile($fileId){
$token = $this->request->getParam('access_token');
\OC::$server->getLogger()->debug('Getting contents of file {fileId} by token {token}.', [ 'app' => $this->appName, 'fileId' => $fileId, 'token' => $token ]);
$arr = explode('_', $fileId, 2);
$version = '0';
if (count($arr) == 2) {
$fileId = $arr[0];
$version = $arr[1];
}
\OC::$server->getLogger()->debug('Getting contents of file {fileId}, version {version} by token {token}.', [ 'app' => $this->appName, 'fileId' => $fileId, 'version' => $version, 'token' => $token ]);
$row = new Db\Wopi();
$row->loadBy('token', $token);
//TODO: Support X-WOPIMaxExpectedSize header.
$res = $row->getPathForToken($fileId, $token);
$res = $row->getPathForToken($fileId, $version, $token);
// If some previous version is requested, fetch it from Files_Version app
if ($version !== '0') {
\OCP\JSON::checkAppEnabled('files_versions');
// Login as this user
$editorid = $res['editor'];
$users = \OC::$server->getUserManager()->search($editorid, 1, 0);
if (count($users) > 0)
{
$user = array_shift($users);
if (strcasecmp($user->getUID(),$editorid) === 0)
{
\OC::$server->getUserSession()->setUser($user);
}
}
\OCP\JSON::checkLoggedIn();
// Setup the FS
\OC_Util::tearDownFS();
\OC_Util::setupFS($editorid, '/' . $editorid . '/files');
list($owner_uid, $filename) = \OCA\Files_Versions\Storage::getUidAndFilename($res['path']);
$versionName = '/files_versions/' . $filename . '.v' . $version;
\OC_Util::tearDownFS();
return new DownloadResponse($this->request, $owner_uid, $versionName);
}
return new DownloadResponse($this->request, $res['owner'], '/files' . $res['path']);
}
@ -424,12 +479,27 @@ class DocumentController extends Controller {
public function wopiPutFile($fileId){
$token = $this->request->getParam('access_token');
\OC::$server->getLogger()->debug('Putting contents of file {fileId} by token {token}.', [ 'app' => $this->appName, 'fileId' => $fileId, 'token' => $token ]);
$arr = explode('_', $fileId, 2);
$version = '0';
if (count($arr) == 2) {
$fileId = $arr[0];
$version = $arr[1];
}
// Changing a previous version of the file is not possible
// Ignore WOPI put if such a request is encountered
if ($version !== '0') {
return array(
'status' => 'success'
);
}
\OC::$server->getLogger()->debug('Putting contents of file {fileId}, version {version} by token {token}.', [ 'app' => $this->appName, 'fileId' => $fileId, 'version' => $version, 'token' => $token ]);
$row = new Db\Wopi();
$row->loadBy('token', $token);
$res = $row->getPathForToken($fileId, $token);
$res = $row->getPathForToken($fileId, $version, $token);
// Log-in as the user to regiser the change under her name.
$editorid = $res['editor'];
@ -525,7 +595,7 @@ class DocumentController extends Controller {
* Get file information about single document with fileId
*/
public function get($fileId){
$documents = array();
$documents = array();
$documents[0] = Storage::getDocumentById($fileId);
return $this->prepareDocuments($documents);

@ -180,6 +180,72 @@
bottom: 0;
}
#revViewerContainer{
position: absolute;
width: 85%;
z-index: 600;
background-color: #ddd !important;
top: 45px;
bottom: 0;
}
#revPanelContainer{
position: absolute;
width: 15%;
z-index: 600;
background-color: #efefef !important;
right: 0;
top: 45px;
bottom: 0;
box-sizing: border-box;
overflow: auto;
}
/* Title */
#revPanelHeader{
height: 69px;
border-bottom: 1px solid #ddd;
margin: 0 0;
box-sizing: border-box;
padding-left: 15px;
padding-top: 15px;
}
#revPanelHeader h2{
margin: 0 0;
}
.loleaflet-font{
font-family: "Segoe UI", Tahoma, Arial, Helvetica, sans-serif !important;
}
#revisionsContainer{
list-style: none;
}
#revisionsContainer li{
width: 100%;
cursor: default;
height: 45px;
float: left;
border-bottom: 1px solid #ddd;
box-sizing: border-box;
padding: 10px 0;
}
#revisionsContainer a{
padding-left: 15px;
}
#revisionsContainer li:hover, #revisionsContainer li.active{
background-color: rgba(0, 0, 0, 0.1);
}
#show-more-versions{
width: 100%;
padding: 10px;
}
#documents-overlay, #documents-overlay-below{
position: fixed;
top: 45px;
@ -189,7 +255,7 @@
filter:alpha(opacity=60);
opacity: .6;
z-index: 1000;
background-color: #fff;
background-color: #fff;
}
#documents-overlay-below{

@ -180,7 +180,8 @@ $.widget('oc.documentOverlay', {
});
var documentsMain = {
isEditormode : false,
isEditorMode : false,
isViewerMode: false,
isGuest : false,
memberId : false,
esId : false,
@ -199,13 +200,174 @@ var documentsMain = {
container : '<div id="mainContainer" class="claro">' +
'</div>',
viewContainer: '<div id="revViewerContainer" class="claro">' +
'<div id="revViewer"></div>' +
'</div>',
revHistoryContainerTemplate: '<div id="revPanelContainer" class="loleaflet-font">' +
'<div id="revPanelHeader">' +
'<h2>Revision History</h2>' +
'<span>{{filename}}</span>' +
'</div>' +
'<div id="revisionsContainer" class="loleaflet-font">' +
'<ul></ul>' +
'</div>' +
'<input type="button" id="show-more-versions" class="loleaflet-font" value="{{moreVersionsLabel}}" />' +
'</div>',
revHistoryItemTemplate: '<li>' +
'<a href="{{downloadUrl}}" class="downloadVersion"><img src="{{downloadIconUrl}}" />' +
'<a class="versionPreview"><span class="versiondate has-tooltip" title="{{relativeTimestamp}}">{{formattedTimestamp}}</span></a>' +
'</a>' +
'</li>',
/* Previous window title */
mainTitle : '',
/* Number of revisions already loaded */
revisionsStart: 0,
init : function(){
documentsMain.UI.mainTitle = $('title').text();
},
showViewer: function(fileId, title){
// remove previous viewer, if open, and set a new one
if (documentsMain.isViewerMode) {
$('#revViewer').remove();
$('#revViewerContainer').prepend($('<div id="revViewer">'));
}
$.get(OC.generateUrl('apps/richdocuments/wopi/token/{fileId}', { fileId: fileId }),
function (result) {
// WOPISrc - URL that loolwsd will access (ie. pointing to ownCloud)
var wopiurl = window.location.protocol + '//' + window.location.host + OC.generateUrl('apps/richdocuments/wopi/files/{file_id}', {file_id: fileId});
var wopisrc = encodeURIComponent(wopiurl);
// urlsrc - the URL from discovery xml that we access for the particular
// document; we add various parameters to that.
// The discovery is available at
// https://<loolwsd-server>:9980/hosting/discovery
var urlsrc = $('li[data-id='+ fileId.replace(/_.*/, '') +']>a').attr('urlsrc') +
"WOPISrc=" + wopisrc +
"&title=" + encodeURIComponent(title) +
"&closebutton=1" +
"&permission=readonly";
// access_token - must be passed via a form post
var access_token = encodeURIComponent(result.token);
// form to post the access token for WOPISrc
var form = '<form id="loleafletform_viewer" name="loleafletform_viewer" target="loleafletframe_viewer" action="' + urlsrc + '" method="post">' +
'<input name="access_token" value="' + access_token + '" type="hidden"/></form>';
// iframe that contains the Collabora Online Viewer
var frame = '<iframe id="loleafletframe_viewer" name= "loleafletframe_viewer" style="width:100%;height:100%;position:absolute;"/>';
$('#revViewer').append(form);
$('#revViewer').append(frame);
// handler for the 'Close' button - we have enabled it via closebutton=1
$('#loleafletframe_viewer').load(function(){
window.addEventListener('message', function(e){
if (e.data === 'close') {
documentsMain.onCloseViewer();
}
});
});
// submit that
$('#loleafletform_viewer').submit();
documentsMain.isViewerMode = true;
});
},
addRevision: function(fileId, version, relativeTimestamp, documentPath) {
var formattedTimestamp = OC.Util.formatDate(parseInt(version) * 1000);
var fileName = documentsMain.fileName.substring(0, documentsMain.fileName.indexOf('.'));
var downloadUrl;
if (version === 0) {
formattedTimestamp = t('richdocuments', 'Latest revision');
downloadUrl = OC.generateUrl('apps/files/download'+ documentPath);
fileId = fileId.replace(/_.*/, '');
} else {
downloadUrl = OC.generateUrl('apps/files_versions/download.php?file={file}&revision={revision}',
{file: documentPath, revision: version});
fileId = fileId + '_' + version;
}
var revHistoryItemTemplate = Handlebars.compile(documentsMain.UI.revHistoryItemTemplate);
var html = revHistoryItemTemplate({
downloadUrl: downloadUrl,
downloadIconUrl: OC.imagePath('core', 'actions/download'),
relativeTimestamp: relativeTimestamp,
formattedTimestamp: formattedTimestamp
});
html = $(html).attr('data-fileid', fileId)
.attr('data-title', fileName + ' - ' + formattedTimestamp);
$('#revisionsContainer ul').append(html);
},
fetchAndFillRevisions: function(documentPath) {
// fill #rev-history with file versions
$.get(OC.generateUrl('apps/files_versions/ajax/getVersions.php?source={documentPath}&start={start}',
{ documentPath: documentPath, start: documentsMain.UI.revisionsStart }),
function(result) {
for(var key in result.data.versions) {
documentsMain.UI.addRevision(documentsMain.fileId,
result.data.versions[key].version,
result.data.versions[key].humanReadableTimestamp,
documentPath);
}
// owncloud only gives 5 version at max in one go
documentsMain.UI.revisionsStart += 5;
if (result.data.endReached) {
// Remove 'More versions' button
$('#show-more-versions').addClass('hidden');
}
});
},
showRevHistory: function(documentPath) {
$(document.body).prepend(documentsMain.UI.viewContainer);
var revHistoryContainerTemplate = Handlebars.compile(documentsMain.UI.revHistoryContainerTemplate);
var revHistoryContainer = revHistoryContainerTemplate({
filename: documentsMain.fileName,
moreVersionsLabel: t('richdocuments', 'More versions...')
});
$(document.body).prepend(revHistoryContainer);
documentsMain.UI.revisionsStart = 0;
// append current document first
documentsMain.UI.addRevision(documentsMain.fileId, 0, t('richdocuments', 'Just now'), documentPath);
// add "Show more versions" button
$('#show-more-versions').click(function(e) {
e.preventDefault();
documentsMain.UI.fetchAndFillRevisions(documentPath);
});
// fake click to load first 5 versions
$('#show-more-versions').click();
// make these revisions clickable/attach functionality
$('#revisionsContainer').on('click', '.versionPreview', function(e) {
e.preventDefault();
documentsMain.UI.showViewer(e.currentTarget.parentElement.dataset.fileid,
e.currentTarget.parentElement.dataset.title);
// mark only current <li> as active
$(e.currentTarget.parentElement.parentElement).find('li').removeClass('active');
$(e.currentTarget.parentElement).addClass('active');
});
// fake click on first revision (i.e current revision)
$('#revisionsContainer li').first().find('.versionPreview').click();
},
showEditor : function(title){
if (documentsMain.isGuest){
// !Login page mess wih WebODF toolbars
@ -239,7 +401,7 @@ var documentsMain = {
// urlsrc - the URL from discovery xml that we access for the particular
// document; we add various parameters to that.
// The discovery is available at
// https://<loolwsd-server>:9980/hosting/discovery
// https://<loolwsd-server>:9980/hosting/discovery
var urlsrc = $('li[data-id='+ documentsMain.fileId +']>a').attr('urlsrc') +
"WOPISrc=" + wopisrc +
"&title=" + encodeURIComponent(title) +
@ -263,8 +425,13 @@ var documentsMain = {
$('#loleafletframe').load(function(){
documentsMain.overlay.documentOverlay('hide');
window.addEventListener('message', function(e){
if (documentsMain.isViewerMode) {
return;
}
if (e.data === 'close') {
documentsMain.onClose();
} else if (e.data === 'rev-history') {
documentsMain.UI.showRevHistory($('li[data-id=' + documentsMain.fileId + ']>a').attr('original-title'));
}
});
});
@ -612,7 +779,7 @@ var documentsMain = {
},
onClose: function() {
onClose: function(force) {
if (!documentsMain.isEditorMode){
return;
}
@ -625,13 +792,21 @@ var documentsMain = {
documentsMain.UI.hideEditor();
$('#ocToolbar').remove();
if (documentsMain.returnToDir) {
if (!force && documentsMain.returnToDir) {
window.location = OC.generateUrl('apps/files?dir={dir}', {dir: documentsMain.returnToDir});
} else {
documentsMain.show();
}
},
onCloseViewer: function() {
$('#revPanelContainer').remove();
$('#revViewerContainer').remove();
documentsMain.isViewerMode = false;
$('#loleafletframe').focus();
},
onTerminate: function(){
var url = '';
if (documentsMain.isGuest){

@ -29,20 +29,23 @@ class Wopi extends \OCA\Richdocuments\Db{
protected $tableName = '`*PREFIX*richdocuments_wopi`';
protected $insertStatement = 'INSERT INTO `*PREFIX*richdocuments_wopi` (`owner_uid`, `editor_uid`, `fileid`, `path`, `token`, `expiry`)
VALUES (?, ?, ?, ?, ?, ?)';
protected $insertStatement = 'INSERT INTO `*PREFIX*richdocuments_wopi` (`owner_uid`, `editor_uid`, `fileid`, `version`, `path`, `token`, `expiry`)
VALUES (?, ?, ?, ?, ?, ?, ?)';
protected $loadStatement = 'SELECT * FROM `*PREFIX*richdocuments_wopi` WHERE `token`= ?';
/*
* Given a fileId, generates a token
* Given a fileId and version, generates a token
* and stores in the database.
* version is 0 if current version of fileId is requested, otherwise
* its the version number as stored by files_version app
* Returns the token.
*/
public function generateFileToken($fileId){
public function generateFileToken($fileId, $version){
// Get the FS view of the current user.
$view = \OC\Files\Filesystem::getView();
// Get the virtual path (if the file is shared).
$path = $view->getPath($fileId);
@ -67,13 +70,14 @@ class Wopi extends \OCA\Richdocuments\Db{
\OCP\Security\ISecureRandom::CHAR_LOWER . \OCP\Security\ISecureRandom::CHAR_UPPER .
\OCP\Security\ISecureRandom::CHAR_DIGITS);
\OC::$server->getLogger()->debug('Issuing token for {editor} file {fileId} owned by {owner}, path {path}: {token}',
[ 'owner' => $owner, 'editor' => $editor, 'fileId' => $fileId, 'path' => $path, 'token' => $token ]);
\OC::$server->getLogger()->debug('Issuing token for {editor} file {fileId}, version {version} owned by {owner}, path {path}: {token}',
[ 'owner' => $owner, 'editor' => $editor, 'fileId' => $fileId, 'version' => $version, 'path' => $path, 'token' => $token ]);
$wopi = new \OCA\Richdocuments\Db\Wopi([
$owner,
$editor,
$fileId,
$version,
$path,
$token,
time() + self::TOKEN_LIFETIME_SECONDS
@ -91,7 +95,7 @@ class Wopi extends \OCA\Richdocuments\Db{
* constructs and validates the path.
* Returns the path, if valid, else false.
*/
public function getPathForToken($fileId, $token){
public function getPathForToken($fileId, $version, $token){
$wopi = new Wopi();
$row = $wopi->loadBy('token', $token)->getData();
@ -110,7 +114,7 @@ class Wopi extends \OCA\Richdocuments\Db{
//$wopi->deleteBy('id', $row['id']);
//return false;
}
if ($row['fileid'] !== $fileId){
if ($row['fileid'] !== $fileId || $row['version'] !== $version){
// File unknown / user unauthorized (for the requested file).
http_response_code(404);
return false;

Loading…
Cancel
Save