Browse Source

Cleanup source code

Signed-off-by: Lukas Reschke <lukas@statuscode.ch>
tags/1.1.22
Lukas Reschke 3 years ago
parent
commit
75af90b4f1
No account linked to committer's email address

+ 46
- 0
appinfo/Application.php View File

@@ -0,0 +1,46 @@
<?php
/**
* @copyright Copyright (c) 2016 Lukas Reschke <lukas@statuscode.ch>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/

namespace OCA\Richdocuments\AppInfo;

use OC\AppFramework\Utility\TimeFactory;
use OCA\Richdocuments\WOPI\DiscoveryManager;
use OCP\AppFramework\App;
use OCP\AppFramework\IAppContainer;

class Application extends App {
public function __construct (array $urlParams = array()) {
parent::__construct('richdocuments', $urlParams);
$container = $this->getContainer();
$container->registerService(
DiscoveryManager::class,
function(IAppContainer $container) {
return new DiscoveryManager(
$container->getServer()->getHTTPClientService(),
$container->getServer()->getAppDataDir('richdocuments'),
$container->getServer()->getConfig(),
$container->getServer()->getL10N('richdocuments'),
new TimeFactory()
);
}
);
}
}

+ 0
- 94
appinfo/application.php View File

@@ -1,94 +0,0 @@
<?php
/**
* ownCloud - Richdocuments App
*
* @author Victor Dubiniuk
* @copyright 2014 Victor Dubiniuk victor.dubiniuk@gmail.com
*
* This file is licensed under the Affero General Public License version 3 or
* later.
*/

namespace OCA\Richdocuments\AppInfo;

use \OCP\AppFramework\App;

use \OCA\Richdocuments\Controller\UserController;
use \OCA\Richdocuments\Controller\SessionController;
use \OCA\Richdocuments\Controller\DocumentController;
use \OCA\Richdocuments\Controller\SettingsController;
use \OCA\Richdocuments\AppConfig;

class Application extends App {
public function __construct (array $urlParams = array()) {
parent::__construct('richdocuments', $urlParams);

$container = $this->getContainer();

/**
* Controllers
*/
$container->registerService('UserController', function($c) {
return new UserController(
$c->query('AppName'),
$c->query('Request')
);
});
$container->registerService('SessionController', function($c) {
return new SessionController(
$c->query('AppName'),
$c->query('Request'),
$c->query('Logger'),
$c->query('UserId')
);
});
$container->registerService('DocumentController', function($c) {
return new DocumentController(
$c->query('AppName'),
$c->query('Request'),
$c->query('CoreConfig'),
$c->query('AppConfig'),
$c->query('L10N'),
$c->query('UserId'),
$c->query('ICacheFactory'),
$c->query('Logger')
);
});
$container->registerService('SettingsController', function($c) {
return new SettingsController(
$c->query('AppName'),
$c->query('Request'),
$c->query('L10N'),
$c->query('AppConfig'),
$c->query('UserId')
);
});

$container->registerService('AppConfig', function($c) {
return new AppConfig(
$c->query('CoreConfig')
);
});

/**
* Core
*/
$container->registerService('Logger', function($c) {
return $c->query('ServerContainer')->getLogger();
});
$container->registerService('CoreConfig', function($c) {
return $c->query('ServerContainer')->getConfig();
});
$container->registerService('L10N', function($c) {
return $c->query('ServerContainer')->getL10N($c->query('AppName'));
});
$container->registerService('UserId', function($c) {
$user = $c->query('ServerContainer')->getUserSession()->getUser();
$uid = is_null($user) ? '' : $user->getUID();
return $uid;
});
$container->registerService('ICacheFactory', function($c) {
return $c->query('ServerContainer')->getMemCacheFactory();
});
}
}

+ 0
- 214
appinfo/database.xml View File

@@ -4,57 +4,6 @@
<create>true</create>
<overwrite>false</overwrite>
<charset>utf8</charset>
<table>
<name>*dbprefix*richdocuments_session</name>
<declaration>

<field>
<name>es_id</name>
<type>text</type>
<notnull>true</notnull>
<length>64</length>
<comments>Editing session id</comments>
</field>
<field>
<name>genesis_url</name>
<type>text</type>
<length>512</length>
<comments>Relative to owner documents storage /welcome.odt</comments>
</field>
<field>
<name>genesis_hash</name>
<type>text</type>
<length>128</length>
<notnull>true</notnull>
<comments>To be sure the genesis did not change</comments>
</field>
<field>
<name>file_id</name>
<type>text</type>
<length>512</length>
<comments>Relative to storage e.g. /welcome.odt</comments>
</field>
<field>
<name>owner</name>
<type>text</type>
<notnull>true</notnull>
<length>64</length>
<comments>oC user who created the session</comments>
</field>

<index>
<name>richdocuments_session_ei_idx</name>
<primary>true</primary>
<unique>true</unique>
<field>
<name>es_id</name>
<sorting>ascending</sorting>
</field>
</index>

</declaration>
</table>

<table>
<name>*dbprefix*richdocuments_member</name>
<declaration>
@@ -68,13 +17,6 @@
<length>4</length>
<comments>Unique per user and session</comments>
</field>
<field>
<name>es_id</name>
<type>text</type>
<notnull>true</notnull>
<length>64</length>
<comments>Related editing session id</comments>
</field>
<field>
<name>uid</name>
<type>text</type>
@@ -118,162 +60,6 @@
</declaration>
</table>

<table>
<name>*dbprefix*richdocuments_op</name>
<declaration>

<field>
<name>seq</name>
<type>integer</type>
<notnull>true</notnull>
<autoincrement>1</autoincrement>
<unsigned>true</unsigned>
<length>4</length>
<comments>Sequence number</comments>
</field>
<field>
<name>es_id</name>
<type>text</type>
<notnull>true</notnull>
<length>64</length>
<comments>Editing session id</comments>
</field>
<field>
<name>member</name>
<type>integer</type>
<default>1</default>
<notnull>true</notnull>
<unsigned>true</unsigned>
<length>4</length>
<comments>User and time specific</comments>
</field>
<field>
<name>optype</name>
<type>text</type>
<notnull>false</notnull>
<length>64</length>
<comments>Operation type</comments>
</field>
<field>
<name>opspec</name>
<type>clob</type>
<notnull>false</notnull>
<comments>json-string</comments>
</field>

<index>
<name>richdocs_seq_pKey</name>
<primary>true</primary>
<field>
<name>seq</name>
<sorting>ascending</sorting>
</field>
</index>
<index>
<name>richdocuments_op_eis_idx</name>
<unique>true</unique>
<field>
<name>es_id</name>
<sorting>ascending</sorting>
</field>
<field>
<name>seq</name>
<sorting>ascending</sorting>
</field>
</index>

</declaration>
</table>
<table>
<name>*dbprefix*richdocuments_invite</name>
<declaration>

<field>
<name>es_id</name>
<type>text</type>
<notnull>true</notnull>
<length>64</length>
<comments>Related editing session id</comments>
</field>
<field>
<name>uid</name>
<type>text</type>
<length>64</length>
</field>
<field>
<name>status</name>
<type>integer</type>
<default>0</default>
<length>2</length>
</field>
<field>
<name>sent_on</name>
<type>integer</type>
<default></default>
<notnull>false</notnull>
<unsigned>true</unsigned>
<length>4</length>
</field>

