diff --git a/rest/index.php b/rest/index.php index d5becdaeb76c16d8fa9d93f2d542634a03534011..cfd934219a330a346afcc19e3d7b73997b27c161 100755 --- a/rest/index.php +++ b/rest/index.php @@ -25,7 +25,6 @@ if (file_exists("custom/{$customId}/src/core/lang/lang-{$language}.php")) { } require_once("src/core/lang/lang-{$language}.php"); - $app = new \Slim\App(['settings' => ['displayErrorDetails' => true, 'determineRouteBeforeAppMiddleware' => true, 'addContentLengthHeader' => true ]]); //Authentication @@ -34,8 +33,7 @@ $app->add(function (\Slim\Http\Request $request, \Slim\Http\Response $response, $route = $request->getAttribute('route'); $currentMethod = empty($route) ? '' : $route->getMethods()[0]; $currentRoute = empty($route) ? '' : $route->getPattern(); - - if (!in_array($currentMethod.$currentRoute, $routesWithoutAuthentication)) { + if (!in_array($currentMethod.$currentRoute, $routesWithoutAuthentication) && preg_match('/POST\/test*/', $currentMethod.$currentRoute) == 0) { $login = \SrcCore\controllers\AuthenticationController::authentication(); if (!empty($login)) { \SrcCore\controllers\CoreController::setGlobals(['login' => $login]); @@ -258,6 +256,7 @@ $app->put('/indexingModels/{id}/enable', \IndexingModel\controllers\IndexingMode $app->delete('/indexingModels/{id}', \IndexingModel\controllers\IndexingModelController::class . ':delete'); //Jnlp +$app->post('/test', \ContentManagement\controllers\JnlpController::class . ':test'); $app->post('/jnlp', \ContentManagement\controllers\JnlpController::class . ':generateJnlp'); $app->get('/jnlp/{jnlpUniqueId}', \ContentManagement\controllers\JnlpController::class . ':renderJnlp'); $app->post('/jnlp/{jnlpUniqueId}', \ContentManagement\controllers\JnlpController::class . ':processJnlp'); diff --git a/src/app/contentManagement/controllers/JnlpController.php b/src/app/contentManagement/controllers/JnlpController.php index e89147ca78014ec200111743049c2abdad0305e0..3e414e33d27fb45862683df4cdd71b661b05dea7 100755 --- a/src/app/contentManagement/controllers/JnlpController.php +++ b/src/app/contentManagement/controllers/JnlpController.php @@ -36,7 +36,7 @@ class JnlpController $jnlpFileNameExt = $jnlpFileName . '.jnlp'; $allCookies = ''; - foreach($_COOKIE as $key => $value) { + foreach ($_COOKIE as $key => $value) { if (!empty($allCookies)) { $allCookies .= '; '; } @@ -380,4 +380,25 @@ class JnlpController return $response->saveXML(); } + + public static function test(Request $request, Response $response) + { + if (($body_stream = file_get_contents("php://input"))===false) { + echo "Bad Request"; + } + + $data = json_decode($body_stream, true); + + if ($data["status"] == 2) { + $downloadUri = $data["url"]; + + if (($new_data = file_get_contents($downloadUri))===false) { + echo "Bad Response"; + } else { + echo $new_data; + //file_put_contents($path_for_save, $new_data, LOCK_EX); + } + } + return $response->withJson(['error' => 0]); + } } diff --git a/src/frontend/app/app-common.module.ts b/src/frontend/app/app-common.module.ts index 8cd927b0f0dc9c7ea80ab76212c39771f24aab3a..e146cac93b0a75acb6a953a4383e33aebce4f479 100755 --- a/src/frontend/app/app-common.module.ts +++ b/src/frontend/app/app-common.module.ts @@ -21,6 +21,7 @@ import { TimeAgoPipe } from '../plugins/timeAgo.pipe'; import { TimeLimitPipe } from '../plugins/timeLimit.pipe'; import { FilterListPipe } from '../plugins/filterList.pipe'; import { FullDatePipe } from '../plugins/fullDate.pipe'; +import { EcplOnlyofficeViewerComponent } from '../plugins/onlyoffice-api-js/onlyoffice-viewer.component'; /*FRONT IMPORTS*/ import { AppMaterialModule } from './app-material.module'; @@ -116,7 +117,8 @@ export class MyHammerConfig extends HammerGestureConfig { TagInputComponent, DiffusionsListComponent, DocumentViewerComponent, - DragDropDirective + DragDropDirective, + EcplOnlyofficeViewerComponent ], exports: [ CommonModule, @@ -155,7 +157,8 @@ export class MyHammerConfig extends HammerGestureConfig { TagInputComponent, DiffusionsListComponent, DocumentViewerComponent, - DragDropDirective + DragDropDirective, + EcplOnlyofficeViewerComponent ], providers: [ HeaderService, diff --git a/src/frontend/app/attachments/attachments-page/attachment-page.component.html b/src/frontend/app/attachments/attachments-page/attachment-page.component.html index 4326ef9251e8d710f39406c0ec10b9a342e7fb07..6994e2acd4a0e0e94c5af5ad7535eb511896138a 100644 --- a/src/frontend/app/attachments/attachments-page/attachment-page.component.html +++ b/src/frontend/app/attachments/attachments-page/attachment-page.component.html @@ -223,7 +223,7 @@ </mat-dialog-content> <div mat-dialog-actions class="actions"> <button mat-raised-button color="primary" *ngIf="!newVersion" (click)="updateAttachment()" - [disabled]="!loading && (!editMode || !attachFormGroup.valid)">{{lang.update}}</button> + [disabled]="!loading && (!editMode || !attachFormGroup.valid)">{{lang.validate}}</button> <button mat-raised-button color="primary" *ngIf="newVersion" (click)="createNewVersion()" [disabled]="!editMode || attachment['encodedFile'].value === null">{{lang.createNewVersion}}</button> <button mat-raised-button color="warn" *ngIf=" !loading && attachment.status.value === 'SIGN'" diff --git a/src/frontend/app/attachments/attachments-page/attachment-page.component.ts b/src/frontend/app/attachments/attachments-page/attachment-page.component.ts index cd4736b45b5a4d179c7a92e67cd3ffb66146d8f4..93b86ff96c65804064bd68a2f784287f3a5f0d24 100644 --- a/src/frontend/app/attachments/attachments-page/attachment-page.component.ts +++ b/src/frontend/app/attachments/attachments-page/attachment-page.component.ts @@ -97,7 +97,7 @@ export class AttachmentPageComponent implements OnInit { type: new FormControl({ value: data.type, disabled: !this.editMode }, [Validators.required]), validationDate: new FormControl({ value: data.validationDate !== null ? new Date(data.validationDate) : null, disabled: !this.editMode }), signedResponse: new FormControl({ value: data.signedResponse, disabled: false }), - encodedFile: new FormControl({ value: null, disabled: !this.editMode }) + encodedFile: new FormControl({ value: '_CURRENT_FILE', disabled: !this.editMode }, [Validators.required]) }; this.versions = data.versions; @@ -175,6 +175,9 @@ export class AttachmentPageComponent implements OnInit { attachmentValues[element] = this.attachment[element].value; } if (element === 'encodedFile') { + if (this.attachment[element].value === '_CURRENT_FILE') { + attachmentValues['encodedFile'] = null; + } attachmentValues['format'] = this.appAttachmentViewer.getFile().format; } } @@ -197,10 +200,14 @@ export class AttachmentPageComponent implements OnInit { datas['attachment_' + element] = this.attachment[element].value; } }); - datas['resId'] = this.attachment['resIdMaster'].value; - this.attachment.encodedFile.setValue(this.appAttachmentViewer.getFile().content); - this.appAttachmentViewer.setDatas(datas); - if (ev !== 'cleanFile') { + if (ev === 'setData') { + this.appAttachmentViewer.setDatas(datas); + } else if (ev === 'cleanFile') { + this.attachment['encodedFile'].setValue(null); + } else { + datas['resId'] = this.attachment['resIdMaster'].value; + this.attachment.encodedFile.setValue(this.appAttachmentViewer.getFile().content); + this.appAttachmentViewer.setDatas(datas); this.setNewVersion(); } } diff --git a/src/frontend/app/viewer/document-viewer.component.html b/src/frontend/app/viewer/document-viewer.component.html index 7dbbf2052f8d70ea0227549a4cb531167257d99a..ceecffcea2c4e39a7904e9bc829a7f970640096d 100644 --- a/src/frontend/app/viewer/document-viewer.component.html +++ b/src/frontend/app/viewer/document-viewer.component.html @@ -1,4 +1,4 @@ -<ng-container *ngIf="editInProgress; else elseTemplate"> +<ng-container *ngIf="editInProgress && edition !== 'onlyoffice'; else elseTemplate"> <div class="editInProgress"> <i class="fas fa-file-word bounce"></i> <div> @@ -9,7 +9,7 @@ </div> </div> </ng-container> -<ng-template #elseTemplate> +<ng-container *ngIf="!editInProgress"> <ng-container *ngIf="noFile; else elseHaveFile"> <div class="noFile"> <i class="far fa-times-circle"></i> @@ -83,4 +83,12 @@ </div> </ng-template> -</ng-template> \ No newline at end of file +</ng-container> +<ng-container *ngIf="editInProgress && edition === 'onlyoffice'"> + <onlyoffice-viewer #onlyoffice style="height:100%;width:100%;" + [onlyofficeKey]="100" + onlyofficeType="docx" + onlyofficeName="Edition" + [editMode]="true" + [resId]="100"></onlyoffice-viewer> +</ng-container> diff --git a/src/frontend/app/viewer/document-viewer.component.ts b/src/frontend/app/viewer/document-viewer.component.ts index 336b7e23bfe4acdf0ade2f41a356e5c6f0724427..d6d089bbd3dd0b65b8ec2c5eb8280a87a5e75e6b 100644 --- a/src/frontend/app/viewer/document-viewer.component.ts +++ b/src/frontend/app/viewer/document-viewer.component.ts @@ -5,7 +5,7 @@ import { NotificationService } from '../notification.service'; import { HeaderService } from '../../service/header.service'; import { AppService } from '../../service/app.service'; import { tap, catchError, finalize, filter, map, exhaustMap } from 'rxjs/operators'; -import { of } from 'rxjs'; +import { of, Subscription } from 'rxjs'; import { ConfirmComponent } from '../../plugins/modal/confirm.component'; import { MatDialogRef, MatDialog, MatSidenav } from '@angular/material'; import { AlertComponent } from '../../plugins/modal/alert.component'; @@ -13,6 +13,7 @@ import { SortPipe } from '../../plugins/sorting.pipe'; import { templateVisitAll } from '@angular/compiler'; import { PluginSelectSearchComponent } from '../../plugins/select-search/select-search.component'; import { FormControl } from '@angular/forms'; +import { EcplOnlyofficeViewerComponent } from '../../plugins/onlyoffice-api-js/onlyoffice-viewer.component'; @Component({ @@ -53,6 +54,8 @@ export class DocumentViewerComponent implements OnInit { intervalLockFile: any; editInProgress: boolean = false; + edition: string = ''; + listTemplates: any[] = []; @@ -512,21 +515,26 @@ export class DocumentViewerComponent implements OnInit { }), filter((data: string) => data === 'ok'), tap(() => { - this.triggerEvent.emit(); - const template = this.listTemplates.filter(template => template.id === templateId)[0]; - this.editInProgress = true; - const jnlp: any = { - objectType: 'resourceCreation', - objectId: template.id, - cookie: document.cookie, - data: this.resourceDatas, - }; - this.http.post('../../rest/jnlp', jnlp).pipe( - tap((data: any) => { - window.location.href = '../../rest/jnlp/' + data.generatedJnlp; - this.checkLockFile(data.jnlpUniqueId, template.extension); - }) - ).subscribe(); + if (this.edition === 'onlyoffice') { + this.editInProgress = true; + } else { + this.triggerEvent.emit(); + const template = this.listTemplates.filter(template => template.id === templateId)[0]; + this.editInProgress = true; + const jnlp: any = { + objectType: 'resourceCreation', + objectId: template.id, + cookie: document.cookie, + data: this.resourceDatas, + }; + this.http.post('../../rest/jnlp', jnlp).pipe( + tap((data: any) => { + window.location.href = '../../rest/jnlp/' + data.generatedJnlp; + this.checkLockFile(data.jnlpUniqueId, template.extension); + }) + ).subscribe(); + } + }), catchError((err: any) => { this.notify.handleErrors(err); @@ -537,6 +545,9 @@ export class DocumentViewerComponent implements OnInit { editAttachment() { this.editInProgress = true; + + this.triggerEvent.emit('setData'); + const jnlp: any = { objectType: 'attachmentModification', objectId: this.resId, diff --git a/src/frontend/plugins/onlyoffice-api-js/editorConfig.ts b/src/frontend/plugins/onlyoffice-api-js/editorConfig.ts new file mode 100644 index 0000000000000000000000000000000000000000..88f60c7aa2559b456eda294a6536cc53405660e5 --- /dev/null +++ b/src/frontend/plugins/onlyoffice-api-js/editorConfig.ts @@ -0,0 +1,34 @@ +export class EditorConfig { + documentType: string; + document: { + fileType: string; + key: string; + title: string; + url: string; + permissions: { + comment: boolean; + download: boolean; + edit: boolean; + print: boolean; + review: boolean; + }; + }; + editorConfig: { + callbackUrl: string; + lang: string; + mode: string; + customization: { + chat: boolean; + comments: boolean; + compactToolbar: boolean; + feedback: boolean; + forcesave: boolean; + goback: boolean; + showReviewChanges: boolean; + zoom: number; + }; + }; + events: { + onDocumentStateChange: any; + }; +} diff --git a/src/frontend/plugins/onlyoffice-api-js/onlyoffice-api.js b/src/frontend/plugins/onlyoffice-api-js/onlyoffice-api.js new file mode 100644 index 0000000000000000000000000000000000000000..5181b6549c4d825b1fa008af89164c2c41617462 --- /dev/null +++ b/src/frontend/plugins/onlyoffice-api-js/onlyoffice-api.js @@ -0,0 +1,813 @@ +/** + * Copyright (c) Ascensio System SIA 2013. All rights reserved + * + * http://www.onlyoffice.com + */ +var onlyofficetesturl = ''; + +;(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', // must be deprecated, use owner instead + owner: 'owner name', + folder: 'path to document', + created: '<creation date>', // must be deprecated, use uploaded instead + uploaded: '<uploaded date>', + sharingSettings: [ + { + user: 'user name', + permissions: '<permissions>', + isLink: false + }, + ... + ] + }, + permissions: { + edit: <can edit>, // default = true + download: <can download>, // default = true + reader: <can view in readable mode>, + review: <can review>, // default = edit + print: <can print>, // default = true + rename: <can rename>, // default = false + changeHistory: <can change history>, // default = false + comment: <can comment in view mode> // default = edit, + modifyFilter: <can add, remove and save filter in the spreadsheet> // default = true + modifyContentControl: <can modify content controls in documenteditor> // default = true + fillForms: <can edit forms in view mode> // default = edit || review + } + }, + editorConfig: { + mode: 'view or edit', + lang: <language code>, + location: <location>, + canCoAuthoring: <can coauthoring documents>, + canBackToFolder: <can return to folder> - deprecated. use "customization.goback" parameter, + createUrl: 'create document url', + sharingSettingsUrl: 'document sharing settings url', + fileChoiceUrl: 'source url', // for mail merge or image from storage + callbackUrl: <url for connection between sdk and portal>, + mergeFolderUrl: 'folder for saving merged file', // must be deprecated, use saveAsUrl instead + saveAsUrl: 'folder for saving files' + licenseUrl: <url for license>, + customerId: <customer id>, + region: <regional settings> // can be 'en-us' or lang code + + user: { + id: 'user id', + name: 'user name' + }, + recent: [ + { + title: 'document title', + url: 'document url', + folder: 'path to document' + }, + ... + ], + templates: [ + { + name: 'template name', + icon: 'template icon url', + url: 'http://...' + }, + ... + ], + customization: { + logo: { + image: url, + imageEmbedded: url, + url: http://... + }, + customer: { + name: 'SuperPuper', + address: 'New-York, 125f-25', + mail: 'support@gmail.com', + www: 'www.superpuper.com', + info: 'Some info', + logo: '' + }, + about: true, + feedback: { + visible: false, + url: http://... + }, + goback: { + url: 'http://...', + text: 'Go to London', + blank: true + }, + chat: true, + comments: true, + zoom: 100, + compactToolbar: false, + leftMenu: true, + rightMenu: true, + hideRightMenu: false, // hide or show right panel on first loading + toolbar: true, + statusBar: true, + autosave: true, + forcesave: false, + commentAuthorOnly: false, + showReviewChanges: false, + help: true, + compactHeader: false, + toolbarNoTabs: false, + toolbarHideFileName: false, + reviewDisplay: 'original' + }, + plugins: { + autostart: ['asc.{FFE1F462-1EA2-4391-990D-4CC84940B754}'], + pluginsData: [ + "helloworld/config.json", + "chess/config.json", + "speech/config.json", + "clipart/config.json", + ] + } + }, + events: { + 'onAppReady': <application ready callback>, + 'onBack': <back to folder callback>, + 'onDocumentStateChange': <document state changed callback> + 'onDocumentReady': <document ready 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: { + licenseUrl: <url for license>, + customerId: <customer id>, + autostart: 'document', // action for app's autostart. for presentations default value is 'player' + embedded: { + embedUrl: 'url', + fullscreenUrl: 'url', + saveUrl: 'url', + shareUrl: 'url', + toolbarDocked: 'top or bottom' + } + }, + events: { + 'onAppReady': <application ready callback>, + 'onBack': <back to folder callback>, + 'onError': <error callback>, + 'onDocumentReady': <document ready callback>, + 'onWarning': <warning callback> + } + } + */ + + // TODO: allow several instances on one page simultaneously + + DocsAPI.DocEditor = function(placeholderId, config) { + var _self = this, + _config = config || {}; + + extend(_config, DocsAPI.DocEditor.defaultConfig); + _config.editorConfig.canUseHistory = _config.events && !!_config.events.onRequestHistory; + _config.editorConfig.canHistoryClose = _config.events && !!_config.events.onRequestHistoryClose; + _config.editorConfig.canHistoryRestore = _config.events && !!_config.events.onRequestRestore; + _config.editorConfig.canSendEmailAddresses = _config.events && !!_config.events.onRequestEmailAddresses; + _config.editorConfig.canRequestEditRights = _config.events && !!_config.events.onRequestEditRights; + _config.editorConfig.canRequestClose = _config.events && !!_config.events.onRequestClose; + _config.editorConfig.canRename = _config.events && !!_config.events.onRequestRename; + _config.editorConfig.canMakeActionLink = _config.events && !!_config.events.onMakeActionLink; + _config.editorConfig.canRequestUsers = _config.events && !!_config.events.onRequestUsers; + _config.editorConfig.canRequestSendNotify = _config.events && !!_config.events.onRequestSendNotify; + _config.editorConfig.mergeFolderUrl = _config.editorConfig.mergeFolderUrl || _config.editorConfig.saveAsUrl; + _config.editorConfig.canRequestSaveAs = _config.events && !!_config.events.onRequestSaveAs; + _config.editorConfig.canRequestInsertImage = _config.events && !!_config.events.onRequestInsertImage; + _config.editorConfig.canRequestMailMergeRecipients = _config.events && !!_config.events.onRequestMailMergeRecipients; + _config.frameEditorId = placeholderId; + + var onMouseUp = function (evt) { + _processMouse(evt); + }; + + var _attachMouseEvents = function() { + if (window.addEventListener) { + window.addEventListener("mouseup", onMouseUp, false) + } else if (window.attachEvent) { + window.attachEvent("onmouseup", onMouseUp); + } + }; + + var _detachMouseEvents = function() { + if (window.removeEventListener) { + window.removeEventListener("mouseup", onMouseUp, false) + } else if (window.detachEvent) { + window.detachEvent("onmouseup", onMouseUp); + } + }; + + var _onAppReady = function() { + if (_config.type === 'mobile') { + document.body.onfocus = function(e) { + setTimeout(function(){ + iframe.contentWindow.focus(); + + _sendCommand({ + command: 'resetFocus', + data: {} + }) + }, 10); + }; + } + + _attachMouseEvents(); + + if (_config.editorConfig) { + _init(_config.editorConfig); + } + + if (_config.document) { + _openDocument(_config.document); + } + }; + + var _callLocalStorage = function(data) { + if (data.cmd == 'get') { + if (data.keys && data.keys.length) { + var af = data.keys.split(','), re = af[0]; + for (i = 0; ++i < af.length;) + re += '|' + af[i]; + + re = new RegExp(re); k = {}; + for (i in localStorage) + if (re.test(i)) k[i] = localStorage[i]; + } else { + k = localStorage; + } + + _sendCommand({ + command: 'internalCommand', + data: { + type: 'localstorage', + keys: k + } + }); + } else + if (data.cmd == 'set') { + var k = data.keys, i; + for (i in k) { + localStorage.setItem(i, k[i]); + } + } + }; + + var _onMessage = function(msg) { + if ( msg ) { + if ( msg.type === "onExternalPluginMessage" ) { + _sendCommand(msg); + } else + if ( msg.frameEditorId == placeholderId ) { + var events = _config.events || {}, + handler = events[msg.event], + res; + + if (msg.event === 'onRequestEditRights' && !handler) { + _applyEditRights(false, 'handler isn\'t defined'); + } else if (msg.event === 'onInternalMessage' && msg.data && msg.data.type == 'localstorage') { + _callLocalStorage(msg.data.data); + } else { + if (msg.event === 'onAppReady') { + _onAppReady(); + } + + if (handler && typeof handler == "function") { + res = handler.call(_self, {target: _self, data: msg.data}); + } + } + } + } + }; + + var _checkConfigParams = function() { + if (_config.document) { + if (!_config.document.url || ((typeof _config.document.fileType !== 'string' || _config.document.fileType=='') && + (typeof _config.documentType !== 'string' || _config.documentType==''))) { + window.alert("One or more required parameter for the config object is not set"); + return false; + } + + var appMap = { + 'text': 'docx', + 'text-pdf': 'pdf', + 'spreadsheet': 'xlsx', + 'presentation': 'pptx' + }, app; + + if (typeof _config.documentType === 'string' && _config.documentType != '') { + app = appMap[_config.documentType.toLowerCase()]; + if (!app) { + window.alert("The \"documentType\" parameter for the config object is invalid. Please correct it."); + return false; + } else if (typeof _config.document.fileType !== 'string' || _config.document.fileType == '') { + _config.document.fileType = app; + } + } + + if (typeof _config.document.fileType === 'string' && _config.document.fileType != '') { + var type = /^(?:(xls|xlsx|ods|csv|xlst|xlsy|gsheet|xlsm|xlt|xltm|xltx|fods|ots)|(pps|ppsx|ppt|pptx|odp|pptt|ppty|gslides|pot|potm|potx|ppsm|pptm|fodp|otp)|(doc|docx|doct|odt|gdoc|txt|rtf|pdf|mht|htm|html|epub|djvu|xps|docm|dot|dotm|dotx|fodt|ott))$/ + .exec(_config.document.fileType); + if (!type) { + window.alert("The \"document.fileType\" parameter for the config object is invalid. Please correct it."); + return false; + } else if (typeof _config.documentType !== 'string' || _config.documentType == ''){ + if (typeof type[1] === 'string') _config.documentType = 'spreadsheet'; else + if (typeof type[2] === 'string') _config.documentType = 'presentation'; else + if (typeof type[3] === 'string') _config.documentType = 'text'; + } + } + + var type = /^(?:(pdf|djvu|xps))$/.exec(_config.document.fileType); + if (type && typeof type[1] === 'string') { + _config.editorConfig.canUseHistory = false; + } + + if (!_config.document.title || _config.document.title=='') + _config.document.title = 'Unnamed.' + _config.document.fileType; + + if (!_config.document.key) { + _config.document.key = 'xxxxxxxxxxxxxxxxxxxx'.replace(/[x]/g, function (c) {var r = Math.random() * 16 | 0; return r.toString(16);}); + } else if (typeof _config.document.key !== 'string') { + window.alert("The \"document.key\" parameter for the config object must be string. Please correct it."); + return false; + } + + _config.document.token = _config.token; + } + + return true; + }; + + (function() { + var result = /[\?\&]placement=(\w+)&?/.exec(window.location.search); + if (!!result && result.length) { + if (result[1] == 'desktop') { + _config.editorConfig.targetApp = result[1]; + // _config.editorConfig.canBackToFolder = false; + if (!_config.editorConfig.customization) _config.editorConfig.customization = {}; + _config.editorConfig.customization.about = false; + _config.editorConfig.customization.compactHeader = false; + + if ( window.AscDesktopEditor ) window.AscDesktopEditor.execCommand('webapps:events', 'loading'); + } + } + })(); + + var target = document.getElementById(placeholderId), + iframe; + + if (target && _checkConfigParams()) { + iframe = createIframe(_config); + target.parentNode && target.parentNode.replaceChild(iframe, target); + var _msgDispatcher = new MessageDispatcher(_onMessage, this); + } + + /* + cmd = { + command: 'commandName', + data: <command specific data> + } + */ + + var _destroyEditor = function(cmd) { + var target = document.createElement("div"); + target.setAttribute('id', placeholderId); + + if (iframe) { + _msgDispatcher && _msgDispatcher.unbindEvents(); + _detachMouseEvents(); + iframe.parentNode && iframe.parentNode.replaceChild(target, iframe); + } + }; + + 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 _showMessage = function(title, msg) { + msg = msg || title; + _sendCommand({ + command: 'showMessage', + data: { + msg: msg + } + }); + }; + + 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 + } + }); + }; + + // TODO: remove processRightsChange, use denyEditingRights + var _processRightsChange = function(enabled, message) { + _sendCommand({ + command: 'processRightsChange', + data: { + enabled: enabled, + message: message + } + }); + }; + + var _denyEditingRights = function(message) { + _sendCommand({ + command: 'processRightsChange', + data: { + enabled: false, + message: message + } + }); + }; + + var _refreshHistory = function(data, message) { + _sendCommand({ + command: 'refreshHistory', + data: { + data: data, + message: message + } + }); + }; + + var _setHistoryData = function(data, message) { + _sendCommand({ + command: 'setHistoryData', + data: { + data: data, + message: message + } + }); + }; + + var _setEmailAddresses = function(data) { + _sendCommand({ + command: 'setEmailAddresses', + data: { + data: data + } + }); + }; + + var _setActionLink = function (data) { + _sendCommand({ + command: 'setActionLink', + data: { + url: data + } + }); + }; + + var _processMailMerge = function(enabled, message) { + _sendCommand({ + command: 'processMailMerge', + data: { + enabled: enabled, + message: message + } + }); + }; + + var _downloadAs = function (data) { + _sendCommand({ + command: 'downloadAs', + data: data + }); + }; + + var _setUsers = function(data) { + _sendCommand({ + command: 'setUsers', + data: data + }); + }; + + var _showSharingSettings = function(data) { + _sendCommand({ + command: 'showSharingSettings', + data: data + }); + }; + + var _setSharingSettings = function(data) { + _sendCommand({ + command: 'setSharingSettings', + data: data + }); + }; + + var _insertImage = function(data) { + _sendCommand({ + command: 'insertImage', + data: data + }); + }; + + var _setMailMergeRecipients = function(data) { + _sendCommand({ + command: 'setMailMergeRecipients', + data: data + }); + }; + + var _processMouse = function(evt) { + var r = iframe.getBoundingClientRect(); + var data = { + type: evt.type, + x: evt.x - r.left, + y: evt.y - r.top, + event: evt + }; + + _sendCommand({ + command: 'processMouse', + data: data + }); + }; + + var _serviceCommand = function(command, data) { + _sendCommand({ + command: 'internalCommand', + data: { + command: command, + data: data + } + }); + }; + + return { + showMessage : _showMessage, + processSaveResult : _processSaveResult, + processRightsChange : _processRightsChange, + denyEditingRights : _denyEditingRights, + refreshHistory : _refreshHistory, + setHistoryData : _setHistoryData, + setEmailAddresses : _setEmailAddresses, + setActionLink : _setActionLink, + processMailMerge : _processMailMerge, + downloadAs : _downloadAs, + serviceCommand : _serviceCommand, + attachMouseEvents : _attachMouseEvents, + detachMouseEvents : _detachMouseEvents, + destroyEditor : _destroyEditor, + setUsers : _setUsers, + showSharingSettings : _showSharingSettings, + setSharingSettings : _setSharingSettings, + insertImage : _insertImage, + setMailMergeRecipients: _setMailMergeRecipients + } + }; + + + DocsAPI.DocEditor.defaultConfig = { + type: 'desktop', + width: '100%', + height: '100%', + editorConfig: { + lang: 'en', + canCoAuthoring: true, + customization: { + about: true, + feedback: false + } + } + }; + + DocsAPI.DocEditor.version = function() { + return '5.4.2'; + }; + + MessageDispatcher = function(fn, scope) { + var _fn = fn, + _scope = scope || window, + eventFn = function(msg) { + _onMessage(msg); + }; + + var _bindEvents = function() { + if (window.addEventListener) { + window.addEventListener("message", eventFn, false) + } + else if (window.attachEvent) { + window.attachEvent("onmessage", eventFn); + } + }; + + var _unbindEvents = function() { + if (window.removeEventListener) { + window.removeEventListener("message", eventFn, false) + } + else if (window.detachEvent) { + window.detachEvent("onmessage", eventFn); + } + }; + + 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); + + return { + unbindEvents: _unbindEvents + } + }; + + function getBasePath() { + return `http://10.2.95.76:8765/web-apps/apps/`; + 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 = appMap['text']; + + if (typeof config.documentType === 'string') { + app = appMap[config.documentType.toLowerCase()]; + } else + if (!!config.document && typeof config.document.fileType === 'string') { + var type = /^(?:(xls|xlsx|ods|csv|xlst|xlsy|gsheet|xlsm|xlt|xltm|xltx|fods|ots)|(pps|ppsx|ppt|pptx|odp|pptt|ppty|gslides|pot|potm|potx|ppsm|pptm|fodp|otp))$/ + .exec(config.document.fileType); + if (type) { + if (typeof type[1] === 'string') app = appMap['spreadsheet']; else + if (typeof type[2] === 'string') app = appMap['presentation']; + } + } + + path += app + "/"; + path += config.type === "mobile" + ? "mobile" + : config.type === "embedded" + ? "embed" + : "main"; + path += "/index.html"; + + return path; + } + + function getAppParameters(config) { + var params = "?_dc=5.4.2-46"; + + if (config.editorConfig && config.editorConfig.lang) + params += "&lang=" + config.editorConfig.lang; + + if (config.editorConfig && config.editorConfig.targetApp!=='desktop') { + if ( (typeof(config.editorConfig.customization) == 'object') && config.editorConfig.customization.loaderName) { + if (config.editorConfig.customization.loaderName !== 'none') params += "&customer=" + config.editorConfig.customization.loaderName; + } else + params += "&customer=ONLYOFFICE"; + if ( (typeof(config.editorConfig.customization) == 'object') && config.editorConfig.customization.loaderLogo) { + if (config.editorConfig.customization.loaderLogo !== '') params += "&logo=" + config.editorConfig.customization.loaderLogo; + } + } + + if (config.frameEditorId) + params += "&frameEditorId=" + config.frameEditorId; + + 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; + iframe.name = "frameEditor"; + iframe.allowFullscreen = true; + iframe.setAttribute("allowfullscreen",""); // for IE11 + iframe.setAttribute("onmousewheel",""); // for Safari on Mac + + if (config.type == "mobile") + { + iframe.style.position = "fixed"; + iframe.style.overflow = "hidden"; + document.body.style.overscrollBehaviorY = "contain"; + } + 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)) { + if (typeof dest[prop] === 'undefined') { + dest[prop] = src[prop]; + } else + if (typeof dest[prop] === 'object' && + typeof src[prop] === 'object') { + extend(dest[prop], src[prop]) + } + } + } + return dest; + } + +})(window.DocsAPI = window.DocsAPI || {}, window, document); + diff --git a/src/frontend/plugins/onlyoffice-api-js/onlyoffice-viewer.component.css b/src/frontend/plugins/onlyoffice-api-js/onlyoffice-viewer.component.css new file mode 100644 index 0000000000000000000000000000000000000000..ab1fc9910a0894f57cbc50ee127d22f0ce88fdf7 --- /dev/null +++ b/src/frontend/plugins/onlyoffice-api-js/onlyoffice-viewer.component.css @@ -0,0 +1,69 @@ +.onlyoffice-modal { + width: 100%; + height: 100%; + position: fixed; + left: 0; + right: 0; + top: 0; + bottom: 0; + background: rgba(0, 0, 0, .5); + z-index: 10001; + padding: 15px; +} + +.modal-dialog { + background-color: #fff; + border: 1px solid #f2f2f2; + margin-top: 0px; + margin-bottom: 0px; + width: 100%; + height: 100%; + display: table; +} + +.modal-header { + height: 6%; + padding: 5px; + display: table-row +} + +h3 { + margin-top: 0; + margin-bottom: 0; + font-size: 16px; + padding: 5px; +} + +button.pull-right { + padding: 0 12px; + background: none; + outline: none; + border: none; + font-size: 25px; + position: absolute; + top: 0; + right: 0; + border-left: 1px solid #dedede; + color: #dedede; +} + +.modal-body { + width: 100%; + max-height: 94%; + overflow: auto; + padding: 0px; + background: #dedede; + display: table-cell; + text-align: center; + vertical-align: middle; +} + +.modal-body .scroller { + max-height: 94%; + overflow: auto; +} + +.onlyoffice-icon-header { + width: 20px; + height: auto; +} \ No newline at end of file diff --git a/src/frontend/plugins/onlyoffice-api-js/onlyoffice-viewer.component.html b/src/frontend/plugins/onlyoffice-api-js/onlyoffice-viewer.component.html new file mode 100644 index 0000000000000000000000000000000000000000..652cdc932dd7bbff703957d512ec2c2a95840265 --- /dev/null +++ b/src/frontend/plugins/onlyoffice-api-js/onlyoffice-viewer.component.html @@ -0,0 +1,19 @@ +<a href="javascript:void(0)" (click)="showModal()" id="onlyoffice-file-{{id}}"> + <img src="/src/images/{{onlyofficeType}}.png" alt="Office File"> +</a> + +<div class="onlyoffice-modal" [hidden]="!showModalWindow" id="onlyoffice-pop-{{id}}"> + <div class="modal-dialog"> + <div class="modal-header"> + <div style="padding: 5px;"> + <img src="/src/images/{{onlyofficeType}}.png" alt="Office File" class="onlyoffice-icon-header pull-left"> + <h3 class="pull-left">{{onlyofficeName}}</h3> + </div> + <button (click)="hideModal()" class="pull-right">x</button> + </div> + <div class="modal-body"> + <div id="placeholder"></div> + </div> + </div> + +</div> \ No newline at end of file diff --git a/src/frontend/plugins/onlyoffice-api-js/onlyoffice-viewer.component.ts b/src/frontend/plugins/onlyoffice-api-js/onlyoffice-viewer.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..27b0b12ce7be08d09c236d2646fbaf39b49167d5 --- /dev/null +++ b/src/frontend/plugins/onlyoffice-api-js/onlyoffice-viewer.component.ts @@ -0,0 +1,120 @@ +import { + Component, + OnInit, + AfterViewInit, + Input, + NgZone, + EventEmitter, + Output, + HostListener +} from '@angular/core'; +import './onlyoffice-api.js'; +import { HttpClient, HttpHeaders } from '@angular/common/http'; +import { Subject, Observable, of } from 'rxjs'; +import { catchError, tap } from 'rxjs/operators'; + +declare var DocsAPI: any; + +@Component({ + selector: 'onlyoffice-viewer', + template: `<button (click)="quit()">close</button><div id="placeholder"></div>`, +}) +export class EcplOnlyofficeViewerComponent implements OnInit, AfterViewInit { + @Input() id: string; + @Input() onlyofficeName: string; + @Input() onlyofficeType: string; + @Input() onlyofficeKey: string; + @Input() resId: number; + @Input() editMode: boolean = false; + + @Output() triggerEvent = new EventEmitter<string>(); + + editorConfig: any; + docEditor: any; + showModalWindow: boolean = false; + + @HostListener('window:message',['$event']) + onMessage(e: any) { + console.log(e); + const response = JSON.parse(e.data); + + if (response.event === 'onDownloadAs') { + this.onDownloadAs(response.data); + } + } + constructor(private zone: NgZone, public http: HttpClient) { } + + quit() { + this.docEditor.downloadAs(); + //this.docEditor.destroyEditor(); + } + + onDocumentStateChange(event: any) { + if (event.data) { + console.log('The document changed'); + console.log(event); + } else { + console.log('Changes are collected on document editing service'); + } + } + + onDownloadAs(url: any) { + const optionRequete = { + headers: new HttpHeaders({ + 'Access-Control-Allow-Origin':'*', + }) + }; + this.http.get(url, optionRequete).pipe( + tap((data: any) => { + console.log(data); + }), + catchError((err: any) => { + console.log(err) + return of(false); + }) + ).subscribe(); + } + + + ngOnInit() { } + + ngAfterViewInit() { + this.editorConfig = { + documentType: 'text', + document: { + fileType: 'odt', + key: 'toto', + title: this.onlyofficeName, + url: `http://10.2.95.76/rep_standard.odt`, + permissions: { + comment: false, + download: true, + edit: this.editMode, + print: true, + review: false + } + }, + editorConfig: { + callbackUrl: 'http://10.2.95.76/maarch_courrier_develop/rest/test', + lang: 'fr-FR', + mode: 'edit', + customization: { + chat: false, + comments: false, + compactToolbar: false, + feedback: false, + forcesave: false, + goback: false, + hideRightMenu: true, + showReviewChanges: false, + zoom: 100 + }, + user: { + id: "1", + name: "Bernard BLIER" + }, + }, + }; + this.docEditor = new DocsAPI.DocEditor('placeholder', this.editorConfig); + } +}