mirror of
https://github.com/Alfresco/alfresco-content-app.git
synced 2025-07-31 17:38:28 +00:00
Keensoft AOS extension (#920)
* aos extension * update karma * lint improvements and fixes * update script * update scripts * update scripts * reduce about 56.4 kB of polyfills for modern browsers * deficated aosHost value to have AOS on separate servers * context menu and toolbar * visibility evaluator * code improvements and fixes * support locking, lock checks
This commit is contained in:
committed by
Cilibiu Bogdan
parent
819f6ddced
commit
65e0a1138c
@@ -20,8 +20,6 @@ jobs:
|
|||||||
name: 'Code quality checks'
|
name: 'Code quality checks'
|
||||||
script:
|
script:
|
||||||
- npm run lint
|
- npm run lint
|
||||||
- npm run spellcheck
|
|
||||||
- npm run format:check
|
|
||||||
- stage: test
|
- stage: test
|
||||||
name: 'Unit tests'
|
name: 'Unit tests'
|
||||||
script:
|
script:
|
||||||
|
53
angular.json
53
angular.json
@@ -45,15 +45,20 @@
|
|||||||
"input": "node_modules/pdfjs-dist/build",
|
"input": "node_modules/pdfjs-dist/build",
|
||||||
"output": "/"
|
"output": "/"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"glob": "**/*.json",
|
|
||||||
"input": "node_modules/@denysvuika/aca-dev-tools/assets",
|
|
||||||
"output": "./assets/plugins"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"glob": "extension.schema.json",
|
"glob": "extension.schema.json",
|
||||||
"input": ".",
|
"input": ".",
|
||||||
"output": "./assets"
|
"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": [
|
"styles": [
|
||||||
@@ -65,7 +70,8 @@
|
|||||||
"node_modules/pdfjs-dist/build/pdf.js",
|
"node_modules/pdfjs-dist/build/pdf.js",
|
||||||
"node_modules/pdfjs-dist/web/pdf_viewer.js",
|
"node_modules/pdfjs-dist/web/pdf_viewer.js",
|
||||||
"node_modules/moment/min/moment.min.js"
|
"node_modules/moment/min/moment.min.js"
|
||||||
]
|
],
|
||||||
|
"es5BrowserSupport": true
|
||||||
},
|
},
|
||||||
"configurations": {
|
"configurations": {
|
||||||
"production": {
|
"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",
|
"defaultProject": "app",
|
||||||
|
12
package-lock.json
generated
12
package-lock.json
generated
@@ -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": {
|
"create-ecdh": {
|
||||||
"version": "4.0.3",
|
"version": "4.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.3.tgz",
|
||||||
|
17
package.json
17
package.json
@@ -4,14 +4,16 @@
|
|||||||
"license": "LGPL-3.0",
|
"license": "LGPL-3.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"ng": "ng",
|
"ng": "ng",
|
||||||
"start": "ng serve --open",
|
"start": "npm run build.extensions && ng serve --open",
|
||||||
"start:prod": "ng serve --prod --open",
|
"start:prod": "ng serve --prod --open",
|
||||||
"build": "node --max-old-space-size=8192 node_modules/@angular/cli/bin/ng build app --prod",
|
"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:dev": "ng build",
|
"build.extensions": "npm run build:aos-extension",
|
||||||
"build.e2e": "node --max-old-space-size=8192 node_modules/@angular/cli/bin/ng build app --configuration=e2e",
|
"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": "ng test app --code-coverage",
|
||||||
"test:ci": "ng test app --code-coverage --watch=false",
|
"test:ci": "npm run build.extensions && ng test app --code-coverage --watch=false",
|
||||||
"lint": "ng lint",
|
"lint": "ng lint && npm run spellcheck && npm run format:check",
|
||||||
"wd:update": "webdriver-manager update --gecko=false",
|
"wd:update": "webdriver-manager update --gecko=false",
|
||||||
"e2e": "npm run wd:update && protractor --baseUrl=http://localhost:4000",
|
"e2e": "npm run wd:update && protractor --baseUrl=http://localhost:4000",
|
||||||
"e2e.local": "npm run wd:update && protractor --baseUrl=http://localhost:4200",
|
"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",
|
"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:check": "prettier --check \"src/{app,environments}/**/*.{ts,js,css,scss,html}\"",
|
||||||
"format:fix": "prettier --write \"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",
|
"build.tomcat.e2e": "./build-tomcat-e2e.sh",
|
||||||
"e2e.tomcat": "npm run wd:update && protractor --baseUrl=http://localhost:4000/content-app/",
|
"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",
|
"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",
|
"@types/selenium-webdriver": "^3.0.8",
|
||||||
"chrome-remote-interface": "^0.26.1",
|
"chrome-remote-interface": "^0.26.1",
|
||||||
"codelyzer": "^4.5.0",
|
"codelyzer": "^4.5.0",
|
||||||
|
"cpr": "^3.0.1",
|
||||||
"cspell": "^3.1.3",
|
"cspell": "^3.1.3",
|
||||||
"jasmine-core": "~2.8.0",
|
"jasmine-core": "~2.8.0",
|
||||||
"jasmine-reporters": "^2.2.1",
|
"jasmine-reporters": "^2.2.1",
|
||||||
|
165
projects/adf-office-services-ext/LICENSE
Normal file
165
projects/adf-office-services-ext/LICENSE
Normal file
@@ -0,0 +1,165 @@
|
|||||||
|
GNU LESSER GENERAL PUBLIC LICENSE
|
||||||
|
Version 3, 29 June 2007
|
||||||
|
|
||||||
|
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||||
|
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.
|
24
projects/adf-office-services-ext/README.md
Normal file
24
projects/adf-office-services-ext/README.md
Normal file
@@ -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).
|
70
projects/adf-office-services-ext/assets/aos.plugin.json
Normal file
70
projects/adf-office-services-ext/assets/aos.plugin.json
Normal file
@@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
34
projects/adf-office-services-ext/karma.conf.js
Normal file
34
projects/adf-office-services-ext/karma.conf.js
Normal file
@@ -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
|
||||||
|
});
|
||||||
|
};
|
14
projects/adf-office-services-ext/ng-package.json
Normal file
14
projects/adf-office-services-ext/ng-package.json
Normal file
@@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
15
projects/adf-office-services-ext/ngi.json
Normal file
15
projects/adf-office-services-ext/ngi.json
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"assets": [
|
||||||
|
{
|
||||||
|
"glob": "**/*.json",
|
||||||
|
"input": "./assets",
|
||||||
|
"output": "./assets/plugins"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"modules": [
|
||||||
|
{
|
||||||
|
"name": "AosExtensionModule",
|
||||||
|
"namespace": "@alfresco/adf-office-services-ext"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
17
projects/adf-office-services-ext/package.json
Normal file
17
projects/adf-office-services-ext/package.json
Normal file
@@ -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"
|
||||||
|
}
|
||||||
|
}
|
9
projects/adf-office-services-ext/src/lib/actions/aos.actions.ts
Executable file
9
projects/adf-office-services-ext/src/lib/actions/aos.actions.ts
Executable file
@@ -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) {}
|
||||||
|
}
|
@@ -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
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
24
projects/adf-office-services-ext/src/lib/effects/aos.effects.ts
Executable file
24
projects/adf-office-services-ext/src/lib/effects/aos.effects.ts
Executable file
@@ -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<AosAction>(AOS_ACTION),
|
||||||
|
map(action => {
|
||||||
|
if (action.payload) {
|
||||||
|
this.aosEditOnlineService.onActionEditOnlineAos(action.payload);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
45
projects/adf-office-services-ext/src/lib/evaluators.ts
Normal file
45
projects/adf-office-services-ext/src/lib/evaluators.ts
Normal file
@@ -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;
|
||||||
|
}
|
37
projects/adf-office-services-ext/src/lib/utils.ts
Normal file
37
projects/adf-office-services-ext/src/lib/utils.ts
Normal file
@@ -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;
|
||||||
|
}
|
5
projects/adf-office-services-ext/src/public_api.ts
Normal file
5
projects/adf-office-services-ext/src/public_api.ts
Normal file
@@ -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';
|
22
projects/adf-office-services-ext/src/test.ts
Normal file
22
projects/adf-office-services-ext/src/test.ts
Normal file
@@ -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);
|
32
projects/adf-office-services-ext/tsconfig.lib.json
Normal file
32
projects/adf-office-services-ext/tsconfig.lib.json
Normal file
@@ -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"
|
||||||
|
]
|
||||||
|
}
|
17
projects/adf-office-services-ext/tsconfig.spec.json
Normal file
17
projects/adf-office-services-ext/tsconfig.spec.json
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"extends": "../../tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "../../out-tsc/spec",
|
||||||
|
"types": [
|
||||||
|
"jasmine",
|
||||||
|
"node"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"src/test.ts"
|
||||||
|
],
|
||||||
|
"include": [
|
||||||
|
"**/*.spec.ts",
|
||||||
|
"**/*.d.ts"
|
||||||
|
]
|
||||||
|
}
|
17
projects/adf-office-services-ext/tslint.json
Normal file
17
projects/adf-office-services-ext/tslint.json
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"extends": "../../tslint.json",
|
||||||
|
"rules": {
|
||||||
|
"directive-selector": [
|
||||||
|
true,
|
||||||
|
"attribute",
|
||||||
|
"lib",
|
||||||
|
"camelCase"
|
||||||
|
],
|
||||||
|
"component-selector": [
|
||||||
|
true,
|
||||||
|
"element",
|
||||||
|
"lib",
|
||||||
|
"kebab-case"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
@@ -1,5 +1,6 @@
|
|||||||
{
|
{
|
||||||
"ecmHost": "{protocol}//{hostname}{:port}",
|
"ecmHost": "{protocol}//{hostname}{:port}",
|
||||||
|
"aosHost": "{protocol}//{hostname}{:port}/alfresco/aos",
|
||||||
"baseShareUrl": null,
|
"baseShareUrl": null,
|
||||||
"providers": "ECM",
|
"providers": "ECM",
|
||||||
"authType": "BASIC",
|
"authType": "BASIC",
|
||||||
|
@@ -1,9 +1,10 @@
|
|||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
|
import { AosExtensionModule } from '@alfresco/adf-office-services-ext';
|
||||||
|
|
||||||
// Main entry point for external extensions only.
|
// Main entry point for external extensions only.
|
||||||
// For any application-specific code use CoreExtensionsModule instead.
|
// For any application-specific code use CoreExtensionsModule instead.
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: []
|
imports: [AosExtensionModule]
|
||||||
})
|
})
|
||||||
export class AppExtensionsModule {}
|
export class AppExtensionsModule {}
|
||||||
|
@@ -7,7 +7,7 @@
|
|||||||
"$license": "LGPL-3.0",
|
"$license": "LGPL-3.0",
|
||||||
"$runtime": "1.5.0",
|
"$runtime": "1.5.0",
|
||||||
"$description": "Core application extensions and features",
|
"$description": "Core application extensions and features",
|
||||||
"$references": [],
|
"$references": ["aos.plugin.json"],
|
||||||
|
|
||||||
"rules": [
|
"rules": [
|
||||||
{
|
{
|
||||||
|
@@ -14,7 +14,14 @@
|
|||||||
"lib": ["es2018", "dom"],
|
"lib": ["es2018", "dom"],
|
||||||
"module": "es2015",
|
"module": "es2015",
|
||||||
"baseUrl": "./",
|
"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
|
"resolveJsonModule": true
|
||||||
},
|
},
|
||||||
"exclude": ["node_modules"],
|
"exclude": ["node_modules"],
|
||||||
|
Reference in New Issue
Block a user