</declaration>
</table>
<table>
<name>*dbprefix*richdocuments_revisions</name>
<declaration>

<field>
<name>es_id</name>
<type>text</type>
<notnull>true</notnull>
<length>64</length>
<comments>Related editing session id</comments>
</field>
<field>
<name>seq_head</name>
<type>integer</type>
<notnull>true</notnull>
<unsigned>true</unsigned>
<length>4</length>
<comments>Sequence head number</comments>
</field>
<field>
<name>member_id</name>
<type>integer</type>
<notnull>true</notnull>
<unsigned>true</unsigned>
<length>4</length>
<comments>the member that saved the revision</comments>
</field>
<field>
<name>file_id</name>
<type>text</type>
<length>512</length>
<comments>Relative to storage e.g. /welcome.odt</comments>
</field>
<field>
<name>save_hash</name>
<type>text</type>
<length>128</length>
<notnull>true</notnull>
<comments>used to lookup revision in documents folder of member, eg hash.odt</comments>
</field>

<index>
<name>richdocuments_rev_eis_idx</name>
<unique>true</unique>
<field>
<name>es_id</name>
<sorting>ascending</sorting>
</field>
<field>
<name>seq_head</name>
<sorting>ascending</sorting>
</field>
</index>

</declaration>
</table>

<table>
<name>*dbprefix*richdocuments_wopi</name>
<declaration>

+ 1
- 1
appinfo/info.xml View File

@@ -5,7 +5,7 @@
<description>Collabora Online allows you to to work with all kinds of office documents directly in your browser. This application requires Collabora Cloudsuite to be installed on one of your servers, please read the documentation to learn more about that.</description>
<summary>Edit office documents directly in your browser.</summary>
<licence>AGPL</licence>
<version>1.1.17</version>
<version>1.1.18</version>
<author>Collabora Productivity based on work of Frank Karlitschek, Victor Dubiniuk</author>
<bugs>https://github.com/nextcloud/richdocuments/issues</bugs>
<repository type="git">https://github.com/nextcloud/richdocuments.git</repository>

+ 10
- 25
appinfo/routes.php View File

@@ -9,38 +9,23 @@
* later.
*/

namespace OCA\Richdocuments;
namespace OCA\Richdocuments\AppInfo;

$application = new \OCA\Richdocuments\AppInfo\Application();
$application->registerRoutes($this, [
return [
'routes' => [
//users
['name' => 'user#rename', 'url' => 'ajax/user/rename', 'verb' => 'POST'],
['name' => 'user#disconnectUser', 'url' => 'ajax/user/disconnect', 'verb' => 'POST'],
['name' => 'user#disconnectGuest', 'url' => 'ajax/user/disconnectGuest', 'verb' => 'POST'],
//session
['name' => 'session#join', 'url' => 'session/user/join/{fileId}', 'verb' => 'POST'],
['name' => 'session#poll', 'url' => 'session/user/poll', 'verb' => 'POST'],
['name' => 'session#save', 'url' => 'session/user/save', 'verb' => 'POST'],
['name' => 'session#joinAsGuest', 'url' => 'session/guest/join/{token}', 'verb' => 'POST'],
['name' => 'session#pollAsGuest', 'url' => 'session/guest/poll/{token}', 'verb' => 'POST'],
['name' => 'session#saveAsGuest', 'url' => 'session/guest/save/{token}', 'verb' => 'POST'],
//documents
['name' => 'document#index', 'url' => 'index', 'verb' => 'GET'],
['name' => 'document#create', 'url' => 'ajax/documents/create', 'verb' => 'POST'],
['name' => 'document#serve', 'url' => 'ajax/genesis/{esId}', 'verb' => 'GET'],
['name' => 'document#rename', 'url' => 'ajax/documents/rename/{fileId}', 'verb' => 'POST'],
['name' => 'document#get', 'url' => 'ajax/documents/get/{fileId}', 'verb' => 'GET'],
['name' => 'document#listAll', 'url' => 'ajax/documents/list', 'verb' => 'GET'],
['name' => 'document#download', 'url' => 'ajax/download.php', 'verb' => 'GET'],
//documents - for WOPI access
['name' => 'document#wopiGetToken', 'url' => 'wopi/token/{fileId}', 'verb' => 'GET'],
['name' => 'document#wopiCheckFileInfo', 'url' => 'wopi/files/{fileId}', 'verb' => 'GET'],
['name' => 'document#wopiGetFile', 'url' => 'wopi/files/{fileId}/contents', 'verb' => 'GET'],
['name' => 'document#wopiPutFile', 'url' => 'wopi/files/{fileId}/contents', 'verb' => 'POST'],

// WOPI access
['name' => 'wopi#getToken', 'url' => 'wopi/token/{fileId}', 'verb' => 'GET'],
['name' => 'wopi#checkFileInfo', 'url' => 'wopi/files/{fileId}', 'verb' => 'GET'],
['name' => 'wopi#getFile', 'url' => 'wopi/files/{fileId}/contents', 'verb' => 'GET'],
['name' => 'wopi#putFile', 'url' => 'wopi/files/{fileId}/contents', 'verb' => 'POST'],

//settings
['name' => 'settings#setSettings', 'url' => 'ajax/admin.php', 'verb' => 'POST'],
['name' => 'settings#getSupportedMimes', 'url' => 'ajax/mimes.php', 'verb' => 'GET'],
['name' => 'settings#getSettings', 'url' => 'ajax/settings.php', 'verb' => 'GET'],
]
]);
];

+ 0
- 753
controller/documentcontroller.php View File

@@ -1,753 +0,0 @@
<?php
/**
* ownCloud - Richdocuments App
*
* @author Victor Dubiniuk
* @copyright 2014 Victor Dubiniuk victor.dubiniuk@gmail.com
*
* This file is licensed under the Affero General Public License version 3 or
* later.
*/

namespace OCA\Richdocuments\Controller;

use \OCP\AppFramework\Controller;
use \OCP\IRequest;
use \OCP\IConfig;
use \OCP\IL10N;
use \OCP\AppFramework\Http\ContentSecurityPolicy;
use \OCP\AppFramework\Http\JSONResponse;
use \OCP\AppFramework\Http\TemplateResponse;

use \OCA\Richdocuments\AppConfig;
use \OCA\Richdocuments\Db;
use \OCA\Richdocuments\Helper;
use \OCA\Richdocuments\Storage;
use \OCA\Richdocuments\Download;
use \OCA\Richdocuments\DownloadResponse;
use \OCA\Richdocuments\File;
use \OCA\Richdocuments\Genesis;
use \OC\Files\View;
use \OCP\ICacheFactory;
use \OCP\ILogger;

class ResponseException extends \Exception {
private $hint;

public function __construct($description, $hint = '') {
parent::__construct($description);
$this->hint = $hint;
}

public function getHint() {
return $this->hint;
}
}

