diff --git a/.editorconfig b/.editorconfig
index 4a874ce23..3a5764abc 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -4,7 +4,7 @@ root = true
[*]
charset = utf-8
indent_style = space
-indent_size = 4
+indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true
diff --git a/.prettierignore b/.prettierignore
new file mode 100644
index 000000000..f48cf2972
--- /dev/null
+++ b/.prettierignore
@@ -0,0 +1,4 @@
+node_modules
+src/assets/i18n
+e2e
+/angular.json
diff --git a/.prettierrc b/.prettierrc
new file mode 100644
index 000000000..544138be4
--- /dev/null
+++ b/.prettierrc
@@ -0,0 +1,3 @@
+{
+ "singleQuote": true
+}
diff --git a/.travis.yml b/.travis.yml
index dbb57886b..5ee60be5d 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,33 +1,40 @@
dist: trusty
sudo: required
-
services:
- docker
-
addons:
chrome: stable
-
+ artifacts:
+ paths:
+ - e2e-output
+ target_paths:
+ - /${TRAVIS_BUILD_NUMBER}
language: node_js
node_js:
- - "8"
-
+ - '8'
before_script:
- # Disable services enabled by default
- sudo /etc/init.d/postgresql stop
-
before_install:
- npm install -g npm@latest
- export DISPLAY=:99.0
- sh -e /etc/init.d/xvfb start
- - sleep 3 # give xvfb some time to start
-
+ - sleep 3
jobs:
include:
- stage: test
- script: npm run lint && npm run spellcheck
+ name: 'Code quality checks'
+ script:
+ - npm run lint
+ - npm run spellcheck
+ - npm run format:check
- stage: test
+ name: 'Unit tests'
script:
- npm run test:ci
- bash <(curl -s https://codecov.io/bash) -X gcov
- stage: e2e
+ name: 'Nginx'
script: npm run build && npm run e2e:docker
+ - stage: e2e
+ name: 'Tomcat'
+ script: npm run build.tomcat.e2e && npm run docker.tomcat.e2e
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 52ec212c3..b4f4f4d5c 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -1,4 +1,13 @@
{
- "javascript.preferences.quoteStyle": "single",
- "typescript.preferences.quoteStyle": "single"
+ "javascript.preferences.quoteStyle": "single",
+ "javascript.preferences.importModuleSpecifier": "relative",
+ "typescript.preferences.quoteStyle": "single",
+ "typescript.preferences.importModuleSpecifier": "relative",
+ "editor.formatOnSave": true,
+ "[html]": {
+ "editor.formatOnSave": false
+ },
+ "[json]": {
+ "editor.formatOnSave": false
+ }
}
diff --git a/Dockerfile b/Dockerfile
index 4541d2f26..bf3087e39 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,9 +1,13 @@
FROM nginx:stable-alpine
-LABEL version="1.3"
+LABEL version="1.4"
LABEL maintainer="Denys Vuika "
COPY nginx.conf /etc/nginx/nginx.conf
+COPY ./docker-entrypoint.sh /
+RUN chmod +x /docker-entrypoint.sh
+
WORKDIR /usr/share/nginx/html
COPY dist/app/ .
+ENTRYPOINT [ "/docker-entrypoint.sh" ]
diff --git a/README.md b/README.md
index 99f75474a..7e4317bab 100644
--- a/README.md
+++ b/README.md
@@ -28,19 +28,24 @@ Isuses can be raised in GitHub or in the Alfresco JIRA project.
Please include a clear description, steps to reproduce and screenshots where appropriate.All issues will be reviewed; bugs will be categorized if reproducible and enhancement/feature suggestions will be considered against existing priorities if the use case serves a general-purpose need.
#### Features added in the latest release
-* Application Extensibility - first phase of extensibilty, see [Documentation](https://alfresco.github.io/alfresco-content-app/#/extending) for details.
-* Sharing Files - activate and deactivate shared file links.
-* Commenting - View and add comments to files and folders.
-* Permissions - update file and folder permissions.
-* Print - print action from the file viewer
-* Improved UX - single click to open folders and files, and right click actions menu
+* Application Extensibility - Phase 2
+ * Document list presets
+ * File viewer actions
+ * Create menu button
+ * Application header
+ * Metadata card configuration
+ * see [Documentation](https://alfresco.github.io/alfresco-content-app/#/extending) for details.
+* Sharing Files
+ * Set expiry date for shared links
+ * Right click action to access shared link settings
+ * Automatic disable of sharing based on respository configuration
Please refer to the [release notes](https://github.com/Alfresco/alfresco-content-app/releases) for details of all changes.
#### High level features planned for H2 2018 (July - December)
-* Library Management - create, find, join and manage file libraries.
-* Sharing Files - add an expiry date to a sharable link.
-* Application Extensibility - Extension framework to provide simple ways to extend the application.
+* Library Management
+ * For end users: Find, join and favorite libraries
+ * For managers: Create libraries, manage users and requests to join
### Want to help?
Want to file a bug, contribute some code, or improve documentation? Excellent! Read up on our guidelines for [contributing](https://github.com/Alfresco/alfresco-content-app/blob/master/CONTRIBUTING.md) and then check out one of our issues in the [Jira](https://issues.alfresco.com/jira/projects/ACA) or [GitHub](https://github.com/Alfresco/alfresco-content-app/issues)
@@ -60,42 +65,6 @@ Want to file a bug, contribute some code, or improve documentation? Excellent! R
| Metadata | The information drawer can be configured in the app.config.json to display metadata information, by default file the Properties Aspect is shown and images will also include EXIF information.|
| Versioning | The version manager provides access and management of previous file versions, and the ability to upload new versions.|
-
-## Development server
-
-Run `npm start` for a dev server. Navigate to `http://localhost:4200/` (opens by default).
-The app will automatically reload if you change any of the source files.
-
-## Build
-
-Run `npm run build` to build the project in the production mode. The build artifacts will be stored in the `dist/` directory.
-
-## Running unit tests
-
-Run `npm test` to execute the unit tests via [Karma](https://karma-runner.github.io).
-
-## Running end-to-end tests
-
-Run the local instance of the application packaged into the docker image together with the ACS images:
-
-```sh
-npm run build
-npm run start:docker
-```
-
-The ACA runs on port 4000 inside the docker container.
-Run `npm run e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).
-
-```sh
-npm run e2e
-```
-
-When testing is over you can stop all corresponding containers:
-
-```sh
-npm run stop:docker
-```
-
## 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/angular.json b/angular.json
index 42c0aba18..bde639d7d 100644
--- a/angular.json
+++ b/angular.json
@@ -47,14 +47,14 @@
"output": "/"
},
{
- "glob": "**/*",
- "input": "node_modules/monaco-editor/min",
- "output": "./assets/monaco"
+ "glob": "**/*.json",
+ "input": "node_modules/@denysvuika/aca-dev-tools/assets",
+ "output": "./assets/plugins"
},
{
- "glob": "**/*.js",
- "input": "node_modules/@ngstack/code-editor/workers",
- "output": "./assets/workers"
+ "glob": "extension.schema.json",
+ "input": ".",
+ "output": "./assets"
}
],
"styles": [
@@ -63,10 +63,9 @@
"src/styles.scss"
],
"scripts": [
- "node_modules/pdfjs-dist/build/pdf.js",
- "node_modules/pdfjs-dist/lib/shared/compatibility.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"
]
},
"configurations": {
@@ -124,7 +123,6 @@
"tsConfig": "src/tsconfig.spec.json",
"scripts": [
"node_modules/pdfjs-dist/build/pdf.js",
- "node_modules/pdfjs-dist/lib/shared/compatibility.js",
"node_modules/pdfjs-dist/web/pdf_viewer.js",
"node_modules/moment/min/moment.min.js"
],
@@ -154,7 +152,7 @@
"output": "/assets"
},
{
- "glob": "pdf.worker.js",
+ "glob": "pdf.worker.min.js",
"input": "node_modules/pdfjs-dist/build",
"output": "/"
}
@@ -204,86 +202,6 @@
}
}
}
- },
- "aca-dev-tools": {
- "root": "projects/aca-dev-tools",
- "sourceRoot": "projects/aca-dev-tools/src",
- "projectType": "library",
- "prefix": "lib",
- "architect": {
- "build": {
- "builder": "@angular-devkit/build-ng-packagr:build",
- "options": {
- "tsConfig": "projects/aca-dev-tools/tsconfig.lib.json",
- "project": "projects/aca-dev-tools/ng-package.json"
- },
- "configurations": {
- "production": {
- "project": "projects/aca-dev-tools/ng-package.prod.json"
- }
- }
- },
- "test": {
- "builder": "@angular-devkit/build-angular:karma",
- "options": {
- "main": "projects/aca-dev-tools/src/test.ts",
- "tsConfig": "projects/aca-dev-tools/tsconfig.spec.json",
- "karmaConfig": "projects/aca-dev-tools/karma.conf.js"
- }
- },
- "lint": {
- "builder": "@angular-devkit/build-angular:tslint",
- "options": {
- "tsConfig": [
- "projects/aca-dev-tools/tsconfig.lib.json",
- "projects/aca-dev-tools/tsconfig.spec.json"
- ],
- "exclude": [
- "**/node_modules/**"
- ]
- }
- }
- }
- },
- "adf-extensions": {
- "root": "projects/adf-extensions",
- "sourceRoot": "projects/adf-extensions/src",
- "projectType": "library",
- "prefix": "lib",
- "architect": {
- "build": {
- "builder": "@angular-devkit/build-ng-packagr:build",
- "options": {
- "tsConfig": "projects/adf-extensions/tsconfig.lib.json",
- "project": "projects/adf-extensions/ng-package.json"
- },
- "configurations": {
- "production": {
- "project": "projects/adf-extensions/ng-package.prod.json"
- }
- }
- },
- "test": {
- "builder": "@angular-devkit/build-angular:karma",
- "options": {
- "main": "projects/adf-extensions/src/test.ts",
- "tsConfig": "projects/adf-extensions/tsconfig.spec.json",
- "karmaConfig": "projects/adf-extensions/karma.conf.js"
- }
- },
- "lint": {
- "builder": "@angular-devkit/build-angular:tslint",
- "options": {
- "tsConfig": [
- "projects/adf-extensions/tsconfig.lib.json",
- "projects/adf-extensions/tsconfig.spec.json"
- ],
- "exclude": [
- "**/node_modules/**"
- ]
- }
- }
- }
}
},
"defaultProject": "app",
diff --git a/build-tomcat-e2e.sh b/build-tomcat-e2e.sh
new file mode 100755
index 000000000..849e7a464
--- /dev/null
+++ b/build-tomcat-e2e.sh
@@ -0,0 +1,13 @@
+npm run build -- --base-href ./
+
+node -e "
+const fs = require('fs');
+const config = require('./dist/app/app.config.json');
+config.baseShareUrl = 'http://localhost:4000/content-app';
+fs.writeFileSync(
+ './dist/app/app.config.json',
+ JSON.stringify(config, null, 2)
+);
+"
+
+jar -cvf docker/tomcat/artifacts/content-app.war -C dist/app/ .
diff --git a/cspell.json b/cspell.json
index ddb8d9a9e..c0c95e14f 100644
--- a/cspell.json
+++ b/cspell.json
@@ -1,56 +1,60 @@
{
- "version": "0.1",
- "language": "en",
- "words": [
- "succes",
- "sharedlinks",
+ "version": "0.1",
+ "language": "en",
+ "words": [
+ "succes",
+ "sharedlinks",
+ "Redistributable",
+ "fullscreen",
- "ngrx",
- "ngstack",
- "sidenav",
- "injectable",
- "truthy",
- "cryptodoc",
- "mysites",
- "afts",
- "classlist",
- "folderlink",
- "filelink",
- "datatable",
- "repo",
- "snackbar",
- "promisify",
- "xdescribe",
- "unfavorite",
- "devtools",
- "gitter",
- "jira",
- "markdownlint",
- "uploader",
- "nginx",
- "docx",
- "SOLR",
+ "ngrx",
+ "ngstack",
+ "sidenav",
+ "injectable",
+ "truthy",
+ "cryptodoc",
+ "mysites",
+ "afts",
+ "classlist",
+ "folderlink",
+ "filelink",
+ "formcontrolname",
+ "datetimepicker",
+ "datatable",
+ "repo",
+ "snackbar",
+ "promisify",
+ "xdescribe",
+ "unfavorite",
+ "devtools",
+ "gitter",
+ "jira",
+ "markdownlint",
+ "uploader",
+ "nginx",
+ "docx",
+ "SOLR",
- "unshare",
- "validators",
- "guid",
- "polyfill",
- "polyfills",
- "jsonp",
- "hammerjs",
- "pdfjs",
- "xpath",
- "tooltip",
- "tooltips",
- "unindent",
- "exif",
- "cardview",
- "webm",
- "keycodes"
- ],
- "dictionaries": [
- "html",
- "en-gb",
- "en_US"
- ]
+ "unshare",
+ "qshare",
+ "validators",
+ "guid",
+ "polyfill",
+ "polyfills",
+ "jsonp",
+ "hammerjs",
+ "pdfjs",
+ "xpath",
+ "tooltip",
+ "tooltips",
+ "unindent",
+ "exif",
+ "cardview",
+ "webm",
+ "keycodes",
+ "denysvuika",
+ "submenu",
+ "submenus"
+ ],
+ "dictionaries": ["html", "en-gb", "en_US"]
}
diff --git a/docker-compose.yml b/docker-compose.yml
index 88b9d750a..e48454677 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -1,95 +1,105 @@
-version: "3"
+version: '3'
services:
- alfresco:
- image: alfresco/alfresco-content-repository-community:6.0.7-ga
- depends_on:
- - postgres
- environment:
- JAVA_OPTS : "
- -Ddb.driver=org.postgresql.Driver
- -Ddb.username=alfresco
- -Ddb.password=alfresco
- -Ddb.url=jdbc:postgresql://postgres:5432/alfresco
- -Dsolr.host=solr6
- -Dsolr.port=8983
- -Dsolr.secureComms=none
- -Dsolr.base.url=/solr
- -Dindex.subsystem.name=solr6
- -Dshare.host=localhost
- -Ddeployment.method=DOCKER_COMPOSE
- -Dcsrf.filter.enabled=false
- "
- networks:
- - internal
- ports:
- - 8080:8080 #Browser port
+ alfresco:
+ image: alfresco/alfresco-content-repository-community:6.1.0-ea
+ depends_on:
+ - postgres
+ environment:
+ JAVA_OPTS: '
+ -Ddb.driver=org.postgresql.Driver
+ -Ddb.username=alfresco
+ -Ddb.password=alfresco
+ -Ddb.url=jdbc:postgresql://postgres:5432/alfresco
+ -Dsolr.host=solr6
+ -Dsolr.port=8983
+ -Dsolr.secureComms=none
+ -Dsolr.base.url=/solr
+ -Dindex.subsystem.name=solr6
+ -Dshare.host=localhost
+ -Ddeployment.method=DOCKER_COMPOSE
+ -Dcsrf.filter.enabled=false
+ '
+ networks:
+ - internal
+ ports:
+ - 8080:8080 #Browser port
- share:
- image: alfresco/alfresco-share:6.0.b
- depends_on:
- - alfresco
- environment:
- - REPO_HOST=alfresco
- - REPO_PORT=8080
- networks:
- - internal
- ports:
- - 8083:8080
+ share:
+ image: alfresco/alfresco-share:6.0.c
+ depends_on:
+ - alfresco
+ environment:
+ - REPO_HOST=alfresco
+ - REPO_PORT=8080
+ networks:
+ - internal
+ ports:
+ - 8083:8080
- postgres:
- image: postgres:10.1
- environment:
- - POSTGRES_PASSWORD=alfresco
- - POSTGRES_USER=alfresco
- - POSTGRES_DB=alfresco
- command: postgres -c max_connections=300 -c log_min_messages=LOG
- networks:
- - internal
- ports:
- - 5432:5432
+ postgres:
+ image: postgres:10.1
+ environment:
+ - POSTGRES_PASSWORD=alfresco
+ - POSTGRES_USER=alfresco
+ - POSTGRES_DB=alfresco
+ command: postgres -c max_connections=300 -c log_min_messages=LOG
+ networks:
+ - internal
+ ports:
+ - 5432:5432
- solr6:
- image: alfresco/alfresco-search-services:1.1.1
- depends_on:
- - alfresco
- environment:
- #Solr needs to know how to register itself with Alfresco
- - SOLR_ALFRESCO_HOST=alfresco
- - SOLR_ALFRESCO_PORT=8080
- #Alfresco needs to know how to call solr
- - SOLR_SOLR_HOST=solr6
- - SOLR_SOLR_PORT=8983
- #Create the default alfresco and archive cores
- - SOLR_CREATE_ALFRESCO_DEFAULTS=alfresco,archive
- networks:
- - internal
- ports:
- - 8983:8983 #Browser port
+ solr6:
+ image: alfresco/alfresco-search-services:1.2.0
+ depends_on:
+ - alfresco
+ environment:
+ #Solr needs to know how to register itself with Alfresco
+ - SOLR_ALFRESCO_HOST=alfresco
+ - SOLR_ALFRESCO_PORT=8080
+ #Alfresco needs to know how to call solr
+ - SOLR_SOLR_HOST=solr6
+ - SOLR_SOLR_PORT=8983
+ #Create the default alfresco and archive cores
+ - SOLR_CREATE_ALFRESCO_DEFAULTS=alfresco,archive
+ networks:
+ - internal
+ ports:
+ - 8983:8983 #Browser port
- content-app:
- image: alfresco/alfresco-content-app:latest
- build: .
- depends_on:
- - alfresco
- networks:
- - internal
- ports:
- - 4001:80
- # volumes:
- # - ./app.config.json:/usr/share/nginx/html/app.config.json
- # - ./nginx.conf:/etc/nginx/conf.d/default.conf
+ activemq:
+ image: alfresco/alfresco-activemq:5.15.6
+ networks:
+ - internal
+ ports:
+ - 8161:8161 # Web Console
+ - 5672:5672 # AMQP
+ - 61616:61616 # OpenWire
+ - 61613:61613 # STOMP
- proxy:
- image: nginx:stable-alpine
- depends_on:
- - content-app
- volumes:
- - ./docker-compose/nginx.conf:/etc/nginx/conf.d/default.conf
- networks:
- - internal
- ports:
- - 4000:80
+ content-app:
+ image: alfresco/alfresco-content-app:latest
+ build: .
+ depends_on:
+ - alfresco
+ networks:
+ - internal
+ ports:
+ - 4001:80
+ # volumes:
+ # - ./app.config.json:/usr/share/nginx/html/app.config.json
+ # - ./nginx.conf:/etc/nginx/conf.d/default.conf
+
+ proxy:
+ image: nginx:stable-alpine
+ depends_on:
+ - content-app
+ volumes:
+ - ./docker-compose/nginx.conf:/etc/nginx/conf.d/default.conf
+ networks:
+ - internal
+ ports:
+ - 4000:80
networks:
- internal:
+ ? internal
diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh
new file mode 100644
index 000000000..1ce3ab2a3
--- /dev/null
+++ b/docker-entrypoint.sh
@@ -0,0 +1,64 @@
+#!/bin/sh
+
+if [ -n "${APP_CONFIG_AUTH_TYPE}" ];then
+ sed -e "s/\"authType\": \".*\"/\"authType\": \"${APP_CONFIG_AUTH_TYPE}\"/g" \
+ -i ./app.config.json
+fi
+
+if [ -n "${APP_CONFIG_OAUTH2_HOST}" ];then
+ replace="\/"
+ encoded=${APP_CONFIG_OAUTH2_HOST//\//$replace}
+ sed -e "s/\"host\": \".*\"/\"host\": \"${encoded}\"/g" \
+ -i ./app.config.json
+fi
+
+if [ -n "${APP_CONFIG_OAUTH2_CLIENTID}" ];then
+ sed -e "s/\"clientId\": \".*\"/\"clientId\": \"${APP_CONFIG_OAUTH2_CLIENTID}\"/g" \
+ -i ./app.config.json
+fi
+
+if [ -n "${APP_CONFIG_OAUTH2_IMPLICIT_FLOW}" ];then
+ sed "/implicitFlow/s/true/${APP_CONFIG_OAUTH2_IMPLICIT_FLOW}/" \
+ -i ./app.config.json
+fi
+
+if [ -n "${APP_CONFIG_OAUTH2_SILENT_LOGIN}" ];then
+ sed "/silentLogin/s/true/${APP_CONFIG_OAUTH2_SILENT_LOGIN}/" \
+ -i ./app.config.json
+fi
+
+if [ -n "${APP_CONFIG_OAUTH2_REDIRECT_SILENT_IFRAME_URI}" ];then
+ replace="\/"
+ encoded=${APP_CONFIG_OAUTH2_REDIRECT_SILENT_IFRAME_URI//\//$replace}
+ sed -e "s/\"redirectSilentIframeUri\": \".*\"/\"redirectSilentIframeUri\": \"${encoded}\"/g" \
+ -i ./app.config.json
+fi
+
+if [ -n "${APP_CONFIG_OAUTH2_REDIRECT_LOGIN}" ];then
+ replace="\/"
+ encoded=${APP_CONFIG_OAUTH2_REDIRECT_LOGIN//\//$replace}
+ sed -e "s/\"redirectUri\": \".*\"/\"redirectUri\": \"${encoded}\"/g" \
+ -i ./app.config.json
+fi
+
+if [ -n "${APP_CONFIG_OAUTH2_REDIRECT_LOGOUT}" ];then
+ replace="\/"
+ encoded=${APP_CONFIG_OAUTH2_REDIRECT_LOGOUT//\//$replace}
+ sed -e "s/\"redirectUriLogout\": \".*\"/\"redirectUriLogout\": \"${encoded}\"/g" \
+ -i ./app.config.json
+fi
+
+if [[ $ACSURL ]]; then
+ sed -i s%{protocol}//{hostname}{:port}%"$ACSURL"%g /usr/share/nginx/html/app.config.json
+fi
+
+if [[ $BASEPATH ]]; then
+ sed -i s%href=\"/\"%href=\""$BASEPATH"\"%g /usr/share/nginx/html/index.html
+fi
+
+if [ -n "${APP_BASE_SHARE_URL}" ];then
+ sed -e "s/\"baseShareUrl\": \".*\"/\"baseShareUrl\": \"${APP_BASE_SHARE_URL}\"/g" \
+ -i ./app.config.json
+fi
+
+nginx -g "daemon off;"
diff --git a/docker/tomcat/Dockerfile b/docker/tomcat/Dockerfile
new file mode 100644
index 000000000..9b173dcc9
--- /dev/null
+++ b/docker/tomcat/Dockerfile
@@ -0,0 +1,5 @@
+FROM tomcat:8.5-alpine
+LABEL version="1.3"
+LABEL maintainer="Denys Vuika "
+
+COPY ./artifacts/content-app.war /usr/local/tomcat/webapps/content-app.war
diff --git a/docker/tomcat/artifacts/.gitignore b/docker/tomcat/artifacts/.gitignore
new file mode 100644
index 000000000..ed5861097
--- /dev/null
+++ b/docker/tomcat/artifacts/.gitignore
@@ -0,0 +1 @@
+*.war
diff --git a/docker/tomcat/artifacts/.gitkeep b/docker/tomcat/artifacts/.gitkeep
new file mode 100644
index 000000000..e69de29bb
diff --git a/docker/tomcat/docker-compose.yml b/docker/tomcat/docker-compose.yml
new file mode 100644
index 000000000..d5b57119c
--- /dev/null
+++ b/docker/tomcat/docker-compose.yml
@@ -0,0 +1,97 @@
+version: '3'
+
+services:
+ alfresco:
+ image: alfresco/alfresco-content-repository-community:6.1.0-ea
+ depends_on:
+ - postgres
+ environment:
+ JAVA_OPTS: '
+ -Ddb.driver=org.postgresql.Driver
+ -Ddb.username=alfresco
+ -Ddb.password=alfresco
+ -Ddb.url=jdbc:postgresql://postgres:5432/alfresco
+ -Dsolr.host=solr6
+ -Dsolr.port=8983
+ -Dsolr.secureComms=none
+ -Dsolr.base.url=/solr
+ -Dindex.subsystem.name=solr6
+ -Dshare.host=localhost
+ -Ddeployment.method=DOCKER_COMPOSE
+ -Dcsrf.filter.enabled=false
+ '
+ networks:
+ - internal
+ ports:
+ - 8080:8080 #Browser port
+
+ share:
+ image: alfresco/alfresco-share:6.0.c
+ depends_on:
+ - alfresco
+ environment:
+ - REPO_HOST=alfresco
+ - REPO_PORT=8080
+ networks:
+ - internal
+ ports:
+ - 8083:8080
+
+ postgres:
+ image: postgres:10.1
+ environment:
+ - POSTGRES_PASSWORD=alfresco
+ - POSTGRES_USER=alfresco
+ - POSTGRES_DB=alfresco
+ command: postgres -c max_connections=300 -c log_min_messages=LOG
+ networks:
+ - internal
+ ports:
+ - 5432:5432
+
+ solr6:
+ image: alfresco/alfresco-search-services:1.2.0
+ depends_on:
+ - alfresco
+ environment:
+ #Solr needs to know how to register itself with Alfresco
+ - SOLR_ALFRESCO_HOST=alfresco
+ - SOLR_ALFRESCO_PORT=8080
+ #Alfresco needs to know how to call solr
+ - SOLR_SOLR_HOST=solr6
+ - SOLR_SOLR_PORT=8983
+ #Create the default alfresco and archive cores
+ - SOLR_CREATE_ALFRESCO_DEFAULTS=alfresco,archive
+ networks:
+ - internal
+ ports:
+ - 8983:8983 #Browser port
+
+ content-app:
+ image: alfresco/alfresco-content-app:development-latest-tomcat8
+ build: .
+ environment:
+ - APP_BASE_SHARE_URL={protocol}//{hostname}{:port}/content-app/
+ depends_on:
+ - alfresco
+ networks:
+ - internal
+ ports:
+ - 4001:8080
+ # volumes:
+ # - ./app.config.json:/usr/share/nginx/html/app.config.json
+ # - ./nginx.conf:/etc/nginx/conf.d/default.conf
+
+ proxy:
+ image: nginx
+ depends_on:
+ - content-app
+ volumes:
+ - ./nginx.conf:/etc/nginx/conf.d/default.conf
+ networks:
+ - internal
+ ports:
+ - 4000:80
+
+networks:
+ ? internal
diff --git a/docker/tomcat/nginx.conf b/docker/tomcat/nginx.conf
new file mode 100644
index 000000000..f0cbdd129
--- /dev/null
+++ b/docker/tomcat/nginx.conf
@@ -0,0 +1,43 @@
+server {
+ listen *:80;
+
+ set $allowOriginSite *;
+ proxy_pass_request_headers on;
+ proxy_pass_header Set-Cookie;
+
+ location / {
+ proxy_pass http://content-app:8080;
+
+ proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;
+ proxy_redirect off;
+ proxy_buffering off;
+ proxy_set_header Host $host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_pass_header Set-Cookie;
+ }
+
+ location /alfresco/ {
+ proxy_pass http://alfresco:8080;
+
+ proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;
+ proxy_redirect off;
+ proxy_buffering off;
+ proxy_set_header Host $host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_pass_header Set-Cookie;
+ }
+
+ location /share/ {
+ proxy_pass http://share:8080;
+
+ proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;
+ proxy_redirect off;
+ proxy_buffering off;
+ proxy_set_header Host $host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_pass_header Set-Cookie;
+ }
+}
diff --git a/docs/README.md b/docs/README.md
index 417c6b6f9..bcc2ee089 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -7,7 +7,7 @@
The Alfresco Content Application is an example application built using
[Alfresco Application Development Framework (ADF)](https://github.com/Alfresco/alfresco-ng2-components) components.
-### Who is this example application for
+### Who is this example application for?
This example application demonstrates to Angular software engineers
how to construct a content application using the Alfresco ADF.
@@ -19,7 +19,7 @@ with a simple and easy to use interface for working with files stored in the Alf
This application was created based on the latest releases from Alfresco:
-- [Alfresco ADF (2.5.0)](https://community.alfresco.com/community/application-development-framework/pages/get-started)
+- [Alfresco ADF (2.6.0)](https://community.alfresco.com/community/application-development-framework/pages/get-started)
- [Alfresco Content Services (6.0.0)](https://www.alfresco.com/platform/content-services-ecm)
or [Alfresco Community Edition (6.0 - General Release: 201806)](https://www.alfresco.com/products/community/download)
@@ -27,8 +27,7 @@ This application was created based on the latest releases from Alfresco:
You also need node.js (LTS) installed to build it locally from source code.
-The latest version of the Alfresco Content platform is required
-due to the application using the latest [REST APIs](https://docs.alfresco.com/5.2/pra/1/topics/pra-welcome.html) developments.
+**Note:** The latest version of the Alfresco Content platform is required due to the application using the latest [REST APIs](https://docs.alfresco.com/5.2/pra/1/topics/pra-welcome.html) developments.
## Features
@@ -46,9 +45,9 @@ using the Alfresco Application Development Framework to easily and quickly creat
There are three main areas of the application controlled by the [Layout component](https://github.com/Alfresco/alfresco-content-app/tree/master/src/app/components/layout):
-- [(1) Application Header](#header)
-- [(2) Side Navigation](#side-navigation)
-- [(3) Document List](#document-list-layout)
+1. [Application Header](#header)
+2. [Side Navigation](#side-navigation)
+3. [Document List](#document-list-layout)

@@ -66,13 +65,13 @@ The application [header](https://github.com/Alfresco/alfresco-content-app/tree/m
Logo & app primary color - logo and color are configurable by updating the
[app.config.json](https://github.com/Alfresco/alfresco-content-app/blob/master/src/app.config.json) file in the root folder of the project.
-Please refer to the [Application Configuration](/getting-started#application-logo) documentation for more information on how to change the logo and color.
+Please refer to the [Application Configuration](/getting-started/configuration) documentation for more information on how to change the logo and color.
#### Search
The application [Search](https://github.com/Alfresco/alfresco-content-app/tree/master/src/app/components/search) -
-uses the [ADF Search Component](https://github.com/Alfresco/alfresco-ng2-components/tree/master/lib/content-services/search)
-the app provides a 'live' search feature, where users can open files and folders directly from the Search API results.
+uses the [ADF Search Component](https://github.com/Alfresco/alfresco-ng2-components/tree/master/lib/content-services/search).
+The app provides a 'live' search feature, where users can open files and folders directly from the Search API results.

@@ -114,16 +113,16 @@ and uploads can be canceled which will stop uploads in progress or permanently d
The navigation links are configurable via the [app.config.json](https://github.com/Alfresco/alfresco-content-app/blob/master/src/app.config.json).
Default configuration creates two sections.
-See [Navigation](/getting-started#navigation) for more information about configuring the side navigation.
+See [Navigation](/getting-started/navigation) for more information about configuring the side navigation.
### Document List Layout
The main area of the application is composed of several individual ADF components:
-- (1) [Breadcrumb](https://alfresco.github.io/adf-component-catalog/components/BreadcrumbComponent.html)
-- (2) [Toolbar](https://alfresco.github.io/adf-component-catalog/components/ToolbarComponent.html)
-- (3) [Document List](https://alfresco.github.io/adf-component-catalog/components/DocumentListComponent.html)
-- (4) [Pagination](https://alfresco.github.io/adf-component-catalog/components/PaginationComponent.html)
+1. [Breadcrumb](https://alfresco.github.io/adf-component-catalog/components/BreadcrumbComponent.html)
+2. [Toolbar](https://alfresco.github.io/adf-component-catalog/components/ToolbarComponent.html)
+3. [Document List](https://alfresco.github.io/adf-component-catalog/components/DocumentListComponent.html)
+4. [Pagination](https://alfresco.github.io/adf-component-catalog/components/PaginationComponent.html)

@@ -155,8 +154,6 @@ and includes extra columns to display where the file is
[located](https://github.com/Alfresco/alfresco-content-app/tree/master/src/app/components/location-link)
in the content repository and who created the shared link.
-A feature for creating and removing Shared Links will be added in the future.
-
#### Recent Files
The Recent Files view shows all the files that have been created or modified within the last 30 days by the current user.
@@ -248,7 +245,7 @@ actions are automatically hidden when the user does not have permission.
Share
- Create and copy a link to a file that can be shared, the links are accessible without granting permissions to the file, and do not require users to login to the application.
+ Create and copy a link to a file that can be shared, the links are accessible without granting permissions to the file, and do not require users to login to the application. Share links can automatically expire based on a date, the minimum expiry date is controlled by the Content Services repository, which is 1 day from the date of creation.
Not applicable.
diff --git a/docs/extending.md b/docs/extending.md
index d91d72938..a1698b65e 100644
--- a/docs/extending.md
+++ b/docs/extending.md
@@ -16,39 +16,42 @@ and any number of external plugins that are references of the main entry point.
The application also comes with the `/src/assets/plugins/` folder
already preconfigured to store external files.
-You can create plugins that change, toggle or extend the following areas:
+You can create plugins that change, toggle, or extend the following areas:
-* Navigation sidebar links and groups
-* Context Menu
-* Sidebar (aka Info Drawer)
-* Toolbar entries
- * buttons
- * menu buttons
- * separators
-* Viewer actions
- * "Open With" entries
- * "More actions" toolbar entries
+- Navigation sidebar links and groups
+- Context Menu
+- Sidebar (aka Info Drawer)
+- Toolbar entries
+ - buttons
+ - menu buttons
+ - separators
+- Viewer actions
+ - "Open With" entries
+ - toolbar entries
+ - buttons
+ - "More actions" buttons
+- Content metadata presets (used on `Properties` tab)
Extensions can also:
-* Overwrite or disable extension points of the main application or other plugins
-* Change rules, actions or any visual element
-* Register new application routes based on empty pages or layouts
-* Register new rule evaluators, components, guards, etc.
+- Overwrite or disable extension points of the main application or other plugins
+- Change rules, actions or any visual element
+- Register new application routes based on empty pages or layouts
+- Register new rule evaluators, components, guards
## Format
-The format is represented by a JSON file with the structure similar to the following one:
+The format is represented by a JSON file with the structure similar to the following:
```json
{
- "$name": "app",
- "$version": "1.0.0",
+ "$name": "app",
+ "$version": "1.0.0",
- "routes": [],
- "actions": [],
- "rules": [],
- "features": {}
+ "routes": [],
+ "actions": [],
+ "rules": [],
+ "features": {}
}
```
@@ -57,57 +60,50 @@ The format is represented by a JSON file with the structure similar to the follo
You can find the JSON schema at the project root folder: [extension.schema.json](https://github.com/Alfresco/alfresco-content-app/blob/master/extension.schema.json).
-Schema allows validating extension files, provides code completion and documentation hints.
+The Schema allows you to validate extension files, provides code completion and documentation hints.
```json
{
- "$schema": "../../extension.schema.json",
- "$name": "app",
- "$version": "1.0.0",
+ "$schema": "../../extension.schema.json",
+ "$name": "app",
+ "$version": "1.0.0"
}
```
### Multiple files
You can have multiple extension files distributed separately.
-All additional files are linked via the `$references` property,
-the order of declaration defines also the order of loading.
+All additional files are linked via the `$references` property.
+The order of declaration defines the order of loading.
```json
{
- "$schema": "../../extension.schema.json",
- "$name": "app",
- "$version": "1.0.0",
- "$references": [
- "plugin1.json",
- "plugin2.json"
- ]
+ "$schema": "../../extension.schema.json",
+ "$name": "app",
+ "$version": "1.0.0",
+ "$references": ["plugin1.json", "plugin2.json"]
}
```
-Always keep in mind that all extension files are merged together at runtime.
-That allows plugins overwriting the code from the main application or altering other plugins.
+All extension files are merged together at runtime.
+This allows plugins to overwrite the code from the main application or to alter other plugins.
-### Startup behaviour
+### Startup behavior
First, the root `app.extensions.json` is loaded by means of the special `Loader` service.
-The file can contain all the necessary declarations for an application to function,
-and having extra plugin files is fully optional.
+The file can contain all the necessary declarations for an application to function. Extra plugin files are fully optional.
Next, the `Loader` traverses the `$references` metadata and loads additional files if provided.
-For the sake of speed the files are loaded in parallel,
-however, once everything is loaded, they are applied in the order of declaration.
+For the sake of speed the files are loaded in parallel, however once everything is loaded, they are applied in the order of declaration.
-After all the external files are fetched, the `Loader` sorts them, removes the metadata properties
-and stacks the resulting JSON objects on top of each other.
+After all the external files are fetched, the `Loader` sorts them, removes the metadata properties and stacks the resulting JSON objects on top of each other.
-Any top-level property name that starts with the `$` symbol is considered a metadata and does not participate in merge process.
-That allows a plugin to carry extra information for maintenance and visualisation purposes,
-for example: `$name`, `$version`, `$description`, `$license`, etc.
+Any top-level property name that starts with the `$` symbol is considered metadata and does not participate in the merge process.
+That allows a plugin to carry extra information for maintenance and visualisation purposes, for example: `$name`, `$version`, `$description`, `$license`, etc.
#### Merging properties
@@ -119,9 +115,9 @@ Before: Plugin 1
```json
{
- "$name": "plugin1",
- "plugin1.key": "value",
- "plugin1.text": "string"
+ "$name": "plugin1",
+ "plugin1.key": "value",
+ "plugin1.text": "string"
}
```
@@ -129,9 +125,9 @@ Before: Plugin 2
```json
{
- "$name": "plugin2",
- "plugin2.key": "value",
- "plugin1.text": "custom string"
+ "$name": "plugin2",
+ "plugin2.key": "value",
+ "plugin1.text": "custom string"
}
```
@@ -139,9 +135,9 @@ Final result:
```json
{
- "plugin1.key": "value",
- "plugin1.text": "custom string",
- "plugin2.key": "value"
+ "plugin1.key": "value",
+ "plugin1.text": "custom string",
+ "plugin2.key": "value"
}
```
@@ -149,7 +145,7 @@ Note that as a result we have two unique properties `plugin1.key` and `plugin2.k
and also a `plugin1.text` that was first defined in the `Plugin 1`, but then overwritten by the `Plugin 2`.
-JSON merging is very powerful concept as it gives you abilities to alter any base application settings,
+JSON merging is a very powerful concept as it gives you the ability to alter any base application settings,
or toggle features in other plugins without rebuilding the application or corresponding plugin libraries.
@@ -161,13 +157,13 @@ Before: Plugin 1
```json
{
- "$name": "plugin1",
- "features": {
- "title": "some title",
- "page1": {
- "title": "page 1"
- }
+ "$name": "plugin1",
+ "features": {
+ "title": "some title",
+ "page1": {
+ "title": "page 1"
}
+ }
}
```
@@ -175,15 +171,15 @@ Before: Plugin 2
```json
{
- "$name": "plugin2",
- "features": {
- "page1": {
- "title": "custom title"
- },
- "page2": {
- "title": "page 2"
- }
+ "$name": "plugin2",
+ "features": {
+ "page1": {
+ "title": "custom title"
+ },
+ "page2": {
+ "title": "page 2"
}
+ }
}
```
@@ -191,35 +187,34 @@ Final result:
```json
{
- "features": {
- "title": "some title",
- "page1": {
- "title": "custom title"
- },
- "page2": {
- "title": "page 2"
- }
+ "features": {
+ "title": "some title",
+ "page1": {
+ "title": "custom title"
+ },
+ "page2": {
+ "title": "page 2"
}
+ }
}
```
-As you can see, the unique properties get merged together in a single object.
-However the last non-unique property always wins and overwrites the previous value.
+You can see the unique properties get merged together in a single object.
+However the last non-unique property overwrites the previous value.
-As per current design it is not possible to delete any application property from the plugin.
-The loader engine supports only overwriting values on purpose.
-Many components however support the `disabled` property you can change from external definition:
+Using the current design it is not possible to delete any application property from the plugin.
+The loader engine only supports overwriting values. Many components however support the `disabled` property you can change using an external definition:
Before: Plugin 1
```json
{
- "$name": "plugin1",
- "feature1": {
- "disabled": false,
- "text": "some-feature",
- "icon": "some-icon"
- }
+ "$name": "plugin1",
+ "feature1": {
+ "disabled": false,
+ "text": "some-feature",
+ "icon": "some-icon"
+ }
}
```
@@ -227,10 +222,10 @@ Before: Plugin 2
```json
{
- "$name": "plugin2",
- "feature1": {
- "disabled": true
- }
+ "$name": "plugin2",
+ "feature1": {
+ "disabled": true
+ }
}
```
@@ -238,11 +233,11 @@ Final result:
```json
{
- "feature1": {
- "disabled": true,
- "text": "some-feature",
- "icon": "some-icon"
- }
+ "feature1": {
+ "disabled": true,
+ "text": "some-feature",
+ "icon": "some-icon"
+ }
}
```
@@ -261,14 +256,14 @@ Before: Plugin 1
```json
{
- "$name": "plugin1",
- "features": [
- { "text": "common 1" },
- {
- "id": "page1",
- "text": "page 1"
- }
- ]
+ "$name": "plugin1",
+ "features": [
+ { "text": "common 1" },
+ {
+ "id": "page1",
+ "text": "page 1"
+ }
+ ]
}
```
@@ -276,14 +271,14 @@ Before: Plugin 2
```json
{
- "$name": "plugin2",
- "features": [
- { "text": "common 2" },
- {
- "id": "page1",
- "text": "custom page"
- }
- ]
+ "$name": "plugin2",
+ "features": [
+ { "text": "common 2" },
+ {
+ "id": "page1",
+ "text": "custom page"
+ }
+ ]
}
```
@@ -291,14 +286,14 @@ Final result:
```json
{
- "features": [
- { "text": "common 1" },
- { "text": "common 2" },
- {
- "id": "page1",
- "text": "custom page"
- }
- ]
+ "features": [
+ { "text": "common 1" },
+ { "text": "common 2" },
+ {
+ "id": "page1",
+ "text": "custom page"
+ }
+ ]
}
```
@@ -310,21 +305,21 @@ Most of the schema elements can be switched off by using the `disabled` property
```json
{
- "$schema": "../../extension.schema.json",
- "$name": "app",
- "$version": "1.0.0",
+ "$schema": "../../extension.schema.json",
+ "$name": "app",
+ "$version": "1.0.0",
- "features": {
- "create": [
- {
- "id": "app.create.folder",
- "disabled": true,
- "order": 100,
- "icon": "create_new_folder",
- "title": "Create Folder"
- }
- ]
- }
+ "features": {
+ "create": [
+ {
+ "id": "app.create.folder",
+ "disabled": true,
+ "order": 100,
+ "icon": "create_new_folder",
+ "title": "Create Folder"
+ }
+ ]
+ }
}
```
@@ -362,31 +357,31 @@ To create a new route, populate the `routes` section with the corresponding entr
```json
{
- "$schema": "../../../extension.schema.json",
- "$version": "1.0.0",
- "$name": "plugin1",
+ "$schema": "../../../extension.schema.json",
+ "$version": "1.0.0",
+ "$name": "plugin1",
- "routes": [
- {
- "id": "plugin1.routes.bin",
- "path": "ext/bin",
- "layout": "app.layout.main",
- "component": "app.components.trashcan"
- }
- ]
+ "routes": [
+ {
+ "id": "plugin1.routes.bin",
+ "path": "ext/bin",
+ "layout": "app.layout.main",
+ "component": "your.component.id"
+ }
+ ]
}
```
### Route properties
-| Name | Description |
-| --- | --- |
-| **id** | Unique identifier. |
-| **path** | Runtime path of the route. |
-| **component** | The main [component](#components) to use for the route. |
-| layout | The layout [component](#components) to use for the route. |
-| auth | List of [authentication guards](#authentication-guards). Defaults to `[ "app.auth" ]`. |
-| data | Custom property bag to carry with the route. |
+| Name | Description |
+| ------------- | -------------------------------------------------------------------------------------- |
+| **id** | Unique identifier. |
+| **path** | Runtime path of the route. |
+| **component** | The main [component](#components) to use for the route. |
+| layout | The layout [component](#components) to use for the route. |
+| auth | List of [authentication guards](#authentication-guards). Defaults to `[ "app.auth" ]`. |
+| data | Custom property bag to carry with the route. |
Use the `app.layout.main` value for the `layout` property to get the default application layout,
with header, navigation sidebar and main content area.
@@ -401,22 +396,22 @@ You can define the full route schema like in the next example:
```json
{
- "$schema": "../../../extension.schema.json",
- "$version": "1.0.0",
- "$name": "plugin1",
+ "$schema": "../../../extension.schema.json",
+ "$version": "1.0.0",
+ "$name": "plugin1",
- "routes": [
- {
- "id": "plugin1.routes.bin",
- "path": "ext/bin",
- "component": "app.components.trashcan",
- "layout": "app.layout.main",
- "auth": [ "app.auth" ],
- "data": {
- "title": "Custom Trashcan"
- }
- }
- ]
+ "routes": [
+ {
+ "id": "plugin1.routes.bin",
+ "path": "ext/bin",
+ "component": "your.component.id",
+ "layout": "app.layout.main",
+ "auth": ["app.auth"],
+ "data": {
+ "title": "Custom Trashcan"
+ }
+ }
+ ]
}
```
@@ -429,8 +424,8 @@ Defaults to the `['app.auth']` value.
Below is the list of the authentication guards main application registers on startup.
-| Key | Type | Description |
-| --- | --- | --- |
+| Key | Type | Description |
+| -------- | ------------ | ------------------------------------------------------------------------- |
| app.auth | AuthGuardEcm | ADF guard, validates ACS authentication and redirects to Login if needed. |
You can refer those guards from within your custom extensions,
@@ -442,16 +437,15 @@ You can register any Angular component to participate in extensibility.
The components are used to create custom:
-* routes and pages
-* toolbar buttons
-* menu items
+- routes and pages
+- toolbar buttons
+- menu items
-| Key | Type | Description |
-| --- | --- | --- |
-| app.layout.main | LayoutComponent | Main application layout with the menu bar, navigation sidebar and main content area to project your components. |
-| app.components.trashcan | TrashcanComponent | Trashcan component, used for demo purposes. |
-| app.toolbar.toggleInfoDrawer | ToggleInfoDrawerComponent | The toolbar button component that toggles Info Drawer for the selection. |
-| app.toolbar.toggleFavorite | ToggleFavoriteComponent | The toolbar button component that toggles Favorite state for the selection. |
+| Key | Type | Description |
+| ---------------------------- | ------------------------- | --------------------------------------------------------------------------------------------------------------- |
+| app.layout.main | LayoutComponent | Main application layout with the menu bar, navigation sidebar and main content area to project your components. |
+| app.toolbar.toggleInfoDrawer | ToggleInfoDrawerComponent | The toolbar button component that toggles Info Drawer for the selection. |
+| app.toolbar.toggleFavorite | ToggleFavoriteComponent | The toolbar button component that toggles Favorite state for the selection. |
See [Registration](#registration) section for more details
@@ -463,35 +457,35 @@ besides registering a new one.
## Actions
-| Name | Description |
-| --- | --- |
-| **id** | Unique identifier. |
-| **type** | Action type, see [Application Actions](#application-actions) for more details. |
-| *payload* | Action payload, a string containing value or expression. |
+| Name | Description |
+| --------- | ------------------------------------------------------------------------------ |
+| **id** | Unique identifier. |
+| **type** | Action type, see [Application Actions](#application-actions) for more details. |
+| _payload_ | Action payload, a string containing value or expression. |
```json
{
- "$schema": "../../../extension.schema.json",
- "$version": "1.0.0",
- "$name": "plugin1",
+ "$schema": "../../../extension.schema.json",
+ "$version": "1.0.0",
+ "$name": "plugin1",
- "actions": [
- {
- "id": "plugin1.actions.settings",
- "type": "NAVIGATE_URL",
- "payload": "/settings"
- },
- {
- "id": "plugin1.actions.info",
- "type": "SNACKBAR_INFO",
- "payload": "I'm a nice little popup raised by extension."
- },
- {
- "id": "plugin1.actions.node-name",
- "type": "SNACKBAR_INFO",
- "payload": "$('Action for ' + context.selection.first.entry.name)"
- }
- ]
+ "actions": [
+ {
+ "id": "plugin1.actions.settings",
+ "type": "NAVIGATE_URL",
+ "payload": "/settings"
+ },
+ {
+ "id": "plugin1.actions.info",
+ "type": "SNACKBAR_INFO",
+ "payload": "I'm a nice little popup raised by extension."
+ },
+ {
+ "id": "plugin1.actions.node-name",
+ "type": "SNACKBAR_INFO",
+ "payload": "$('Action for ' + context.selection.first.entry.name)"
+ }
+ ]
}
```
@@ -519,20 +513,18 @@ $([1, 2, 1 + 2]) // [1, 2, 3]
Application is using NgRx (Reactive libraries for Angular, inspired by Redux).
To get more information on NxRx please refer to the following resources:
-* [Comprehensive Introduction to @ngrx/store](https://gist.github.com/btroncone/a6e4347326749f938510)
+- [Comprehensive Introduction to @ngrx/store](https://gist.github.com/btroncone/a6e4347326749f938510)
Most of the application features are already exposed in the form of NgRx Actions and corresponding Effects.
You can invoke any action via a single `Store` dispatcher, similar to the following:
```typescript
export class MyComponent {
+ constructor(private store: Store) {}
- constructor(private store: Store) {}
-
- onClick() {
- this.store.dispatch(new SearchByTermAction('*'));
- }
-
+ onClick() {
+ this.store.dispatch(new SearchByTermAction('*'));
+ }
}
```
@@ -543,12 +535,11 @@ Another example demonstrates viewing a node from a custom application service AP
```typescript
export class MyService {
+ constructor(private store: Store) {}
- constructor(private store: Store) {}
-
- viewFile(node: MinimalNodeEntity) {
- this.store.dispatch(new ViewFileAction(node));
- }
+ viewFile(node: MinimalNodeEntity) {
+ this.store.dispatch(new ViewFileAction(node));
+ }
}
```
@@ -566,23 +557,23 @@ and provide a new `Create Folder (plugin1)` command that invokes the `CREATE_FOL
```json
{
- "$schema": "../../../extension.schema.json",
- "$version": "1.0.0",
- "$name": "plugin1",
+ "$schema": "../../../extension.schema.json",
+ "$version": "1.0.0",
+ "$name": "plugin1",
- "features": {
- "create": [
- {
- "id": "plugin1.create.folder",
- "type": "default",
- "icon": "create_new_folder",
- "title": "Create Folder (plugin1)",
- "actions": {
- "click": "CREATE_FOLDER"
- }
- }
- ]
- }
+ "features": {
+ "create": [
+ {
+ "id": "plugin1.create.folder",
+ "type": "default",
+ "icon": "create_new_folder",
+ "title": "Create Folder (plugin1)",
+ "actions": {
+ "click": "CREATE_FOLDER"
+ }
+ }
+ ]
+ }
}
```
@@ -591,41 +582,45 @@ and perform document list reload if needed.
Below is the list of public actions types you can use in the plugin definitions as a reference to the action:
-| Name | Payload | Description |
-| --- | --- | --- |
-| SET_CURRENT_FOLDER | Node | Notify components about currently opened folder. |
-| SET_CURRENT_URL | string | Notify components about current browser URL. |
-| SET_USER_PROFILE | Person | Assign current user profile. |
-| TOGGLE_INFO_DRAWER | n/a | Toggle info drawer for the selected node. |
-| ADD_FAVORITE | MinimalNodeEntity[] | Add nodes (or selection) to favorites. |
-| REMOVE_FAVORITE | MinimalNodeEntity[] | Removes nodes (or selection) from favorites. |
-| DELETE_LIBRARY | string | Delete a Library by id. Takes selected node if payload not provided. |
-| CREATE_LIBRARY | n/a | Invoke a "Create Library" dialog. |
-| SET_SELECTED_NODES | MinimalNodeEntity[] | Notify components about selected nodes. |
-| DELETE_NODES | MinimalNodeEntity[] | Delete the nodes (or selection). Supports undo actions. |
-| UNDO_DELETE_NODES | any[] | Reverts deletion of nodes (or selection). |
-| RESTORE_DELETED_NODES | MinimalNodeEntity[] | Restores deleted nodes (or selection). Typically used with Trashcan. |
-| PURGE_DELETED_NODES | MinimalNodeEntity[] | Permanently delete nodes (or selection). Typically used with Trashcan. |
-| DOWNLOAD_NODES | MinimalNodeEntity[] | Download nodes (or selections). Creates a ZIP archive for folders or multiple items. |
-| CREATE_FOLDER | string | Invoke a "Create Folder" dialog for the opened folder (or the parent folder id in the payload). |
-| EDIT_FOLDER | MinimalNodeEntity | Invoke an "Edit Folder" dialog for the node (or selection). |
-| SHARE_NODE | MinimalNodeEntity | Invoke a "Share" dialog for the node (or selection). |
-| UNSHARE_NODES | MinimalNodeEntity[] | Remove nodes (or selection) from the shared nodes (does not remove content). |
-| COPY_NODES | MinimalNodeEntity[] | Invoke a "Copy" dialog for the nodes (or selection). Supports undo actions. |
-| MOVE_NODES | MinimalNodeEntity[] | Invoke a "Move" dialog for the nodes (or selection). Supports undo actions. |
-| MANAGE_PERMISSIONS | MinimalNodeEntity | Invoke a "Manage Permissions" dialog for the node (or selection). |
-| MANAGE_VERSIONS | MinimalNodeEntity | Invoke a "Manage Versions" dialog for the node (or selection). |
-| NAVIGATE_URL | string | Navigate to a given route URL within the application. |
-| NAVIGATE_ROUTE | any[] | Navigate to a particular Route (supports parameters) |
-| NAVIGATE_FOLDER | MinimalNodeEntity | Navigate to a folder based on the Node properties. |
-| NAVIGATE_PARENT_FOLDER | MinimalNodeEntity | Navigate to a containing folder based on the Node properties. |
-| SEARCH_BY_TERM | string | Perform a simple search by the term and navigate to Search results. |
-| SNACKBAR_INFO | string | Show information snackbar with the message provided. |
-| SNACKBAR_WARNING | string | Show warning snackbar with the message provided. |
-| SNACKBAR_ERROR | string | Show error snackbar with the message provided. |
-| UPLOAD_FILES | n/a | Invoke "Upload Files" dialog and upload files to the currently opened folder. |
-| UPLOAD_FOLDER | n/a | Invoke "Upload Folder" dialog and upload selected folder to the currently opened one. |
-| VIEW_FILE | MinimalNodeEntity | Preview the file (or selection) in the Viewer. |
+| Name | Payload | Description |
+| ---------------------- | ------------------- | ----------------------------------------------------------------------------------------------- |
+| SET_CURRENT_FOLDER | Node | Notify components about currently opened folder. |
+| SET_CURRENT_URL | string | Notify components about current browser URL. |
+| SET_USER_PROFILE | Person | Assign current user profile. |
+| TOGGLE_INFO_DRAWER | n/a | Toggle info drawer for the selected node. |
+| ADD_FAVORITE | MinimalNodeEntity[] | Add nodes (or selection) to favorites. |
+| REMOVE_FAVORITE | MinimalNodeEntity[] | Removes nodes (or selection) from favorites. |
+| DELETE_LIBRARY | string | Delete a Library by id. Takes selected node if payload not provided. |
+| CREATE_LIBRARY | n/a | Invoke a "Create Library" dialog. |
+| SET_SELECTED_NODES | MinimalNodeEntity[] | Notify components about selected nodes. |
+| DELETE_NODES | MinimalNodeEntity[] | Delete the nodes (or selection). Supports undo actions. |
+| UNDO_DELETE_NODES | any[] | Reverts deletion of nodes (or selection). |
+| RESTORE_DELETED_NODES | MinimalNodeEntity[] | Restores deleted nodes (or selection). Typically used with Trashcan. |
+| PURGE_DELETED_NODES | MinimalNodeEntity[] | Permanently delete nodes (or selection). Typically used with Trashcan. |
+| DOWNLOAD_NODES | MinimalNodeEntity[] | Download nodes (or selections). Creates a ZIP archive for folders or multiple items. |
+| CREATE_FOLDER | string | Invoke a "Create Folder" dialog for the opened folder (or the parent folder id in the payload). |
+| EDIT_FOLDER | MinimalNodeEntity | Invoke an "Edit Folder" dialog for the node (or selection). |
+| SHARE_NODE | MinimalNodeEntity | Invoke a "Share" dialog for the node (or selection). |
+| UNSHARE_NODES | MinimalNodeEntity[] | Remove nodes (or selection) from the shared nodes (does not remove content). |
+| COPY_NODES | MinimalNodeEntity[] | Invoke a "Copy" dialog for the nodes (or selection). Supports undo actions. |
+| MOVE_NODES | MinimalNodeEntity[] | Invoke a "Move" dialog for the nodes (or selection). Supports undo actions. |
+| MANAGE_PERMISSIONS | MinimalNodeEntity | Invoke a "Manage Permissions" dialog for the node (or selection). |
+| MANAGE_VERSIONS | MinimalNodeEntity | Invoke a "Manage Versions" dialog for the node (or selection). |
+| NAVIGATE_URL | string | Navigate to a given route URL within the application. |
+| NAVIGATE_ROUTE | any[] | Navigate to a particular Route (supports parameters) |
+| NAVIGATE_FOLDER | MinimalNodeEntity | Navigate to a folder based on the Node properties. |
+| NAVIGATE_PARENT_FOLDER | MinimalNodeEntity | Navigate to a containing folder based on the Node properties. |
+| NAVIGATE_LIBRARY | string | Navigate to library |
+| SEARCH_BY_TERM | string | Perform a simple search by the term and navigate to Search results. |
+| SNACKBAR_INFO | string | Show information snackbar with the message provided. |
+| SNACKBAR_WARNING | string | Show warning snackbar with the message provided. |
+| SNACKBAR_ERROR | string | Show error snackbar with the message provided. |
+| UPLOAD_FILES | n/a | Invoke "Upload Files" dialog and upload files to the currently opened folder. |
+| UPLOAD_FOLDER | n/a | Invoke "Upload Folder" dialog and upload selected folder to the currently opened one. |
+| VIEW_FILE | MinimalNodeEntity | Preview the file (or selection) in the Viewer. |
+| PRINT_FILE | MinimalNodeEntity | Print the file opened in the Viewer (or selected). |
+| FULLSCREEN_VIEWER | n/a | Enters fullscreen mode to view the file opened in the Viewer. |
+| LOGOUT | n/a | Log out and redirect to Login screen |
## Rules
@@ -636,16 +631,16 @@ Every rule is backed by a condition evaluator.
```json
{
- "$schema": "../../../extension.schema.json",
- "$version": "1.0.0",
- "$name": "plugin1",
+ "$schema": "../../../extension.schema.json",
+ "$version": "1.0.0",
+ "$name": "plugin1",
- "rules": [
- {
- "id": "app.trashcan",
- "type": "app.navigation.isTrashcan"
- }
- ]
+ "rules": [
+ {
+ "id": "app.trashcan",
+ "type": "app.navigation.isTrashcan"
+ }
+ ]
}
```
@@ -653,22 +648,22 @@ Rules can accept other rules as parameters:
```json
{
- "$schema": "../../../extension.schema.json",
- "$version": "1.0.0",
- "$name": "plugin1",
+ "$schema": "../../../extension.schema.json",
+ "$version": "1.0.0",
+ "$name": "plugin1",
- "rules": [
- {
- "id": "app.toolbar.favorite.canAdd",
- "type": "core.every",
- "parameters": [
- { "type": "rule", "value": "app.selection.canAddFavorite" },
- { "type": "rule", "value": "app.navigation.isNotRecentFiles" },
- { "type": "rule", "value": "app.navigation.isNotSharedFiles" },
- { "type": "rule", "value": "app.navigation.isNotSearchResults" }
- ]
- }
- ]
+ "rules": [
+ {
+ "id": "app.toolbar.favorite.canAdd",
+ "type": "core.every",
+ "parameters": [
+ { "type": "rule", "value": "app.selection.canAddFavorite" },
+ { "type": "rule", "value": "app.navigation.isNotRecentFiles" },
+ { "type": "rule", "value": "app.navigation.isNotSharedFiles" },
+ { "type": "rule", "value": "app.navigation.isNotSearchResults" }
+ ]
+ }
+ ]
}
```
@@ -684,45 +679,45 @@ in case you do not need providing extra parameters, or chaining multiple rules t
You can create new rules by chaining other rules and evaluators.
-| Key | Description |
-| --- | --- |
-| core.every | Evaluates to `true` if all chained rules evaluate to `true`. |
-| core.some | Evaluates to `true` if at least one of the chained rules evaluates to `true`. |
-| core.not | Evaluates to `true` if all chained rules evaluate to `false`. |
+| Key | Description |
+| ---------- | ----------------------------------------------------------------------------- |
+| core.every | Evaluates to `true` if all chained rules evaluate to `true`. |
+| core.some | Evaluates to `true` if at least one of the chained rules evaluates to `true`. |
+| core.not | Evaluates to `true` if all chained rules evaluate to `false`. |
Below is an example of the composite rule definition that combines the following conditions:
-* user has selected a single file
-* user is not using **Trashcan** page
+- user has selected a single file
+- user is not using the **Trashcan** page
```json
{
- "$schema": "../../../extension.schema.json",
- "$version": "1.0.0",
- "$name": "plugin1",
+ "$schema": "../../../extension.schema.json",
+ "$version": "1.0.0",
+ "$name": "plugin1",
- "rules": [
+ "rules": [
+ {
+ "id": "app.toolbar.canViewFile",
+ "type": "core.every",
+ "parameters": [
{
- "id": "app.toolbar.canViewFile",
- "type": "core.every",
- "parameters": [
- {
- "type": "rule",
- "value": "app.selection.file"
- },
- {
- "type": "rule",
- "value": "core.not",
- "parameters": [
- {
- "type": "rule",
- "value": "app.navigation.isTrashcan"
- }
- ]
- }
- ]
+ "type": "rule",
+ "value": "app.selection.file"
+ },
+ {
+ "type": "rule",
+ "value": "core.not",
+ "parameters": [
+ {
+ "type": "rule",
+ "value": "app.navigation.isTrashcan"
+ }
+ ]
}
- ]
+ ]
+ }
+ ]
}
```
@@ -730,26 +725,26 @@ You can now declare a toolbar button action that is based on the rule above.
```json
{
- "$schema": "../../../extension.schema.json",
- "$version": "1.0.0",
- "$name": "plugin1",
+ "$schema": "../../../extension.schema.json",
+ "$version": "1.0.0",
+ "$name": "plugin1",
- "features": {
- "toolbar": [
- {
- "id": "app.toolbar.preview",
- "type": "button",
- "title": "View File",
- "icon": "open_in_browser",
- "actions": {
- "click": "VIEW_FILE"
- },
- "rules": {
- "visible": "app.toolbar.canViewFile"
- }
- },
- ]
- }
+ "features": {
+ "toolbar": [
+ {
+ "id": "app.toolbar.preview",
+ "type": "button",
+ "title": "View File",
+ "icon": "open_in_browser",
+ "actions": {
+ "click": "VIEW_FILE"
+ },
+ "rules": {
+ "visible": "app.toolbar.canViewFile"
+ }
+ }
+ ]
+ }
}
```
@@ -757,28 +752,26 @@ The button will be visible only when the linked rule evaluates to `true`.
### Application Evaluators
-| Key | Description |
-| --- | --- |
-| app.selection.canDelete | User has permission to delete selected node(s). |
-| app.selection.canDownload | User can download selected node(s). |
-| app.selection.notEmpty | At least one node is selected. |
-| app.selection.canUnshare | User is able to remove selected node(s) from public sharing. |
-| app.selection.canAddFavorite | User can add selected node(s) to favorites. |
-| app.selection.canRemoveFavorite | User can remove selected node(s) from favorites. |
-| app.selection.first.canUpdate | User has permission to update selected node(s). |
-| app.selection.file | A single File node is selected. |
-| app.selection.file.canShare | User is able share selected file. |
-| app.selection.library | A single Library node is selected. |
-| app.selection.folder | A single Folder node is selected. |
-| app.selection.folder.canUpdate | User has permissions to update selected folder. |
+| Key | Description |
+| ------------------------------- | ------------------------------------------------------------ |
+| app.selection.canDelete | User has permission to delete selected node(s). |
+| app.selection.canDownload | User can download selected node(s). |
+| app.selection.notEmpty | At least one node is selected. |
+| app.selection.canUnshare | User is able to remove selected node(s) from public sharing. |
+| app.selection.canAddFavorite | User can add selected node(s) to favorites. |
+| app.selection.canRemoveFavorite | User can remove selected node(s) from favorites. |
+| app.selection.first.canUpdate | User has permission to update selected node(s). |
+| app.selection.file | A single File node is selected. |
+| app.selection.file.canShare | User is able to share the selected file. |
+| app.selection.library | A single Library node is selected. |
+| app.selection.folder | A single Folder node is selected. |
+| app.selection.folder.canUpdate | User has permissions to update the selected folder. |
### Navigation Evaluators
-The application exposes a set of navigation-related evaluators
-to help developers restrict or enable certain actions based on the route or page displayed.
+The application exposes a set of navigation-related evaluators to help developers restrict or enable certain actions based on the route or page displayed.
-The negated evaluators are provided just to simplify development,
-and to avoid having a complex rule trees just to negate the rules,
+The negated evaluators are provided just to simplify development, and to avoid having complex rule trees just to negate the rules,
for example mixing `core.every` and `core.not`.
@@ -786,22 +779,22 @@ You can also negate any rule by utilizing a `!` prefix:
`!app.navigation.isTrashcan` is the opposite of the `app.navigation.isTrashcan`.
-| Key | Description |
-| --- | --- |
-| app.navigation.folder.canCreate | User can create content in the currently opened folder. |
-| app.navigation.folder.canUpload | User can upload content to the currently opened folder. |
-| app.navigation.isTrashcan | User is using **Trashcan** page. |
-| app.navigation.isNotTrashcan | Current page is not a **Trashcan**. |
-| app.navigation.isLibraries | User is using **Libraries** page. |
-| app.navigation.isNotLibraries | Current page is not **Libraries**. |
-| app.navigation.isSharedFiles | User is using **Shared Files** page. |
-| app.navigation.isNotSharedFiles | Current page is not **Shared Files**. |
-| app.navigation.isFavorites | User is using **Favorites** page. |
-| app.navigation.isNotFavorites | Current page is not **Favorites** |
-| app.navigation.isRecentFiles | User is using **Recent Files** page. |
-| app.navigation.isNotRecentFiles | Current page is not **Recent Files**. |
-| app.navigation.isSearchResults | User is using **Search Results** page. |
-| app.navigation.isNotSearchResults | Current page is not **Search Results**. |
+| Key | Description |
+| --------------------------------- | ------------------------------------------------------- |
+| app.navigation.folder.canCreate | User can create content in the currently opened folder. |
+| app.navigation.folder.canUpload | User can upload content to the currently opened folder. |
+| app.navigation.isTrashcan | User is using the **Trashcan** page. |
+| app.navigation.isNotTrashcan | Current page is not a **Trashcan**. |
+| app.navigation.isLibraries | User is using the **Libraries** page. |
+| app.navigation.isNotLibraries | Current page is not **Libraries**. |
+| app.navigation.isSharedFiles | User is using the **Shared Files** page. |
+| app.navigation.isNotSharedFiles | Current page is not **Shared Files**. |
+| app.navigation.isFavorites | User is using the **Favorites** page. |
+| app.navigation.isNotFavorites | Current page is not **Favorites** |
+| app.navigation.isRecentFiles | User is using the **Recent Files** page. |
+| app.navigation.isNotRecentFiles | Current page is not **Recent Files**. |
+| app.navigation.isSearchResults | User is using the **Search Results** page. |
+| app.navigation.isNotSearchResults | Current page is not the **Search Results**. |
See [Registration](#registration) section for more details
@@ -812,27 +805,27 @@ on how to register your own entries to be re-used at runtime.
The rule in the example below evaluates to `true` if all the conditions are met:
-* user has selected node(s)
-* user is not using **Trashcan** page
-* user is not using **Libraries** page
+- user has selected node(s)
+- user is not using the **Trashcan** page
+- user is not using the **Libraries** page
```json
{
- "$schema": "../../../extension.schema.json",
- "$version": "1.0.0",
- "$name": "plugin1",
+ "$schema": "../../../extension.schema.json",
+ "$version": "1.0.0",
+ "$name": "plugin1",
- "rules": [
- {
- "id": "app.toolbar.canCopyNode",
- "type": "core.every",
- "parameters": [
- { "type": "rule", "value": "app.selection.notEmpty" },
- { "type": "rule", "value": "app.navigation.isNotTrashcan" },
- { "type": "rule", "value": "app.navigation.isNotLibraries" }
- ]
- }
- ]
+ "rules": [
+ {
+ "id": "app.toolbar.canCopyNode",
+ "type": "core.every",
+ "parameters": [
+ { "type": "rule", "value": "app.selection.notEmpty" },
+ { "type": "rule", "value": "app.navigation.isNotTrashcan" },
+ { "type": "rule", "value": "app.navigation.isNotLibraries" }
+ ]
+ }
+ ]
}
```
@@ -842,33 +835,36 @@ This section contains application-specific features that may vary depending on t
The ACA supports the following set of extension points:
-* Create menu
-* Navigation Bar
-* Toolbar
-* Context Menu
-* Viewer
-* Sidebar (aka Info Drawer)
+- Create menu
+- Navigation Bar
+- Toolbar
+- Context Menu
+- Viewer
+- Sidebar (aka Info Drawer)
+- Content metadata presets (for `Properties` tab)
-All the customisations are stored in the `features` section of the configuration file:
+All the customizations are stored in the `features` section of the configuration file:
```json
{
- "$schema": "../../../extension.schema.json",
- "$version": "1.0.0",
- "$name": "plugin1",
+ "$schema": "../../../extension.schema.json",
+ "$version": "1.0.0",
+ "$name": "plugin1",
- "features": {
- "create": [],
- "navbar": [],
- "toolbar": [],
- "contextMenu": [],
- "viewer": {
- "toolbar:": [],
- "openWith": [],
- "content": []
- },
- "sidebar": []
- }
+ "features": {
+ "create": [],
+ "navbar": [],
+ "toolbar": [],
+ "contextMenu": [],
+ "viewer": {
+ "toolbarActions:": [],
+ "toolbarMoreMenu:": [],
+ "openWith": [],
+ "content": []
+ },
+ "sidebar": [],
+ "content-metadata-presets": []
+ }
}
```
@@ -882,25 +878,25 @@ are based on `ContentActionRef` interface implementation:
```ts
interface ContentActionRef {
- id: string;
- type: ContentActionType;
+ id: string;
+ type: ContentActionType;
- title?: string;
- description?: string;
- order?: number;
- icon?: string;
- disabled?: boolean;
- children?: Array;
- component?: string;
- actions?: {
- click?: string;
- [key: string]: string;
- };
- rules?: {
- enabled?: string;
- visible?: string;
- [key: string]: string;
- };
+ title?: string;
+ description?: string;
+ order?: number;
+ icon?: string;
+ disabled?: boolean;
+ children?: Array;
+ component?: string;
+ actions?: {
+ click?: string;
+ [key: string]: string;
+ };
+ rules?: {
+ enabled?: string;
+ visible?: string;
+ [key: string]: string;
+ };
}
```
@@ -914,181 +910,181 @@ You can populate the menu with an extra entries like in the example below:
```json
{
- "$schema": "../../../extension.schema.json",
- "$version": "1.0.0",
- "$name": "plugin1",
+ "$schema": "../../../extension.schema.json",
+ "$version": "1.0.0",
+ "$name": "plugin1",
- "features": {
- "create": [
- {
- "id": "plugin1.create.folder",
- "icon": "create_new_folder",
- "title": "Create Folder (plugin1)",
- "actions": {
- "click": "CREATE_FOLDER"
- },
- "rules": {
- "enabled": "app.navigation.folder.canCreate"
- }
- },
- {
- "id": "plugin1.create.uploadFile",
- "icon": "file_upload",
- "title": "Upload Files (plugin1)",
- "actions": {
- "click": "UPLOAD_FILES"
- },
- "rules": {
- "enabled": "app.navigation.folder.canUpload"
- }
- }
- ]
- }
+ "features": {
+ "create": [
+ {
+ "id": "plugin1.create.folder",
+ "icon": "create_new_folder",
+ "title": "Create Folder (plugin1)",
+ "actions": {
+ "click": "CREATE_FOLDER"
+ },
+ "rules": {
+ "enabled": "app.navigation.folder.canCreate"
+ }
+ },
+ {
+ "id": "plugin1.create.uploadFile",
+ "icon": "file_upload",
+ "title": "Upload Files (plugin1)",
+ "actions": {
+ "click": "UPLOAD_FILES"
+ },
+ "rules": {
+ "enabled": "app.navigation.folder.canUpload"
+ }
+ }
+ ]
+ }
}
```
Please refer to the [Content Actions](#content-actions) section for more details on supported properties.
-It is also possible to update or disable existing entries from within the external extension files. You will need to know the `id` of the target element to customise.
+It is also possible to update or disable existing entries from within the external extension files. You will need to know the `id` of the target element to customize.
### Navigation Bar
-Navigation bar consists of Link elements (`NavBarLinkRef`) organized into Groups (`NavBarGroupRef`).
+The Navigation bar consists of Link elements (`NavBarLinkRef`) organized into Groups (`NavBarGroupRef`).
```ts
export interface NavBarGroupRef {
- id: string;
- items: Array;
+ id: string;
+ items: Array;
- order?: number;
- disabled?: boolean;
+ order?: number;
+ disabled?: boolean;
}
export interface NavBarLinkRef {
- id: string;
- icon: string;
- title: string;
- route: string;
+ id: string;
+ icon: string;
+ title: string;
+ route: string;
- url?: string; // evaluated at runtime based on route ref
- description?: string;
- order?: number;
- disabled?: boolean;
+ url?: string; // evaluated at runtime based on route ref
+ description?: string;
+ order?: number;
+ disabled?: boolean;
}
```
-You extensions can perform the following actions at runtime:
+Your extensions can perform the following actions at runtime:
-* Register new groups with links
-* Insert new links into existing groups
-* Update properties of the existing links
-* Disable existing links or entire groups
+- Register new groups with links
+- Insert new links into existing groups
+- Update properties of the existing links
+- Disable existing links or entire groups
```json
{
- "$schema": "../../../extension.schema.json",
- "$version": "1.0.0",
- "$name": "plugin1",
+ "$schema": "../../../extension.schema.json",
+ "$version": "1.0.0",
+ "$name": "plugin1",
- "features": {
- "navbar": [
- {
- "id": "app.navbar.primary",
- "items": [
- {
- "id": "app.navbar.personalFiles",
- "icon": "folder",
- "title": "Personal Files",
- "route": "personal-files"
- },
- {
- "id": "app.navbar.libraries",
- "icon": "group_work",
- "title": "Libraries",
- "route": "libraries"
- }
- ]
- },
- {
- "id": "app.navbar.secondary",
- "items": [
- {
- "id": "app.navbar.shared",
- "icon": "people",
- "title": "Shared",
- "route": "shared"
- }
- ]
- }
+ "features": {
+ "navbar": [
+ {
+ "id": "app.navbar.primary",
+ "items": [
+ {
+ "id": "app.navbar.personalFiles",
+ "icon": "folder",
+ "title": "Personal Files",
+ "route": "personal-files"
+ },
+ {
+ "id": "app.navbar.libraries",
+ "icon": "group_work",
+ "title": "Libraries",
+ "route": "libraries"
+ }
]
- }
+ },
+ {
+ "id": "app.navbar.secondary",
+ "items": [
+ {
+ "id": "app.navbar.shared",
+ "icon": "people",
+ "title": "Shared",
+ "route": "shared"
+ }
+ ]
+ }
+ ]
+ }
}
```
### Sidebar (Info Drawer)
-You can provide the following customisations for the Sidebar (aka Info Drawer) component:
+You can provide the following customizations for the Sidebar (aka Info Drawer) component:
-* Add extra tabs with custom components
-* Disable tabs from the main application or extensions
-* Replace content or properties of existing tabs
+- Add extra tabs with custom components
+- Disable tabs from the main application or extensions
+- Replace content or properties of existing tabs
```json
{
- "$schema": "../../../extension.schema.json",
- "$version": "1.0.0",
- "$name": "plugin1",
+ "$schema": "../../../extension.schema.json",
+ "$version": "1.0.0",
+ "$name": "plugin1",
- "features": {
- "sidebar": [
- {
- "id": "app.sidebar.properties",
- "order": 100,
- "title": "Properties",
- "component": "app.components.tabs.metadata"
- },
- {
- "id": "app.sidebar.comments",
- "order": 200,
- "title": "Comments",
- "component": "app.components.tabs.comments"
- }
- ]
- }
+ "features": {
+ "sidebar": [
+ {
+ "id": "app.sidebar.properties",
+ "order": 100,
+ "title": "Properties",
+ "component": "app.components.tabs.metadata"
+ },
+ {
+ "id": "app.sidebar.comments",
+ "order": 200,
+ "title": "Comments",
+ "component": "app.components.tabs.comments"
+ }
+ ]
+ }
}
```
The example above renders two tabs:
-* `Properties` tab that references `app.components.tabs.metadata` component
-* `Comments` tab that references `app.components.tabs.comments` component
+- `Properties` tab that references the `app.components.tabs.metadata` component
+- `Comments` tab that references the `app.components.tabs.comments` component
All corresponding components must be registered for runtime use.
-See [Registration](#registration) section for more details
+See the [Registration](#registration) section for more details
on how to register your own entries to be re-used at runtime.
#### Tab properties
-| Name | Description |
-| --- | --- |
-| **id** | Unique identifier. |
-| **component** | The main [component](#components) to use for the route. |
-| **title** | Tab title or resource key. |
-| icon | Tab icon |
-| disabled | Toggles disabled state. Can be assigned from other plugins. |
-| order | The order of the element. |
+| Name | Description |
+| ------------- | ----------------------------------------------------------- |
+| **id** | Unique identifier. |
+| **component** | The main [component](#components) to use for the route. |
+| **title** | Tab title or resource key. |
+| icon | Tab icon |
+| disabled | Toggles disabled state. Can be assigned from other plugins. |
+| order | The order of the element. |
#### Tab components
Every component you assign for the tab content receives the following additional properties at runtime:
-| Name | Type | Description |
-| --- | --- | --- |
+| Name | Type | Description |
+| ---- | ---------------------- | --------------------------- |
| node | MinimalNodeEntryEntity | Node entry to be displayed. |
### Toolbar
@@ -1097,48 +1093,48 @@ The toolbar extension point is represented by an array of Content Action referen
```json
{
- "$schema": "../../../extension.schema.json",
- "$version": "1.0.0",
- "$name": "plugin1",
+ "$schema": "../../../extension.schema.json",
+ "$version": "1.0.0",
+ "$name": "plugin1",
- "features": {
- "toolbar": [
- {
- "id": "app.toolbar.preview",
- "title": "View",
- "icon": "open_in_browser",
- "actions": {
- "click": "VIEW_FILE"
- },
- "rules": {
- "visible": "app.toolbar.canViewFile"
- }
- },
- {
- "id": "app.toolbar.download",
- "title": "Download",
- "icon": "get_app",
- "actions": {
- "click": "DOWNLOAD_NODES"
- },
- "rules": {
- "visible": "app.toolbar.canDownload"
- }
- }
- ]
- }
+ "features": {
+ "toolbar": [
+ {
+ "id": "app.toolbar.preview",
+ "title": "View",
+ "icon": "open_in_browser",
+ "actions": {
+ "click": "VIEW_FILE"
+ },
+ "rules": {
+ "visible": "app.toolbar.canViewFile"
+ }
+ },
+ {
+ "id": "app.toolbar.download",
+ "title": "Download",
+ "icon": "get_app",
+ "actions": {
+ "click": "DOWNLOAD_NODES"
+ },
+ "rules": {
+ "visible": "app.toolbar.canDownload"
+ }
+ }
+ ]
+ }
}
```
The content actions are applied to the toolbars for the following Views:
-* Personal Files
-* Libraries
-* Shared
-* Recent Files
-* Favorites
-* Trash
-* Search Results
+- Personal Files
+- Libraries
+- Shared
+- Recent Files
+- Favorites
+- Trash
+- Search Results
### Context Menu
@@ -1147,46 +1143,46 @@ You may want to define a list of content actions backed by Rules and wired with
```json
{
- "$schema": "../../../extension.schema.json",
- "$version": "1.0.0",
- "$name": "plugin1",
+ "$schema": "../../../extension.schema.json",
+ "$version": "1.0.0",
+ "$name": "plugin1",
- "features": {
- "contextMenu": [
- {
- "id": "app.context.menu.download",
- "order": 100,
- "title": "Download",
- "icon": "get_app",
- "actions": {
- "click": "DOWNLOAD_NODES"
- },
- "rules": {
- "visible": "app.toolbar.canDownload"
- }
- },
- ]
- }
+ "features": {
+ "contextMenu": [
+ {
+ "id": "app.context.menu.download",
+ "order": 100,
+ "title": "Download",
+ "icon": "get_app",
+ "actions": {
+ "click": "DOWNLOAD_NODES"
+ },
+ "rules": {
+ "visible": "app.toolbar.canDownload"
+ }
+ }
+ ]
+ }
}
```
-Note that you can re-use any rules and evaluators available.
+Note, you can re-use any rules and evaluators that are available.
In the example above, the context menu action `Download` utilizes the `app.toolbar.canDownload` rule,
declared in the `rules` section:
```json
{
- "rules": [
- {
- "id": "app.toolbar.canDownload",
- "type": "core.every",
- "parameters": [
- { "type": "rule", "value": "app.selection.canDownload" },
- { "type": "rule", "value": "app.navigation.isNotTrashcan" }
- ]
- }
- ]
+ "rules": [
+ {
+ "id": "app.toolbar.canDownload",
+ "type": "core.every",
+ "parameters": [
+ { "type": "rule", "value": "app.selection.canDownload" },
+ { "type": "rule", "value": "app.navigation.isNotTrashcan" }
+ ]
+ }
+ ]
}
```
@@ -1194,52 +1190,54 @@ declared in the `rules` section:
Viewer component in ACA supports the following extension points:
-* Content Viewers
-* `More` toolbar actions
-* `Open With` actions
+- Content Viewers
+- Toolbar actions
+- `More` toolbar actions
+- `Open With` actions
```json
{
- "$schema": "../../../extension.schema.json",
- "$version": "1.0.0",
- "$name": "plugin1",
+ "$schema": "../../../extension.schema.json",
+ "$version": "1.0.0",
+ "$name": "plugin1",
- "features": {
- "viewer": {
- "content": [],
- "toolbar:": [],
- "openWith": []
- }
+ "features": {
+ "viewer": {
+ "content": [],
+ "toolbarActions:": [],
+ "toolbarMoreMenu:": [],
+ "openWith": []
}
+ }
}
```
#### Content View
-You can provide custom components that render particular type of the content based on extensions.
+You can provide custom components that render a particular type of the content based on extensions.
```json
{
- "$schema": "../../../extension.schema.json",
- "$version": "1.0.0",
- "$name": "plugin1",
+ "$schema": "../../../extension.schema.json",
+ "$version": "1.0.0",
+ "$name": "plugin1",
- "features": {
- "viewer": {
- "content": [
- {
- "id": "app.viewer.pdf",
- "fileExtension": "pdf",
- "component": "app.components.tabs.metadata"
- },
- {
- "id": "app.viewer.docx",
- "fileExtension": "docx",
- "component": "app.components.tabs.comments"
- }
- ]
+ "features": {
+ "viewer": {
+ "content": [
+ {
+ "id": "app.viewer.pdf",
+ "fileExtension": "pdf",
+ "component": "app.components.tabs.metadata"
+ },
+ {
+ "id": "app.viewer.docx",
+ "fileExtension": "docx",
+ "component": "app.components.tabs.comments"
}
+ ]
}
+ }
}
```
@@ -1248,41 +1246,73 @@ and `DOCX` view with the `comments` tab.
Every custom component receives the following properties at runtime:
-| Name | Type | Description |
-| --- | --- | --- |
-| node | MinimalNodeEntryEntity | Node entry to be displayed. |
-| url | string | File content URL. |
-| extension | string | File name extension. |
+| Name | Type | Description |
+| --------- | ---------------------- | --------------------------- |
+| node | MinimalNodeEntryEntity | Node entry to be displayed. |
+| url | string | File content URL. |
+| extension | string | File name extension. |
#### Toolbar actions
-The ADF Viewer component allows providing custom entries for the `More` menu button on the toolbar.
-The ACA provides an extension point for this menu that you can utilise to populate custom menu items:
+The default toolbar actions from the ACA viewer can be customized through extensions to be replaced, modified or disabled.
+New viewer toolbar actions can also be added from the extensions config:
```json
{
- "$schema": "../../../extension.schema.json",
- "$version": "1.0.0",
- "$name": "plugin1",
+ "$schema": "../../../extension.schema.json",
+ "$version": "1.0.0",
+ "$name": "plugin1",
- "features": {
- "viewer": {
- "toolbar:": [
- {
- "id": "app.viewer.share",
- "order": 300,
- "title": "Share",
- "icon": "share",
- "actions": {
- "click": "SHARE_NODE"
- },
- "rules": {
- "visible": "app.selection.file.canShare"
- }
- }
- ]
+ "features": {
+ "viewer": {
+ "toolbarActions": [
+ {
+ "id": "app.viewer.versions",
+ "order": 500,
+ "title": "APP.ACTIONS.VERSIONS",
+ "icon": "history",
+ "actions": {
+ "click": "MANAGE_VERSIONS"
+ },
+ "rules": {
+ "visible": "app.toolbar.versions"
+ }
}
+ ],
+ "toolbarMoreMenu": [...]
}
+ }
+}
+```
+
+The ADF Viewer component allows you to provide custom entries for the `More` menu button on the toolbar.
+The ACA provides an extension point for this menu that you can utilize to populate custom menu items:
+
+```json
+{
+ "$schema": "../../../extension.schema.json",
+ "$version": "1.0.0",
+ "$name": "plugin1",
+
+ "features": {
+ "viewer": {
+ "toolbarActions": [...],
+ "toolbarMoreMenu": [
+ {
+ "id": "app.viewer.share",
+ "order": 300,
+ "title": "Share",
+ "icon": "share",
+ "actions": {
+ "click": "SHARE_NODE"
+ },
+ "rules": {
+ "visible": "app.selection.file.canShare"
+ }
+ }
+ ]
+ }
+ }
}
```
@@ -1295,45 +1325,151 @@ and invoke it from the custom `Open With` menu entry called `Snackbar`.
```json
{
- "$schema": "../../../extension.schema.json",
- "$version": "1.0.0",
- "$name": "plugin1",
+ "$schema": "../../../extension.schema.json",
+ "$version": "1.0.0",
+ "$name": "plugin1",
- "actions": [
- {
- "id": "plugin1.actions.info",
- "type": "SNACKBAR_INFO",
- "payload": "I'm a nice little popup raised by extension."
- },
- ],
-
- "features": {
- "viewer": {
- "openWith": [
- {
- "id": "plugin1.viewer.openWith.action1",
- "type": "button",
- "icon": "build",
- "title": "Snackbar",
- "actions": {
- "click": "plugin1.actions.info"
- }
- }
- ]
- }
+ "actions": [
+ {
+ "id": "plugin1.actions.info",
+ "type": "SNACKBAR_INFO",
+ "payload": "I'm a nice little popup raised by extension."
}
+ ],
+
+ "features": {
+ "viewer": {
+ "openWith": [
+ {
+ "id": "plugin1.viewer.openWith.action1",
+ "type": "button",
+ "icon": "build",
+ "title": "Snackbar",
+ "actions": {
+ "click": "plugin1.actions.info"
+ }
+ }
+ ]
+ }
+ }
}
```
As with other content actions, custom plugins can disable, update or extend `Open With` actions.
+### Content metadata presets
+
+The content metadata presets are needed by the [Content Metadata Component](https://alfresco.github.io/adf-component-catalog/components/ContentMetadataComponent.html#readme) to render the properties of metadata aspects for a given node.
+The different aspects and their properties are configured in the `app.config.json` file, but they can also be set on runtime through extension files.
+
+Configuring these presets from `app.extensions.json` will overwrite the default application setting.
+Settings them from custom plugins allows user to disable, update or extend these presets.
+Check out more info about merging extensions [here](#merging-properties).
+
+The `content-metadata-presets` elements can be switched off by setting the `disabled` property.
+This can be applied also for nested items, allowing disabling down to aspect level.
+
+
+In order to modify or disable existing entries, you need to know the id of the target element, along with its parents ids.
+
+
+Your extensions can perform the following actions at runtime:
+* Add new presets items.
+* Add new items to existing presets at any level.
+* Disable specific items down to the aspect level.
+* Modify any existing item based on id.
+
+Regarding properties, you can either:
+ * Add new properties to existing aspect, or
+ * Redefine the properties of an aspect.
+
+Review this code snippet to see how you can overwrite the properties for `exif:exif` aspect from an external plugin:
+```json
+ {
+ "$schema": "../../../extension.schema.json",
+ "$version": "1.0.0",
+ "$name": "plugin1",
+
+ "features": {
+ "content-metadata-presets": [
+ {
+ "id": "app.content.metadata.custom",
+ "custom": [
+ {
+ "id": "app.content.metadata.customGroup",
+ "items": [
+ {
+ "id": "app.content.metadata.exifAspect",
+ "disabled": true
+ },
+ {
+ "id": "app.content.metadata.exifAspect2",
+ "aspect": "exif:exif",
+ "properties": [
+ "exif:orientation",
+ "exif:manufacturer",
+ "exif:model",
+ "exif:software"
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ }
+```
+This external plugin disables the initial `exif:exif` aspect already defined in the `app.extensions.json` and defines other properties for the `exif:exif` aspect.
+Here is the initial setting from `app.extension.json`:
+```json
+...
+ "content-metadata-presets": [
+ {
+ "id": "app.content.metadata.custom",
+ "custom": [
+ {
+ "id": "app.content.metadata.customGroup",
+ "title": "APP.CONTENT_METADATA.EXIF_GROUP_TITLE",
+ "items": [
+ {
+ "id": "app.content.metadata.exifAspect",
+ "aspect": "exif:exif",
+ "properties": [
+ "exif:pixelXDimension",
+ "exif:pixelYDimension",
+ "exif:dateTimeOriginal",
+ "exif:exposureTime",
+ "exif:fNumber",
+ "exif:flash",
+ "exif:focalLength",
+ "exif:isoSpeedRatings",
+ "exif:orientation",
+ "exif:manufacturer",
+ "exif:model",
+ "exif:software"
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+...
+
+```
+
+In order to allow the content-metadata presets to be extended, the settings from `app.config.json` must be copied to the `app.extensions.json` file and its ids must be added to all the items.
+Having ids allows external plugins to extend the current setting.
+
+
## Registration
You can use `ExtensionService` to register custom components, authentication guards,
rule evaluators, etc.
It is recommended to register custom content from within the module constructor.
-In that case all plugins will be available right after main application component is ready.
+In that case all plugins will be available right after the main application component is ready.
Update the main application module `app.module.ts`, or create your own module,
and use the following snippet to register custom content:
@@ -1375,13 +1511,12 @@ According to Angular rules, all components that are created dynamically at runti
need to be registered within the `entryComponents` section of the NgModule.
-The registration API is not limited to the custom content only.
+The Registration API is not limited to the custom content only.
You can replace any existing entries by replacing the values from your module.
## Creating custom evaluator
-Rule evaluators are plain JavaScript (or TypeScript) functions
-that take `RuleContext` reference and an optional list of `RuleParameter` instances.
+Rule evaluators are plain JavaScript (or TypeScript) functions that take `RuleContext` references and an optional list of `RuleParameter` instances.
Application provides a special [RuleEvaluator](https://github.com/Alfresco/alfresco-content-app/blob/master/src/app/extensions/rule.extensions.ts#L30) type alias for evaluator functions:
@@ -1389,14 +1524,14 @@ Application provides a special [RuleEvaluator](https://github.com/Alfresco/alfre
export type RuleEvaluator = (context: RuleContext, ...args: any[]) => boolean;
```
-Create a function that is going to check if user has selected one or multiple nodes.
+Create a function that is going to check if a user has selected one or multiple nodes.
```typescript
export function hasSelection(
- context: RuleContext,
- ...args: RuleParameter[]
+ context: RuleContext,
+ ...args: RuleParameter[]
): boolean {
- return !context.selection.isEmpty;
+ return !context.selection.isEmpty;
}
```
@@ -1405,27 +1540,27 @@ that provides each evaluator access to runtime entities.
```typescript
export interface RuleContext {
- selection: SelectionState;
- navigation: NavigationState;
- permissions: NodePermissions;
+ selection: SelectionState;
+ navigation: NavigationState;
+ permissions: NodePermissions;
- getEvaluator(key: string): RuleEvaluator;
+ getEvaluator(key: string): RuleEvaluator;
}
```
-The `SelectionState` interface exposes information about global selection state:
+The `SelectionState` interface exposes information about the global selection state:
```typescript
export interface SelectionState {
- count: number;
- nodes: MinimalNodeEntity[];
- libraries: SiteEntry[];
- isEmpty: boolean;
- first?: MinimalNodeEntity;
- last?: MinimalNodeEntity;
- folder?: MinimalNodeEntity;
- file?: MinimalNodeEntity;
- library?: SiteEntry;
+ count: number;
+ nodes: MinimalNodeEntity[];
+ libraries: SiteEntry[];
+ isEmpty: boolean;
+ first?: MinimalNodeEntity;
+ last?: MinimalNodeEntity;
+ folder?: MinimalNodeEntity;
+ file?: MinimalNodeEntity;
+ library?: SiteEntry;
}
```
@@ -1433,7 +1568,7 @@ Next, register the function you have created earlier with the `ExtensionService`
```typescript
extensions.setEvaluators({
- 'plugin1.rules.hasSelection': hasSelection
+ 'plugin1.rules.hasSelection': hasSelection
});
```
@@ -1441,7 +1576,7 @@ Now, the `plugin1.rules.hasSelection` evaluator can be used as an inline rule re
or part of the composite rule like `core.every`.
-See [Registration](#registration) section for more details
+See the [Registration](#registration) section for more details
on how to register your own entries to be re-used at runtime.
@@ -1451,10 +1586,10 @@ on how to register your own entries to be re-used at runtime.
In this tutorial, we are going to implement the following features:
-* update the **Trashcan** component to receive and log route parameters
-* create a new route that points to the **Trashcan** component and uses main layout
-* create an action reference that allows redirecting to the new route
-* create a button in the **New** menu that invokes an action
+- Update the **Trashcan** component to receive and log route parameters.
+- Create a new route that points to the **Trashcan** component and uses the main layout.
+- Create an action reference that allows redirecting to the new route.
+- Create a button in the **New** menu that invokes an action.
Update `src/app/components/trashcan/trashcan.component.ts` and append the following code to the `ngOnInit` body:
@@ -1480,32 +1615,32 @@ export class TrashcanComponent {
}
```
-The code above logs current route parameters to the browser console
-and is a simple proof the integration works as expected.
+The code above logs the current route parameters to the browser console
+and is proof the integration works as expected.
Next, add a new route definition as in the example below:
```json
{
- "$schema": "../../../extension.schema.json",
- "$version": "1.0.0",
- "$name": "plugin1",
+ "$schema": "../../../extension.schema.json",
+ "$version": "1.0.0",
+ "$name": "plugin1",
- "routes": [
- {
- "id": "custom.routes.trashcan",
- "path": "ext/trashcan/:nodeId",
- "component": "app.components.trashcan",
- "layout": "app.layout.main",
- "auth": [ "app.auth" ]
- }
- ]
+ "routes": [
+ {
+ "id": "custom.routes.trashcan",
+ "path": "ext/trashcan/:nodeId",
+ "component": "your.component.id",
+ "layout": "app.layout.main",
+ "auth": ["app.auth"]
+ }
+ ]
}
```
The template above creates a new route reference with the id `custom.routes.trashcan` that points to the `ext/trashcan/` route and accepts the `nodeId` parameter.
-Also, we are going to use default application layout (`app.layout.main`)
+Also, we are going to use the default application layout (`app.layout.main`)
and authentication guards (`app.auth`).
Next, create an action reference for the `NAVIGATE_ROUTE` application action
@@ -1567,3 +1702,385 @@ node: 10
You have successfully created a new menu button that invokes your custom action
and redirects you to the extra application route.
+
+### Dialog actions
+
+In this tutorial, we are going to create an action that invokes a custom material dialog.
+
+
+Please read more details on Dialog components here: [Dialog Overview](https://material.angular.io/components/dialog/overview)
+
+
+#### Create a dialog
+
+```sh
+ng g component dialogs/my-extension-dialog --module=app
+```
+
+According to Angular rules, the component needs to also be registered within the `entryComponents` section of the module.
+
+Update the `src/app/app.module.ts` file according to the example below:
+
+```ts
+@NgModule({
+ imports: [...],
+ declarations: [
+ ...,
+ MyExtensionDialogComponent
+ ],
+ entryComponents: [
+ ...,
+ MyExtensionDialogComponent
+ ]
+})
+```
+
+Update `my-extension-dialog.component.ts`:
+
+```ts
+import { Component } from '@angular/core';
+import { MatDialogRef } from '@angular/material';
+
+@Component({
+ selector: 'aca-my-extension-dialog',
+ templateUrl: './my-extension-dialog.component.html',
+ styleUrls: ['./my-extension-dialog.component.scss']
+})
+export class MyExtensionDialogComponent {
+ constructor(public dialogRef: MatDialogRef) {}
+}
+```
+
+Update `my-extension-dialog.component.html`:
+
+```html
+
Delete all
+Are you sure?
+
+
+
+
+
+```
+
+#### Create an action
+
+Append the following code to the `src/app/store/actions/app.actions.ts`:
+
+```ts
+export const SHOW_MY_DIALOG = 'SHOW_MY_DIALOG';
+
+export class ShowMydDialogAction implements Action {
+ readonly type = SHOW_MY_DIALOG;
+}
+```
+
+See also:
+
+- [Comprehensive Introduction to @ngrx/store](https://gist.github.com/btroncone/a6e4347326749f938510)
+
+#### Create an effect
+
+Update `src/app/store/effects/app.effects.ts`:
+
+```ts
+import { ShowMydDialogAction, SHOW_MY_DIALOG } from '../actions/app.actions';
+
+@Injectable()
+export class AppEffects {
+ constructor(...) {}
+
+ @Effect({ dispatch: false })
+ showMyDialog$ = this.actions$.pipe(
+ ofType(SHOW_MY_DIALOG),
+ map(() => {})
+ );
+
+ // ...
+}
+```
+
+See also:
+
+- [Comprehensive Introduction to @ngrx/store](https://gist.github.com/btroncone/a6e4347326749f938510)
+
+Update to raise a dialog
+
+```ts
+import { MatDialog } from '@angular/material';
+import { MyExtensionDialogComponent } from '../../dialogs/my-extension-dialog/my-extension-dialog.component';
+
+@Injectable()
+export class AppEffects {
+ constructor(
+ ...,
+ private dialog: MatDialog
+ ) {}
+
+ @Effect({ dispatch: false })
+ showMyDialog$ = this.actions$.pipe(
+ ofType(SHOW_MY_DIALOG),
+ map(() => {
+ this.dialog.open(MyExtensionDialogComponent)
+ })
+ );
+
+ ...
+
+}
+```
+
+#### Register a toolbar action
+
+Update the `src/assets/app.extensions.json` file, and insert a new entry to the `features.toolbar` section:
+
+```json
+{
+ ...,
+
+ "features": {
+ "toolbar": [
+ {
+ "id": "my.custom.toolbar.button",
+ "order": 10,
+ "title": "Custom action",
+ "icon": "extension",
+ "actions": {
+ "click": "SHOW_MY_DIALOG"
+ }
+ }
+ ]
+ }
+}
+```
+
+Now, once you run the application, you should see an extra button that invokes your dialog on every click.
+
+## Redistributable libraries
+
+Extension libraries are based on the standard Angular libraries and definition files in the JSON format.
+
+
+Please read more details in the following article: [Library support in Angular CLI 6](https://github.com/angular/angular-cli/wiki/stories-create-library#library-support-in-angular-cli-6)
+
+
+See also
+
+- The Angular Library Series - Creating a Library with the Angular CLI
+ - Part 1: https://blog.angularindepth.com/creating-a-library-in-angular-6-87799552e7e5
+ - Part 2: https://blog.angularindepth.com/creating-a-library-in-angular-6-part-2-6e2bc1e14121
+
+### Creating extension library
+
+First, generate a new project within the workspace:
+
+```sh
+ng generate library my-extension
+```
+
+You will get a new project in the `projects/my-extensions` folder.
+By default, the project contains at least the following content:
+
+- Example component `my-extension.component.ts`
+- Example service `my-extension.service.ts`
+- Angular Module example `my-extension.module.ts`
+
+Next, build the project with the following command:
+
+```sh
+ng build my-extension
+```
+
+Angular CLI automatically configures Typescript path mappings for the project, so that you do not need any additional steps to link the library.
+
+#### Register dynamic components
+
+Update `my-extension.module.ts` and put all the content you plan to use at runtime dynamically to the `entryComponents` section of the module.
+
+```typescript
+@NgModule({
+ imports: [],
+ declarations: [MyExtensionComponent],
+ exports: [MyExtensionComponent],
+ entryComponents: [MyExtensionComponent]
+})
+export class MyExtensionModule {}
+```
+
+Now we need to register `MyExtensionComponent` as an extension component.
+Update the code as in the next example:
+
+```typescript
+import { ExtensionService } from '@alfresco/adf-extensions';
+
+@NgModule({...})
+export class MyExtensionModule {
+ constructor(extensions: ExtensionService) {
+ extensions.setComponents({
+ 'my-extension.main.component': MyExtensionComponent,
+ });
+ }
+}
+```
+
+Now you can use the `my-extension.main.component` identifier in the JSON definitions
+if you want to reference the `MyExtensionComponent`.
+
+#### Plugin definition file
+
+Create a new `assets/my-extension.json` file in the library project root folder with the following content:
+
+```json
+{
+ "$schema": "../../../extension.schema.json",
+ "$version": "1.0.0",
+ "$name": "plugin1",
+ "$description": "demo plugin",
+
+ "routes": [
+ {
+ "id": "my.extension.route",
+ "path": "ext/my/route",
+ "component": "my-extension.main.component"
+ }
+ ],
+
+ "features": {
+ "navbar": [
+ {
+ "id": "my.extension.nav",
+ "items": [
+ {
+ "id": "my.extension.main",
+ "icon": "extension",
+ "title": "My Extension",
+ "route": "my.extension.route"
+ }
+ ]
+ }
+ ]
+ }
+}
+```
+
+Update the root `package.json` file and append the following entry to the `scripts` section:
+
+```json
+{
+ "scripts": {
+ ...,
+
+ "build:my-extension":
+ "ng build my-extension && cpr projects/my-extension/assets dist/my-extension/assets --deleteFirst"
+ }
+}
+```
+
+You can now use that script to build the library and copy assets to the output folder.
+
+
+It is good practice to provide installation instructions for your library in the `README.md` file.
+Be sure to mention that developers should have a build rule to copy your plugin definition file to the `assets/plugins` folder of the main application.
+
+
+### Publishing library to NPM
+
+Before you publish you should always rebuild the library:
+
+```sh
+npm run build:my-extension
+```
+
+Go to the output folder and run the publish command.
+
+```sh
+cd dist/my-extension
+npm publish
+```
+
+Note, you are required to have a valid [NPM](https://www.npmjs.com/) account.
+
+
+See more details in the [Publishing your library](https://github.com/angular/angular-cli/wiki/stories-create-library#publishing-your-library) article.
+
+
+### Consuming extension library
+
+Assuming you have published your extension library to NPM, you can install it using the standard command:
+
+```sh
+npm install my-extension
+```
+
+This installs the library and all its dependencies.
+
+
+You do not need to install the library in the original workspace as the application is already configured to use the local version from the `dist` folder.
+
+
+#### Copy assets
+
+Edit the `angular.json` configuration file and add the following rule if you develop and test extension libraries in the same workspace.
+
+```json
+{
+ "glob": "**/*.json",
+ "input": "dist/my-extension/assets",
+ "output": "/assets/plugins"
+}
+```
+
+Use the following rule if you are installing an extension from NPM:
+
+```json
+{
+ "glob": "**/*.json",
+ "input": "node_modules/my-extension/assets",
+ "output": "/assets/plugins"
+}
+```
+
+#### Register module
+
+In the main application, edit the `src/app/extensions.module.ts` file and append the module declaration as in the next example:
+
+```typescript
+...
+import { MyExtensionModule } from 'my-extension';
+
+@NgModule({
+ ...
+ imports: [
+ ...,
+ MyExtensionModule
+ ]
+})
+export class AppExtensionsModule {}
+```
+
+#### Register plugin
+
+Finally, update the `assets/app.extensions.json` file and add a reference to the new plugin:
+
+```json
+{
+ "$references": [
+ ...,
+ "my-extension.json"
+ ]
+}
+```
+
+### Testing library
+
+Run the application and ensure you have an extra navigation sidebar entry:
+
+```sh
+npm start
+```
+
+Click the `My Extension` link and in the main content area you will see the extension component coming from your library.
+
+
+Depending on the application setup, you may need enabling external plugins via the `Settings` dialog available for `admin` users (clicking the application profile button).
+
diff --git a/docs/extending/extending.md b/docs/extending/extending.md
new file mode 100644
index 000000000..8b1378917
--- /dev/null
+++ b/docs/extending/extending.md
@@ -0,0 +1 @@
+
diff --git a/docs/getting-started.md b/docs/getting-started.md
deleted file mode 100644
index b70cdbb45..000000000
--- a/docs/getting-started.md
+++ /dev/null
@@ -1,560 +0,0 @@
----
-title: Getting Started
----
-
-# Getting Started
-
-## Prerequisites
-
-This application uses the latest releases from Alfresco:
-
-- [Alfresco ADF (2.4.0)](https://community.alfresco.com/community/application-development-framework/pages/get-started)
-- [Alfresco Content Services (5.2.3)](https://www.alfresco.com/platform/content-services-ecm)
- or [Alfresco Community Edition (201802 EA)](https://www.alfresco.com/products/community/download)
-
-
-You also need node.js (LTS) installed to build it locally from source code.
-
-
-The latest version of the Alfresco Content platform is required
-due to the application using the latest [REST APIs](https://docs.alfresco.com/5.2/pra/1/topics/pra-welcome.html) developments.
-
-## Building from source
-
-The Content App is based on [Angular CLI](https://cli.angular.io), and you can use all the commands, generators and blueprints supported by the CLI.
-
-### Prerequisites for building
-
-- [Node.js](https://nodejs.org/en/) LTS
-- [Angular CLI](https://cli.angular.io/) 1.7.3
-
-### Cloning and running
-
-Use the following commands to clone the project, install dependencies and run it.
-
-```sh
-git clone https://github.com/Alfresco/alfresco-content-app.git
-cd alfresco-content-app
-npm install
-npm start
-```
-
-The application runs at port `4200` by default, and should automatically open in the default browser once project compilation finishes.
-
-### Proxy settings
-
-The Content App provides a proxy configuration for local development server
-that allows you to address specific scenarios with CORS and native authentication dialog.
-
-You can find settings in the "proxy.conf.js" file in the project root directory.
-
-
-The proxy settings get automatically applied every time you run the application with "npm start" script.
-You must restart the application every time you change the settings values.
-
-
-### Running unit tests
-
-Run `npm test` to execute the unit tests via [Karma](https://karma-runner.github.io).
-
-## Internationalization (i18n)
-
-The Content Application provides support for the following languages:
-
-- German (`de`)
-- English (`en`)
-- Spanish (`es`)
-- French (`fr`)
-- Italian (`it`)
-- Japanese (`ja`)
-- Norwegian (`nb`)
-- Dutch (`nl`)
-- Brazilian Portuguese (`pt-BR`)
-- Russian (`ru`)
-- Simplified Chinese (`zh-CN`)
-
-The fallback locale is the English one, however current browser language is taken as the default one automatically when the application starts.
-
-### User-defined language
-
-You can allow users to set custom language that gets saved to user preferences.
-The main application menu already has the [ADF Language Menu](https://github.com/Alfresco/alfresco-ng2-components/blob/master/docs/core/language-menu.component.md) component integrated and pre-filled with the supported items.
-
-To change the default language set edit the `app.config.json` file and add or remove items:
-
-```json
-{
- ...,
- "languages": [
- {
- "key": "de",
- "label": "German"
- },
- {
- "key": "en",
- "label": "English"
- },
- {
- "key": "es",
- "label": "Spanish"
- },
- ...
- ]
-}
-```
-
-The file is located at the following path: `/src/app.config.json`.
-
-### Custom languages
-
-To add a custom language, add a new "JSON" file to the "/src/assets/i18n" folder
-with the name of the target locale, for instance, a "de.json" for the "German".
-
-Translate the resource strings based on the default "en.json" file.
-You can copy the content over to your newly created file and replace English values with translated text.
-
-```json
-{
- "APP": {
- "SIGN_IN": "Anmelden",
- "SIGN_OUT": "Abmelden",
- "NEW_MENU": {
- "LABEL": "Neu",
- "MENU_ITEMS": {
- "CREATE_FOLDER": "Ordner erstellen",
- "UPLOAD_FILE": "Datei hochladen",
- "UPLOAD_FOLDER": "Ordner hochladen"
- },
- ...
- }
- },
- ...
-}
-```
-
-The Content Application automatically bundles your file upon project build.
-You can test your locale by changing the browser language settings and reloading the page.
-
-Optionally, you can extend the [ADF Language Menu](https://github.com/Alfresco/alfresco-ng2-components/blob/master/docs/core/language-menu.component.md) component with the newly added language by updating the `app.config.json` file.
-
-### Customizing ADF translations
-
-In addition to creating a custom language file for the Content Application,
-you can also provide translations for the ADF resources.
-
-Your `/src/assets/i18n/.json` file can reflect the structure of one of the ADF language files:
-
-- ADF Core ([en.json](https://github.com/Alfresco/alfresco-ng2-components/blob/master/lib/core/i18n/en.json))
-- ADF Content Services ([en.json](https://github.com/Alfresco/alfresco-ng2-components/blob/master/lib/content-services/i18n/en.json))
-- ADF Process Services ([en.json](https://github.com/Alfresco/alfresco-ng2-components/blob/master/lib/process-services/i18n/en.json))
-- ADF Insights ([en.json](https://github.com/Alfresco/alfresco-ng2-components/blob/master/lib/insights/i18n/en.json))
-
-At runtime, the application-level strings have the highest priority.
-That means you can replace the value of any ADF resource string if needed.
-
-For example, let's change the title of the "Create Folder" dialog shipped with the ADF.
-Modify the `/src/assets/i18n/en.json` file and append the "CORE" section like in the example below:
-
-```json
-{
- "APP": {
- ...
- },
- "CORE": {
- "FOLDER_DIALOG": {
- "CREATE_FOLDER_TITLE": "Custom title"
- }
- }
-}
-```
-
-Now, if you run the application and click the "New → Create Folder" menu,
-the title of the dialog should look like the following:
-
-
-
-### Language picker
-
-You can enable internal language picker in the `app.config.json` file:
-
-```json
-{
- ...,
-
- "languagePicker": true,
-
- ...
-}
-```
-
-
-
-## CORS
-
-The ACA already comes with the proxy configuration for Angular CLI to address CORS-related issues for development.
-Also, the docker images contain Nginx settings needed for CORS when developing and debugging application locally.
-
-### Chrome Workaround
-
-For the Chrome browser, you can use the following plugin that allows you to toggle CORS:
-[Allow-Control-Allow-Origin](https://chrome.google.com/webstore/detail/allow-control-allow-origi/nlfbmbojpeacfghkpbjhddihlkkiljbi)
-
-### Firefox Workaround
-
-Firefox users can try the following plugin: [CORS Everywhere](https://addons.mozilla.org/en-Gb/firefox/addon/cors-everywhere/)
-
-### Safari Workaround
-
-If you are developing or testing with Safari then you can use the "Develop" menu to toggle the CORS mode.
-Please note that the page must be reloaded every time you change CORS settings.
-
-
-
-### See also
-
-- [Using CORS](https://www.html5rocks.com/en/tutorials/cors/)
-
-## Configuration
-
-The Content Application provides support for a global settings file `app.config.json` that you can use to customize the behavior of ACA and ADF components.
-
-### Server settings
-
-Once the Content Application starts, it needs to know where the Alfresco Content Services (either Community or Enterprise) server is.
-The "ecmHost" property allows you to set the address of the server using the dynamic or static format.
-
-#### Dynamic address
-
-The example below demonstrates the most common dynamic format for development environment:
-
-```json
-{
- "ecmHost": "http://{hostname}{:port}",
- ...
-}
-```
-
-The configuration above assumes you are running ACS and Content App on the same server and port
-and allows deploying to different servers having the same unified configuration file.
-
-For example, a proxy server at `localhost:4200` hosting the Content App as the root application,
-and `localhost:4200/alfresco` for the ACS repository.
-
-At runtime, the application is going to automatically substitute the "{hostname}" value with the original hostname.
-Optionally it can also use the value of the original port if present, for example, "4200" at local machines, or skip the value for port 80.
-
-#### Static address
-
-Alternatively, you can provide a static address for the ACS server if necessary:
-
-```json
-{
- "ecmHost": "http://localhost:4200",
- ...
-}
-```
-
-### Application settings
-
-There are many settings you can change to alter the default behavior of the application.
-
-#### Application Name
-
-The following block allows you to change the name of the application.
-
-```json
-{
- ...,
- "application": {
- "name": "Alfresco Example Content Application"
- }
-}
-```
-
-The value of the `application.name` key gets appended to every browser tab title at runtime
-with the format `[page title] - [application name]`,
-for example: "Personal Files - Alfresco Example Content Application".
-
-#### Application Logo
-
-The default logo displayed in the top left corner of the Alfresco Content Application can be easily changed:
-
-1. Place your custom logo image file in the [app-name]/src/assets/images folder. The displayed image will resize automatically, an image with extreme width/height might not retain its dimensions.
-
-2. In the app.config.json file, set the value of the application.logo to contain the name of the custom logo image: "logo": "/assets/images/[image-name].[extension]"
-
-
-```json
-{
- ...,
- "application": {
- "logo": "/assets/images/alfresco-logo-white.svg"
- }
-}
-```
-
-#### Header Background color
-
-You can change the header background color by specifying color code for the "headerColor" key:
-
-```json
-{
- ...,
- "headerColor": "#2196F3"
-}
-```
-
-#### Restricted content
-
-You can restrict users from uploading certain types of files and folders by setting or extending the list of rules at the "files.excluded" path.
-
-By default, the application ships with the following rules already predefined:
-
-```json
-{
- ...,
- "files": {
- "excluded": [
- ".DS_Store",
- "desktop.ini",
- "thumbs.db",
- ".git"
- ]
- },
- ...
-}
-```
-
-
-You can get more details on the supported rules in the following article: Upload Service.
-
-
-#### Pagination settings
-
-You can change the default settings of the pagination that gets applied to all the document lists in the application.
-
-```json
-{
- ...,
- "pagination": {
- "supportedPageSizes": [
- 25,
- 50,
- 100
- ]
- },
- ...
-}
-```
-
-### Your custom settings
-
-You can store any information in the application configuration file, and access it at runtime by using the `AppConfigService` service provided by ADF.
-
-
-Please refer to the AppConfigService documentation to get more details on Application Configuration features and API available.
-
-
-## Navigation
-
-The Alfresco Content Application provides the following navigation links:
-
-- Personal Files
-- File Libraries
-- Shared
-- Recent Files
-- Favorites
-- Trash
-
-The side navigation provides support to customize the appearance of the links by editing the `app.config.json`.
-
-### Customization
-
-Navigation configuration supports array and object like schema. Defining an object helps navigation to render visual delimiters between different groups of links.
-
-```json
-{
- "navigation": {
- "main": [
- ...
- ],
- "secondary": [
- ...
- ]
- }
-}
-```
-
-
-
-```json
-{
- "navigation": [
- { ... },
- { ... },
- ...
- ]
-}
-```
-
-
-
-#### Customize icons and text
-
-`icon` - supported value can be anything from [Material Design](https://material.io/icons) icons library. If not defined, the link will render just the label value.
-
-`title` - instructs the link to render a native browser tooltip with the given value. It can be a string or a i18n defined reference. If not defined, the link will not show a tooltip.
-
-`label` - represents the visual name of the link. It can be a string or a i18n defined reference.
-
-
- Changing ` "route": { "url": "/..." } ` value will affect the navigation since these are mapped to application routing system.
-
-
-#### Custom text (i18n)
-
-To change the `title` and `label` of navigation links edit the values under `BROWSE` entry found at `/src/assets/i18n/en.json`
-
-```json
-"APP" : {
- ...
- "BROWSE": {
- "PERSONAL": {
- "TITLE": "Personal Files",
- "SIDENAV_LINK": {
- "LABEL": "Personal Files",
- "TOOLTIP": "View your Personal Files"
- }
- },
- ...
- }
-}
-```
-
-For more information about internationalization see [Internationalization (i18n)](#internationalization-i18n) section.
-
-### User-defined navigation
-
-To add custom navigation link for the application, first we need to create a component.
-
-`src/app/components/custom-page/custom-page.component.ts`
-
-```js
-import { Component } from '@angular/core';
-
-@Component({
-template: `
-
{{ title }}
- `
-})
-export class CustomPage {
- title = 'My Custom Page'
-}
-```
-
-Register the component in ```app.module.ts```
-
-```javascript
-
- ...
- import { CustomPage } from './components/custom-page/custom-page.component';
-
- @NgModule({
- ...
- declarations: [
- ...,
- CustomPage
- ],
- ...
-})
-
-```
-
-In the `app.config.json` define a link entry which will point to the custom page
-
-```json
-{
- ...,
- "navigation": [
- "main": [ ... ],
- "secondary": [ ... ],
- "custom": [
- {
- "icon": "work",
- "label": "Link",
- "title": "My custom link",
- "route": {
- "url": "/custom-route"
- }
- }
- ]
- ]
-}
-
-```
-
-Map the `/custom-route` in `app.routes.ts` as a child of `LayoutComponent` definition.
-
-```js
-
- import { CustomPage } from './components/custom-page/custom-page.component.ts';
-
- ...
- {
- path: '',
- component: LayoutComponent,
- children: [
- ...,
- {
- path: 'custom-route',
- component: CustomPage
- }
- ]
- }
- ...,
-
-```
-
-
-
-For more information about the content of a custom page see [Document List Layout](/#document-list-layout) section.
-
-## Docker
-
-The ACA comes with the ACS 6.0 Community Edition preconfigured.
-The application runs in to modes:
-
-- Development (runs latest source code, requires building application)
-- Preview (runs with latest published containers, master branch)
-
-### Development Mode
-
-Run the local instance of the application packaged into the docker image together with the ACS images:
-
-```sh
-npm run build
-npm run start:docker
-```
-
-The ACA runs on port `4000` when served from within container.
-
-Use the following command to stop all the containers:
-
-```sh
-npm run stop:docker
-```
-
-### Preview Mode
-
-
-With this mode, you do not need building application from source code or installing dependencies.
-
-
-To run the latest published container go to the `docker-compose` folder and start docker compose from there:
-
-```sh
-cd docker-compose
-docker-compose up
-```
-
-The application is available at the `http://localhost:3000` address.
diff --git a/docs/getting-started/README.md b/docs/getting-started/README.md
new file mode 100644
index 000000000..e3ed3c9b6
--- /dev/null
+++ b/docs/getting-started/README.md
@@ -0,0 +1,16 @@
+---
+---
+
+# Getting started
+
+Learn how to start developing with the Alfresco Content Application.
+
+## Contents
+
+- [Prerequisites](/getting-started/prerequisites)
+- [Building from source](/getting-started/building-from-source)
+- [Internationalization (i18n)](/getting-started/internationalization)
+- [CORS](/getting-started/cors)
+- [Configuration](/getting-started/configuration)
+- [Navigation](/getting-started/navigation)
+- [Docker](/getting-started/docker)
\ No newline at end of file
diff --git a/docs/getting-started/building-from-source.md b/docs/getting-started/building-from-source.md
new file mode 100644
index 000000000..084b27922
--- /dev/null
+++ b/docs/getting-started/building-from-source.md
@@ -0,0 +1,39 @@
+---
+---
+
+# Building from source
+
+The Content App is based on [Angular CLI](https://cli.angular.io), and you can use all the commands, generators, and blueprints supported by the CLI.
+
+## Prerequisites for building
+
+- [Node.js](https://nodejs.org/en/) LTS
+- [Angular CLI](https://cli.angular.io/) 1.7.3
+
+## Cloning and running
+
+Use the following commands to clone the project, install dependencies and run it.
+
+```sh
+git clone https://github.com/Alfresco/alfresco-content-app.git
+cd alfresco-content-app
+npm install
+npm start
+```
+
+The application runs at port `4200` by default, and should automatically open in the default browser once the project has compiled.
+
+## Proxy settings
+
+The Content App provides a proxy configuration for a local development server that allows you to address specific scenarios with CORS and a native authentication dialog.
+
+You can find settings in the "proxy.conf.js" file in the project root directory.
+
+
+The proxy settings get automatically applied every time you run the application with the "npm start" script.
+You must restart the application every time you change its settings.
+
+
+## Running unit tests
+
+Run `npm test` to execute the unit tests via [Karma](https://karma-runner.github.io).
\ No newline at end of file
diff --git a/docs/getting-started/configuration.md b/docs/getting-started/configuration.md
new file mode 100644
index 000000000..98e331c98
--- /dev/null
+++ b/docs/getting-started/configuration.md
@@ -0,0 +1,179 @@
+---
+---
+
+# Configuration
+
+The Alfresco Content Application provides support for the global settings file `app.config.json` that you can use to customize the behavior of the Content Application and ADF components.
+
+## Server settings
+
+Once the Content Application starts, it needs to know where the Alfresco Content Services server is, and what the file sharing URL will be.
+
+### Content Services Address
+
+The "ecmHost" property allows you to set the address of the server using the dynamic or static format.
+
+#### Dynamic address
+
+The example below demonstrates the most common dynamic format for a development environment:
+
+```json
+{
+ "ecmHost": "http://{hostname}{:port}",
+ ...
+}
+```
+
+The configuration above assumes you are running ACS and the Alfresco Content Application on the same server and port
+and allows deploying to different servers having the same unified configuration file.
+
+For example, a proxy server at `localhost:4200` hosting the Alfresco Content Application as the root application,
+and `localhost:4200/alfresco` for the ACS repository.
+
+At runtime, the application is going to automatically substitute the "{hostname}" value with the original hostname.
+Optionally it can also use the value of the original port if present, for example, "4200" at local machines, or skip the value for port 80.
+
+#### Static address
+
+Alternatively, you can provide a static address for the ACS server if necessary:
+
+```json
+{
+ "ecmHost": "http://localhost:4200",
+ ...
+}
+```
+
+### Shared Files Address
+
+The "baseShareUrl" property tells the application how to construct the address where users will access shared files.
+
+#### Default
+
+When the default value is set the application will construct the File Share URL from the "ecmHost" property:
+
+```json
+{
+ ...
+ "baseShareUrl": null,
+ ...
+}
+```
+
+#### Configuration
+
+If you run the application from a different server than the Content Services server the "baseShareUrl" property should must be configured correctly, for example:
+
+```json
+{
+ ...
+ "baseShareUrl": "http://{serveraddress}{:port}",
+ ...
+}
+```
+
+
+If you run the application as part of Tomcat and not in the root (subfolder), then "baseShareUrl" value should contain full address to the app, for example: "baseShareUrl": "http://{serveraddress}{:port}/{folder}".
+
+
+
+## Application settings
+
+There are many settings you can change to alter the default behavior of the application.
+
+### Application Name
+
+The following block allows you to change the name of the application.
+
+```json
+{
+ ...,
+ "application": {
+ "name": "Alfresco Example Content Application"
+ }
+}
+```
+
+The value of the `application.name` key gets appended to every browser tab title at runtime
+with the format `[page title] - [application name]`,
+for example: "Personal Files - Alfresco Example Content Application".
+
+### Application Logo
+
+The default logo displayed in the top left corner of the Alfresco Content Application can be changed:
+
+1. Place your custom logo image file in the [app-name]/src/assets/images folder. The displayed image will resize automatically, an image with extreme width/height might not retain its dimensions.
+
+2. In the app.config.json file, set the value of the application.logo to contain the name of the custom logo image: "logo": "/assets/images/[image-name].[extension]"
+
+
+```json
+{
+ ...,
+ "application": {
+ "logo": "/assets/images/alfresco-logo-white.svg"
+ }
+}
+```
+
+### Header Background color
+
+You can change the header background color by specifying the color code for the "headerColor" key:
+
+```json
+{
+ ...,
+ "headerColor": "#2196F3"
+}
+```
+
+### Restricted content
+
+You can restrict users from uploading certain types of files and folders by setting or extending the list of rules at the "files.excluded" path.
+
+By default, the application ships with the following rules already predefined:
+
+```json
+{
+ ...,
+ "files": {
+ "excluded": [
+ ".DS_Store",
+ "desktop.ini",
+ "thumbs.db",
+ ".git"
+ ]
+ },
+ ...
+}
+```
+
+
+You can get more details on the supported rules in the following article: Upload Service.
+
+
+### Pagination settings
+
+You can change the default settings of the pagination that gets applied to all the document lists in the application.
+
+```json
+{
+ ...,
+ "pagination": {
+ "supportedPageSizes": [
+ 25,
+ 50,
+ 100
+ ]
+ },
+ ...
+}
+```
+
+## Your custom settings
+
+You can store any information in the application configuration file, and access it at runtime by using the `AppConfigService` service provided by ADF.
+
+
+Please refer to the AppConfigService documentation to get more details on Application Configuration features and API's available.
+
diff --git a/docs/getting-started/cors.md b/docs/getting-started/cors.md
new file mode 100644
index 000000000..6396ce7f2
--- /dev/null
+++ b/docs/getting-started/cors.md
@@ -0,0 +1,27 @@
+---
+---
+
+# CORS
+
+The Alfresco Content Application comes with the proxy configuration for Angular CLI to address CORS-related issues for development.
+Also, the docker images contain Nginx settings needed for CORS when developing and debugging an application locally.
+
+## Chrome Workaround
+
+For the Chrome browser, you can use the following plugin that allows you to toggle CORS:
+[Allow-Control-Allow-Origin](https://chrome.google.com/webstore/detail/allow-control-allow-origi/nlfbmbojpeacfghkpbjhddihlkkiljbi)
+
+## Firefox Workaround
+
+For the Chrome browser, you can use the following plugin: [CORS Everywhere](https://addons.mozilla.org/en-Gb/firefox/addon/cors-everywhere/)
+
+## Safari Workaround
+
+If you are developing or testing with Safari then you can use the "Develop" menu to toggle the CORS mode.
+Note, the page must be reloaded every time you change the CORS settings.
+
+
+
+## See also
+
+- [Using CORS](https://www.html5rocks.com/en/tutorials/cors/)
diff --git a/docs/getting-started/docker.md b/docs/getting-started/docker.md
new file mode 100644
index 000000000..8b3a0f6ed
--- /dev/null
+++ b/docs/getting-started/docker.md
@@ -0,0 +1,42 @@
+---
+---
+
+# Docker
+
+The ACA comes with the ACS 6.0 Community Edition preconfigured.
+The application runs in two modes:
+
+- Development (runs latest source code, requires building application)
+- Preview (runs with latest published containers, master branch)
+
+## Development Mode
+
+Run the local instance of the application packaged into the docker image together with the ACS images:
+
+```sh
+npm run build
+npm run start:docker
+```
+
+The ACA runs on port `4000` when served from within container.
+
+Use the following command to stop all the containers:
+
+```sh
+npm run stop:docker
+```
+
+## Preview Mode
+
+
+With this mode, you do not need building application from source code or installing dependencies.
+
+
+To run the latest published container go to the `docker-compose` folder and start docker compose from there:
+
+```sh
+cd docker-compose
+docker-compose up
+```
+
+The application is available at the `http://localhost:3000` address.
diff --git a/docs/getting-started/internationalization.md b/docs/getting-started/internationalization.md
new file mode 100644
index 000000000..b929d4b9d
--- /dev/null
+++ b/docs/getting-started/internationalization.md
@@ -0,0 +1,134 @@
+---
+---
+
+# Internationalization (i18n)
+
+The Content Application provides support for the following languages:
+
+- German (`de`)
+- English (`en`)
+- Spanish (`es`)
+- French (`fr`)
+- Italian (`it`)
+- Japanese (`ja`)
+- Norwegian (`nb`)
+- Dutch (`nl`)
+- Brazilian Portuguese (`pt-BR`)
+- Russian (`ru`)
+- Simplified Chinese (`zh-CN`)
+
+The default language is English, however the current browser language is taken as the default one automatically when the application starts.
+
+## User-defined language
+
+You can allow users to set custom languages that are saved to their preferences.
+The main application menu already has the [ADF Language Menu](https://github.com/Alfresco/alfresco-ng2-components/blob/master/docs/core/language-menu.component.md) component integrated and pre-filled with the supported items.
+
+To change the default language set edit the `app.config.json` file and add or remove items:
+
+```json
+{
+ ...,
+ "languages": [
+ {
+ "key": "de",
+ "label": "German"
+ },
+ {
+ "key": "en",
+ "label": "English"
+ },
+ {
+ "key": "es",
+ "label": "Spanish"
+ },
+ ...
+ ]
+}
+```
+
+The file is located here: `/src/app.config.json`.
+
+## Custom languages
+
+To add a custom language, add a new "JSON" file to the "/src/assets/i18n" folder
+with the name of the target language, for instance, a "de.json" for "German".
+
+Translate the resource strings based on the default "en.json" file.
+You can copy the content over to your newly created file and replace the English values with translated text.
+
+```json
+{
+ "APP": {
+ "SIGN_IN": "Anmelden",
+ "SIGN_OUT": "Abmelden",
+ "NEW_MENU": {
+ "LABEL": "Neu",
+ "MENU_ITEMS": {
+ "CREATE_FOLDER": "Ordner erstellen",
+ "UPLOAD_FILE": "Datei hochladen",
+ "UPLOAD_FOLDER": "Ordner hochladen"
+ },
+ ...
+ }
+ },
+ ...
+}
+```
+
+The Content Application automatically bundles your file when the project builds.
+You can test your locale by changing the browser language settings and reloading the page.
+
+Optionally, you can extend the [ADF Language Menu](https://github.com/Alfresco/alfresco-ng2-components/blob/master/docs/core/language-menu.component.md) component with the newly added language by updating the `app.config.json` file.
+
+## Customizing ADF translations
+
+In addition to creating a custom language file for the Content Application,
+you can also provide translations for the ADF resources.
+
+Your `/src/assets/i18n/.json` file can reflect the structure of one of the ADF language files:
+
+- ADF Core ([en.json](https://github.com/Alfresco/alfresco-ng2-components/blob/master/lib/core/i18n/en.json))
+- ADF Content Services ([en.json](https://github.com/Alfresco/alfresco-ng2-components/blob/master/lib/content-services/i18n/en.json))
+- ADF Process Services ([en.json](https://github.com/Alfresco/alfresco-ng2-components/blob/master/lib/process-services/i18n/en.json))
+- ADF Insights ([en.json](https://github.com/Alfresco/alfresco-ng2-components/blob/master/lib/insights/i18n/en.json))
+
+At runtime, the application-level strings have the highest priority.
+That means you can replace the value of any ADF resource string if needed.
+
+For example, let's change the title of the "Create Folder" dialog that was shipped with ADF.
+Modify the `/src/assets/i18n/en.json` file and append the "CORE" section like in the example below:
+
+```json
+{
+ "APP": {
+ ...
+ },
+ "CORE": {
+ "FOLDER_DIALOG": {
+ "CREATE_FOLDER_TITLE": "Custom title"
+ }
+ }
+}
+```
+
+Now, if you run the application and click the "New > Create Folder" menu,
+the title of the dialog should look like the following:
+
+
+
+## Language picker
+
+You can enable internal language picker in the `app.config.json` file:
+
+```json
+{
+ ...,
+
+ "languagePicker": true,
+
+ ...
+}
+```
+
+
diff --git a/docs/getting-started/navigation.md b/docs/getting-started/navigation.md
new file mode 100644
index 000000000..6a2858bb6
--- /dev/null
+++ b/docs/getting-started/navigation.md
@@ -0,0 +1,166 @@
+---
+---
+
+# Navigation
+
+The Alfresco Content Application provides the following navigation links:
+
+- Personal Files
+- File Libraries
+- Shared
+- Recent Files
+- Favorites
+- Trash
+
+The side navigation provides support to customize the appearance of the links by editing the `app.config.json`.
+
+## Customization
+
+Navigation configuration supports array and object like schema. Defining an object helps navigation to render visual delimiters between different groups of links.
+
+```json
+{
+ "navigation": {
+ "main": [
+ ...
+ ],
+ "secondary": [
+ ...
+ ]
+ }
+}
+```
+
+
+
+```json
+{
+ "navigation": [
+ { ... },
+ { ... },
+ ...
+ ]
+}
+```
+
+
+
+### Customize icons and text
+
+`icon` - supported value can be anything from [Material Design](https://material.io/icons) icons library. If not defined, the link will render just the label value.
+
+`title` - instructs the link to render a native browser tooltip with the given value. It can be a string or a i18n defined reference. If not defined, the link will not show a tooltip.
+
+`label` - represents the visual name of the link. It can be a string or a i18n defined reference.
+
+
+ Changing ` "route": { "url": "/..." } ` value will affect the navigation since these are mapped to application routing system.
+
+
+### Custom text (i18n)
+
+To change the `title` and `label` of navigation links edit the values under `BROWSE` entry found at `/src/assets/i18n/en.json`
+
+```json
+"APP" : {
+ ...
+ "BROWSE": {
+ "PERSONAL": {
+ "TITLE": "Personal Files",
+ "SIDENAV_LINK": {
+ "LABEL": "Personal Files",
+ "TOOLTIP": "View your Personal Files"
+ }
+ },
+ ...
+ }
+}
+```
+
+For more information about internationalization see [Internationalization (i18n)](/getting-started/internationalization) section.
+
+## User-defined navigation
+
+To add custom navigation link for the application, first we need to create a component.
+
+`src/app/components/custom-page/custom-page.component.ts`
+
+```js
+import { Component } from '@angular/core';
+
+@Component({
+template: `
+
{{ title }}
+ `
+})
+export class CustomPage {
+ title = 'My Custom Page'
+}
+```
+
+Register the component in ```app.module.ts```
+
+```javascript
+
+ ...
+ import { CustomPage } from './components/custom-page/custom-page.component';
+
+ @NgModule({
+ ...
+ declarations: [
+ ...,
+ CustomPage
+ ],
+ ...
+})
+
+```
+
+In the `app.config.json` define a link entry which will point to the custom page
+
+```json
+{
+ ...,
+ "navigation": [
+ "main": [ ... ],
+ "secondary": [ ... ],
+ "custom": [
+ {
+ "icon": "work",
+ "label": "Link",
+ "title": "My custom link",
+ "route": {
+ "url": "/custom-route"
+ }
+ }
+ ]
+ ]
+}
+
+```
+
+Map the `/custom-route` in `app.routes.ts` as a child of `LayoutComponent` definition.
+
+```js
+
+ import { CustomPage } from './components/custom-page/custom-page.component.ts';
+
+ ...
+ {
+ path: '',
+ component: LayoutComponent,
+ children: [
+ ...,
+ {
+ path: 'custom-route',
+ component: CustomPage
+ }
+ ]
+ }
+ ...,
+
+```
+
+
+
+For more information about the content of a custom page see [Document List Layout](/#document-list-layout) section.
diff --git a/docs/getting-started/prerequisites.md b/docs/getting-started/prerequisites.md
new file mode 100644
index 000000000..bdb5dc7a9
--- /dev/null
+++ b/docs/getting-started/prerequisites.md
@@ -0,0 +1,17 @@
+---
+---
+
+# Prerequisites
+
+This application uses the latest releases from Alfresco:
+
+- [Alfresco ADF (2.6.0)](https://community.alfresco.com/community/application-development-framework/pages/get-started)
+- [Alfresco Content Services (6.0)](https://www.alfresco.com/platform/content-services-ecm)
+ or [Alfresco Community Edition (6.0 - General Release: 201806)](https://www.alfresco.com/products/community/download)
+
+
+You also need node.js (LTS) installed to build it locally from source code.
+
+
+The latest version of the Alfresco Content platform is required
+due to the application using the latest [REST APIs](https://docs.alfresco.com/5.2/pra/1/topics/pra-welcome.html) developments.
\ No newline at end of file
diff --git a/docs/help.md b/docs/help.md
index b1cbe4176..6c4f91632 100644
--- a/docs/help.md
+++ b/docs/help.md
@@ -4,11 +4,11 @@ title: Get Help
# Where to get help
-There are several ways to get help with building applications using the Alfresco Application Development Framework:
+See below for the different ways you can get help when building applications using the Alfresco Application Development Framework.
## Alfresco Community
-Visit the Alfresco Community space where you can find many resources to help you get started building your application,
+Visit Alfresco Community for resources that will help you find information to get started building your application,
along with blog posts from the Alfresco developers and much more:
https://community.alfresco.com/community/application-development-framework
@@ -20,8 +20,8 @@ https://gitter.im/Alfresco/alfresco-ng2-components
## Alfresco Developer Support
-Developer Support is a subscription-based support offering delivered remotely
-by a dedicated team of development-focused, senior Support Engineers.
+Developer Support is a remote subscription-based support team that offers
+development-focused, senior Support Engineers to help you with your installation.
This support offering is best for customers and partners that require on-going support
for their development teams and customized code.
diff --git a/docs/index.html b/docs/index.html
index 2d5a42a54..c4f7d641e 100644
--- a/docs/index.html
+++ b/docs/index.html
@@ -14,6 +14,7 @@