init repo
This commit is contained in:
421
OfficeWeb/apps/api/documents/api.js
Normal file
421
OfficeWeb/apps/api/documents/api.js
Normal file
@@ -0,0 +1,421 @@
|
||||
/**
|
||||
* Copyright (c) Ascensio System SIA 2013. All rights reserved
|
||||
*
|
||||
* http://www.onlyoffice.com
|
||||
*/
|
||||
|
||||
;(function(DocsAPI, window, document, undefined) {
|
||||
|
||||
/*
|
||||
|
||||
# Full #
|
||||
|
||||
config = {
|
||||
type: 'desktop or mobile',
|
||||
width: '100% by default',
|
||||
height: '100% by default',
|
||||
documentType: 'text' | 'spreadsheet' | 'presentation',
|
||||
document: {
|
||||
title: 'document title',
|
||||
url: 'document url'
|
||||
fileType: 'document file type',
|
||||
options: <advanced options>,
|
||||
key: 'key',
|
||||
vkey: 'vkey',
|
||||
info: {
|
||||
author: 'author name',
|
||||
folder: 'path to document',
|
||||
created: <creation date>,
|
||||
sharingSettings: [
|
||||
{
|
||||
user: 'user name',
|
||||
permissions: <permissions>
|
||||
},
|
||||
...
|
||||
]
|
||||
},
|
||||
permissions: {
|
||||
edit: <can edit>,
|
||||
download: <can download>,
|
||||
reader: <can view in readable mode>
|
||||
}
|
||||
},
|
||||
editorConfig: {
|
||||
mode: 'view or edit',
|
||||
lang: <language code>,
|
||||
canCoAuthoring: <can coauthoring documents>,
|
||||
canAutosave: <can autosave documents>,
|
||||
canBackToFolder: <can return to folder>,
|
||||
createUrl: 'create document url', // editor will add '?title={document title}&template={template name}&action=create', prevent to create a new document if empty
|
||||
sharingSettingsUrl: 'document sharing settings url',
|
||||
user: {
|
||||
id: 'user id',
|
||||
name: 'full user name'
|
||||
},
|
||||
recent: [
|
||||
{
|
||||
title: 'document title',
|
||||
url: 'document url',
|
||||
folder: 'path to document'
|
||||
},
|
||||
...
|
||||
],
|
||||
templates: [
|
||||
{
|
||||
name: 'template name',
|
||||
icon: 'template icon url'
|
||||
},
|
||||
...
|
||||
],
|
||||
branding: {
|
||||
logoUrl: 'header logo url', // default size 88 x 30
|
||||
backgroundColor: 'header background color',
|
||||
textColor: 'header text color'
|
||||
}
|
||||
},
|
||||
events: {
|
||||
'onReady': <document ready callback>,
|
||||
'onBack': <back to folder callback>,
|
||||
'onDocumentStateChange': <document state changed callback>,
|
||||
'onSave': <save request callback>
|
||||
}
|
||||
}
|
||||
|
||||
# Embedded #
|
||||
|
||||
config = {
|
||||
type: 'embedded',
|
||||
width: '100% by default',
|
||||
height: '100% by default',
|
||||
documentType: 'text' | 'spreadsheet' | 'presentation',
|
||||
document: {
|
||||
title: 'document title',
|
||||
url: 'document url',
|
||||
fileType: 'document file type',
|
||||
key: 'key',
|
||||
vkey: 'vkey'
|
||||
},
|
||||
editorConfig: {
|
||||
embedded: {
|
||||
embedUrl: 'url',
|
||||
fullscreenUrl: 'url',
|
||||
saveUrl: 'url',
|
||||
shareUrl: 'url',
|
||||
toolbarDocked: 'top or bottom'
|
||||
}
|
||||
},
|
||||
events: {
|
||||
'onReady': <document ready callback>,
|
||||
'onBack': <back to folder callback>,
|
||||
'onError': <error callback>,
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// TODO: allow several instances on one page simultaneously
|
||||
|
||||
DocsAPI.DocEditor = function(placeholderId, config) {
|
||||
var _self = this,
|
||||
_config = config || {};
|
||||
|
||||
extend(_config, DocsAPI.DocEditor.defaultConfig);
|
||||
|
||||
var _onReady = function() {
|
||||
if (_config.type === 'mobile') {
|
||||
document.body.onfocus = function(e) {
|
||||
setTimeout(function(){
|
||||
iframe.contentWindow.focus();
|
||||
|
||||
_sendCommand({
|
||||
command: 'resetFocus',
|
||||
data: {}
|
||||
})
|
||||
}, 10);
|
||||
};
|
||||
}
|
||||
|
||||
window.onmouseup = function(evt) {
|
||||
_processMouse(evt);
|
||||
};
|
||||
|
||||
if (_config.editorConfig) {
|
||||
_init(_config.editorConfig);
|
||||
}
|
||||
|
||||
if (_config.document) {
|
||||
_openDocument(_config.document);
|
||||
}
|
||||
};
|
||||
|
||||
var _onMessage = function(msg) {
|
||||
if (msg) {
|
||||
var events = _config.events || {},
|
||||
handler = events[msg.event],
|
||||
res;
|
||||
|
||||
if (msg.event === 'onRequestEditRights' && !handler) {
|
||||
_applyEditRights(true, 'handler is\'n defined');
|
||||
} else {
|
||||
if (msg.event === 'onReady') {
|
||||
_onReady();
|
||||
}
|
||||
|
||||
if (handler) {
|
||||
res = handler.call(_self, { target: _self, data: msg.data });
|
||||
if (msg.event === 'onSave' && res !== false) {
|
||||
_processSaveResult(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
var target = document.getElementById(placeholderId),
|
||||
iframe;
|
||||
|
||||
if (target) {
|
||||
iframe = createIframe(_config);
|
||||
target.parentNode && target.parentNode.replaceChild(iframe, target);
|
||||
this._msgDispatcher = new MessageDispatcher(_onMessage, this);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
cmd = {
|
||||
command: 'commandName',
|
||||
data: <command specific data>
|
||||
}
|
||||
*/
|
||||
var _sendCommand = function(cmd) {
|
||||
if (iframe && iframe.contentWindow)
|
||||
postMessage(iframe.contentWindow, cmd);
|
||||
};
|
||||
|
||||
var _init = function(editorConfig) {
|
||||
_sendCommand({
|
||||
command: 'init',
|
||||
data: {
|
||||
config: editorConfig
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
var _openDocument = function(doc) {
|
||||
_sendCommand({
|
||||
command: 'openDocument',
|
||||
data: {
|
||||
doc: doc
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
var _showError = function(title, msg) {
|
||||
_showMessage(title, msg, "error");
|
||||
};
|
||||
|
||||
// severity could be one of: "error", "info" or "warning"
|
||||
var _showMessage = function(title, msg, severity) {
|
||||
if (typeof severity !== 'string') {
|
||||
severity = "info";
|
||||
}
|
||||
_sendCommand({
|
||||
command: 'showMessage',
|
||||
data: {
|
||||
title: title,
|
||||
msg: msg,
|
||||
severity: severity
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
var _applyEditRights = function(allowed, message) {
|
||||
_sendCommand({
|
||||
command: 'applyEditRights',
|
||||
data: {
|
||||
allowed: allowed,
|
||||
message: message
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
var _processSaveResult = function(result, message) {
|
||||
_sendCommand({
|
||||
command: 'processSaveResult',
|
||||
data: {
|
||||
result: result,
|
||||
message: message
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
var _processRightsChange = function(enabled, message) {
|
||||
_sendCommand({
|
||||
command: 'processRightsChange',
|
||||
data: {
|
||||
enabled: enabled,
|
||||
message: message
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
var _processMouse = function(evt) {
|
||||
var r = iframe.getBoundingClientRect();
|
||||
var data = {
|
||||
type: evt.type,
|
||||
x: evt.x - r.left,
|
||||
y: evt.y - r.top
|
||||
};
|
||||
|
||||
_sendCommand({
|
||||
command: 'processMouse',
|
||||
data: data
|
||||
});
|
||||
};
|
||||
|
||||
var _serviceCommand = function(command, data) {
|
||||
_sendCommand({
|
||||
command: 'internalCommand',
|
||||
data: {
|
||||
command: command,
|
||||
data: data
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
showError : _showError,
|
||||
showMessage : _showMessage,
|
||||
applyEditRights : _applyEditRights,
|
||||
processSaveResult : _processSaveResult,
|
||||
processRightsChange : _processRightsChange,
|
||||
serviceCommand : _serviceCommand
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
DocsAPI.DocEditor.defaultConfig = {
|
||||
type: 'desktop',
|
||||
width: '640px',
|
||||
height: '480px'
|
||||
};
|
||||
|
||||
|
||||
MessageDispatcher = function(fn, scope) {
|
||||
var _fn = fn,
|
||||
_scope = scope || window;
|
||||
|
||||
var _bindEvents = function() {
|
||||
if (window.addEventListener) {
|
||||
window.addEventListener("message", function(msg) {
|
||||
_onMessage(msg);
|
||||
}, false)
|
||||
}
|
||||
else if (window.attachEvent) {
|
||||
window.attachEvent("onmessage", function(msg) {
|
||||
_onMessage(msg);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
var _onMessage = function(msg) {
|
||||
// TODO: check message origin
|
||||
if (msg && window.JSON) {
|
||||
|
||||
try {
|
||||
var msg = window.JSON.parse(msg.data);
|
||||
if (_fn) {
|
||||
_fn.call(_scope, msg);
|
||||
}
|
||||
} catch(e) {}
|
||||
}
|
||||
};
|
||||
|
||||
_bindEvents.call(this);
|
||||
};
|
||||
|
||||
function getBasePath() {
|
||||
var scripts = document.getElementsByTagName('script'),
|
||||
match;
|
||||
|
||||
for (var i = scripts.length - 1; i >= 0; i--) {
|
||||
match = scripts[i].src.match(/(.*)api\/documents\/api.js/i);
|
||||
if (match) {
|
||||
return match[1];
|
||||
}
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
function getExtensionPath() {
|
||||
if ("undefined" == typeof(extensionParams) || null == extensionParams["url"])
|
||||
return null;
|
||||
return extensionParams["url"] + "apps/";
|
||||
}
|
||||
|
||||
function getAppPath(config) {
|
||||
var extensionPath = getExtensionPath(),
|
||||
path = extensionPath ? extensionPath : getBasePath(),
|
||||
appMap = {
|
||||
'text': 'documenteditor',
|
||||
'text-pdf': 'documenteditor',
|
||||
'spreadsheet': 'spreadsheeteditor',
|
||||
'presentation': 'presentationeditor'
|
||||
},
|
||||
app;
|
||||
|
||||
if (typeof config.documentType === 'string') {
|
||||
app = appMap[config.documentType.toLowerCase()];
|
||||
}
|
||||
|
||||
path += (app || appMap['text']) + "/";
|
||||
path += config.type === "mobile"
|
||||
? "mobile"
|
||||
: config.type === "embedded"
|
||||
? "embed"
|
||||
: "main";
|
||||
path += "/index.html";
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
function getAppParameters(config) {
|
||||
var params = "?_dc=0";
|
||||
|
||||
if (config.editorConfig && config.editorConfig.lang)
|
||||
params += "&lang=" + config.editorConfig.lang;
|
||||
|
||||
return params;
|
||||
}
|
||||
|
||||
function createIframe(config) {
|
||||
var iframe = document.createElement("iframe");
|
||||
|
||||
iframe.src = getAppPath(config) + getAppParameters(config);
|
||||
iframe.width = config.width;
|
||||
iframe.height = config.height;
|
||||
iframe.align = "top";
|
||||
iframe.frameBorder = 0;
|
||||
|
||||
return iframe;
|
||||
}
|
||||
|
||||
function postMessage(wnd, msg) {
|
||||
if (wnd && wnd.postMessage && window.JSON) {
|
||||
// TODO: specify explicit origin
|
||||
wnd.postMessage(window.JSON.stringify(msg), "*");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function extend(dest, src) {
|
||||
for (var prop in src) {
|
||||
if (src.hasOwnProperty(prop) && typeof dest[prop] === 'undefined') {
|
||||
dest[prop] = src[prop];
|
||||
}
|
||||
}
|
||||
return dest;
|
||||
}
|
||||
|
||||
})(window.DocsAPI = window.DocsAPI || {}, window, document);
|
||||
28
OfficeWeb/apps/api/documents/cache-scripts.html
Normal file
28
OfficeWeb/apps/api/documents/cache-scripts.html
Normal file
@@ -0,0 +1,28 @@
|
||||
<!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=9"/>
|
||||
<meta name="description" content="" />
|
||||
<meta name="keywords" content="" />
|
||||
<style type="text/css"></style>
|
||||
</head>
|
||||
<body>
|
||||
<script type="text/javascript" src="../../../3rdparty/extjs/ext-all.js"></script>
|
||||
<script type="text/javascript" src="../../../3rdparty/jquery/jquery-1.7.1.min.js"></script>
|
||||
<script type="text/javascript" src="../../../3rdparty/jquery/mousewheel/jquery.mousewheel.js"></script>
|
||||
<script type="text/javascript" src="../../../3rdparty/jquery/jscrollpane/jquery.jscrollpane.min.js"></script>
|
||||
<script type="text/javascript" src="../../../3rdparty/sockjs/sockjs-0.3.min.js"></script>
|
||||
<script type="text/javascript" src="../../../3rdparty/underscore/underscore-min.js"></script>
|
||||
<script type="text/javascript" src="../../../3rdparty/xregexp/xregexp-all-min.js"></script>
|
||||
<script type="text/javascript" src="../../../sdk/Common/AllFonts.js"></script>
|
||||
<script type="text/javascript" src="../../../sdk/Word/sdk-all.js"></script>
|
||||
<div id="editor_sdk">
|
||||
<script type="text/javascript">
|
||||
var editor = new asc_docs_api("editor_sdk");
|
||||
editor.SetFontsPath("../../../sdk/Fonts/");
|
||||
editor.LoadFontsFromServer();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
224
OfficeWeb/apps/api/documents/index.html
Normal file
224
OfficeWeb/apps/api/documents/index.html
Normal file
@@ -0,0 +1,224 @@
|
||||
<!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'],
|
||||
document: doc,
|
||||
editorConfig: cfg,
|
||||
events: {
|
||||
'onReady': onDocEditorReady,
|
||||
'onBack': onBack,
|
||||
'onDocumentStateChange': onDocumentStateChange,
|
||||
'onRequestEditRights': onRequestEditRights,
|
||||
'onSave': onDocumentSave,
|
||||
'onError': onError
|
||||
}
|
||||
});
|
||||
|
||||
// Document Editor event handlers
|
||||
|
||||
function onDocEditorReady(event) {
|
||||
if (event.target) {
|
||||
//console.log('Ready! Editor: ', event.target);
|
||||
}
|
||||
}
|
||||
|
||||
function onBack(event) {
|
||||
//console.log('Go back!');
|
||||
}
|
||||
|
||||
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 onDocumentSave(event) {
|
||||
var url = event.data;
|
||||
// if you want to async save process return false
|
||||
// and call api.processSaveResult when ready
|
||||
}
|
||||
|
||||
function onError(event) {
|
||||
// critical error happened
|
||||
// examine event.data.errorCode and event.data.errorDescription for details
|
||||
}
|
||||
|
||||
|
||||
// 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"],
|
||||
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,
|
||||
canBackToFolder : true,
|
||||
canCreateNew : true,
|
||||
createUrl : 'http://www.example.com/create',
|
||||
|
||||
user: {
|
||||
id: urlParams["userid"] || 'uid-901', name: urlParams["username"] || 'Hamish Mitchell'
|
||||
},
|
||||
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'},
|
||||
{name: 'Letter', icon: '../../api/documents/resources/templates/letter.png'},
|
||||
{name: 'List', icon: '../../api/documents/resources/templates/list.png'},
|
||||
{name: 'Plan', icon: '../../api/documents/resources/templates/plan.png'}
|
||||
],
|
||||
embedded : {
|
||||
embedUrl : 'http://onlyoffice.com/embed',
|
||||
fullscreenUrl : 'http://onlyoffice.com/fullscreen',
|
||||
saveUrl : 'http://onlyoffice.com/download',
|
||||
shareUrl : 'http://tl.com/72b4la97',
|
||||
toolbarDocked : 'top'
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// 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>
|
||||
BIN
OfficeWeb/apps/api/documents/resources/templates/contracts.png
Normal file
BIN
OfficeWeb/apps/api/documents/resources/templates/contracts.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.3 KiB |
BIN
OfficeWeb/apps/api/documents/resources/templates/letter.png
Normal file
BIN
OfficeWeb/apps/api/documents/resources/templates/letter.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.5 KiB |
BIN
OfficeWeb/apps/api/documents/resources/templates/list.png
Normal file
BIN
OfficeWeb/apps/api/documents/resources/templates/list.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.2 KiB |
BIN
OfficeWeb/apps/api/documents/resources/templates/plan.png
Normal file
BIN
OfficeWeb/apps/api/documents/resources/templates/plan.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.6 KiB |
Reference in New Issue
Block a user