class DocumentController extends Controller {

private $uid;
private $l10n;
private $settings;
private $appConfig;
private $cache;
private $logger;
const ODT_TEMPLATE_PATH = '/assets/odttemplate.odt';

public function __construct($appName, IRequest $request, IConfig $settings, AppConfig $appConfig, IL10N $l10n, $uid, ICacheFactory $cache, ILogger $logger){
parent::__construct($appName, $request);
$this->uid = $uid;
$this->l10n = $l10n;
$this->settings = $settings;
$this->appConfig = $appConfig;
$this->cache = $cache->create($appName);
$this->logger = $logger;
}

/**
* @param \SimpleXMLElement $discovery
* @param string $mimetype
*/
private function getWopiSrcUrl($discovery_parsed, $mimetype) {
if(is_null($discovery_parsed) || $discovery_parsed == false) {
return null;
}

$result = $discovery_parsed->xpath(sprintf('/wopi-discovery/net-zone/app[@name=\'%s\']/action', $mimetype));
if ($result && count($result) > 0) {
return array(
'urlsrc' => (string)$result[0]['urlsrc'],
'action' => (string)$result[0]['name']
);
}

return null;
}

/**
* Log the user with given $userid.
* This function should only be used from public controller methods where no
* existing session exists, for example, when loolwsd is directly calling a
* public method with its own access token. After validating the access
* token, and retrieving the correct user with help of access token, it can
* be set as current user with help of this method.
*
* @param string $userid
*/
private function loginUser($userid) {
\OC_Util::tearDownFS();

$users = \OC::$server->getUserManager()->search($userid, 1, 0);
if (count($users) > 0) {
$user = array_shift($users);
if (strcasecmp($user->getUID(), $userid) === 0) {
// clear the existing sessions, if any
\OC::$server->getSession()->close();

// initialize a dummy memory session
$session = new \OC\Session\Memory('');
// wrap it
$cryptoWrapper = \OC::$server->getSessionCryptoWrapper();
$session = $cryptoWrapper->wrapSession($session);
// set our session
\OC::$server->setSession($session);

\OC::$server->getUserSession()->setUser($user);
}
}

\OC_Util::setupFS();
}

/**
* Log out the current user
* This is helpful when we are artifically logged in as someone
*/
private function logoutUser() {
\OC_Util::tearDownFS();

\OC::$server->getSession()->close();
}

private function responseError($message, $hint = ''){
$errors = array('errors' => array(array('error' => $message, 'hint' => $hint)));
$response = new TemplateResponse('', 'error', $errors, 'error');
return $response;
}

/**
* Return the original wopi url or test wopi url
*/
private function getWopiUrl($tester) {
$wopiurl = '';
if ($tester) {
$wopiurl = $this->appConfig->getAppValue('test_wopi_url');
} else {
$wopiurl = $this->appConfig->getAppValue('wopi_url');
}

return $wopiurl;
}

/**
* Return true if the currently logged in user is a tester.
* This depends on whether current user is the member of one of the groups
* mentioned in settings (test_server_groups)
*/
private function isTester() {
$tester = false;

$user = \OC::$server->getUserSession()->getUser()->getUID();
$testgroups = array_filter(explode('|', $this->appConfig->getAppValue('test_server_groups')));
\OC::$server->getLogger()->debug('Testgroups are {testgroups}', [
'app' => $this->appName,
'testgroups' => $testgroups
]);
foreach ($testgroups as $testgroup) {
$test = \OC::$server->getGroupManager()->get($testgroup);
if ($test !== null && sizeof($test->searchUsers($user)) > 0) {
\OC::$server->getLogger()->debug('User {user} found in {group}', [
'app' => $this->appName,
'user' => $user,
'group' => $testgroup
]);

$tester = true;
break;
}
}

return $tester;
}

/** Return the content of discovery.xml - either from cache, or download it.
*/
private function getDiscovery(){
\OC::$server->getLogger()->debug('getDiscovery(): Getting discovery.xml from the cache.');

$tester = $this->isTester();
$wopiRemote = $this->getWopiUrl($tester);
$discoveryKey = 'discovery.xml';
if ($tester) {
$discoveryKey = 'discovery.xml_test';
}
// Provides access to information about the capabilities of a WOPI client
// and the mechanisms for invoking those abilities through URIs.
$wopiDiscovery = $wopiRemote . '/hosting/discovery';

// Read the memcached value (if the memcache is installed)
$discovery = $this->cache->get($discoveryKey);

if (is_null($discovery)) {
$contact_admin = $this->l10n->t('Please contact the "%s" administrator.', array($wopiRemote));

try {
$wopiClient = \OC::$server->getHTTPClientService()->newClient();
$discovery = $wopiClient->get($wopiDiscovery)->getBody();
}
catch (\Exception $e) {
$error_message = $e->getMessage();
if (preg_match('/^cURL error ([0-9]*):/', $error_message, $matches)) {
$admin_check = $this->l10n->t('Please ask your administrator to check the Collabora Online server setting. The exact error message was: ') . $error_message;

$curl_error = $matches[1];
switch ($curl_error) {
case '1':
throw new ResponseException($this->l10n->t('Collabora Online: The protocol specified in "%s" is not allowed.', array($wopiRemote)), $admin_check);
case '3':
throw new ResponseException($this->l10n->t('Collabora Online: Malformed URL "%s".', array($wopiRemote)), $admin_check);
case '6':
throw new ResponseException($this->l10n->t('Collabora Online: Cannot resolve the host "%s".', array($wopiRemote)), $admin_check);
case '7':
throw new ResponseException($this->l10n->t('Collabora Online: Cannot connect to the host "%s".', array($wopiRemote)), $admin_check);
case '60':
throw new ResponseException($this->l10n->t('Collabora Online: SSL certificate is not installed.'), $this->l10n->t('Please ask your administrator to add ca-chain.cert.pem to the ca-bundle.crt, for example "cat /etc/loolwsd/ca-chain.cert.pem >> <server-installation>/resources/config/ca-bundle.crt" . The exact error message was: ') . $error_message);
}
}
throw new ResponseException($this->l10n->t('Collabora Online unknown error: ') . $error_message, $contact_admin);
}

if (!$discovery) {
throw new ResponseException($this->l10n->t('Collabora Online: Unable to read discovery.xml from "%s".', array($wopiRemote)), $contact_admin);
}

\OC::$server->getLogger()->debug('Storing the discovery.xml under key ' . $discoveryKey . ' to the cache.');
$this->cache->set($discoveryKey, $discovery, 3600);
}

return $discovery;
}

/** Prepare document(s) structure
*/
private function prepareDocuments($rawDocuments){
$discovery_parsed = null;
try {
$discovery = $this->getDiscovery();

$loadEntities = libxml_disable_entity_loader(true);
$discovery_parsed = simplexml_load_string($discovery);
libxml_disable_entity_loader($loadEntities);

if ($discovery_parsed === false) {
$this->cache->remove('discovery.xml');
$wopiRemote = $this->getWopiUrl($this->isTester());

return array(
'status' => 'error',
'message' => $this->l10n->t('Collabora Online: discovery.xml from "%s" is not a well-formed XML string.', array($wopiRemote)),
'hint' => $this->l10n->t('Please contact the "%s" administrator.', array($wopiRemote))
);
}
}
catch (ResponseException $e) {
return array(
'status' => 'error',
'message' => $e->getMessage(),
'hint' => $e->getHint()
);
}

$fileIds = array();
$documents = array();
$lolang = strtolower(str_replace('_', '-', $this->settings->getUserValue($this->uid, 'core', 'lang', 'en')));
foreach ($rawDocuments as $key=>$document) {
if (is_object($document)){
$documents[] = $document->getData();
} else {
$documents[$key] = $document;
}
$documents[$key]['icon'] = preg_replace('/\.png$/', '.svg', \OCP\Template::mimetype_icon($document['mimetype']));
$documents[$key]['hasPreview'] = \OC::$server->getPreviewManager()->isMimeSupported($document['mimetype']);
$ret = $this->getWopiSrcUrl($discovery_parsed, $document['mimetype']);
$documents[$key]['urlsrc'] = $ret['urlsrc'];
$documents[$key]['action'] = $ret['action'];
$documents[$key]['lolang'] = $lolang;
$fileIds[] = $document['fileid'];
}

usort($documents, function($a, $b){
return @$b['mtime']-@$a['mtime'];
});

$session = new Db\Session();
$sessions = $session->getCollectionBy('file_id', $fileIds);

$members = array();
$member = new Db\Member();
foreach ($sessions as $session) {
$members[$session['es_id']] = $member->getActiveCollection($session['es_id']);
}

return array(
'status' => 'success', 'documents' => $documents,'sessions' => $sessions,'members' => $members
);
}

/**
* @NoAdminRequired
* @NoCSRFRequired
*/
public function index(){
$wopiRemote = $this->getWopiUrl($this->isTester());
if (($parts = parse_url($wopiRemote)) && isset($parts['scheme']) && isset($parts['host'])) {
$webSocketProtocol = "ws://";
if ($parts['scheme'] == "https") {
$webSocketProtocol = "wss://";
}
$webSocket = sprintf(
"%s%s%s",
$webSocketProtocol,
$parts['host'],
isset($parts['port']) ? ":" . $parts['port'] : "");
}
else {
return $this->responseError($this->l10n->t('Collabora Online: Invalid URL "%s".', array($wopiRemote)), $this->l10n->t('Please ask your administrator to check the Collabora Online server setting.'));
}

$user = \OC::$server->getUserSession()->getUser();
$usergroups = array_filter(\OC::$server->getGroupManager()->getUserGroupIds($user));
$usergroups = join('|', $usergroups);
\OC::$server->getLogger()->debug('User is in groups: {groups}', [ 'app' => $this->appName, 'groups' => $usergroups ]);

\OC::$server->getNavigationManager()->setActiveEntry( 'richdocuments_index' );
$maxUploadFilesize = \OCP\Util::maxUploadFilesize("/");
$response = new TemplateResponse('richdocuments', 'documents', [
'enable_previews' => $this->settings->getSystemValue('enable_previews', true),
'uploadMaxFilesize' => $maxUploadFilesize,
'uploadMaxHumanFilesize' => \OCP\Util::humanFileSize($maxUploadFilesize),
'allowShareWithLink' => $this->settings->getAppValue('core', 'shareapi_allow_links', 'yes'),
'wopi_url' => $webSocket,
'doc_format' => $this->appConfig->getAppValue('doc_format')
]);

$policy = new ContentSecurityPolicy();
$policy->addAllowedScriptDomain('\'self\' http://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js http://cdnjs.cloudflare.com/ajax/libs/jquery-mousewheel/3.1.12/jquery.mousewheel.min.js \'unsafe-eval\' ' . $wopiRemote);
/* frame-src is deprecated on Firefox, but Safari wants it! */
$policy->addAllowedFrameDomain('\'self\' http://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js http://cdnjs.cloudflare.com/ajax/libs/jquery-mousewheel/3.1.12/jquery.mousewheel.min.js \'unsafe-eval\' ' . $wopiRemote . ' blob:');
$policy->addAllowedChildSrcDomain('\'self\' http://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js http://cdnjs.cloudflare.com/ajax/libs/jquery-mousewheel/3.1.12/jquery.mousewheel.min.js \'unsafe-eval\' ' . $wopiRemote);
$policy->addAllowedConnectDomain($webSocket);
$policy->addAllowedImageDomain('*');
$policy->allowInlineScript(true);
$policy->addAllowedFontDomain('data:');
$response->setContentSecurityPolicy($policy);

return $response;
}

/**
* @NoAdminRequired
*/
public function create(){
$mimetype = $this->request->post['mimetype'];
$filename = $this->request->post['filename'];
$dir = $this->request->post['dir'];

$view = new View('/' . $this->uid . '/files');
if (!$dir){
$dir = '/';
}

$basename = $this->l10n->t('New Document.odt');
switch ($mimetype) {
case 'application/vnd.oasis.opendocument.spreadsheet':
$basename = $this->l10n->t('New Spreadsheet.ods');
break;
case 'application/vnd.oasis.opendocument.presentation':
$basename = $this->l10n->t('New Presentation.odp');
break;
case 'application/vnd.openxmlformats-officedocument.wordprocessingml.document':
$basename = $this->l10n->t('New Document.docx');
break;
case 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet':
$basename = $this->l10n->t('New Spreadsheet.xlsx');
break;
case 'application/vnd.openxmlformats-officedocument.presentationml.presentation':
$basename = $this->l10n->t('New Presentation.pptx');
break;
default:
// to be safe
$mimetype = 'application/vnd.oasis.opendocument.text';
break;
}

if (!$filename){
$path = Helper::getNewFileName($view, $dir . '/' . $basename);
} else {
$path = $dir . '/' . $filename;
}

$content = '';
if (class_exists('\OC\Files\Type\TemplateManager')){
$manager = \OC_Helper::getFileTemplateManager();
$content = $manager->getTemplate($mimetype);
}

if (!$content){
$content = file_get_contents(dirname(__DIR__) . self::ODT_TEMPLATE_PATH);
}

$discovery_parsed = null;
try {
$discovery = $this->getDiscovery();

$loadEntities = libxml_disable_entity_loader(true);
$discovery_parsed = simplexml_load_string($discovery);
libxml_disable_entity_loader($loadEntities);

if ($discovery_parsed === false) {
$this->cache->remove('discovery.xml');
$wopiRemote = $this->getWopiUrl($this->isTester());

return array(
'status' => 'error',
'message' => $this->l10n->t('Collabora Online: discovery.xml from "%s" is not a well-formed XML string.', array($wopiRemote)),
'hint' => $this->l10n->t('Please contact the "%s" administrator.', array($wopiRemote))
);
}
}
catch (ResponseException $e) {
return array(
'status' => 'error',
'message' => $e->getMessage(),
'hint' => $e->getHint()
);
}

if ($content && $view->file_put_contents($path, $content)){
$info = $view->getFileInfo($path);
$ret = $this->getWopiSrcUrl($discovery_parsed, $mimetype);
$response = array(
'status' => 'success',
'fileid' => $info['fileid'],
'urlsrc' => $ret['urlsrc'],
'action' => $ret['action'],
'lolang' => $this->settings->getUserValue($this->uid, 'core', 'lang', 'en'),
'data' => \OCA\Files\Helper::formatFileInfo($info)
);
} else {
$response = array(
'status' => 'error',
'message' => (string) $this->l10n->t('Can\'t create document')
);
}
return $response;
}

/**
* @NoAdminRequired
* Generates and returns an access token for a given fileId.
* Only for authenticated users!
*/
public function wopiGetToken($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 ]);

