diff --git a/appinfo/database.xml b/appinfo/database.xml
index 443a4f54..7d7c9fce 100644
--- a/appinfo/database.xml
+++ b/appinfo/database.xml
@@ -274,4 +274,53 @@
+
+ *dbprefix*richdocuments_wopi
+
+
+ id
+ integer
+ true
+ 1
+ true
+ 4
+ Unique per token
+
+
+ uid
+ text
+ 64
+ UserId - a textual user identifier (unique?)
+
+
+ fileid
+ integer
+ true
+ 4
+ The unique ID of the file authorized
+
+
+ path
+ text
+ true
+ 512
+ Relative to storage e.g. /welcome.odt
+
+
+ token
+ text
+
+ true
+ 32
+ File access token
+
+
+ expiry
+ integer
+ true
+ 4
+ Expiration time of the token
+
+
+
diff --git a/appinfo/info.xml b/appinfo/info.xml
index 5d9b74a5..4db1815e 100644
--- a/appinfo/info.xml
+++ b/appinfo/info.xml
@@ -4,7 +4,7 @@
Collabora Online Development Edition
An ownCloud app to work with office documents
AGPL
- 0.12.0
+ 0.13.0
Collabora Productivity based on work of Frank Karlitschek, Victor Dubiniuk
https://www.collaboraoffice.com/
git://gerrit.libreoffice.org/online.git
diff --git a/appinfo/routes.php b/appinfo/routes.php
index fbcba020..e80c48b0 100644
--- a/appinfo/routes.php
+++ b/appinfo/routes.php
@@ -36,6 +36,10 @@ $application->registerRoutes($this, [
['name' => 'document#localLoad', 'url' => 'load/{fileId}', 'verb' => 'POST'],
['name' => 'document#localSave', 'url' => 'save/{fileId}', 'verb' => 'POST'],
['name' => 'document#localClose', 'url' => 'close/{fileId}', 'verb' => 'POST'],
+ //documents - for WOPI access
+ ['name' => 'document#wopiGetToken', 'url' => 'wopi/token/{fileId}', 'verb' => 'GET'],
+ ['name' => 'document#wopiGetFile', 'url' => 'wopi/files/{fileId}/contents', 'verb' => 'GET'],
+ ['name' => 'document#wopiPutFile', 'url' => 'wopi/files/{fileId}/contents', 'verb' => 'POST'],
//settings
['name' => 'settings#savePersonal', 'url' => 'ajax/personal.php', 'verb' => 'POST'],
['name' => 'settings#setUnstable', 'url' => 'ajax/config/unstable', 'verb' => 'POST'],
diff --git a/controller/documentcontroller.php b/controller/documentcontroller.php
index 2033457a..9d33fe72 100644
--- a/controller/documentcontroller.php
+++ b/controller/documentcontroller.php
@@ -265,6 +265,70 @@ class DocumentController extends Controller{
);
}
+ /**
+ * Generates and returns an access token for a given fileId.
+ * Only for authenticated users!
+ */
+ public function wopiGetToken($fileId){
+ \OC::$server->getLogger()->debug('Generating WOPI Token for file {fileId}.', [ 'app' => $this->appName, 'fileId' => $fileId ]);
+
+ $row = new Db\Wopi();
+ $token = $row->generateFileToken($fileId);
+
+ // Return the token.
+ return array(
+ 'status' => 'success',
+ 'token' => $token
+ );
+ }
+
+ /**
+ * @NoAdminRequired
+ * @NoCSRFRequired
+ * @PublicPage
+ * Given an access token and a fileId, returns the contents of the file.
+ * Expects a valid token in access_token parameter.
+ */
+ 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 ]);
+
+ $row = new Db\Wopi();
+ $row->loadBy('token', $token);
+
+ //TODO: Support X-WOPIMaxExpectedSize header.
+ $res = $row->getPathForToken($fileId, $token);
+ return new DownloadResponse($this->request, $res['user'], $res['path']);
+ }
+
+ /**
+ * @NoAdminRequired
+ * @NoCSRFRequired
+ * @PublicPage
+ * Given an access token and a fileId, replaces the files with the request body.
+ * Expects a valid token in access_token parameter.
+ */
+ 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 ]);
+
+ $row = new Db\Wopi();
+ $row->loadBy('token', $token);
+
+ $res = $row->getPathForToken($token);
+ $view = new \OC\Files\View('/' . $res['user'] . '/');
+
+ // Read the contents of the file from the POST body and store.
+ $content = file_get_contents('php://input');
+ $view->file_put_contents($res['path'], $content);
+
+ return array(
+ 'status' => 'success'
+ );
+ }
+
/**
* @NoAdminRequired
* @PublicPage
diff --git a/js/documents.js b/js/documents.js
index 5b8d16eb..a9ea2ebd 100644
--- a/js/documents.js
+++ b/js/documents.js
@@ -180,27 +180,38 @@ var documentsMain = {
$('title').text(title + ' - ' + documentsMain.UI.mainTitle);
- // TODO. wopiurl = get from discovery xml
- var wopiurl = $('#wopi-url').val() + '/loleaflet/dist/loleaflet.html';
- var wopisrc = documentsMain.url;
- var action = wopiurl + '?' + wopisrc;
- var token = oc_requesttoken;
+ $.get(OC.generateUrl('apps/richdocuments/wopi/token/{fileId}', { fileId: documentsMain.fileId }),
+ function (result) {
+ if (!result || result.status === 'error') {
+ if (result && result.message){
+ documentsMain.IU.notify(result.message);
+ }
+ documentsMain.onEditorShutdown(t('richdocuments', 'Failed to aquire access token. Please re-login and try again.'));
+ return;
+ }
- var form = '';
- var frame = '';
+ //TODO: Get WOPISrc from the discovery XML.
+ var url = OC.generateUrl('apps/richdocuments/wopi/files/{file_id}/contents?access_token={token}',
+ {file_id: documentsMain.fileId, token: encodeURIComponent(result.token)});
+ documentsMain.url = window.location.protocol + '//' + window.location.host + url;
+
+ var viewer = window.location.protocol + '//' + window.location.host + '/loleaflet/dist/loleaflet.html?' +
+ 'file_path=' + encodeURIComponent(documentsMain.url) +
+ '&host=' + 'ws://' + window.location.hostname + ':9980' +
+ '&permission=' + 'view' +
+ '×tamp=' + '';
+
+ var frame = '';
+ $('#mainContainer').append(frame);
+ });
- $('#mainContainer').append(form);
- $('#mainContainer').append(frame);
documentsMain.overlay.documentOverlay('hide');
$('#loleafletframe').load(function(){
- // avoid Blocked a frame with origin different domains
-
- /*var iframe = $('#loleafletframe').contents();
+ var iframe = $('#loleafletframe').contents();
iframe.find('#tb_toolbar-up_item_close').click(function() {
documentsMain.onClose();
- });*/
- /*var frameWindow = $('#loleafletframe')[0].contentWindow;
+ });
+ var frameWindow = $('#loleafletframe')[0].contentWindow;
(function() {
cloudSuiteOnClick = frameWindow.onClick;
frameWindow.onClick = function() {
@@ -209,9 +220,8 @@ var documentsMain = {
cloudSuiteOnClick.apply(this, arguments);
frameWindow.map.options.doc = documentsMain.url;
};
- })();*/
+ })();
});
- $('#loleafletform').submit();
},
hideEditor : function(){
@@ -506,11 +516,6 @@ var documentsMain = {
},
loadDocument: function() {
- // Provides access to information about a file and allows
- // for file-level operations.
- // HTTP://server/<...>/wopi*/files/
- var url = window.location.protocol + '//' + window.location.host + OC.generateUrl('apps/documents/wopi/files/{file_id}', {file_id: documentsMain.fileId}, false);
- documentsMain.url = 'WOPISrc=' + encodeURIComponent(url);
documentsMain.UI.showEditor(documentsMain.fileName);
},
diff --git a/lib/db/wopi.php b/lib/db/wopi.php
new file mode 100644
index 00000000..306204d3
--- /dev/null
+++ b/lib/db/wopi.php
@@ -0,0 +1,101 @@
+getPath($fileId);
+
+ if (!$view->is_file($path)) {
+ throw new \Exception('Invalid fileId.');
+ }
+
+ $token = \OC::$server->getSecureRandom()->getMediumStrengthGenerator()->generate(32,
+ \OCP\Security\ISecureRandom::CHAR_LOWER . \OCP\Security\ISecureRandom::CHAR_UPPER .
+ \OCP\Security\ISecureRandom::CHAR_DIGITS);
+
+ \OC::$server->getLogger()->debug('Issuing token for {user} file {fileId}: {token}',
+ [ 'user' => $user, 'fileId' => $fileId, 'token' => $token ]);
+
+ $wopi = new \OCA\Richdocuments\Db\Wopi([
+ $user,
+ $fileId,
+ $path,
+ $token,
+ time() + self::TOKEN_LIFETIME_SECONDS
+ ]);
+
+ if (!$wopi->insert()){
+ throw new \Exception('Failed to add wopi token into database');
+ }
+
+ return $token;
+ }
+
+ /*
+ * Given a token, validates it and
+ * constructs and validates the path.
+ * Returns the path, if valid, else false.
+ */
+ public function getPathForToken($fileId, $token){
+
+ $wopi = new Wopi();
+ $row = $wopi->loadBy('token', $token)->getData();
+ \OC::$server->getLogger()->debug('Loaded WOPI Token record: {row}.', [ 'row' => $row ]);
+
+ //TODO: validate.
+ if ($row['expiry'] > time() || $row['fileid'] !== $fileId){
+ // Expired token!
+ //$wopi->deleteBy('id', $row['id']);
+ //return false;
+ }
+
+ $user = $row['uid'];
+ $view = new \OC\Files\View('/' . $user . '/');
+ $path = $row['path'];
+
+ if (!$view->is_file($path)) {
+ throw new \Exception('Invalid file path.');
+ }
+
+ return array('user' => $user, 'path' => $path);
+ }
+}