Merge branch 'soon'

pull/1/head
ansuz 5 years ago
commit 1200834800

@ -9,7 +9,7 @@ www/common/onlyoffice/sdkjs
www/common/onlyoffice/web-apps www/common/onlyoffice/web-apps
www/common/onlyoffice/x2t www/common/onlyoffice/x2t
www/common/onlyoffice/v1 www/common/onlyoffice/v1
www/common/onlyoffice/v2 www/common/onlyoffice/v2*
server.js server.js
www/common/old-media-tag.js www/common/old-media-tag.js

@ -0,0 +1,49 @@
/* jshint esversion: 6 */
/* global process */
const Nacl = require('tweetnacl/nacl-fast');
// XXX npm "os" and "child_process"
// TODO if this process is using too much CPU, we can use "cluster" to add load balancing to this code
//console.log('New child process', process.pid);
process.on('message', function (data) {
//console.log('In process', process.pid);
//console.log(+new Date(), "Message received by subprocess");
if (!data || !data.key || !data.msg || !data.txid) {
return void process.send({
error:'E_INVAL'
});
}
const txid = data.txid;
var signedMsg;
try {
signedMsg = Nacl.util.decodeBase64(data.msg);
} catch (e) {
return void process.send({
txid: txid,
error: 'E_BAD_MESSAGE',
});
}
var validateKey;
try {
validateKey = Nacl.util.decodeBase64(data.key);
} catch (e) {
return void process.send({
txid: txid,
error:'E_BADKEY'
});
}
// validate the message
const validated = Nacl.sign.open(signedMsg, validateKey);
if (!validated) {
return void process.send({
txid: txid,
error:'FAILED'
});
}
process.send({
txid: txid,
});
});

@ -3,6 +3,7 @@
const Core = module.exports; const Core = module.exports;
const Util = require("../common-util"); const Util = require("../common-util");
const escapeKeyCharacters = Util.escapeKeyCharacters; const escapeKeyCharacters = Util.escapeKeyCharacters;
//const { fork } = require('child_process');
/* Use Nacl for checking signatures of messages */ /* Use Nacl for checking signatures of messages */
const Nacl = require("tweetnacl/nacl-fast"); const Nacl = require("tweetnacl/nacl-fast");