$view = \OC\Files\Filesystem::getView();
$path = $view->getPath($fileId);
$updatable = (bool)$view->isUpdatable($path);

// Check if the editor (user who is accessing) is in editable group
// UserCanWrite only if
// 1. No edit groups are set or
// 2. if they are set, it is in one of the edit groups
$editorUid = \OC::$server->getUserSession()->getUser()->getUID();
$editGroups = array_filter(explode('|', $this->appConfig->getAppValue('edit_groups')));
if ($updatable && count($editGroups) > 0) {
$updatable = false;
foreach($editGroups as $editGroup) {
$editorGroup = \OC::$server->getGroupManager()->get($editGroup);
if ($editorGroup !== null && sizeof($editorGroup->searchUsers($editorUid)) > 0) {
\OC::$server->getLogger()->debug("Editor {editor} is in edit group {group}", [
'app' => $this->appName,
'editor' => $editorUid,
'group' => $editGroup
]);
$updatable = true;
break;
}
}
}

// If token is for some versioned file
if ($version !== '0') {
\OC::$server->getLogger()->debug('setting updatable to false');
$updatable = false;
}

\OC::$server->getLogger()->debug('File with {fileid} has updatable set to {updatable}', [ 'app' => $this->appName, 'fileid' => $fileId, 'updatable' => $updatable ]);

