diff --git a/.travis.yml b/.travis.yml index a9230e1b1..8cad2d778 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,8 +20,6 @@ jobs: name: 'Code quality checks' script: - npm run lint - - npm run spellcheck - - npm run format:check - stage: test name: 'Unit tests' script: diff --git a/angular.json b/angular.json index 2961f4555..e7700381c 100644 --- a/angular.json +++ b/angular.json @@ -45,15 +45,20 @@ "input": "node_modules/pdfjs-dist/build", "output": "/" }, - { - "glob": "**/*.json", - "input": "node_modules/@denysvuika/aca-dev-tools/assets", - "output": "./assets/plugins" - }, { "glob": "extension.schema.json", "input": ".", "output": "./assets" + }, + { + "glob": "**/*.json", + "input": "node_modules/@alfresco/adf-office-services-ext/assets", + "output": "./assets/plugins" + }, + { + "glob": "**/*.json", + "input": "projects/adf-office-services-ext/assets", + "output": "./assets/plugins" } ], "styles": [ @@ -62,10 +67,11 @@ "src/styles.scss" ], "scripts": [ - "node_modules/pdfjs-dist/build/pdf.js", - "node_modules/pdfjs-dist/web/pdf_viewer.js", - "node_modules/moment/min/moment.min.js" - ] + "node_modules/pdfjs-dist/build/pdf.js", + "node_modules/pdfjs-dist/web/pdf_viewer.js", + "node_modules/moment/min/moment.min.js" + ], + "es5BrowserSupport": true }, "configurations": { "production": { @@ -222,6 +228,41 @@ } } } + }, + "adf-office-services-ext": { + "root": "projects/adf-office-services-ext", + "sourceRoot": "projects/adf-office-services-ext/src", + "projectType": "library", + "prefix": "lib", + "architect": { + "build": { + "builder": "@angular-devkit/build-ng-packagr:build", + "options": { + "tsConfig": "projects/adf-office-services-ext/tsconfig.lib.json", + "project": "projects/adf-office-services-ext/ng-package.json" + } + }, + "test": { + "builder": "@angular-devkit/build-angular:karma", + "options": { + "main": "projects/adf-office-services-ext/src/test.ts", + "tsConfig": "projects/adf-office-services-ext/tsconfig.spec.json", + "karmaConfig": "projects/adf-office-services-ext/karma.conf.js" + } + }, + "lint": { + "builder": "@angular-devkit/build-angular:tslint", + "options": { + "tsConfig": [ + "projects/adf-office-services-ext/tsconfig.lib.json", + "projects/adf-office-services-ext/tsconfig.spec.json" + ], + "exclude": [ + "**/node_modules/**" + ] + } + } + } } }, "defaultProject": "app", diff --git a/package-lock.json b/package-lock.json index 6eb2aff3a..9f2c42682 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3000,6 +3000,18 @@ } } }, + "cpr": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/cpr/-/cpr-3.0.1.tgz", + "integrity": "sha1-uaVQOLfNgaNcF7l2GJW9hJau8eU=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.5", + "minimist": "^1.2.0", + "mkdirp": "~0.5.1", + "rimraf": "^2.5.4" + } + }, "create-ecdh": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.3.tgz", diff --git a/package.json b/package.json index f809c9d4a..13e678c6e 100644 --- a/package.json +++ b/package.json @@ -4,14 +4,16 @@ "license": "LGPL-3.0", "scripts": { "ng": "ng", - "start": "ng serve --open", + "start": "npm run build.extensions && ng serve --open", "start:prod": "ng serve --prod --open", - "build": "node --max-old-space-size=8192 node_modules/@angular/cli/bin/ng build app --prod", - "build:dev": "ng build", - "build.e2e": "node --max-old-space-size=8192 node_modules/@angular/cli/bin/ng build app --configuration=e2e", + "build:aos-extension": "rm -rf dist/@alfresco/adf-office-services-ext && ng build adf-office-services-ext && cp projects/adf-office-services-ext/ngi.json dist/@alfresco/adf-office-services-ext && cpr projects/adf-office-services-ext/assets dist/@alfresco/adf-office-services-ext/assets", + "build.extensions": "npm run build:aos-extension", + "build.app": "node --max-old-space-size=8192 node_modules/@angular/cli/bin/ng build app", + "build": "npm run build.extensions && npm run build.app -- --prod", + "build.e2e": "npm run build.extensions && npm run build.app -- --prod --configuration=e2e", "test": "ng test app --code-coverage", - "test:ci": "ng test app --code-coverage --watch=false", - "lint": "ng lint", + "test:ci": "npm run build.extensions && ng test app --code-coverage --watch=false", + "lint": "ng lint && npm run spellcheck && npm run format:check", "wd:update": "webdriver-manager update --gecko=false", "e2e": "npm run wd:update && protractor --baseUrl=http://localhost:4000", "e2e.local": "npm run wd:update && protractor --baseUrl=http://localhost:4200", @@ -23,7 +25,7 @@ "inspect.bundle": "ng build app --prod --stats-json && npx webpack-bundle-analyzer dist/app/stats.json", "format:check": "prettier --check \"src/{app,environments}/**/*.{ts,js,css,scss,html}\"", "format:fix": "prettier --write \"src/{app,environments}/**/*.{ts,js,css,scss,html}\"", - "build.tomcat": "npm run build -- --base-href ./ && jar -cvf docker/tomcat/artifacts/content-app.war -C dist/app/ .", + "build.tomcat": "npm run build.extensions && npm run build.app -- --prod --base-href ./ && jar -cvf docker/tomcat/artifacts/content-app.war -C dist/app/ .", "build.tomcat.e2e": "./build-tomcat-e2e.sh", "e2e.tomcat": "npm run wd:update && protractor --baseUrl=http://localhost:4000/content-app/", "docker.tomcat.start": "cd docker/tomcat && docker-compose up -d --build && npm run wait:app", @@ -78,6 +80,7 @@ "@types/selenium-webdriver": "^3.0.8", "chrome-remote-interface": "^0.26.1", "codelyzer": "^4.5.0", + "cpr": "^3.0.1", "cspell": "^3.1.3", "jasmine-core": "~2.8.0", "jasmine-reporters": "^2.2.1", diff --git a/projects/adf-office-services-ext/LICENSE b/projects/adf-office-services-ext/LICENSE new file mode 100644 index 000000000..65c5ca88a --- /dev/null +++ b/projects/adf-office-services-ext/LICENSE @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/projects/adf-office-services-ext/README.md b/projects/adf-office-services-ext/README.md new file mode 100644 index 000000000..6d04ec313 --- /dev/null +++ b/projects/adf-office-services-ext/README.md @@ -0,0 +1,24 @@ +# AdfOfficeServicesExt + +This library was generated with [Angular CLI](https://github.com/angular/angular-cli) version 7.2.0. + +## Code scaffolding + +Run `ng generate component component-name --project adf-office-services-ext` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module --project adf-office-services-ext`. +> Note: Don't forget to add `--project adf-office-services-ext` or else it will be added to the default project in your `angular.json` file. + +## Build + +Run `ng build adf-office-services-ext` to build the project. The build artifacts will be stored in the `dist/` directory. + +## Publishing + +After building your library with `ng build adf-office-services-ext`, go to the dist folder `cd dist/adf-office-services-ext` and run `npm publish`. + +## Running unit tests + +Run `ng test adf-office-services-ext` to execute the unit tests via [Karma](https://karma-runner.github.io). + +## Further help + +To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md). diff --git a/projects/adf-office-services-ext/assets/aos.plugin.json b/projects/adf-office-services-ext/assets/aos.plugin.json new file mode 100644 index 000000000..115574af3 --- /dev/null +++ b/projects/adf-office-services-ext/assets/aos.plugin.json @@ -0,0 +1,70 @@ +{ + "$schema": "../../../extension.schema.json", + "$id": "9a635542-d87a-4558-ae64-ffa199d1a364", + "$version": "1.0.0", + "$name": "keensoft.aos.plugin", + "$description": "Extension that provides Office Edit Online Action", + "$vendor": "Keensoft", + "$license": "LGPL-3.0", + "$runtime": "1.6.0", + + "actions": [ + { + "id": "aos.openWith.office", + "type": "AOS_ACTION", + "payload": "$(context.selection.first.entry)" + } + ], + + "features": { + "toolbar": [ + { + "id": "app.toolbar.more", + "children": [ + { + "id": "aos.toolbar.openWith.office", + "order": 90, + "icon": "adf:application/msword", + "title": "Edit in Microsoft Office™", + "actions": { + "click": "aos.openWith.office" + }, + "rules": { + "visible": "aos.canOpenWithOffice" + } + } + ] + } + ], + "contextMenu": [ + { + "id": "aos.context.openWith.office", + "order": 90, + "icon": "adf:application/msword", + "title": "Edit in Microsoft Office™", + "actions": { + "click": "aos.openWith.office" + }, + "rules": { + "visible": "aos.canOpenWithOffice" + } + } + ], + "viewer": { + "openWith": [ + { + "id": "aos.viewer.openWith.office", + "type": "button", + "icon": "adf:application/msword", + "title": "Microsoft Office™", + "actions": { + "click": "aos.openWith.office" + }, + "rules": { + "visible": "aos.canOpenWithOffice" + } + } + ] + } + } +} diff --git a/projects/adf-office-services-ext/karma.conf.js b/projects/adf-office-services-ext/karma.conf.js new file mode 100644 index 000000000..dd9b7e664 --- /dev/null +++ b/projects/adf-office-services-ext/karma.conf.js @@ -0,0 +1,34 @@ +// Karma configuration file, see link for more information +// https://karma-runner.github.io/1.0/config/configuration-file.html + +module.exports = function(config) { + config.set({ + basePath: '', + frameworks: ['jasmine', '@angular-devkit/build-angular'], + plugins: [ + require('karma-jasmine'), + require('karma-chrome-launcher'), + require('karma-jasmine-html-reporter'), + require('karma-coverage-istanbul-reporter'), + require('@angular-devkit/build-angular/plugins/karma') + ], + client: { + clearContext: false // leave Jasmine Spec Runner output visible in browser + }, + coverageIstanbulReporter: { + dir: require('path').join( + __dirname, + '../../coverage/adf-office-services-ext' + ), + reports: ['html', 'lcovonly'], + fixWebpackSourcePaths: true + }, + reporters: ['progress', 'kjhtml'], + port: 9876, + colors: true, + logLevel: config.LOG_INFO, + autoWatch: true, + browsers: ['Chrome'], + singleRun: false + }); +}; diff --git a/projects/adf-office-services-ext/ng-package.json b/projects/adf-office-services-ext/ng-package.json new file mode 100644 index 000000000..c63ea38c6 --- /dev/null +++ b/projects/adf-office-services-ext/ng-package.json @@ -0,0 +1,14 @@ +{ + "$schema": "../../node_modules/ng-packagr/ng-package.schema.json", + "dest": "../../dist/@alfresco/adf-office-services-ext", + "lib": { + "entryFile": "src/public_api.ts", + "flatModuleFile": "adf-office-services-ext", + "umdModuleIds": { + "@alfresco/js-api": "@alfresco/js-api", + "@alfresco/adf-core": "@alfresco/adf-core", + "@alfresco/adf-extensions": "@alfresco/adf-extensions", + "@ngrx/effects": "@ngrx/effects" + } + } +} diff --git a/projects/adf-office-services-ext/ngi.json b/projects/adf-office-services-ext/ngi.json new file mode 100644 index 000000000..471c8fb4d --- /dev/null +++ b/projects/adf-office-services-ext/ngi.json @@ -0,0 +1,15 @@ +{ + "assets": [ + { + "glob": "**/*.json", + "input": "./assets", + "output": "./assets/plugins" + } + ], + "modules": [ + { + "name": "AosExtensionModule", + "namespace": "@alfresco/adf-office-services-ext" + } + ] +} diff --git a/projects/adf-office-services-ext/package.json b/projects/adf-office-services-ext/package.json new file mode 100644 index 000000000..77d977ebe --- /dev/null +++ b/projects/adf-office-services-ext/package.json @@ -0,0 +1,17 @@ +{ + "name": "@alfresco/adf-office-services-ext", + "version": "1.0.0", + "license": "LGPL-3.0", + "author": { + "name": "Keensoft", + "url": "http://www.keensoft.es/en/" + }, + "peerDependencies": { + "@angular/common": "^7.2.0", + "@angular/core": "^7.2.0", + "@ngrx/effects": "^7.2.0", + "@ngrx/store": "^7.2.0", + "@alfresco/adf-extensions": "3.0.0-3a5fe3fb92cdc7397cbfd3d0869833ea1f1fe8d5", + "@alfresco/js-api": "3.0.0-d7850f421268e21861e2cd219441b7343efd27ba" + } +} diff --git a/projects/adf-office-services-ext/src/lib/actions/aos.actions.ts b/projects/adf-office-services-ext/src/lib/actions/aos.actions.ts new file mode 100755 index 000000000..1a3bd37a0 --- /dev/null +++ b/projects/adf-office-services-ext/src/lib/actions/aos.actions.ts @@ -0,0 +1,9 @@ +import { Action } from '@ngrx/store'; +import { MinimalNodeEntryEntity } from '@alfresco/js-api'; + +export const AOS_ACTION = 'AOS_ACTION'; + +export class AosAction implements Action { + readonly type = AOS_ACTION; + constructor(public payload: MinimalNodeEntryEntity) {} +} diff --git a/projects/adf-office-services-ext/src/lib/aos-extension.module.ts b/projects/adf-office-services-ext/src/lib/aos-extension.module.ts new file mode 100644 index 000000000..9f6065730 --- /dev/null +++ b/projects/adf-office-services-ext/src/lib/aos-extension.module.ts @@ -0,0 +1,20 @@ +import { ExtensionService } from '@alfresco/adf-extensions'; +import { NgModule } from '@angular/core'; +import { EffectsModule } from '@ngrx/effects'; + +import { AosEditOnlineService } from './aos-extension.service'; +import { AosEffects } from './effects/aos.effects'; + +import { canOpenWithOffice } from './evaluators'; + +@NgModule({ + imports: [EffectsModule.forFeature([AosEffects])], + providers: [AosEditOnlineService] +}) +export class AosExtensionModule { + constructor(extensions: ExtensionService) { + extensions.setEvaluators({ + 'aos.canOpenWithOffice': canOpenWithOffice + }); + } +} diff --git a/projects/adf-office-services-ext/src/lib/aos-extension.service.ts b/projects/adf-office-services-ext/src/lib/aos-extension.service.ts new file mode 100644 index 000000000..3c89204cc --- /dev/null +++ b/projects/adf-office-services-ext/src/lib/aos-extension.service.ts @@ -0,0 +1,131 @@ +/* cspell:disable */ +import { + AppConfigService, + AuthenticationService, + NotificationService, + AlfrescoApiService +} from '@alfresco/adf-core'; +import { Injectable } from '@angular/core'; +import { MinimalNodeEntryEntity } from '@alfresco/js-api'; +import { supportedExtensions, getFileExtension } from './utils'; + +@Injectable({ + providedIn: 'root' +}) +export class AosEditOnlineService { + constructor( + private alfrescoAuthenticationService: AuthenticationService, + private appConfigService: AppConfigService, + private notificationService: NotificationService, + private apiService: AlfrescoApiService + ) {} + + onActionEditOnlineAos(node: MinimalNodeEntryEntity): void { + if (node && node.isFile && node.properties) { + if (node.isLocked) { + // const checkedOut = node.aspectNames.find( + // (aspect: string) => aspect === 'cm:checkedOut' + // ); + const checkedOut = node.properties['cm:lockType'] === 'WRITE_LOCK'; + const lockOwner = node.properties['cm:lockOwner']; + const differentLockOwner = + lockOwner.id !== this.alfrescoAuthenticationService.getEcmUsername(); + + if (checkedOut && differentLockOwner) { + this.onAlreadyLockedNotification(node.id, lockOwner); + } else { + this.triggerEditOnlineAos(node); + } + } else { + this.triggerEditOnlineAos(node); + } + } + } + + private getUserAgent(): string { + return navigator.userAgent.toLowerCase(); + } + + private isWindows(): boolean { + return this.getUserAgent().indexOf('win') !== -1 ? true : false; + } + + private isMacOs(): boolean { + return this.getUserAgent().indexOf('mac') !== -1 ? true : false; + } + + private onAlreadyLockedNotification(nodeId: string, lockOwner: string) { + this.notificationService.openSnackMessage( + `Document {nodeId} locked by {lockOwner}`, + 3000 + ); + } + + private getProtocolForFileExtension(fileExtension: string) { + return supportedExtensions[fileExtension]; + } + + private triggerEditOnlineAos(node: MinimalNodeEntryEntity): void { + const aosHost = this.appConfigService.get('aosHost'); + const url = `${aosHost}/_aos_nodeid/${node.id}/${node.name}`; + const fileExtension = getFileExtension(node.name); + const protocolHandler = this.getProtocolForFileExtension(fileExtension); + + if (protocolHandler === undefined) { + this.notificationService.openSnackMessage( + `No protocol handler found for {fileExtension}`, + 3000 + ); + return; + } + + if (!this.isWindows() && !this.isMacOs()) { + this.notificationService.openSnackMessage( + 'Only supported for Windows and Mac', + 3000 + ); + } else { + this.apiService.nodesApi + .lockNode(node.id, { + type: 'ALLOW_OWNER_CHANGES', + lifetime: 'PERSISTENT' + }) + .then( + () => { + this.aos_tryToLaunchOfficeByMsProtocolHandler(protocolHandler, url); + }, + () => { + this.notificationService.openSnackMessage( + 'Cannot lock the node for editing.', + 3000 + ); + } + ); + } + } + + private aos_tryToLaunchOfficeByMsProtocolHandler( + protocolHandler: string, + url: string + ) { + const protocolUrl = protocolHandler + ':ofe%7Cu%7C' + url; + + const input = document.createElement('input'); + const inputTop = document.body.scrollTop + 10; + input.setAttribute( + 'style', + 'z-index: 1000; background-color: rgba(0, 0, 0, 0); ' + + 'border: none; outline: none; position: absolute; left: 10px; top: ' + + inputTop + + 'px;' + ); + document.getElementsByTagName('body')[0].appendChild(input); + input.focus(); + location.href = protocolUrl; + + setTimeout(function() { + input.onblur = null; + input.remove(); + }, 500); + } +} diff --git a/projects/adf-office-services-ext/src/lib/effects/aos.effects.ts b/projects/adf-office-services-ext/src/lib/effects/aos.effects.ts new file mode 100755 index 000000000..b2fee2842 --- /dev/null +++ b/projects/adf-office-services-ext/src/lib/effects/aos.effects.ts @@ -0,0 +1,24 @@ +import { Injectable } from '@angular/core'; +import { Actions, Effect, ofType } from '@ngrx/effects'; +import { map } from 'rxjs/operators'; + +import { AOS_ACTION, AosAction } from '../actions/aos.actions'; +import { AosEditOnlineService } from '../aos-extension.service'; + +@Injectable() +export class AosEffects { + constructor( + private actions$: Actions, + private aosEditOnlineService: AosEditOnlineService + ) {} + + @Effect({ dispatch: false }) + openOffice$ = this.actions$.pipe( + ofType(AOS_ACTION), + map(action => { + if (action.payload) { + this.aosEditOnlineService.onActionEditOnlineAos(action.payload); + } + }) + ); +} diff --git a/projects/adf-office-services-ext/src/lib/evaluators.ts b/projects/adf-office-services-ext/src/lib/evaluators.ts new file mode 100644 index 000000000..992e672de --- /dev/null +++ b/projects/adf-office-services-ext/src/lib/evaluators.ts @@ -0,0 +1,45 @@ +import { RuleContext, RuleParameter } from '@alfresco/adf-extensions'; +import { getFileExtension, supportedExtensions } from './utils'; + +export function canOpenWithOffice( + context: RuleContext, + ...args: RuleParameter[] +): boolean { + const file = context.selection.file; + + if (!file || !file.entry || !file.entry.properties) { + return false; + } + + if (file.entry.isLocked) { + return false; + } + + const extension = getFileExtension(file.entry.name); + if (!extension || !supportedExtensions[extension]) { + return false; + } + + /* + if (file.entry && file.entry.aspectNames) { + const checkedOut = file.entry.aspectNames.find( + (aspect: string) => aspect === 'cm:checkedOut' + ); + + if (checkedOut) { + return false; + } + } + */ + + if (file.entry.properties['cm:lockType'] === 'WRITE_LOCK') { + return false; + } + + const lockOwner = file.entry.properties['cm:lockOwner']; + if (lockOwner && lockOwner.id !== context.profile.id) { + return false; + } + + return true; +} diff --git a/projects/adf-office-services-ext/src/lib/utils.ts b/projects/adf-office-services-ext/src/lib/utils.ts new file mode 100644 index 000000000..43bfa0684 --- /dev/null +++ b/projects/adf-office-services-ext/src/lib/utils.ts @@ -0,0 +1,37 @@ +/* cspell:disable */ +export const supportedExtensions = { + doc: 'ms-word', + docx: 'ms-word', + docm: 'ms-word', + dot: 'ms-word', + dotx: 'ms-word', + dotm: 'ms-word', + xls: 'ms-excel', + xlsx: 'ms-excel', + xlsb: 'ms-excel', + xlsm: 'ms-excel', + xlt: 'ms-excel', + xltx: 'ms-excel', + xltm: 'ms-excel', + ppt: 'ms-powerpoint', + pptx: 'ms-powerpoint', + pot: 'ms-powerpoint', + potx: 'ms-powerpoint', + potm: 'ms-powerpoint', + pptm: 'ms-powerpoint', + pps: 'ms-powerpoint', + ppsx: 'ms-powerpoint', + ppam: 'ms-powerpoint', + ppsm: 'ms-powerpoint', + sldx: 'ms-powerpoint', + sldm: 'ms-powerpoint' +}; +/* cspell:enable */ + +export function getFileExtension(fileName: string): string { + if (fileName) { + const match = fileName.match(/\.([^\./\?\#]+)($|\?|\#)/); + return match ? match[1] : null; + } + return null; +} diff --git a/projects/adf-office-services-ext/src/public_api.ts b/projects/adf-office-services-ext/src/public_api.ts new file mode 100644 index 000000000..af56fbcbe --- /dev/null +++ b/projects/adf-office-services-ext/src/public_api.ts @@ -0,0 +1,5 @@ +export * from './lib/aos-extension.service'; +export * from './lib/actions/aos.actions'; +export * from './lib/effects/aos.effects'; + +export * from './lib/aos-extension.module'; diff --git a/projects/adf-office-services-ext/src/test.ts b/projects/adf-office-services-ext/src/test.ts new file mode 100644 index 000000000..e11ff1c97 --- /dev/null +++ b/projects/adf-office-services-ext/src/test.ts @@ -0,0 +1,22 @@ +// This file is required by karma.conf.js and loads recursively all the .spec and framework files + +import 'core-js/es7/reflect'; +import 'zone.js/dist/zone'; +import 'zone.js/dist/zone-testing'; +import { getTestBed } from '@angular/core/testing'; +import { + BrowserDynamicTestingModule, + platformBrowserDynamicTesting +} from '@angular/platform-browser-dynamic/testing'; + +declare const require: any; + +// First, initialize the Angular testing environment. +getTestBed().initTestEnvironment( + BrowserDynamicTestingModule, + platformBrowserDynamicTesting() +); +// Then we find all the tests. +const context = require.context('./', true, /\.spec\.ts$/); +// And load the modules. +context.keys().map(context); diff --git a/projects/adf-office-services-ext/tsconfig.lib.json b/projects/adf-office-services-ext/tsconfig.lib.json new file mode 100644 index 000000000..3fe337fcf --- /dev/null +++ b/projects/adf-office-services-ext/tsconfig.lib.json @@ -0,0 +1,32 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "../../out-tsc/lib", + "target": "es2015", + "module": "es2015", + "moduleResolution": "node", + "declaration": true, + "sourceMap": true, + "inlineSources": true, + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "importHelpers": true, + "types": [], + "lib": [ + "dom", + "es2018" + ] + }, + "angularCompilerOptions": { + "annotateForClosureCompiler": true, + "skipTemplateCodegen": true, + "strictMetadataEmit": true, + "fullTemplateTypeCheck": true, + "strictInjectionParameters": true, + "enableResourceInlining": true + }, + "exclude": [ + "src/test.ts", + "**/*.spec.ts" + ] +} diff --git a/projects/adf-office-services-ext/tsconfig.spec.json b/projects/adf-office-services-ext/tsconfig.spec.json new file mode 100644 index 000000000..16da33db0 --- /dev/null +++ b/projects/adf-office-services-ext/tsconfig.spec.json @@ -0,0 +1,17 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "../../out-tsc/spec", + "types": [ + "jasmine", + "node" + ] + }, + "files": [ + "src/test.ts" + ], + "include": [ + "**/*.spec.ts", + "**/*.d.ts" + ] +} diff --git a/projects/adf-office-services-ext/tslint.json b/projects/adf-office-services-ext/tslint.json new file mode 100644 index 000000000..124133f84 --- /dev/null +++ b/projects/adf-office-services-ext/tslint.json @@ -0,0 +1,17 @@ +{ + "extends": "../../tslint.json", + "rules": { + "directive-selector": [ + true, + "attribute", + "lib", + "camelCase" + ], + "component-selector": [ + true, + "element", + "lib", + "kebab-case" + ] + } +} diff --git a/src/app.config.json b/src/app.config.json index dc8b4edc8..1295eebec 100644 --- a/src/app.config.json +++ b/src/app.config.json @@ -1,5 +1,6 @@ { "ecmHost": "{protocol}//{hostname}{:port}", + "aosHost": "{protocol}//{hostname}{:port}/alfresco/aos", "baseShareUrl": null, "providers": "ECM", "authType": "BASIC", diff --git a/src/app/extensions.module.ts b/src/app/extensions.module.ts index 03cebd028..071595f62 100644 --- a/src/app/extensions.module.ts +++ b/src/app/extensions.module.ts @@ -1,9 +1,10 @@ import { NgModule } from '@angular/core'; +import { AosExtensionModule } from '@alfresco/adf-office-services-ext'; // Main entry point for external extensions only. // For any application-specific code use CoreExtensionsModule instead. @NgModule({ - imports: [] + imports: [AosExtensionModule] }) export class AppExtensionsModule {} diff --git a/src/assets/app.extensions.json b/src/assets/app.extensions.json index 8b9e83629..0c49bb2a9 100644 --- a/src/assets/app.extensions.json +++ b/src/assets/app.extensions.json @@ -7,7 +7,7 @@ "$license": "LGPL-3.0", "$runtime": "1.5.0", "$description": "Core application extensions and features", - "$references": [], + "$references": ["aos.plugin.json"], "rules": [ { diff --git a/tsconfig.json b/tsconfig.json index f06fa76b9..b6dad6889 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -14,7 +14,14 @@ "lib": ["es2018", "dom"], "module": "es2015", "baseUrl": "./", - "paths": {}, + "paths": { + "@alfresco/adf-office-services-ext": [ + "dist/@alfresco/adf-office-services-ext" + ], + "@alfresco/adf-office-services-ext/*": [ + "dist/@alfresco/adf-office-services-ext/*" + ] + }, "resolveJsonModule": true }, "exclude": ["node_modules"],