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"],