$row = new Db\Wopi();
$serverHost = $this->request->getServerProtocol() . '://' . $this->request->getServerHost();
$token = $row->generateFileToken($fileId, $version, $updatable, $serverHost);

// Return the token.
return array(
'status' => 'success',
'token' => $token
);
}

/**
* @NoAdminRequired
* @NoCSRFRequired
* @PublicPage
* Returns general info about a file.
*/
public function wopiCheckFileInfo($fileId){
$token = $this->request->getParam('access_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, $version, $token);
if ($res == false || http_response_code() != 200)
{
return false;
}

// Login the user to see his mount locations
$this->loginUser($res['owner']);
$view = new \OC\Files\View('/' . $res['owner'] . '/files');
$info = $view->getFileInfo($res['path']);
$this->logoutUser();

if (!$info) {
http_response_code(404);
return false;
}

$editorName = \OC::$server->getUserManager()->get($res['editor'])->getDisplayName();
return array(
'BaseFileName' => $info['name'],
'Size' => $info['size'],
'Version' => $version,
'UserId' => $res['editor'],
'UserFriendlyName' => $editorName,
'UserCanWrite' => $res['canwrite'] ? true : false,
'PostMessageOrigin' => $res['server_host']
);
}

/**
* @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');

$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, $version, $token);
$ownerid = $res['owner'];

// Login the user to see his mount locations
$this->loginUser($ownerid);
$view = new \OC\Files\View('/' . $res['owner'] . '/files');
$info = $view->getFileInfo($res['path']);

if (!$info) {
http_response_code(404);
return false;
}

$filename = '';
// If some previous version is requested, fetch it from Files_Version app
if ($version !== '0') {
\OCP\JSON::checkAppEnabled('files_versions');

$filename = '/files_versions/' . $info['name'] . '.v' . $version;
} else {
$filename = '/files' . $res['path'];
}

$this->logoutUser();

return new DownloadResponse($this->request, $ownerid, $filename);
}

/**
* @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');

$arr = explode('_', $fileId, 2);
$version = '0';
if (count($arr) == 2) {
$fileId = $arr[0];
$version = $arr[1];
}

\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, $version, $token);
if (!$res['canwrite']) {
return array(
'status' => 'error',
'message' => 'Permission denied'
);
}

// Log-in as the user to regiser the change under her name.
$editorid = $res['editor'];
// This call is made from loolwsd, so we need to initialize the
// session before we can make the user who opened the document
// login. This is necessary to make activity app register the
// change made to this file under this user's (editorid) name.
$this->loginUser($editorid);

// Set up the filesystem view for the owner (where the file actually is).
$userid = $res['owner'];
$root = '/' . $userid . '/files';
$view = new \OC\Files\View($root);

// Read the contents of the file from the POST body and store.
$content = fopen('php://input', 'r');
\OC::$server->getLogger()->debug('Storing file {fileId} by {editor} owned by {owner}.', [ 'app' => $this->appName, 'fileId' => $fileId, 'editor' => $editorid, 'owner' => $userid ]);

// Setup the FS which is needed to emit hooks (versioning).
\OC_Util::tearDownFS();
\OC_Util::setupFS($userid);

$view->file_put_contents($res['path'], $content);

$this->logoutUser();

return array(
'status' => 'success'
);
}

/**
* @NoAdminRequired
* @PublicPage
* Process partial/complete file download
*/
public function serve($esId){
$session = new Db\Session();
$session->load($esId);

$filename = $session->getGenesisUrl() ? $session->getGenesisUrl() : '';
return new DownloadResponse($this->request, $session->getOwner(), $filename);
}

/**
* @NoAdminRequired
*/
public function download($path){
if (!$path){
$response = new JSONResponse();
$response->setStatus(Http::STATUS_BAD_REQUEST);
return $response;
}

$fullPath = '/files' . $path;
$fileInfo = \OC\Files\Filesystem::getFileInfo($path);
if ($fileInfo){
$file = new File($fileInfo->getId());
$genesis = new Genesis($file);
$fullPath = $genesis->getPath();
}
return new DownloadResponse($this->request, $this->uid, $fullPath);
}


/**
* @NoAdminRequired
*/
public function rename($fileId){
$name = $this->request->post['name'];

$view = \OC\Files\Filesystem::getView();
$path = $view->getPath($fileId);

if ($name && $view->is_file($path) && $view->isUpdatable($path)) {
$newPath = dirname($path) . '/' . $name;
if ($view->rename($path, $newPath)) {
return array('status' => 'success');
}
}
return array(
'status' => 'error',
'message' => (string) $this->l10n->t('You don\'t have permission to rename this document')
);
}

/**
* @NoAdminRequired
* Get file information about single document with fileId
*/
public function get($fileId){
$documents = array();
$documents[0] = Storage::getDocumentById($fileId);

return $this->prepareDocuments($documents);
}


/**
* @NoAdminRequired
* lists the documents the user has access to (including shared files, once the code in core has been fixed)
* also adds session and member info for these files
*/
public function listAll(){
return $this->prepareDocuments(Storage::getDocuments());
}
}

+ 0
- 314
controller/sessioncontroller.php View File

@@ -1,314 +0,0 @@
<?php
/**
* ownCloud - Richdocuments App
*
* @author Victor Dubiniuk
* @copyright 2014 Victor Dubiniuk victor.dubiniuk@gmail.com
*
* This file is licensed under the Affero General Public License version 3 or
* later.
*/

namespace OCA\Richdocuments\Controller;

use \OCP\AppFramework\Controller;
use \OCP\IRequest;
use \OCP\AppFramework\Http;
use \OCP\AppFramework\Http\JSONResponse;