@ -76,6 +76,8 @@ module.exports.create = function (config, cb) {
domain: config.domain domain: config.domain
}; };
HK.initializeValidationWorkers(Env);
(function () { (function () {
var pes = config.premiumUploadSize; var pes = config.premiumUploadSize;
if (!isNaN(pes) && pes >= Env.maxUploadSize) { if (!isNaN(pes) && pes >= Env.maxUploadSize) {

@ -6,6 +6,8 @@ const nThen = require('nthen');
const Util = require("./common-util"); const Util = require("./common-util");
const MetaRPC = require("./commands/metadata"); const MetaRPC = require("./commands/metadata");
const Nacl = require('tweetnacl/nacl-fast'); const Nacl = require('tweetnacl/nacl-fast');
const { fork } = require('child_process');
const numCPUs = require('os').cpus().length;
const now = function () { return (new Date()).getTime(); }; const now = function () { return (new Date()).getTime(); };
const ONE_DAY = 1000 * 60 * 60 * 24; // one day in milliseconds const ONE_DAY = 1000 * 60 * 60 * 24; // one day in milliseconds
@ -921,7 +923,68 @@ HK.onDirectMessage = function (Env, Server, seq, userId, json) {
* adds timestamps to incoming messages * adds timestamps to incoming messages
* writes messages to the store * writes messages to the store
*/ */
HK.initializeValidationWorkers = function (Env) {
if (typeof(Env.validateMessage) !== 'undefined') {
return void console.error("validation workers are already initialized");
}
// Create our workers
const workers = [];
for (let i = 0; i < numCPUs; i++) {
workers.push(fork('lib/check-signature.js'));
}
const response = Util.response();
var initWorker = function (worker) {
worker.on('message', function (res) {
if (!res || !res.txid) { return; }
//console.log(+new Date(), "Received verification response");
response.handle(res.txid, [res.error]);
});
// Spawn a new process in one ends
worker.on('exit', function () {
// XXX make sure it's dead?
var idx = workers.indexOf(worker);
if (idx !== -1) {
workers.splice(idx, 1);
}
// Spawn a new one
var w = fork('lib/check-signature.js');
workers.push(w);
initWorker(w);
});
};
workers.forEach(initWorker);
var nextWorker = 0;
Env.validateMessage = function (signedMsg, key, _cb) {
// let's be paranoid about asynchrony and only calling back once..
var cb = Util.once(Util.mkAsync(_cb));
var txid = Util.uid();
// expect a response within 15s
response.expect(txid, cb, 15000);
nextWorker = (nextWorker + 1) % workers.length;
if (workers.length === 0 || typeof(workers[nextWorker].send) !== 'function') {
console.error(workers);
throw new Error("INVALID_WORKERS");
}
// Send the request
workers[nextWorker].send({
txid: txid,
msg: signedMsg,
key: key,
});
};
};
HK.onChannelMessage = function (Env, Server, channel, msgStruct) { HK.onChannelMessage = function (Env, Server, channel, msgStruct) {
//console.log(+new Date(), "onChannelMessage");
const Log = Env.Log; const Log = Env.Log;
// TODO our usage of 'channel' here looks prone to errors // TODO our usage of 'channel' here looks prone to errors
@ -962,20 +1025,21 @@ HK.onChannelMessage = function (Env, Server, channel, msgStruct) {
let signedMsg = (isCp) ? msgStruct[4].replace(CHECKPOINT_PATTERN, '') : msgStruct[4]; let signedMsg = (isCp) ? msgStruct[4].replace(CHECKPOINT_PATTERN, '') : msgStruct[4];
// convert the message from a base64 string into a Uint8Array // convert the message from a base64 string into a Uint8Array
// FIXME this can fail and the client won't notice //const txid = Util.uid();
signedMsg = Nacl.util.decodeBase64(signedMsg);
// FIXME this can blow up // Listen for messages
// TODO check that that won't cause any problems other than not being able to append... //console.log(+new Date(), "Send verification request");
const validateKey = Nacl.util.decodeBase64(metadata.validateKey); Env.validateMessage(signedMsg, metadata.validateKey, w(function (err) {
// validate the message // no errors means success
const validated = Nacl.sign.open(signedMsg, validateKey); if (!err) { return; }
if (!validated) { // validation can fail in multiple ways
// don't go any further if the message fails validation if (err === 'FAILED') {
w.abort(); // we log this case, but not others for some reason
Log.info("HK_SIGNED_MESSAGE_REJECTED", 'Channel '+channel.id); Log.info("HK_SIGNED_MESSAGE_REJECTED", 'Channel '+channel.id);
return;
} }
// always abort if there was an error...
return void w.abort();
}));
}).nThen(function () { }).nThen(function () {
// do checkpoint stuff... // do checkpoint stuff...
@ -1002,7 +1066,9 @@ HK.onChannelMessage = function (Env, Server, channel, msgStruct) {
msgStruct.push(now()); msgStruct.push(now());
// storeMessage // storeMessage
//console.log(+new Date(), "Storing message");
storeMessage(Env, channel, JSON.stringify(msgStruct), isCp, getHash(msgStruct[4], Log)); storeMessage(Env, channel, JSON.stringify(msgStruct), isCp, getHash(msgStruct[4], Log));
//console.log(+new Date(), "Message stored");
}); });
}; };

@ -380,6 +380,7 @@ define([
}; };
dialog.getButtons = function (buttons, onClose) { dialog.getButtons = function (buttons, onClose) {
if (!buttons) { return; }
if (!Array.isArray(buttons)) { return void console.error('Not an array'); } if (!Array.isArray(buttons)) { return void console.error('Not an array'); }
if (!buttons.length) { return; } if (!buttons.length) { return; }
var navs = []; var navs = [];
@ -458,6 +459,7 @@ define([
setTimeout(function () { setTimeout(function () {
Notifier.notify(); Notifier.notify();
}); });
return frame;
}; };
UI.alert = function (msg, cb, opt) { UI.alert = function (msg, cb, opt) {

@ -241,9 +241,13 @@ define([
cpIndex: 0 cpIndex: 0
}; };
var getEditor = function () {
return window.frames[0].editor || window.frames[0].editorCell;
};
var getContent = function () { var getContent = function () {
try { try {
return window.frames[0].editor.asc_nativeGetFile(); return getEditor().asc_nativeGetFile();
} catch (e) { } catch (e) {
console.error(e); console.error(e);
return; return;
@ -256,7 +260,7 @@ define([
// loadable by users joining after the checkpoint // loadable by users joining after the checkpoint
var fixSheets = function () { var fixSheets = function () {
try { try {
var editor = window.frames[0].editor; var editor = getEditor();
// if we are not in the sheet app // if we are not in the sheet app
// we should not call this code // we should not call this code
if (typeof editor.GetSheets === 'undefined') { return; } if (typeof editor.GetSheets === 'undefined') { return; }
@ -274,6 +278,7 @@ define([
}; };
var onUploaded = function (ev, data, err) { var onUploaded = function (ev, data, err) {
content.saveLock = undefined;
if (err) { if (err) {
console.error(err); console.error(err);
return void UI.alert(Messages.oo_saveError); return void UI.alert(Messages.oo_saveError);
@ -285,7 +290,6 @@ define([
index: ev.index index: ev.index
}; };
oldHashes = JSON.parse(JSON.stringify(content.hashes)); oldHashes = JSON.parse(JSON.stringify(content.hashes));
content.saveLock = undefined;
// If this is a migration, set the new version // If this is a migration, set the new version
if (APP.migrate) { if (APP.migrate) {
delete content.migration; delete content.migration;
@ -293,7 +297,6 @@ define([
} }
APP.onLocal(); APP.onLocal();
APP.realtime.onSettle(function () { APP.realtime.onSettle(function () {
fixSheets();
UI.log(Messages.saved); UI.log(Messages.saved);
APP.realtime.onSettle(function () { APP.realtime.onSettle(function () {
if (APP.migrate) { if (APP.migrate) {
@ -325,6 +328,9 @@ define([
sframeChan.query('Q_OO_SAVE', data, function (err) { sframeChan.query('Q_OO_SAVE', data, function (err) {
onUploaded(ev, data, err); onUploaded(ev, data, err);
}); });
},
onError: function (err) {
onUploaded(null, null, err);
} }
}; };
APP.FM = common.createFileManager(fmConfig); APP.FM = common.createFileManager(fmConfig);
@ -338,29 +344,25 @@ define([
hash: ooChannel.lastHash, hash: ooChannel.lastHash,
index: ooChannel.cpIndex index: ooChannel.cpIndex
}; };
fixSheets();
APP.FM.handleFile(blob, data); APP.FM.handleFile(blob, data);
}; };
var makeCheckpoint = function (force) { var makeCheckpoint = function (force) {
if (!common.isLoggedIn()) { return; } if (!common.isLoggedIn()) { return; }
var locked = content.saveLock; var locked = content.saveLock;
var lastCp = getLastCp();
var needCp = force || ooChannel.cpIndex % CHECKPOINT_INTERVAL === 0 ||
(ooChannel.cpIndex - lastCp.index) > CHECKPOINT_INTERVAL;
if (!needCp) { return; }
if (!locked || !isUserOnline(locked) || force) { if (!locked || !isUserOnline(locked) || force) {
content.saveLock = myOOId; content.saveLock = myOOId;
APP.onLocal(); APP.onLocal();
APP.realtime.onSettle(function () { APP.realtime.onSettle(function () {
saveToServer(); saveToServer();
}); });
return;
} }
// The save is locked by someone else. If no new checkpoint is created
// in the next 20 to 40 secondes and the lock is kept by the same user,
// force the lock and make a checkpoint.
var saved = stringify(content.hashes);
var to = 20000 + (Math.random() * 20000);
setTimeout(function () {
if (stringify(content.hashes) === saved && locked === content.saveLock) {
makeCheckpoint(force);
}
}, to);
}; };
var restoreLastCp = function () { var restoreLastCp = function () {
content.saveLock = myOOId; content.saveLock = myOOId;
@ -374,6 +376,23 @@ define([
}); });
}); });
}; };
// Add a timeout to check if a checkpoint was correctly saved by the locking user
// and "unlock the sheet" or "make a checkpoint" if needed
var cpTo;
var checkCheckpoint = function () {
clearTimeout(cpTo);
var saved = stringify(content.hashes);
var locked = content.saveLock;
var to = 20000 + (Math.random() * 20000);
cpTo = setTimeout(function () {
// If no checkpoint was added and the same user still has the lock
// then make a checkpoint if needed (cp interval)
if (stringify(content.hashes) === saved && locked === content.saveLock) {
content.saveLock = undefined;
makeCheckpoint();
}
}, to);
};
var openRtChannel = function (cb) { var openRtChannel = function (cb) {
@ -512,6 +531,9 @@ define([
delete content.locks[id]; delete content.locks[id];
} }
}); });
if (content.saveLock && !isUserOnline(content.saveLock)) {
delete content.saveLock;
}
}; };
var handleAuth = function (obj, send) { var handleAuth = function (obj, send) {
@ -576,7 +598,22 @@ define([
}; };
// Add a lock // Add a lock
var isLockedModal = {
content: UI.dialog.customModal(h('div.cp-oo-x2tXls', [
h('span.fa.fa-spin.fa-spinner'),
h('span', Messages.oo_isLocked)
]))
};
var handleLock = function (obj, send) { var handleLock = function (obj, send) {
if (content.saveLock) {
if (!isLockedModal.modal) {
isLockedModal.modal = UI.openCustomModal(isLockedModal.content);
}
setTimeout(function () {
handleLock(obj, send);
}, 50);
return;
}
content.locks = content.locks || {}; content.locks = content.locks || {};
// Send the lock to other users // Send the lock to other users
var msg = { var msg = {
@ -591,13 +628,27 @@ define([
deleteOfflineLocks(); deleteOfflineLocks();
// Prepare callback // Prepare callback
if (cpNfInner) { if (cpNfInner) {
var onPatchSent = function () { var onPatchSent = function (again) {
cpNfInner.offPatchSent(onPatchSent); if (!again) { cpNfInner.offPatchSent(onPatchSent); }
// Answer to our onlyoffice // Answer to our onlyoffice
if (!content.saveLock) {
if (isLockedModal.modal) {
isLockedModal.modal.closeModal();
delete isLockedModal.modal;
$('#cp-app-oo-editor > iframe')[0].contentWindow.focus();
}
send({ send({
type: "getLock", type: "getLock",
locks: getLock() locks: getLock()
}); });
} else {
if (!isLockedModal.modal) {
isLockedModal.modal = UI.openCustomModal(isLockedModal.content);
}
setTimeout(function () {
onPatchSent(true);
}, 50);
}
}; };
cpNfInner.onPatchSent(onPatchSent); cpNfInner.onPatchSent(onPatchSent);
} }
@ -674,11 +725,7 @@ define([
ooChannel.cpIndex++; ooChannel.cpIndex++;
ooChannel.lastHash = hash; ooChannel.lastHash = hash;
// Check if a checkpoint is needed // Check if a checkpoint is needed
var lastCp = getLastCp();
if (common.isLoggedIn() && (ooChannel.cpIndex % CHECKPOINT_INTERVAL === 0 ||
(ooChannel.cpIndex - lastCp.index) > CHECKPOINT_INTERVAL)) {
makeCheckpoint(); makeCheckpoint();
}
// Remove my lock // Remove my lock
delete content.locks[getId()]; delete content.locks[getId()];
oldLocks = JSON.parse(JSON.stringify(content.locks)); oldLocks = JSON.parse(JSON.stringify(content.locks));
@ -1231,7 +1278,7 @@ define([
UI.removeModals(); UI.removeModals();
UI.confirm(Messages.oo_uploaded, function (yes) { UI.confirm(Messages.oo_uploaded, function (yes) {
try { try {
window.frames[0].editor.setViewModeDisconnect(); getEditor().setViewModeDisconnect();
} catch (e) {} } catch (e) {}
if (!yes) { return; } if (!yes) { return; }
common.gotoURL(); common.gotoURL();
@ -1428,7 +1475,7 @@ define([
if (window.CP_DEV_MODE) { if (window.CP_DEV_MODE) {
var $save = common.createButton('save', true, {}, function () { var $save = common.createButton('save', true, {}, function () {
saveToServer(); makeCheckpoint(true);
}); });
$save.appendTo($rightside); $save.appendTo($rightside);
} }
@ -1516,7 +1563,7 @@ define([
Title.updateTitle(Title.defaultTitle); Title.updateTitle(Title.defaultTitle);
} }
var version = 'v2/'; var version = 'v2a/';
// Old version detected: use the old OO and start the migration if we can // Old version detected: use the old OO and start the migration if we can
if (privateData.ooForceVersion) { if (privateData.ooForceVersion) {
if (privateData.ooForceVersion === "1") { if (privateData.ooForceVersion === "1") {
@ -1536,6 +1583,16 @@ define([
} }
} }
// If the sheet is locked by an offline user, remove it
if (content && content.saveLock && !isUserOnline(content.saveLock)) {
content.saveLock = undefined;
APP.onLocal();
} else if (content && content.saveLock) {
// If someone is currently creating a checkpoint (and locking the sheet),
// make sure it will end (maybe you'll have to make the checkpoint yourself)
checkCheckpoint();
}
var s = h('script', { var s = h('script', {
type:'text/javascript', type:'text/javascript',
src: '/common/onlyoffice/'+version+'web-apps/apps/api/documents/api.js' src: '/common/onlyoffice/'+version+'web-apps/apps/api/documents/api.js'
@ -1565,6 +1622,7 @@ define([
}; };
var reloadPopup = false; var reloadPopup = false;
config.onRemote = function () { config.onRemote = function () {
if (initializing) { return; } if (initializing) { return; }
var userDoc = APP.realtime.getUserDoc(); var userDoc = APP.realtime.getUserDoc();
@ -1573,14 +1631,23 @@ define([
metadataMgr.updateMetadata(json.metadata); metadataMgr.updateMetadata(json.metadata);
} }
var wasLocked = content.saveLock;
var wasMigrating = content.migration; var wasMigrating = content.migration;
content = json.content; content = json.content;
if (content.saveLock && wasLocked !== content.saveLock) {
// Someone new is creating a checkpoint: fix the sheets ids
fixSheets();
// If the checkpoint is not saved in 20s to 40s, do it ourselves
checkCheckpoint();
}
if (content.hashes) { if (content.hashes) {
var latest = getLastCp(true); var latest = getLastCp(true);
var newLatest = getLastCp(); var newLatest = getLastCp();
if (newLatest.index > latest.index) { if (newLatest.index > latest.index) {
fixSheets();
sframeChan.query('Q_OO_SAVE', { sframeChan.query('Q_OO_SAVE', {
url: newLatest.file url: newLatest.file
}, function () { }); }, function () { });

File diff suppressed because one or more lines are too long

@ -1,17 +0,0 @@
plus.png
x:6 y:6
plus_2x.png
x:12 y:12
plus_copy.png
x:6 y:12
plus_copy_2x.png
x:12 y:25
text_copy.png
x:2 y:11
text_copy_2x.png
x:5 y:23

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 102 KiB

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

Before

Width:  |  Height:  |  Size: 131 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 163 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 540 KiB

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

File diff suppressed because one or more lines are too long

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

Before

Width:  |  Height:  |  Size: 384 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 128 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 362 KiB

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

Before

Width:  |  Height:  |  Size: 704 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 837 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1001 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 870 B

@ -1,346 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>ONLYOFFICE Documents</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=IE8"/>
<meta name="description" content="" />
<meta name="keywords" content="" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-touch-fullscreen" content="yes">
<style type="text/css">
html {
height: 100%;
}
body {
height: 100%;
margin: 0;
padding: 0;
overflow: hidden;
}
#wrap {
position:absolute;
left:0;
top:0;
right:0;
bottom:0;
}
</style>
</head>
<body>
<div id="wrap">
<div id="placeholder"></div>
</div>
<script type="text/javascript" src="api.js"></script>
<script>
(function() {
// Url parameters
var urlParams = getUrlParams(),
cfg = getEditorConfig(urlParams),
doc = getDocumentData(urlParams);
// Document Editor
var docEditor = new DocsAPI.DocEditor('placeholder', {
type: urlParams['type'],
width: '100%',
height: '100%',
documentType: urlParams['doctype'] || 'text',
document: doc,
editorConfig: cfg,
events: {
'onReady': onDocEditorReady,
'onDocumentStateChange': onDocumentStateChange,
'onRequestEditRights': onRequestEditRights,
'onRequestHistory': onRequestHistory,
'onRequestHistoryData': onRequestHistoryData,
'onRequestEmailAddresses': onRequestEmailAddresses,
'onRequestStartMailMerge': onRequestStartMailMerge,
'onRequestHistoryClose': onRequestHistoryClose,
'onError': onError
}
});
// Document Editor event handlers
function onRequestEmailAddresses() {
docEditor.setEmailAddresses({emailAddresses: ['aaa@mail.ru'], createEmailAccountUrl: 'http://ya.ru'});
}
function onRequestHistory() {
docEditor.refreshHistory({
'currentVersion': 3,
'history': [
{
'user': {
id: '8952d4ee-e8a5-42bf-86f0-6cd77801ec15',
name: 'Татьяна Щербакова'
},
'changes': null,
'created': '1/18/2015 6:38 PM',
'version': 1,
'versionGroup': 1,
'key': 'wyX9AwRq_677SWKjhfk='
},
{
'user': {
id: '8952d4ee-e8a5-42bf-86f0-6cd77801ec15',
name: 'Татьяна Щербакова'
},
'changes': [
{
'user': {
id: '8952d4ee-e8a5-42bf-86f0-6cd77801ec15',
name: 'Татьяна Щербакова'
},
'created': '1/19/2015 6:30 PM'
},
{
'user': {
'id': '8952d4ee-e8a5-42bf-11f0-6cd77801ec15',
'name': 'Александр Трофимов'
},
'created': '1/19/2015 6:32 PM'
},
{
'user': {
id: '8952d4ee-e8a5-42bf-86f0-6cd77801ec15',
name: 'Татьяна Щербакова'
},
'created': '1/19/2015 6:38 PM'
}
],
'created': '2/19/2015 6:38 PM',
'version': 2,
'versionGroup': 1,
'key': 'wyX9AwRq_677SWKjhfk='
},
{
'user': {
id: '895255ee-e8a5-42bf-86f0-6cd77801ec15',
name: 'Me'
},
'changes': null,
'created': '2/21/2015 6:38 PM',
'version': 3,
'versionGroup': 2,
'key': 'wyX9AwRq_677SWKjhfk='
},
{
'user': {
id: '8952d4ee-e8a5-42bf-11f0-6cd77801ec15',
name: 'Александр Трофимов'
},
'changes': null,
'created': '2/22/2015 6:37 PM',
'version': 4,
'versionGroup': 3,
'key': 'wyX9AwRq_677SWKjhfk='
},
{
'user': {
id: '8952d4ee-e8a5-42bf-11f0-6cd33801ec15',
name: 'Леонид Орлов'
},
'changes': null,
'created': '2/24/2015 6:29 PM',
'version': 5,
'versionGroup': 3,
'key': 'wyX9AwRq_677SWKjhfk='
}]
});
}
function onRequestHistoryData(revision) {
docEditor.setHistoryData(
{
'version': revision.data,
'url': 'http://isa2',
'urlDiff': 'http://isa2',
'changesUrl': 'http://isa2'
}
);
}
function onRequestStartMailMerge() {
docEditor.processMailMerge(true, 'some error message');
}
function onRequestHistoryClose() {
// reload page
}
function onDocEditorReady(event) {
if (event.target) {
//console.log('Ready! Editor: ', event.target);
}
}
function onDocumentStateChange(event) {
var isModified = event.data;
//console.log(isModified);
}
function onRequestEditRights(event) {
// occurs whenever the user tryes to enter edit mode
docEditor.applyEditRights(true, "Someone is editing this document right now. Please try again later.");
}
function onError(event) {
// critical error happened
// examine event.data.errorCode and event.data.errorDescription for details
}
function onDownloadAs(event) {
// return url of downloaded doc
// console.log(event.data);
}
// helpers
function getUrlParams() {
var e,
a = /\+/g, // Regex for replacing addition symbol with a space
r = /([^&=]+)=?([^&]*)/g,
d = function (s) { return decodeURIComponent(s.replace(a, " ")); },
q = window.location.search.substring(1),
urlParams = {};
while (e = r.exec(q))
urlParams[d(e[1])] = d(e[2]);
return urlParams;
}
function getDocumentData(urlParams) {
return {
key: urlParams["key"],
url: urlParams["url"] || '_offline_',
title: urlParams["title"],
fileType: urlParams["filetype"],
vkey: urlParams["vkey"],
permissions: {
edit: true,
download: true,
reader: true
}
};
}
function getEditorConfig(urlParams) {
return {
mode : urlParams["mode"] || 'edit',
lang : urlParams["lang"] || 'en',
canCoAuthoring : true,
createUrl : 'http://www.example.com/create',
user: {
id: urlParams["userid"] || 'uid-901', firstname: urlParams["userfname"] || 'Mitchell', lastname: urlParams["userlname"] || 'Hamish'
},
recent : [
{title: 'Memory.docx', url: 'http://onlyoffice.com', folder: 'Document Editor'},
{title: 'Description.doc', url: 'http://onlyoffice.com', folder: 'Document Editor'},
{title: 'DocEditor_right.xsl', url: 'http://onlyoffice.com', folder: 'Spreadsheet Editor'},
{title: 'api.rtf', url: 'http://onlyoffice.com', folder: 'Unnamed folder'}
],
// templates : [
// {name: 'Contracts', icon: '../../api/documents/resources/templates/contracts.png', url: 'http://...'},
// {name: 'Letter', icon: '../../api/documents/resources/templates/letter.png', url: 'http://...'},
// {name: 'List', icon: '../../api/documents/resources/templates/list.png', url: 'http://...'},
// {name: 'Plan', icon: '../../api/documents/resources/templates/plan.png', url: 'http://...'}
// ],
embedded : {
embedUrl : 'http://onlyoffice.com/embed',
fullscreenUrl : 'http://onlyoffice.com/fullscreen',
saveUrl : 'http://onlyoffice.com/download',
shareUrl : 'http://tl.com/72b4la97',
toolbarDocked : 'top'
}
,customization: {
// logo: {
// image: 'https://dylnrgbh910l3.cloudfront.net/studio/tag/i8.8.237/skins/default/images/onlyoffice_logo/editor_logo_general.png', // default size 86 x 20
// imageEmbedded: 'https://d2hw9csky753gb.cloudfront.net/studio/tag/i8.8.237/skins/default/images/onlyoffice_logo/editor_embedded_logo.png', // default size 124 x 20
// url: 'http://...'
// },
// backgroundColor: '#ffffff',
// textColor: '#ff0000',
// customer: {
// name: 'SuperPuper',
// address: 'New-York, 125f-25',
// mail: 'support@gmail.com',
// www: 'www.superpuper.com',
// info: 'Some info',
// logo: 'https://img.imgsmail.ru/r/default/portal/0.1.29/logo.png' // default size 216 x 35
// },
// goback: {text: 'Go To London', url: 'http://...'},
about: true,
feedback: true
}
};
}
// Mobile version
function isMobile(){
var prefixes = {
ios: 'i(?:Pad|Phone|Pod)(?:.*)CPU(?: iPhone)? OS ',
android: '(Android |HTC_|Silk/)',
blackberry: 'BlackBerry(?:.*)Version\/',
rimTablet: 'RIM Tablet OS ',
webos: '(?:webOS|hpwOS)\/',
bada: 'Bada\/'
},
i, prefix, match;
for (i in prefixes){
if (prefixes.hasOwnProperty(i)) {
prefix = prefixes[i];
if (navigator.userAgent.match(new RegExp('(?:'+prefix+')([^\\s;]+)')))
return true;
}
}
return false;
}
var fixSize = function() {
var wrapEl = document.getElementById('wrap');
if (wrapEl){
wrapEl.style.height = screen.availHeight + 'px';
window.scrollTo(0, -1);
wrapEl.style.height = window.innerHeight + 'px';
}
};
var fixIpadLandscapeIos7 = function() {
if (navigator.userAgent.match(/iPad;.*CPU.*OS 7_\d/i)) {
var wrapEl = document.getElementById('wrap');
if (wrapEl){
wrapEl.style.position = "fixed";
wrapEl.style.bottom = 0;
wrapEl.style.width = "100%";
}
}
};
if (isMobile()){
window.addEventListener('load', fixSize);
window.addEventListener('resize', fixSize);
fixIpadLandscapeIos7();
}
})();
</script>
</body>
</html>

@ -1,55 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>Create forms</title>
<meta charset="utf-8" />
<meta name="description" content="Insert rich text content controls to create a form with input fields that can be filled in by other users, or protect some parts of the document from being edited or deleted" />
<link type="text/css" rel="stylesheet" href="../editor.css" />
<script type="text/javascript" src="../callback.js"></script>
</head>
<body>
<div class="mainpart">
<h1>Create forms</h1>
<p>Using rich text content controls you can create a form with input fields that can be filled in by other users, or protect some parts of the document from being edited or deleted. Rich text content controls are objects containing text that can be formatted. Inline controls cannot contain more than one paragraph, while floating controls can contain several paragraphs, lists, and objects (images, shapes, tables etc.). </p>
<h3>Adding controls</h3>
<p>To create a new <b>inline</b> control,</p>
<ol>
<li>position the insertion point within a line of the text where you want the control to be added,<br />or select a text passage you want to become the control contents.</li>
<li>press <b>Shift+F1</b></li>
</ol>
<p>The control will be inserted at the insertion point within a line of the existing text. Inline controls do not allow adding line breaks and cannot contain other objects such as images, tables etc.</p>
<p><img alt="New inline content control" src="../images/addedcontentcontrol.png" /></p>
<p>To create a new <b>floating</b> control,</p>
<ol>
<li>position the insertion point at the end of a paragraph after which you want the control to be added,<br />or select one or more of the existing paragraphs you want to become the control contents.</li>
<li>press <b>Shift+F2</b></li>
</ol>
<p>The control will be inserted in a new paragraph. Floating controls allow adding line breaks, i.e. can contain multiple paragraphs as well as some objects, such as images, tables, other content controls etc.</p>
<p><img alt="Floating content control" src="../images/floatingcontentcontrol.png" /></p>
<p class="note"><b>Note</b>: The content control border is visible when the control is selected only. The borders do not appear on a printed version.</p>
<h3>Moving controls</h3>
<p>Controls can be <b>moved</b> to another place in the document: click the button to the left of the control border to select the control and drag it without releasing the mouse button to another position in the document text.</p>
<p><img alt="Moving content control" src="../images/movecontentcontrol.png" /></p>
<p>You can also <b>copy and paste</b> inline controls: select the necessary control and use the <b>Ctrl+C/Ctrl+V</b> key combinations.</p>
<h3>Editing control contents</h3>
<p>To switch to the control editing mode, press <b>Shift+F9</b>. You will be able to navigate between the controls only using the keyboard arrow buttons. <!--To exit from this mode and be able to edit regular text in your document--></p>
<p>Replace the default text within the control ("Your text here") with your own one: select the default text, press the <b>Delete</b> key and type in a new text or copy a text passage from anywhere and paste it into the content control.</p>
<p>Text within the rich text content control of any type (both inline and floating) can be formatted using the icons on the top toolbar: you can adjust the <a href="../UsageInstructions/FontTypeSizeColor.htm" onclick="onhyperlinkclick(this)">font type, size, color</a>, apply <a href="../UsageInstructions/DecorationStyles.htm" onclick="onhyperlinkclick(this)">decoration styles</a> and <a href="../UsageInstructions/FormattingPresets.htm" onclick="onhyperlinkclick(this)">formatting presets</a>. It's also possible to use the <b>Paragraph - Advanced settings</b> window accessible from the contextual menu or from the right sidebar to change the text properties. Text within floating controls can be formatted like a regular text of the document, i.e. you can set <a href="../UsageInstructions/LineSpacing.htm" onclick="onhyperlinkclick(this)">line spacing</a>, change <a href="../UsageInstructions/ParagraphIndents.htm" onclick="onhyperlinkclick(this)">paragraph indents</a>, adjust <a href="../UsageInstructions/SetTabStops.htm" onclick="onhyperlinkclick(this)">tab stops</a>.</p>
<h3>Protecting controls</h3>
<p>You can prevent users from editing or deleting some individual content controls. Use one of the following keyboard shortcuts:</p>
<ul>
<li>To protect a control from being edited (but not from being deleted), select the necessary control and press <b>Shift+F6</b></li>
<li>To protect a control from being deleted (but not from being edited), select the necessary control and press <b>Shift+F7</b></li>
<li>To protect a control from being both edited and deleted, select the necessary control and press <b>Shift+F8</b></li>
<li>To disable the protection from editing/deleting, select the necessary control and press <b>Shift+F5</b></li>
</ul>
<h3>Removing controls</h3>
<p>If you do not need a control anymore, you can use one of the following keyboard shortcuts:</p>
<ul>
<li>To remove a control and all its contents, select the necessary control and press <b>Shift+F3</b></li>
<li>To remove a control and leave all its contents, select the necessary control and press <b>Shift+F4</b></li>
</ul>
</div>
</body>
</html>

@ -1,55 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>Создание форм</title>
<meta charset="utf-8" />
<meta name="description" content="Вставляйте элементы управления содержимым 'форматированный текст' создать форму с полями ввода, которую могут заполнять другие пользователи, или защитить некоторые части документа от редактирования или удаления" />
<link type="text/css" rel="stylesheet" href="../editor.css" />
<script type="text/javascript" src="../callback.js"></script>
</head>
<body>
<div class="mainpart">
<h1>Создание форм</h1>
<p>Используя элементы управления содержимым "форматированный текст", вы можете создать форму с полями ввода, которую могут заполнять другие пользователи, или защитить некоторые части документа от редактирования или удаления. Элементы управления содержимым "форматированный текст" - это объекты, содержащие текст, который можно форматировать. Встроенные элементы управления могут содержать не более одного абзаца, тогда как плавающие элементы управления могут содержать несколько абзацев, списки и объекты (изображения, фигуры, таблицы и так далее). </p>
<h3>Добавление элементов управления</h3>
<p>Для создания нового <b>встроенного</b> элемента управления,</p>
<ol>
<li>установите курсор в строке текста там, где требуется добавить элемент управления,<br />или выделите фрагмент текста, который должен стать содержимым элемента управления.</li>
<li>нажмите сочетание клавиш <b>Shift+F1</b></li>
</ol>
<p>Элемент управления будет вставлен в позицию курсора в строке существующего текста. Встроенные элементы управления не позволяют добавлять разрывы строки и не могут содержать другие объекты, такие как изображения, таблицы и так далее.</p>
<p><img alt="Новый встроенный элемент управления" src="../images/addedcontentcontrol.png" /></p>
<p>Для создания нового <b>плавающего</b> элемента управления,</p>
<ol>
<li>установите курсор в конце абзаца, после которого требуется добавить элемент управления,<br />или выделите один или несколько существующих абзацев, которые должны стать содержимым элемента управления.</li>
<li>нажмите сочетание клавиш <b>Shift+F2</b></li>
</ol>
<p>Элемент управления будет вставлен в новом абзаце. Плавающие элементы управления позволяют добавлять разрывы строки, то есть могут содержать несколько абзацев, а также какие-либо объекты, такие как изображения, таблицы, другие элементы управления содержимым и так далее.</p>
<p><img alt="Плавающий элемент управления" src="../images/floatingcontentcontrol.png" /></p>
<p class="note"><b>Примечание</b>: Граница элемента управления содержимым видна только при выделении элемента управления. Границы не отображаются в печатной версии.</p>
<h3>Перемещение элементов управления</h3>
<p>Элементы управления можно <b>перемещать</b> на другое место в документе: нажмите на кнопку слева от границы элемента управления, чтобы выделить элемент управления, и перетащите его, не отпуская кнопку мыши, на другое место в тексте документа.</p>
<p><img alt="Перемещение элементов управления" src="../images/movecontentcontrol.png" /></p>
<p>Встроенные элементы управления можно также <b>копировать и вставлять</b>: выделите нужный элемент управления и используйте сочетания клавиш <b>Ctrl+C/Ctrl+V</b>.</p>
<h3>Редактирование содержимого элементов управления</h3>
<p>Для перехода в режим редактирования элементов управления нажмите сочетание клавиш <b>Shift+F9</b>. Вы сможете перемещаться только между элементами управления, используя кнопки со стрелками на клавиатуре. <!--To exit from this mode and be able to edit regular text in your document--></p>
<p>Замените стандартный текст в элементе управления ("Введите ваш текст") на свой собственный: выделите стандартный текст, нажмите клавишу <b>Delete</b> и введите новый текст или скопируйте откуда-нибудь фрагмент текста и вставьте его в элемент управления содержимым.</p>
<p>Текст внутри элемента управления содержимым "форматированный текст" любого типа (и встроенного, и плавающего) можно отформатировать с помощью значков на верхней панели инструментов: вы можете изменить <a href="../UsageInstructions/FontTypeSizeColor.htm" onclick="onhyperlinkclick(this)">тип, размер, цвет шрифта</a>, применить <a href="../UsageInstructions/DecorationStyles.htm" onclick="onhyperlinkclick(this)">стили оформления</a> и <a href="../UsageInstructions/FormattingPresets.htm" onclick="onhyperlinkclick(this)">предустановленные стили форматирования</a>. Для изменения свойств текста можно также использовать окно <b>Абзац - Дополнительные параметры</b>, доступное из контекстного меню или с правой боковой панели. Текст в плавающих элементах управления можно форматировать, как обычный текст документа, то есть вы можете задать <a href="../UsageInstructions/LineSpacing.htm" onclick="onhyperlinkclick(this)">междустрочный интервал</a>, изменить <a href="../UsageInstructions/ParagraphIndents.htm" onclick="onhyperlinkclick(this)">отступы абзаца</a>, настроить <a href="../UsageInstructions/SetTabStops.htm" onclick="onhyperlinkclick(this)">позиции табуляции</a>.</p>
<h3>Защита элементов управления</h3>
<p>Можно запретить пользователям редактирование или удаление некоторых отдельных элементов управления содержимым. Используйте одно из следующих сочетаний клавиш:</p>
<ul>
<li>Чтобы защитить элемент управления от редактирования (но не от удаления), выделите нужный элемент управления и нажмите сочетание клавиш <b>Shift+F6</b></li>
<li>Чтобы защитить элемент управления от удаления (но не от редактирования), выделите нужный элемент управления и нажмите сочетание клавиш <b>Shift+F7</b></li>
<li>Чтобы защитить элемент управления от редактирования и удаления одновременно, выделите нужный элемент управления и нажмите сочетание клавиш <b>Shift+F8</b></li>
<li>Чтобы снять защиту от редактирования/удаления, выделите нужный элемент управления и нажмите сочетание клавиш <b>Shift+F5</b></li>
</ul>
<h3>Удаление элементов управления</h3>
<p>Если вам больше не нужен какой-то элемент управления, вы можете использовать одно из следующих сочетаний клавиш:</p>
<ul>
<li>Чтобы удалить элемент управления и все его содержимое, выделите нужный элемент управления и нажмите сочетание клавиш <b>Shift+F3</b></li>
<li>Чтобы удалить элемент управления и оставить все его содержимое, выделите нужный элемент управления и нажмите сочетание клавиш <b>Shift+F4</b></li>
</ul>
</div>
</body>
</html>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 584 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

@ -1,39 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>FV Function</title>
<meta charset="utf-8" />
<meta name="description" content="" />
<link type="text/css" rel="stylesheet" href="../editor.css" />
</head>
<body>
<div class="mainpart">
<h1>FV Function</h1>
<p>The <b>FV</b> function is one of the financial functions. It is used to calculate the future value of an investment based on a specified interest rate and a constant payment schedule.</p>
<p>The <b>FV</b> function syntax is:</p>
<p style="text-indent: 150px;"><b><em>FV(rate, nper, pmt [, [pv] [,[type]]])</em></b></p>
<p><em>where</em></p>
<p style="text-indent: 50px;"><b><em>rate</em></b> is the interest rate for the investment.</p>
<p style="text-indent: 50px;"><b><em>nper</em></b> is a number of payments.</p>
<p style="text-indent: 50px;"><b><em>pmt</em></b> is a payment amount.</p>
<p style="text-indent: 50px;"><b><em>pv</em></b> is a present value of the payments. It is an optional argument. If it is omitted, the function will assume <b><em>pv</em></b> to be 0.</p>
<p style="text-indent: 50px;"><b><em>type</em></b> is a period when the payments are due. It is an optional argument. If it is set to 0 or omitted, the function will assume the payments to be due at the end of the period. If <b><em>type</em></b> is set to 1, the payments are due at the beginning of the period.</p>
<p class="note"><b>Note:</b> cash paid out (such as deposits to savings) is represented by negative numbers; cash received (such as dividend checks) is represented by positive numbers.</p>
<p>The numeric values can be entered manually or included into the cell you make reference to.</p>
<p>To apply the <b>FV</b> function,</p>
<ol>
<li>select the cell where you wish to display the result,</li>
<li>click the <b>Insert Function</b> <img alt="Insert Function icon" src="../images/insertfunction.png" /> icon situated at the top toolbar,
<br />or right-click within a selected cell and select the <b>Insert Function</b> option from the menu,
<br />or click the <img alt="Function icon" src="../images/function.png" /> icon situated at the formula bar,
</li>
<li>select the <b>Financial</b> function group from the list,</li>
<li>click the <b>FV</b> function,</li>
<li>enter the required arguments separating them by commas,</li>
<li>press the <b>Enter</b> button.</li>
</ol>
<p>The result will be displayed in the selected cell.</p>
<p style="text-indent: 150px;"><img alt="FV Function" src="../images/fv.png" /></p>
</div>
</body>
</html>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

File diff suppressed because one or more lines are too long

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save