path
text
diff --git a/controller/documentcontroller.php b/controller/documentcontroller.php
index e5f49d76..61d92d2e 100644
--- a/controller/documentcontroller.php
+++ b/controller/documentcontroller.php
@@ -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);
diff --git a/css/style.css b/css/style.css
index ccdcad83..51c7cecf 100644
--- a/css/style.css
+++ b/css/style.css
@@ -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{
diff --git a/js/documents.js b/js/documents.js
index b24d12cf..c81b27ff 100644
--- a/js/documents.js
+++ b/js/documents.js
@@ -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 : '' +
'
',
+ viewContainer: '',
+
+ revHistoryContainerTemplate: '' +
+ '' +
+ '
' +
+ '
' +
+ '
',
+
+ revHistoryItemTemplate: '' +
+ '' +
+ '{{formattedTimestamp}}' +
+ '' +
+ '',
+
/* 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($(''));
+ }
+
+ $.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://: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 = '';
+
+ // iframe that contains the Collabora Online Viewer
+ var frame = '';
+
+ $('#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 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://:9980/hosting/discovery
+ // https://: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){
diff --git a/lib/db/wopi.php b/lib/db/wopi.php
index 01fade90..423e5038 100644
--- a/lib/db/wopi.php
+++ b/lib/db/wopi.php
@@ -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;