use \OCA\Richdocuments\Db;
use \OCA\Richdocuments\File;
use \OCA\Richdocuments\Helper;
use OCA\Richdocuments\Filter;
use \OC\Files\View;

class BadRequestException extends \Exception {

protected $body = "";

public function setBody($body){
$this->body = $body;
}

public function getBody(){
return $this->body;
}
}

class SessionController extends Controller{

protected $uid;
protected $logger;
protected $shareToken;

public function __construct($appName, IRequest $request, $logger, $uid){
parent::__construct($appName, $request);
$this->uid = $uid;
$this->logger = $logger;
}

/**
* @NoAdminRequired
* @PublicPage
*/
public function joinAsGuest($token, $name){
$uid = substr($name, 0, 16);

try {
$file = File::getByShareToken($token);
if ($file->isPasswordProtected() && !$file->checkPassword('')){
throw new \Exception('Not authorized');
}

$response = array_merge(
Db\Session::start($uid, $file),
[ 'status'=>'success' ]
);
} catch (\Exception $e){
$this->logger->warning('Starting a session failed. Reason: ' . $e->getMessage(), ['app' => $this->appName]);
$response = [ 'status'=>'error' ];
}

return $response;
}

/**
* @NoAdminRequired
* @PublicPage
*/
public function pollAsGuest($command, $args){
$this->shareToken = $this->request->getParam('token');
return $this->poll($command, $args);
}

/**
* Store the document content to its origin
* @NoAdminRequired
* @PublicPage
*/
public function saveAsGuest(){
$this->shareToken = $this->request->getParam('token');
return $this->save();
}

/**
* @NoAdminRequired
*/
public function join($fileId){
try {
$view = \OC\Files\Filesystem::getView();
$path = $view->getPath($fileId);

$file = new File($fileId);
$response = Db\Session::start($this->uid, $file);

$response = array_merge(
$response,
[ 'status'=>'success' ]
);
} catch (\Exception $e){
$this->logger->warning('Starting a session failed. Reason: ' . $e->getMessage(), [ 'app' => $this->appName ]);
$response = [ 'status'=>'error' ];
}

return $response;
}

/**
* @NoAdminRequired
*/
public function poll($command, $args){
$response = new JSONResponse();

try{
$esId = isset($args['es_id']) ? $args['es_id'] : null;
$session = $this->loadSession($esId);

$memberId = isset($args['member_id']) ? $args['member_id'] : null;
$member = $this->loadMember($memberId);

$this->validateSession($session);

switch ($command){
case 'sync_ops':
$seqHead = (string) isset($args['seq_head']) ? $args['seq_head'] : null;
if (!is_null($seqHead)){
$ops = isset($args['client_ops']) ? $args['client_ops'] : [];

$op = new Db\Op();
$currentHead = $op->getHeadSeq($esId);

try {
$member->updateActivity($memberId);
} catch (\Exception $e){
//Db error. Not critical
}
$response->setData(
$session->syncOps($memberId, $currentHead, $seqHead, $ops)
);

$inactiveMembers = $member->updateByTimeout($esId);
foreach ($inactiveMembers as $inactive){
$op->removeCursor($esId, $inactive);
$op->removeMember($esId, $inactive);
}
} else {
// Error - no seq_head passed
throw new BadRequestException();
}

break;
default:
$ex = new BadRequestException();
$ex->setBody(
implode(',', $this->request->getParams())
);
throw $ex;
}
} catch (BadRequestException $e){
$response->setStatus(Http::STATUS_BAD_REQUEST);
$response->setData(
[ 'err' => 'bad request:[' . $e->getBody() . ']' ]
);
}
return $response;
}

/**
* Store the document content to its origin
* @NoAdminRequired
*/
public function save(){
$response = new JSONResponse();
try {
$esId = $this->request->server['HTTP_WEBODF_SESSION_ID'];
$session = $this->loadSession($esId);

$memberId = $this->request->server['HTTP_WEBODF_MEMBER_ID'];
$currentMember = $this->loadMember($memberId, $esId);

// Extra info for future usage
// $sessionRevision = $this->request->server['HTTP_WEBODF_SESSION_REVISION'];

//NB ouch! New document content is passed as an input stream content
$stream = fopen('php://input','r');
if (!$stream){
throw new \Exception('New content missing');
}
$content = stream_get_contents($stream);

try {
if ($currentMember->getIsGuest()){
$file = File::getByShareToken($currentMember->getToken());
} else {
$file = new File($session->getFileId());
}

$view = $file->getOwnerView(true);
$path = $file->getPath(true);
} catch (\Exception $e){
//File was deleted or unshared. We need to save content as new file anyway
//Sorry, but for guests it would be lost :(
if ($this->uid){
$view = new View('/' . $this->uid . '/files');

$dir = '/';
$path = Helper::getNewFileName($view, $dir . 'New Document.odt');
} else {
throw $e;
}
}

$member = new Db\Member();
$members = $member->getActiveCollection($esId);
$memberIds = array_map(
function($x){
return ($x['member_id']);
},
$members
);

// Active users except current user
$memberCount = count($memberIds) - 1;

if ($view->file_exists($path)){
$currentHash = $view->hash('sha1', $path, false);

if (!Helper::isVersionsEnabled() && $currentHash !== $session->getGenesisHash()){
// Original file was modified externally. Save to a new one
$path = Helper::getNewFileName($view, $path, '-conflict');
}

$mimetype = $view->getMimeType($path);
} else {
$mimetype = Storage::MIMETYPE_LIBREOFFICE_WORDPROCESSOR;
}

$data = Filter::write($content, $mimetype);

if ($view->file_put_contents($path, $data['content'])){
// Not a last user
if ($memberCount>0){
// Update genesis hash to prevent conflicts
$this->logger->debug('Update hash', [ 'app' => $this->appName ]);
$session->updateGenesisHash($esId, sha1($data['content']));
} else {
// Last user. Kill session data
Db\Session::cleanUp($esId);
}

$view->touch($path);
}
$response->setData(['status'=>'success']);
} catch (\Exception $e){
$response->setStatus(Http::STATUS_INTERNAL_SERVER_ERROR);
$response->setData([]);
$this->logger->warning('Saving failed. Reason:' . $e->getMessage(), [ 'app' => $this->appName ]);
}

return $response;
}

protected function validateSession($session){
try {
if (is_null($this->shareToken)) {
new File($session->getFileId());
} else {
File::getByShareToken($this->shareToken);
}
} catch (\Exception $e){
$this->logger->warning('Error. Session no longer exists. ' . $e->getMessage(), [ 'app' => $this->appName ]);
$ex = new BadRequestException();
$ex->setBody(
implode(',', $this->request->getParams())
);
throw $ex;
}
}

protected function loadSession($esId){
if (!$esId){
throw new \Exception('Session id can not be empty');
}

$session = new Db\Session();
$session->load($esId);
if (!$session->getEsId()){
throw new \Exception('Session does not exist');
}
return $session;
}

protected function loadMember($memberId, $expectedEsId = null){
if (!$memberId){
throw new \Exception('Member id can not be empty');
}
$member = new Db\Member();
$member->load($memberId);
//check if member belongs to the session
if (!is_null($expectedEsId) && $expectedEsId !== $member->getEsId()){
throw new \Exception($memberId . ' does not belong to session ' . $expectedEsId);
}
return $member;
}
}

+ 0
- 81
controller/usercontroller.php View File

@@ -1,81 +0,0 @@
<?php
/**
* ownCloud - Richdocuments App
*
* @author Victor Dubiniuk
* @copyright 2014 Victor Dubiniuk victor.dubiniuk@gmail.com
*
* This file is licensed under the Affero General Public License version 3 or
* later.
*/


namespace OCA\Richdocuments\Controller;

use \OCP\AppFramework\Controller;
use \OCP\IRequest;
use \OCP\AppFramework\Http\JSONResponse;

use \OCA\Richdocuments\Db;

class UserController extends Controller {
public function __construct($appName, IRequest $request){
parent::__construct($appName, $request);
}
/**
* @NoAdminRequired
*/
public function disconnectUser($memberId, $esId){
return $this->disconnect($memberId, $esId);
}
/**
* @NoAdminRequired
* @PublicPage
*/
public function disconnectGuest($memberId, $esId){
return $this->disconnect($memberId, $esId);
}
private function disconnect($memberId, $esId){
$member = new Db\Member();
$member->loadBy('member_id', $memberId);
if ($esId && $member->hasData()){
if ($member->getEsId() === $esId && $member->getStatus() == Db\Member::MEMBER_STATUS_ACTIVE){
$member->deactivate(array($memberId));
$op = new Db\Op();
$op->removeMember($esId, $memberId);
}
}
return array('status'=>'success');
}
/**
* @NoAdminRequired
* @PublicPage
* @param int $memberId
* @param string $name
*/
public function rename($memberId, $name){
$member = new Db\Member();
$member->load($memberId);

if ($member->getEsId()
&& $member->getStatus() == Db\Member::MEMBER_STATUS_ACTIVE
&& $member->getIsGuest()
){
$guestMark = Db\Member::getGuestPostfix();
if (substr($name, -strlen($guestMark)) !== $guestMark){
$name = $name . ' ' . $guestMark;
}
$op = new Db\Op();
$op->changeNick($member->getEsId(), $memberId, $name);
}
return array('status'=>'success');
}
}

+ 0
- 170
css/share.css View File

@@ -1,170 +0,0 @@
/* Copyright (c) 2011, Jan-Christoph Borchardt, http://jancborchardt.net
This file is licensed under the Affero General Public License version 3 or later.
See the COPYING-README file. */

#dropdown {
background: #eee;
border-bottom-left-radius: 3px;
border-bottom-right-radius: 3px;
box-shadow: 0 2px 3px rgba(50, 50, 50, .4);
display: block;
margin-right: 0;
position: absolute;
right: 0;
width: 420px;
z-index: 500;
padding: 16px;
}

@media only screen and (min-width: 768px) and (max-width: 990px) {
#dropdown {
/* this limits the dropdown to float below the sidebar for mid narrow screens */
left: 20px;
}
}

#dropdown.shareDropDown .unshare.icon-loading-small {
margin-top: 1px;
}

#dropdown.shareDropDown .shareWithLoading,
#dropdown.shareDropDown .linkShare .icon-loading-small {
display: inline-block !important;
padding-left: 10px;
}
#dropdown.shareDropDown .shareWithLoading {
position: relative;
right: 70px;
top: 2px;
}
#dropdown.shareDropDown .icon-loading-small.hidden {
display: none !important;
}

#dropdown .shareWithRemoteInfo {
padding: 11px 20px;
}

#dropdown .avatar {
margin-right: 8px;
display: inline-block;
overflow: hidden;
vertical-align: middle;
width: 32px;
height: 32px;
}

#shareWithList {
list-style-type:none;
padding:8px;
}

#shareWithList li {
padding-top: 10px;
padding-bottom: 10px;
font-weight: bold;
line-height: 21px;
white-space: normal;
}

#shareWithList .unshare img, #shareWithList .showCruds img {
vertical-align:text-bottom; /* properly align icons */
}

#shareWithList label input[type=checkbox]{
margin-left: 0;
position: relative;
}
#shareWithList .username{
padding-right: 8px;
white-space: nowrap;
text-overflow: ellipsis;
max-width: 254px;
display: inline-block;
overflow: hidden;
vertical-align: middle;
}
#shareWithList li label{
margin-right: 8px;
}
#dropdown label {
font-weight:400;
white-space: nowrap;
}

#dropdown input[type="checkbox"] {
margin:0 3px 0 8px;
vertical-align: middle;
}

a.showCruds {
display:inline;
opacity:.5;
}

a.unshare {
display:inline;
float:right;
opacity:.5;
padding:5px 0 0 5px !important;
margin-top:-5px;
}

#link {
border-top:1px solid #ddd;
padding-top:8px;
}

#dropdown input[type="text"],#dropdown input[type="password"] {
width: 86%;
margin-left: 7px;
}

#dropdown form {
font-size: 100%;
margin-left: 0;
margin-right: 0;
}

#linkText,#linkPass,#expiration {
display:none;
}

#link #showPassword img {
padding-left:5px;
width:12px;
}

.reshare,#link label,
#expiration label {
display: inline-block;
padding: 6px 4px;
}

a.showCruds:hover,a.unshare:hover {
opacity:1;
}

#defaultExpireMessage, /* fix expire message going out of box */
.reshare { /* fix shared by text going out of box */
white-space:normal;
}

#defaultExpireMessage { /* show message on new line */
display: block;
padding-left: 4px;
/* TODO: style the dropdown in a proper way - border-box, etc. */
width: 90%;
}

.ui-autocomplete { /* limit dropdown height to 4 1/2 entries */
max-height:103px;
overflow-y:auto;
overflow-x:hidden;
}

.notCreatable {
padding-left: 12px;
padding-top: 12px;
color: #999;
}

+ 0
- 104
js/admin.js View File

@@ -15,17 +15,6 @@ var documentsSettings = {
);
},

saveGroups: function(groups) {
var data = {
'edit_groups': groups
};

$.post(
OC.filePath('richdocuments', 'ajax', 'admin.php'),
data
);
},

saveDocFormat: function(format) {
$.post(
OC.filePath('richdocuments', 'ajax', 'admin.php'),
@@ -33,112 +22,19 @@ var documentsSettings = {
);
},

saveTestWopi: function(groups, server) {
var data = {
'test_wopi_url': server,
'test_server_groups': groups
};

OC.msg.startAction('#test-documents-admin-msg', t('richdocuments', 'Saving...'));
$.post(
OC.filePath('richdocuments', 'ajax', 'admin.php'),
data,
documentsSettings.afterSaveTestWopi
);
},

afterSaveTestWopi: function(response) {
$('#test_wopi_apply').attr('disabled', false);
OC.msg.finishedAction('#test-documents-admin-msg', response);
},

afterSave : function(response){
$('#wopi_apply').attr('disabled', false);
OC.msg.finishedAction('#documents-admin-msg', response);
},

initEditGroups: function() {
var groups = $('#edit_group_select').val();
if (groups !== '') {
OC.Settings.setupGroupsSelect($('#edit_group_select'));
$('.edit-groups-enable').attr('checked', 'checked');
} else {
$('.edit-groups-enable').attr('checked', null);
}
},

initTestWopiServer: function() {
var groups = $(document).find('#test_server_group_select').val();
var testserver = $(document).find('#test_wopi_url').val();

if (groups === '' || testserver === '') {
$('.test-server-enable').attr('checked', null);
} else {
OC.Settings.setupGroupsSelect($('#test_server_group_select'));
$('.test-server-enable').attr('checked', 'checked');
}
},

initialize: function() {
documentsSettings.initEditGroups();
documentsSettings.initTestWopiServer();

$('#wopi_apply').on('click', documentsSettings.save);

$(document).on('change', '.test-server-enable', function() {
var page = $(this).parent();
var $select = page.find('#test_server_group_select');
$select.val('');

page.find('#test-server-section').toggleClass('hidden', !this.checked);
if (this.checked) {
OC.Settings.setupGroupsSelect($select, {
placeholder: t('core', 'None')
});
} else {
$select.select2('destroy');
page.find('#test_wopi_url').val('');

documentsSettings.saveTestWopi('', '');
}
});

$(document).on('click', '#test_wopi_apply', function() {
var groups = $(this).parent().find('#test_server_group_select').val();
var testserver = $(this).parent().find('#test_wopi_url').val();

if (groups !== '' && testserver !== '') {
documentsSettings.saveTestWopi(groups, testserver);
} else {
OC.msg.finishedError('#test-documents-admin-msg', 'Both fields required');
}
});

$(document).on('change', '.doc-format-ooxml', function() {
var ooxml = this.checked;
documentsSettings.saveDocFormat(ooxml ? 'ooxml' : 'odf');
});

$(document).on('change', '#edit_group_select', function() {
var element = $(this).parent().find('input.edit-groups-enable');
var groups = $(this).val();
documentsSettings.saveGroups(groups);
});

$(document).on('change', '.edit-groups-enable', function() {
var $select = $(this).parent().find('#edit_group_select');
$select.val('');

if (this.checked) {
OC.Settings.setupGroupsSelect($select, {
placeholder: t('core', 'All')
});
} else {
$select.select2('destroy');
}

$select.change();
});
}
};


+ 108
- 262
js/documents.js View File

@@ -18,40 +18,11 @@ $.widget('oc.documentGrid', {
},

_load : function (fileId){
var that = this;
var url = 'apps/richdocuments/ajax/documents/list';
var dataObj = {};
if (fileId){
url = 'apps/richdocuments/ajax/documents/get/{fileId}';
dataObj = { fileId: fileId };
}

return $.getJSON(OC.generateUrl(url, dataObj))
.done(function (result) {
if (!result || result.status === 'error') {
documentsMain.loadError = true;
if (result && result.message) {
documentsMain.loadErrorMessage = result.message;
}
else {
documentsMain.loadErrorMessage = t('richdocuments', 'Failed to load the document, please contact your administrator.');
}

if (result && result.hint) {
documentsMain.loadErrorHint = result.hint;
}
}
else {
that.options.documents = result.documents;
that.options.sessions = result.sessions;
that.options.members = result.members;
documentsMain.urlsrc = result.documents[0].urlsrc;
documentsMain.fullPath = result.documents[0].path;
}
})
.fail(function(data){
console.log(t('richdocuments','Failed to load documents.'));
});
var url = OC.generateUrl('apps/richdocuments/wopi/token/{file_id}', {file_id: fileId});
$.get(
url,
documentsMain.initSession
);
},

_render : function (data){
@@ -152,39 +123,36 @@ var documentsMain = {
$('#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 = documentsMain.urlsrc +
"WOPISrc=" + wopisrc +
"&title=" + encodeURIComponent(title) +
"&lang=" + OC.getLocale() +
"&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);

// submit that
$('#loleafletform_viewer').submit();
documentsMain.isViewerMode = true;
});
// 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 = documentsMain.urlsrc +
"WOPISrc=" + wopisrc +
"&title=" + encodeURIComponent(title) +
"&lang=" + OC.getLocale() +
"&permission=readonly";

// access_token - must be passed via a form post
var access_token = encodeURIComponent(documentsMain.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);

// submit that
$('#loleafletform_viewer').submit();
documentsMain.isViewerMode = true;

// for closing revision mode
$('#revPanelHeader .closeButton').click(function(e) {
@@ -344,91 +312,81 @@ var documentsMain = {

$('title').text(title + ' - ' + documentsMain.UI.mainTitle);

$.get(OC.generateUrl('apps/richdocuments/wopi/token/{fileId}', { fileId: fileId }),
function (result) {
if (!result || result.status === 'error') {
if (result && result.message){
documentsMain.UI.notify(result.message);
}
documentsMain.onEditorShutdown(t('richdocuments', 'Failed to aquire access token. Please re-login and try again.'));
return;
}

// 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: documentsMain.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 = documentsMain.urlsrc +
"WOPISrc=" + wopisrc +
"&title=" + encodeURIComponent(title) +
"&lang=" + OC.getLocale() +
"&closebutton=1" +
"&revisionhistory=1";
if (!documentsMain.canEdit || action === "view") {
urlsrc += "&permission=readonly";
}
// 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: documentsMain.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 = documentsMain.urlsrc +
"WOPISrc=" + wopisrc +
"&title=" + encodeURIComponent(title) +
"&lang=" + OC.getLocale() +
"&closebutton=1" +
"&revisionhistory=1";
if (!documentsMain.canEdit || action === "view") {
urlsrc += "&permission=readonly";
}

// access_token - must be passed via a form post
var access_token = encodeURIComponent(result.token);
// access_token - must be passed via a form post
var access_token = encodeURIComponent(documentsMain.token);

// form to post the access token for WOPISrc
var form = '<form id="loleafletform" name="loleafletform" target="loleafletframe" action="' + urlsrc + '" method="post">' +
'<input name="access_token" value="' + access_token + '" type="hidden"/></form>';
// form to post the access token for WOPISrc
var form = '<form id="loleafletform" name="loleafletform" target="loleafletframe" action="' + urlsrc + '" method="post">' +
'<input name="access_token" value="' + access_token + '" type="hidden"/></form>';

// iframe that contains the Collabora Online
var frame = '<iframe id="loleafletframe" name= "loleafletframe" allowfullscreen style="width:100%;height:100%;position:absolute;" />';
// iframe that contains the Collabora Online
var frame = '<iframe id="loleafletframe" name= "loleafletframe" allowfullscreen style="width:100%;height:100%;position:absolute;" />';

$('#mainContainer').append(form);
$('#mainContainer').append(frame);
$('#mainContainer').append(form);
$('#mainContainer').append(frame);

// Listen for App_LoadingStatus as soon as possible
$('#loleafletframe').ready(function() {
var editorInitListener = function(e) {
var msg = JSON.parse(e.data);
if (msg.MessageId === 'App_LoadingStatus') {
window.removeEventListener('message', editorInitListener, false);
}
};
window.addEventListener('message', editorInitListener, false);
});
// Listen for App_LoadingStatus as soon as possible
$('#loleafletframe').ready(function() {
var editorInitListener = function(e) {