ACS-8610: cleanup demo shell protractor tests (#10148)

This commit is contained in:
Denys Vuika 2024-09-10 08:36:42 -04:00 committed by GitHub
parent a754976ad9
commit f1208d45c3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
402 changed files with 180 additions and 45538 deletions

View File

@ -10,10 +10,7 @@ node_modules
scripts
src
lib
integration
tools
demo-shell/src/
demo-shell/resources/
/angular.json
/desktop.ini
/cspell.json
@ -21,5 +18,4 @@ demo-shell/resources/
/.stylelintignore
/ALFRESCOCORS.md
/CONTRIBUTING.md
/appveyor.yml
/BROWSER-SUPPORT.md

View File

@ -18,7 +18,7 @@ module.exports = {
{
files: ['*.ts'],
parserOptions: {
project: ['tsconfig.json', 'e2e/tsconfig.e2e.json'],
project: ['tsconfig.json'],
createDefaultProgram: true
},
extends: [

View File

@ -1,92 +0,0 @@
name: Append content to Artifact
description: 'Allow the user to append content to an existing artifact'
inputs:
artifact-name:
description: 'The name of the artifact'
required: true
type: string
file-name:
description: 'The name of the file with extension created in the artifact'
required: true
type: string
content:
description: 'The content to append'
type: string
default: ""
runs:
using: "composite"
steps:
- run: echo "Artifact Append"
shell: bash
- name: Download artifact
uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0
with:
name: ${{ inputs.artifact-name }}
pattern: ${{ inputs.artifact-name }}-*
merge-multiple: true
- run: ls
shell: bash
- name: Append content
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
env:
contentFile: ${{ inputs.content }}
fileName: ${{ inputs.file-name }}
with:
script: |
const fs = require('fs');
const affectedLib = process.env.contentFile;
const fileName = process.env.fileName;
core.info(`Input Filename: ${fileName}`);
core.info(`Input content: ${affectedLib}`);
const content = read(fileName)
core.info(`File content: ${content}`);
appendContent(content, affectedLib);
function read(filename) {
try {
const contentFile = fs.readFileSync(filename, 'utf8').replace('\n','');
return contentFile;
} catch (err) {
core.error(err);
}
}
function write(filename, content) {
try {
fs.writeFileSync(filename, content);
} catch (err) {
core.error(err);
}
}
function appendContent(content, append) {
let changedContent;
const libs = content.split(' ');
if (libs?.length>0) {
if (libs.length === 1 && libs[0] === '') {
libs[0] = append;
changedContent = libs[0];
}
else if (!libs.includes(append)) {
libs.push(append);
changedContent = libs.join(' ');
} else {
core.info(`Lib ${append} already affected`);
}
}
if (changedContent != undefined){
core.info(`File content append: ${changedContent}`)
write(fileName, changedContent);
}
}
- name: Upload artifact
uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0
with:
name: ${{ inputs.artifact-name }}
path: ${{ inputs.file-name }}

View File

@ -1,42 +0,0 @@
name: Extract Artifact
description: 'Allow the user to extract content from an artifact'
inputs:
artifact-name:
description: 'The name of the artifact'
required: true
type: string
file-name:
description: 'The name of the file with extension created in the artifact'
required: true
type: string
content:
description: 'The init content the file should have'
type: string
default: ""
outputs:
result:
description: "the value extrated from the file inside the artifact"
value: ${{ steps.extract.outputs.result }}
runs:
using: "composite"
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- run: echo "Artifact Extract"
shell: bash
- name: Download artifact
uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0
with:
name: ${{ inputs.artifact-name }}
pattern: ${{ inputs.artifact-name }}-*
merge-multiple: true
- id: extract
shell: bash
run: |
value=`cat ${{ inputs.file-name }}`
echo "print $value"
echo "result=$value" >> $GITHUB_OUTPUT

View File

@ -1,32 +0,0 @@
name: Initialize Artifact
description: 'Allow the user to initialize an empty artifact used globally'
inputs:
artifact-name:
description: 'The name of the artifact'
required: true
type: string
file-name:
description: 'The name of the file with extension created in the artifact'
required: true
type: string
content:
description: 'The init content the file should have'
type: string
default: ""
runs:
using: "composite"
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- name: Create empty artifact
shell: bash
run:
echo "${{inputs.content}}" > ${{ inputs.file-name }}
- name: Upload artifact
uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0
with:
name: ${{ inputs.artifact-name }}
path: ${{ inputs.file-name }}

View File

@ -1,204 +0,0 @@
name: "e2e"
description: "e2e"
inputs:
e2e-test-id:
description: "Test id"
required: true
e2e-artifact-id:
description: "Artifact id"
required: true
e2e-test-folder:
description: "Test folder"
required: true
e2e-test-provider:
description: "Test provider"
required: true
e2e-test-auth:
description: "Test auth"
required: true
output:
description: "Output path"
required: true
check-cs-env:
required: true
description: check cs env
default: "false"
check-ps-env:
required: true
description: check ps env
default: "false"
check-external-cs-env:
required: true
description: check external cs env
default: "false"
check-ps-cloud-env:
required: true
description: check ps cloud env
default: "false"
e2e-tar-name: #
description: tarball name
required: false
default: e2e.tar.gz
apa-proxy: #
description: "proxy host"
required: true
deps:
description: "Library dependencies"
required: false
default: ""
runs:
using: "composite"
steps:
- name: Determine if affected
shell: bash
id: determine-affected
run: |
isAffected=false
affectedLibs=$(npx nx print-affected --type=lib --select=projects ${NX_CALCULATION_FLAGS} --plain)
if [[ $affectedLibs =~ "${{ inputs.e2e-test-id }}" ]]; then
isAffected=true
fi;
echo "Determine if ${{ inputs.e2e-test-id }} is affected: $isAffected";
echo "isAffected=$isAffected" >> $GITHUB_OUTPUT
- name: print value
shell: bash
run: |
echo "value: ${{ steps.determine-affected.outputs.isAffected }}"
- name: use APA as PROXY host if apa-proxy is set
shell: bash
run: |
if [[ -n "${{ inputs.apa-proxy }}" ]]; then
echo "APA proxy set."
echo "PROXY_HOST_BPM=${E2E_HOST_APA}" >> $GITHUB_ENV
echo "PROXY_HOST_ECM=${E2E_IDENTITY_HOST_APA}" >> $GITHUB_ENV
echo "HOST_SSO=${E2E_IDENTITY_HOST_APA}" >> $GITHUB_ENV
fi
- name: install aws cli
shell: bash
run: pip install awscli
- name: download smartrunner test results from s3 bucket if they exist
shell: bash
env:
REMOTE_PATH: smart-runner/${{ github.run_id}}/${{ inputs.e2e-test-folder }}-${{ inputs.e2e-artifact-id}}/e2e.tar.gz
run: |
set -u;
mkdir -p "${SMART_RUNNER_PATH}"
if [[ $(aws s3 ls "s3://${S3_BUILD_BUCKET_SHORT_NAME}/adf/${REMOTE_PATH}" > /dev/null; echo $?) -eq 0 ]]; then
echo "downloading test files"
aws s3 cp "s3://${S3_BUILD_BUCKET_SHORT_NAME}/adf/${REMOTE_PATH}" .;
tar xzf ${{ inputs.e2e-tar-name }};
else
echo "nothing to download";
fi
- name: check EXTERNAL-CS is UP
shell: bash
if: ${{ inputs.check-external-cs-env == 'true' && steps.determine-affected.outputs.isAffected == 'true' }}
run: |
echo "running: check EXTERNAL-CS is UP"
set -u;
./node_modules/@alfresco/adf-cli/bin/adf-cli \
check-cs-env \
--host "$EXTERNAL_ACS_HOST" \
-u "$E2E_USERNAME" \
-p "$E2E_PASSWORD" || exit 1
- name: Check CS is UP
shell: bash
if: ${{ inputs.check-cs-env == 'true' && steps.determine-affected.outputs.isAffected == 'true' }}
run: |
echo "Running: Check CS is UP"
set -u;
./node_modules/@alfresco/adf-cli/bin/adf-cli \
check-cs-env \
--host "$E2E_HOST" \
-u "$E2E_USERNAME" \
-p "$E2E_PASSWORD" || exit 1
- name: check PS is UP
shell: bash
if: ${{ inputs.check-ps-env == 'true' && steps.determine-affected.outputs.isAffected == 'true' }}
run: |
echo "Running: check PS is UP"
set -u;
./node_modules/@alfresco/adf-cli/bin/adf-cli init-aps-env \
--host "$E2E_HOST" \
-u "$E2E_USERNAME" \
-p "$E2E_PASSWORD" \
--license "$AWS_S3_BUCKET_ACTIVITI_LICENSE" || exit 1
- name: check PS-CLOUD is UP
shell: bash
if: ${{ inputs.check-ps-cloud-env == 'true' && steps.determine-affected.outputs.isAffected == 'true' }}
run: |
echo "running: check PS-CLOUD is UP"
set -u;
./node_modules/@alfresco/adf-cli/bin/adf-cli init-aae-env \
--oauth "$E2E_IDENTITY_HOST_APA" \
--host "$E2E_HOST_APA" \
--modelerUsername "$E2E_MODELER_USERNAME" \
--modelerPassword "$E2E_MODELER_PASSWORD" \
--devopsUsername "$E2E_DEVOPS_USERNAME" \
--devopsPassword "$E2E_DEVOPS_PASSWORD" \
--clientId 'alfresco' || exit 1
- name: variables sanitization
env:
FOLDER: "${{ inputs.e2e-test-folder }}"
PROVIDER: "${{ inputs.e2e-test-provider }}"
AUTH_TYPE: "${{ inputs.e2e-test-auth }}"
E2E_TEST_ID: "${{ inputs.e2e-test-id }}"
DEPS: "${{ inputs.deps }}"
shell: bash
run: |
set -u;
echo $PROXY_HOST_BPM
echo "GIT_HASH=$GIT_HASH" >> $GITHUB_ENV
- name: run test
id: e2e_run
if: ${{ steps.determine-affected.outputs.isAffected == 'true' }}
env:
FOLDER: "${{ inputs.e2e-test-folder }}"
PROVIDER: "${{ inputs.e2e-test-provider }}"
AUTH_TYPE: "${{ inputs.e2e-test-auth }}"
E2E_TEST_ID: "${{ inputs.e2e-test-id }}"
DEPS: "${{ inputs.deps }}"
shell: bash
run: |
set -u;
if [[ ${{ inputs.e2e-test-folder }} == 'content-services/upload' ]]; then
export DISPLAY=:99
chromedriver --url-base=/wd/hub &
sudo Xvfb -ac :99 -screen 0 1280x1024x24 > /dev/null 2>&1 & # optional
bash ./scripts/github/e2e/e2e.sh "$E2E_TEST_ID" "$DEPS" "browser" || exit 1
else
bash ./scripts/github/e2e/e2e.sh "$E2E_TEST_ID" "$DEPS" || exit 1
fi
- name: Trace failing e2e
if: ${{ steps.determine-affected.outputs.isAffected == 'true' && github.event_name == 'schedule' && failure() }}
uses: ./.github/actions/artifact-append
with:
artifact-name: "global-e2e-result-${{ inputs.e2e-test-id }}"
file-name: e2e-failures.txt
content: "${{ inputs.e2e-test-id }}"
- name: upload artifacts on gh
id: upload_gh
if: ${{ steps.determine-affected.outputs.isAffected == 'true' }}
uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0
with:
name: e2e-artifact-output-${{inputs.e2e-artifact-id}}
path: /home/runner/work/alfresco-ng2-components/alfresco-ng2-components/e2e-output-*
- name: upload smart-runner tests results on s3 to cache tests
shell: bash
if: always()
env:
REMOTE_PATH: "smart-runner/${{ github.run_id}}/${{ inputs.e2e-test-folder }}-${{inputs.e2e-artifact-id}}/e2e.tar.gz"
# description: always upload newer results
run: |
tar czf "${{ inputs.e2e-tar-name }}" "${SMART_RUNNER_PATH}"
aws s3 cp "${{ inputs.e2e-tar-name }}" "s3://${S3_BUILD_BUCKET_SHORT_NAME}/adf/${REMOTE_PATH}"

View File

@ -1,13 +0,0 @@
name: 'Install Google Chrome'
description: 'Install Google Chrome'
runs:
using: "composite"
steps:
- name: Install google chrome
shell: bash
run: |
wget -q https://dl.google.com/linux/chrome/deb/pool/main/g/google-chrome-stable/google-chrome-stable_114.0.5735.133-1_amd64.deb
sudo apt install -y --allow-downgrades ./google-chrome-stable_114.0.5735.133-1_amd64.deb
sudo ln -s /usr/bin/google-chrome /usr/bin/chrome
chrome --version

View File

@ -30,18 +30,6 @@ updates:
directory: "/"
schedule:
interval: "weekly"
- package-ecosystem: "github-actions"
directory: "/.github/actions/artifact-append"
schedule:
interval: "weekly"
- package-ecosystem: "github-actions"
directory: "/.github/actions/artifact-extract"
schedule:
interval: "weekly"
- package-ecosystem: "github-actions"
directory: "/.github/actions/artifact-initialize"
schedule:
interval: "weekly"
- package-ecosystem: "github-actions"
directory: "/.github/actions/before-install"
schedule:
@ -50,10 +38,6 @@ updates:
directory: "/.github/actions/download-node-modules-and-artifacts"
schedule:
interval: "weekly"
- package-ecosystem: "github-actions"
directory: "/.github/actions/e2e"
schedule:
interval: "weekly"
- package-ecosystem: "github-actions"
directory: "/.github/actions/enable-dryrun"
schedule:
@ -78,10 +62,6 @@ updates:
directory: "/.github/actions/setup"
schedule:
interval: "weekly"
- package-ecosystem: "github-actions"
directory: "/.github/actions/setup-chrome"
schedule:
interval: "weekly"
- package-ecosystem: "github-actions"
directory: "/.github/actions/slack-group-area"
schedule:

View File

@ -1,87 +0,0 @@
name: "cron e2e daily"
on:
workflow_dispatch:
schedule:
- cron: '0 12 * * 1-5' #At 12:00 on every day-of-week from Monday through Friday.
env:
BASE_REF: ${{ github.base_ref }}
HEAD_REF: ${{ github.head_ref }}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
GITHUB_BRANCH: ${{ github.ref_name }}
GH_BUILD_DIR: ${{ github.workspace }}
GH_COMMIT: ${{ github.sha }}
BUILD_ID: ${{ github.run_id }}
GH_RUN_NUMBER: ${{ github.run_attempt }}
GH_BUILD_NUMBER: ${{ github.run_id }}
JOB_ID: ${{ github.run_id }}
PROXY_HOST_BPM: ${{ secrets.E2E_HOST }}
E2E_HOST_APA: ${{ secrets.E2E_HOST_APA }}
E2E_IDENTITY_HOST_APA: ${{ secrets.E2E_IDENTITY_HOST_APA }}
E2E_HOST: ${{ secrets.E2E_HOST }}
E2E_USERNAME: ${{ secrets.E2E_ADMIN_EMAIL_IDENTITY }}
E2E_PASSWORD: ${{ secrets.E2E_PASSWORD }}
E2E_ADMIN_EMAIL_IDENTITY: ${{ secrets.E2E_ADMIN_EMAIL_IDENTITY }}
E2E_ADMIN_PASSWORD_IDENTITY: ${{ secrets.E2E_ADMIN_PASSWORD_IDENTITY }}
USERNAME_ADF: ${{ secrets.E2E_ADMIN_EMAIL_IDENTITY }}
PASSWORD_ADF: ${{ secrets.E2E_PASSWORD }}
URL_HOST_ADF: "http://localhost:4200"
IDENTITY_ADMIN_EMAIL: ${{ secrets.E2E_ADMIN_EMAIL_IDENTITY }}
IDENTITY_ADMIN_PASSWORD: ${{ secrets.E2E_ADMIN_PASSWORD_IDENTITY }}
AWS_S3_BUCKET_ACTIVITI_LICENSE: ${{ secrets.AWS_S3_BUCKET_ACTIVITI_LICENSE }}
HOST_SSO: ${{ secrets.HOST_SSO }}
LOG_LEVEL: "ERROR"
E2E_LOG_LEVEL: "ERROR"
E2E_MODELER_USERNAME: ${{ secrets.E2E_MODELER_USERNAME }}
E2E_MODELER_PASSWORD: ${{ secrets.E2E_MODELER_PASSWORD }}
EXTERNAL_ACS_HOST: ${{ secrets.EXTERNAL_ACS_HOST }}
E2E_DEVOPS_USERNAME: ${{ secrets.E2E_DEVOPS_USERNAME }}
E2E_DEVOPS_PASSWORD: ${{ secrets.E2E_DEVOPS_PASSWORD }}
USERNAME_SUPER_ADMIN_ADF: ${{ secrets.USERNAME_SUPER_ADMIN_ADF }}
PASSWORD_SUPER_ADMIN_ADF: ${{ secrets.PASSWORD_SUPER_ADMIN_ADF }}
HR_USER: ${{ secrets.HR_USER }}
HR_USER_PASSWORD: ${{ secrets.HR_USER_PASSWORD }}
SMART_RUNNER_PATH: ".protractor-smartrunner"
S3_DBP_PATH: ${{ secrets.S3_DBP_PATH }}
S3_BUILD_BUCKET_SHORT_NAME: ${{ secrets.S3_BUILD_BUCKET_SHORT_NAME }}
NODE_OPTIONS: "--max-old-space-size=5120"
DOCKER_REPOSITORY_DOMAIN: ${{ secrets.DOCKER_REPOSITORY_DOMAIN }}
DOCKER_REPOSITORY_USER: ${{ secrets.DOCKER_REPOSITORY_USER }}
DOCKER_REPOSITORY_PASSWORD: ${{ secrets.DOCKER_REPOSITORY_PASSWORD }}
DOCKER_REPOSITORY_STORYBOOK: "${{ secrets.DOCKER_REPOSITORY_DOMAIN }}/alfresco/storybook"
DOCKER_REPOSITORY: "${{ secrets.DOCKER_REPOSITORY_DOMAIN }}/alfresco/demo-shell"
REPO_OWNER: "Alfresco"
REPO_NAME: "alfresco-ng2-components"
DEMO_SHELL_DIR: "./dist/demo-shell"
STORYBOOK_DIR: "./dist/storybook/stories"
BUILT_LIBS_DIR: "./dist/libs"
NODE_MODULES_DIR: "./node_modules"
SMART_RUNNER_DIRECTORY: ".protractor-smartrunner"
SAVE_SCREENSHOT: true
REDIRECT_URI: /
BROWSER_RUN: false
MAXINSTANCES: 2
PLAYWRIGHT_WORKERS: 2
PLAYWRIGHT_STORYBOOK_E2E_HOST: http://localhost
PLAYWRIGHT_STORYBOOK_E2E_PORT: 4400
PROXY_HOST_ECM: ${{ secrets.E2E_HOST }}
jobs:
init-artifact:
runs-on: ubuntu-latest
name: Initialize artifacts
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- uses: ./.github/actions/artifact-initialize
with:
artifact-name: global-e2e-result
file-name: e2e-failures.txt
run-e2e:
name: run e2e
uses: ./.github/workflows/pull-request.yml
with:
cron-run: true
secrets: inherit

View File

@ -25,59 +25,22 @@ env:
GH_RUN_NUMBER: ${{ github.run_attempt }}
GH_BUILD_NUMBER: ${{ github.run_id }}
JOB_ID: ${{ github.run_id }}
PROXY_HOST_BPM: ${{ secrets.E2E_HOST }}
E2E_IDENTITY_HOST_APA: ${{ secrets.E2E_IDENTITY_HOST_APA }}
E2E_HOST_APA: ${{ secrets.E2E_HOST_APA }}
E2E_HOST: ${{ secrets.E2E_HOST }}
E2E_USERNAME: ${{ secrets.E2E_ADMIN_EMAIL_IDENTITY }}
E2E_PASSWORD: ${{ secrets.E2E_PASSWORD }}
E2E_ADMIN_EMAIL_IDENTITY: ${{ secrets.E2E_ADMIN_EMAIL_IDENTITY }}
E2E_ADMIN_PASSWORD_IDENTITY: ${{ secrets.E2E_ADMIN_PASSWORD_IDENTITY }}
USERNAME_ADF: ${{ secrets.E2E_ADMIN_EMAIL_IDENTITY }}
PASSWORD_ADF: ${{ secrets.E2E_PASSWORD }}
URL_HOST_ADF: "http://localhost:4200"
IDENTITY_ADMIN_EMAIL: ${{ secrets.E2E_ADMIN_EMAIL_IDENTITY }}
IDENTITY_ADMIN_PASSWORD: ${{ secrets.E2E_ADMIN_PASSWORD_IDENTITY }}
AWS_S3_BUCKET_ACTIVITI_LICENSE: ${{ secrets.AWS_S3_BUCKET_ACTIVITI_LICENSE }}
HOST_SSO: ${{ secrets.HOST_SSO }}
LOG_LEVEL: "ERROR"
E2E_LOG_LEVEL: "ERROR"
E2E_MODELER_USERNAME: ${{ secrets.E2E_MODELER_USERNAME }}
E2E_MODELER_PASSWORD: ${{ secrets.E2E_MODELER_PASSWORD }}
EXTERNAL_ACS_HOST: ${{ secrets.EXTERNAL_ACS_HOST }}
E2E_DEVOPS_USERNAME: ${{ secrets.E2E_DEVOPS_USERNAME }}
E2E_DEVOPS_PASSWORD: ${{ secrets.E2E_DEVOPS_PASSWORD }}
USERNAME_SUPER_ADMIN_ADF: ${{ secrets.USERNAME_SUPER_ADMIN_ADF }}
PASSWORD_SUPER_ADMIN_ADF: ${{ secrets.PASSWORD_SUPER_ADMIN_ADF }}
HR_USER: ${{ secrets.HR_USER }}
HR_USER_PASSWORD: ${{ secrets.HR_USER_PASSWORD }}
SMART_RUNNER_PATH: ".protractor-smartrunner"
S3_DBP_PATH: ${{ secrets.S3_DBP_PATH }}
S3_BUILD_BUCKET_SHORT_NAME: ${{ secrets.S3_BUILD_BUCKET_SHORT_NAME }}
NODE_OPTIONS: "--max-old-space-size=5120"
DOCKER_REPOSITORY_DOMAIN: ${{ secrets.DOCKER_REPOSITORY_DOMAIN }}
DOCKER_REPOSITORY_USER: ${{ secrets.DOCKER_REPOSITORY_USER }}
DOCKER_REPOSITORY_PASSWORD: ${{ secrets.DOCKER_REPOSITORY_PASSWORD }}
DOCKER_REPOSITORY_STORYBOOK: "${{ secrets.DOCKER_REPOSITORY_DOMAIN }}/alfresco/storybook"
DOCKER_REPOSITORY: "${{ secrets.DOCKER_REPOSITORY_DOMAIN }}/alfresco/demo-shell"
NPM_REGISTRY_ADDRESS: ${{ secrets.NPM_REGISTRY_ADDRESS }}
NPM_REGISTRY_TOKEN: ${{ secrets.NPM_REGISTRY_TOKEN }}
BOT_GITHUB_TOKEN: ${{ secrets.BOT_GITHUB_TOKEN }}
REPO_OWNER: "Alfresco"
REPO_NAME: "alfresco-ng2-components"
DEMO_SHELL_DIR: "./dist/demo-shell"
STORYBOOK_DIR: "./dist/storybook/stories"
BUILT_LIBS_DIR: "./dist/libs"
NODE_MODULES_DIR: "./node_modules"
SMART_RUNNER_DIRECTORY: ".protractor-smartrunner"
SAVE_SCREENSHOT: true
REDIRECT_URI: /
BROWSER_RUN: false
MAXINSTANCES: 2
PLAYWRIGHT_WORKERS: 2
PLAYWRIGHT_STORYBOOK_E2E_HOST: http://localhost
PLAYWRIGHT_STORYBOOK_E2E_PORT: 4400
PROXY_HOST_ECM: ${{ secrets.E2E_HOST }}
jobs:

View File

@ -42,56 +42,19 @@ env:
GH_RUN_NUMBER: ${{ github.run_attempt }}
GH_BUILD_NUMBER: ${{ github.run_id }}
JOB_ID: ${{ github.run_id }}
PROXY_HOST_BPM: ${{ secrets.E2E_HOST }}
E2E_IDENTITY_HOST_APA: ${{ secrets.E2E_IDENTITY_HOST_APA }}
E2E_HOST_APA: ${{ secrets.E2E_HOST_APA }}
E2E_HOST: ${{ secrets.E2E_HOST }}
E2E_USERNAME: ${{ secrets.E2E_ADMIN_EMAIL_IDENTITY }}
E2E_PASSWORD: ${{ secrets.E2E_PASSWORD }}
E2E_ADMIN_EMAIL_IDENTITY: ${{ secrets.E2E_ADMIN_EMAIL_IDENTITY }}
E2E_ADMIN_PASSWORD_IDENTITY: ${{ secrets.E2E_ADMIN_PASSWORD_IDENTITY }}
USERNAME_ADF: ${{ secrets.E2E_ADMIN_EMAIL_IDENTITY }}
PASSWORD_ADF: ${{ secrets.E2E_PASSWORD }}
URL_HOST_ADF: "http://localhost:4200"
IDENTITY_ADMIN_EMAIL: ${{ secrets.E2E_ADMIN_EMAIL_IDENTITY }}
IDENTITY_ADMIN_PASSWORD: ${{ secrets.E2E_ADMIN_PASSWORD_IDENTITY }}
AWS_S3_BUCKET_ACTIVITI_LICENSE: ${{ secrets.AWS_S3_BUCKET_ACTIVITI_LICENSE }}
HOST_SSO: ${{ secrets.HOST_SSO }}
LOG_LEVEL: "ERROR"
E2E_LOG_LEVEL: "ERROR"
E2E_MODELER_USERNAME: ${{ secrets.E2E_MODELER_USERNAME }}
E2E_MODELER_PASSWORD: ${{ secrets.E2E_MODELER_PASSWORD }}
EXTERNAL_ACS_HOST: ${{ secrets.EXTERNAL_ACS_HOST }}
E2E_DEVOPS_USERNAME: ${{ secrets.E2E_DEVOPS_USERNAME }}
E2E_DEVOPS_PASSWORD: ${{ secrets.E2E_DEVOPS_PASSWORD }}
USERNAME_SUPER_ADMIN_ADF: ${{ secrets.USERNAME_SUPER_ADMIN_ADF }}
PASSWORD_SUPER_ADMIN_ADF: ${{ secrets.PASSWORD_SUPER_ADMIN_ADF }}
HR_USER: ${{ secrets.HR_USER }}
HR_USER_PASSWORD: ${{ secrets.HR_USER_PASSWORD }}
SMART_RUNNER_PATH: ".protractor-smartrunner"
S3_DBP_PATH: ${{ secrets.S3_DBP_PATH }}
S3_BUILD_BUCKET_SHORT_NAME: ${{ secrets.S3_BUILD_BUCKET_SHORT_NAME }}
NODE_OPTIONS: "--max-old-space-size=5120"
DOCKER_REPOSITORY_DOMAIN: ${{ secrets.DOCKER_REPOSITORY_DOMAIN }}
DOCKER_REPOSITORY_USER: ${{ secrets.DOCKER_REPOSITORY_USER }}
DOCKER_REPOSITORY_PASSWORD: ${{ secrets.DOCKER_REPOSITORY_PASSWORD }}
DOCKER_REPOSITORY_STORYBOOK: "${{ secrets.DOCKER_REPOSITORY_DOMAIN }}/alfresco/storybook"
DOCKER_REPOSITORY: "${{ secrets.DOCKER_REPOSITORY_DOMAIN }}/alfresco/demo-shell"
REPO_OWNER: "Alfresco"
REPO_NAME: "alfresco-ng2-components"
DEMO_SHELL_DIR: "./dist/demo-shell"
STORYBOOK_DIR: "./dist/storybook/stories"
BUILT_LIBS_DIR: "./dist/libs"
NODE_MODULES_DIR: "./node_modules"
SMART_RUNNER_DIRECTORY: ".protractor-smartrunner"
SAVE_SCREENSHOT: true
REDIRECT_URI: /
BROWSER_RUN: false
MAXINSTANCES: 2
PLAYWRIGHT_WORKERS: 2
PLAYWRIGHT_STORYBOOK_E2E_HOST: http://localhost
PLAYWRIGHT_STORYBOOK_E2E_PORT: 4400
PROXY_HOST_ECM: ${{ secrets.E2E_HOST }}
jobs:
pre-checks:
@ -273,198 +236,9 @@ jobs:
- uses: ./.github/actions/setup
- uses: ./.github/actions/download-node-modules-and-artifacts
- run: npx nx affected:build $NX_CALCULATION_FLAGS --prod
- run: npx nx build demoshell --configuration production
- run: npx nx affected --target=build-storybook $NX_CALCULATION_FLAGS --configuration=ci --parallel=1
- uses: ./.github/actions/upload-node-modules-and-artifacts
e2e-storybook:
timeout-minutes: 20
name: "e2e: storybook"
needs: [build-libs, lint, unit-tests]
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
with:
fetch-depth: 0 # Fetch all history for all
- uses: ./.github/actions/setup
- uses: ./.github/actions/download-node-modules-and-artifacts
- name: Process Cloud Storybook Playwright
run: |
npx playwright install chromium
sudo sysctl -w fs.inotify.max_user_watches=524288
npx nx affected --target=e2e-playwright $NX_CALCULATION_FLAGS || exit 1
- uses: ./.github/actions/upload-node-modules-and-artifacts
e2e:
timeout-minutes: 90
name: "e2e: ${{ matrix.e2e-test.description }}"
needs: [build-libs, lint, unit-tests]
runs-on: ubuntu-latest
strategy:
fail-fast: false
# max-parallel: 4
matrix:
e2e-test:
- description: "Core"
test-id: "core"
artifact-id: "core"
folder: "core"
provider: "ALL"
auth: "OAUTH"
check-cs-env: "true"
check-ps-env: "true"
deps: "testing"
- description: "Content: Components"
test-id: "content-services"
artifact-id: "content-services-components"
folder: "content-services/components"
provider: "ECM"
auth: "BASIC"
check-cs-env: "true"
deps: "testing"
- description: "Content: Directives"
test-id: "content-services"
artifact-id: "content-services-directives"
folder: "content-services/directives"
provider: "ECM"
auth: "BASIC"
check-cs-env: "true"
deps: "testing"
- description: "Content: Document List"
test-id: "content-services"
artifact-id: "content-services-document-list"
folder: "content-services/document-list"
provider: "ECM"
auth: "BASIC"
check-cs-env: "true"
deps: "testing"
- description: "Content: Metadata"
test-id: "content-services"
artifact-id: "content-services-metadata"
folder: "content-services/metadata"
provider: "ECM"
auth: "BASIC"
check-cs-env: "true"
deps: "testing"
- description: "Content: Upload and Versioning"
test-id: "content-services"
artifact-id: "content-services-upload"
folder: "content-services/upload"
provider: "ECM"
auth: "BASIC"
check-cs-env: "true"
deps: "testing"
- description: "Search"
test-id: "content-services"
artifact-id: "content-services-search"
folder: "search"
provider: "ECM"
auth: "BASIC"
check-cs-env: "true"
deps: "testing"
- description: "Process: Form"
test-id: "process-services"
artifact-id: "process-services-form"
folder: "process-services/form"
provider: "BPM"
auth: "OAUTH"
check-ps-env: "true"
check-external-cs-env: "true"
deps: "testing"
- description: "Process: Process"
test-id: "process-services"
artifact-id: "process-services-process"
folder: "process-services/process"
provider: "BPM"
auth: "OAUTH"
check-ps-env: "true"
check-external-cs-env: "true"
deps: "testing"
- description: "Process: Tasks"
test-id: "process-services"
artifact-id: "process-services-tasks"
folder: "process-services/tasks"
provider: "BPM"
auth: "OAUTH"
check-ps-env: "true"
check-external-cs-env: "true"
deps: "testing"
- description: "Process: Widget"
test-id: "process-services"
artifact-id: "process-services-widgets"
folder: "process-services/widgets"
provider: "BPM"
auth: "OAUTH"
check-ps-env: "true"
check-external-cs-env: "true"
deps: "testing"
- description: "Process Cloud: Form"
test-id: "process-services-cloud"
artifact-id: "process-services-cloud-form-field"
folder: "process-services-cloud/form-field"
provider: "ALL"
auth: "OAUTH"
apa-proxy: true
check-cs-env: "true"
check-ps-cloud-env: "true"
deps: "testing"
- description: "Process Cloud: Process"
test-id: "process-services-cloud"
artifact-id: "process-services-cloud-process"
folder: "process-services-cloud/process"
provider: "ALL"
auth: "OAUTH"
apa-proxy: true
check-cs-env: "true"
check-ps-cloud-env: "true"
deps: "testing"
- description: "Process Cloud: Start Task"
test-id: "process-services-cloud"
artifact-id: "process-services-cloud-start-task"
folder: "process-services-cloud/start-task"
provider: "ALL"
auth: "OAUTH"
apa-proxy: true
check-cs-env: "true"
check-ps-cloud-env: "true"
deps: "testing"
- description: "Process Cloud: Tasks List"
test-id: "process-services-cloud"
artifact-id: "process-services-cloud-task-list"
folder: "process-services-cloud/task-list"
provider: "ALL"
auth: "OAUTH"
apa-proxy: true
check-cs-env: "true"
check-ps-cloud-env: "true"
deps: "testing"
steps:
- name: Checkout repository
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
with:
fetch-depth: 0 # Fetch all history for all
- uses: ./.github/actions/setup
with:
enable-cache: "true"
enable-node-modules-cache: "true"
- uses: ./.github/actions/download-node-modules-and-artifacts
- name: e2e
uses: ./.github/actions/e2e
with:
e2e-test-id: ${{ matrix.e2e-test.test-id }}
e2e-test-folder: ${{ matrix.e2e-test.folder }}
e2e-artifact-id: ${{matrix.e2e-test.artifact-id}}
e2e-test-provider: ${{ matrix.e2e-test.provider }}
e2e-test-auth: ${{ matrix.e2e-test.auth }}
check-cs-env: ${{ matrix.e2e-test.check-cs-env }}
check-ps-env: ${{ matrix.e2e-test.check-ps-env }}
check-ps-cloud-env: ${{ matrix.e2e-test.check-ps-cloud-env }}
check-external-cs-env: ${{ matrix.e2e-test.check-external-cs-env }}
apa-proxy: ${{ matrix.e2e-test.apa-proxy }}
deps: ${{ matrix.e2e-test.deps }}
PR-forbidden-labels:
if: ${{ inputs.cron-run == '' || inputs.cron-run == 'false' }}
runs-on: ubuntu-latest
@ -496,7 +270,7 @@ jobs:
if: ${{ always() }}
runs-on: ubuntu-latest
name: Final Results
needs: [check-if-pr-is-approved, pre-checks, setup, unit-tests, lint, build-libs, e2e, e2e-storybook]
needs: [check-if-pr-is-approved, pre-checks, setup, unit-tests, lint, build-libs]
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- name: identify-slack-group

View File

@ -22,57 +22,20 @@ env:
GH_RUN_NUMBER: ${{ github.run_attempt }}
GH_BUILD_NUMBER: ${{ github.run_id }}
JOB_ID: ${{ github.run_id }}
PROXY_HOST_BPM: ${{ secrets.E2E_HOST }}
E2E_IDENTITY_HOST_APA: ${{ secrets.E2E_IDENTITY_HOST_APA }}
E2E_HOST_APA: ${{ secrets.E2E_HOST_APA }}
E2E_HOST: ${{ secrets.E2E_HOST }}
E2E_USERNAME: ${{ secrets.E2E_ADMIN_EMAIL_IDENTITY }}
E2E_PASSWORD: ${{ secrets.E2E_PASSWORD }}
E2E_ADMIN_EMAIL_IDENTITY: ${{ secrets.E2E_ADMIN_EMAIL_IDENTITY }}
E2E_ADMIN_PASSWORD_IDENTITY: ${{ secrets.E2E_ADMIN_PASSWORD_IDENTITY }}
#USERNAME_ADF: ${{ secrets.E2E_USERNAME }}
USERNAME_ADF: ${{ secrets.E2E_ADMIN_EMAIL_IDENTITY }}
PASSWORD_ADF: ${{ secrets.E2E_PASSWORD }}
URL_HOST_ADF: "http://localhost:4200"
IDENTITY_ADMIN_EMAIL: ${{ secrets.E2E_ADMIN_EMAIL_IDENTITY }}
IDENTITY_ADMIN_PASSWORD: ${{ secrets.E2E_ADMIN_PASSWORD_IDENTITY }}
AWS_S3_BUCKET_ACTIVITI_LICENSE: ${{ secrets.AWS_S3_BUCKET_ACTIVITI_LICENSE }}
HOST_SSO: ${{ secrets.HOST_SSO }}
LOG_LEVEL: "ERROR"
E2E_LOG_LEVEL: "ERROR"
E2E_MODELER_USERNAME: ${{ secrets.E2E_MODELER_USERNAME }}
E2E_MODELER_PASSWORD: ${{ secrets.E2E_MODELER_PASSWORD }}
EXTERNAL_ACS_HOST: ${{ secrets.EXTERNAL_ACS_HOST }}
E2E_DEVOPS_USERNAME: ${{ secrets.E2E_DEVOPS_USERNAME }}
E2E_DEVOPS_PASSWORD: ${{ secrets.E2E_DEVOPS_PASSWORD }}
USERNAME_SUPER_ADMIN_ADF: ${{ secrets.USERNAME_SUPER_ADMIN_ADF }}
PASSWORD_SUPER_ADMIN_ADF: ${{ secrets.PASSWORD_SUPER_ADMIN_ADF }}
HR_USER: ${{ secrets.HR_USER }}
HR_USER_PASSWORD: ${{ secrets.HR_USER_PASSWORD }}
SMART_RUNNER_PATH: ".protractor-smartrunner"
S3_DBP_PATH: ${{ secrets.S3_DBP_PATH }}
S3_BUILD_BUCKET_SHORT_NAME: ${{ secrets.S3_BUILD_BUCKET_SHORT_NAME }}
NODE_OPTIONS: "--max-old-space-size=5120"
DOCKER_REPOSITORY_DOMAIN: ${{ secrets.DOCKER_REPOSITORY_DOMAIN }}
DOCKER_REPOSITORY_USER: ${{ secrets.DOCKER_REPOSITORY_USER }}
DOCKER_REPOSITORY_PASSWORD: ${{ secrets.DOCKER_REPOSITORY_PASSWORD }}
DOCKER_REPOSITORY_STORYBOOK: "${{ secrets.DOCKER_REPOSITORY_DOMAIN }}/alfresco/storybook"
DOCKER_REPOSITORY: "${{ secrets.DOCKER_REPOSITORY_DOMAIN }}/alfresco/demo-shell"
GITHUB_TOKEN: ${{ secrets.BOT_GITHUB_TOKEN }}
REPO_OWNER: "Alfresco"
REPO_NAME: "alfresco-ng2-components"
DEMO_SHELL_DIR: "./dist/demo-shell"
STORYBOOK_DIR: "./dist/storybook/stories"
BUILT_LIBS_DIR: "./dist/libs"
NODE_MODULES_DIR: "./node_modules"
SMART_RUNNER_DIRECTORY: ".protractor-smartrunner"
SAVE_SCREENSHOT: true
REDIRECT_URI: /
BROWSER_RUN: false
MAXINSTANCES: 2
PLAYWRIGHT_WORKERS: 2
PLAYWRIGHT_STORYBOOK_E2E_HOST: http://localhost
PLAYWRIGHT_STORYBOOK_E2E_PORT: 4400
jobs:
setup:
@ -135,7 +98,7 @@ jobs:
setMigrations();
- name: Build libraries
run: |
npx nx affected:build $NX_CALCULATION_FLAGS --prod --exclude="demoshell" --skip-nx-cache
npx nx affected:build $NX_CALCULATION_FLAGS --prod --skip-nx-cache
npx nx affected $NX_CALCULATION_FLAGS --target=pretheme
npx nx affected $NX_CALCULATION_FLAGS --target=build-schematics
- uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2

View File

@ -32,57 +32,22 @@ env:
GH_RUN_NUMBER: ${{ github.run_attempt }}
GH_BUILD_NUMBER: ${{ github.run_id }}
JOB_ID: ${{ github.run_id }}
PROXY_HOST_BPM: ${{ secrets.E2E_HOST }}
E2E_IDENTITY_HOST_APA: ${{ secrets.E2E_IDENTITY_HOST_APA }}
E2E_HOST_APA: ${{ secrets.E2E_HOST_APA }}
E2E_HOST: ${{ secrets.E2E_HOST }}
E2E_USERNAME: ${{ secrets.E2E_ADMIN_EMAIL_IDENTITY }}
E2E_PASSWORD: ${{ secrets.E2E_PASSWORD }}
E2E_ADMIN_EMAIL_IDENTITY: ${{ secrets.E2E_ADMIN_EMAIL_IDENTITY }}
E2E_ADMIN_PASSWORD_IDENTITY: ${{ secrets.E2E_ADMIN_PASSWORD_IDENTITY }}
#USERNAME_ADF: ${{ secrets.E2E_USERNAME }}
USERNAME_ADF: ${{ secrets.E2E_ADMIN_EMAIL_IDENTITY }}
PASSWORD_ADF: ${{ secrets.E2E_PASSWORD }}
URL_HOST_ADF: "http://localhost:4200"
IDENTITY_ADMIN_EMAIL: ${{ secrets.E2E_ADMIN_EMAIL_IDENTITY }}
IDENTITY_ADMIN_PASSWORD: ${{ secrets.E2E_ADMIN_PASSWORD_IDENTITY }}
AWS_S3_BUCKET_ACTIVITI_LICENSE: ${{ secrets.AWS_S3_BUCKET_ACTIVITI_LICENSE }}
HOST_SSO: ${{ secrets.HOST_SSO }}
LOG_LEVEL: "ERROR"
E2E_LOG_LEVEL: "ERROR"
E2E_MODELER_USERNAME: ${{ secrets.E2E_MODELER_USERNAME }}
E2E_MODELER_PASSWORD: ${{ secrets.E2E_MODELER_PASSWORD }}
EXTERNAL_ACS_HOST: ${{ secrets.EXTERNAL_ACS_HOST }}
E2E_DEVOPS_USERNAME: ${{ secrets.E2E_DEVOPS_USERNAME }}
E2E_DEVOPS_PASSWORD: ${{ secrets.E2E_DEVOPS_PASSWORD }}
USERNAME_SUPER_ADMIN_ADF: ${{ secrets.USERNAME_SUPER_ADMIN_ADF }}
PASSWORD_SUPER_ADMIN_ADF: ${{ secrets.PASSWORD_SUPER_ADMIN_ADF }}
HR_USER: ${{ secrets.HR_USER }}
HR_USER_PASSWORD: ${{ secrets.HR_USER_PASSWORD }}
SMART_RUNNER_PATH: ".protractor-smartrunner"
S3_DBP_PATH: ${{ secrets.S3_DBP_PATH }}
S3_BUILD_BUCKET_SHORT_NAME: ${{ secrets.S3_BUILD_BUCKET_SHORT_NAME }}
NODE_OPTIONS: "--max-old-space-size=5120"
DOCKER_REPOSITORY_DOMAIN: ${{ secrets.DOCKER_REPOSITORY_DOMAIN }}
DOCKER_REPOSITORY_USER: ${{ secrets.DOCKER_REPOSITORY_USER }}
DOCKER_REPOSITORY_PASSWORD: ${{ secrets.DOCKER_REPOSITORY_PASSWORD }}
DOCKER_REPOSITORY_STORYBOOK: "${{ secrets.DOCKER_REPOSITORY_DOMAIN }}/alfresco/storybook"
DOCKER_REPOSITORY: "${{ secrets.DOCKER_REPOSITORY_DOMAIN }}/alfresco/demo-shell"
GITHUB_TOKEN: ${{ secrets.BOT_GITHUB_TOKEN }}
REPO_OWNER: "Alfresco"
REPO_NAME: "alfresco-ng2-components"
DEMO_SHELL_DIR: "./dist/demo-shell"
STORYBOOK_DIR: "./dist/storybook/stories"
BUILT_LIBS_DIR: "./dist/libs"
NODE_MODULES_DIR: "./node_modules"
SMART_RUNNER_DIRECTORY: ".protractor-smartrunner"
SAVE_SCREENSHOT: true
REDIRECT_URI: /
BROWSER_RUN: false
MAXINSTANCES: 2
PLAYWRIGHT_WORKERS: 2
PLAYWRIGHT_STORYBOOK_E2E_HOST: http://localhost
PLAYWRIGHT_STORYBOOK_E2E_PORT: 4400
jobs:
setup:
@ -107,32 +72,6 @@ jobs:
npx nx run testing:bundle
- uses: ./.github/actions/upload-node-modules-and-artifacts
release-demoshell:
needs: [setup]
timeout-minutes: 15
if: github.event.pull_request.merged == true || github.ref_name == 'master' || github.ref_name == 'master-patch-*'
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
with:
fetch-depth: 1
- run: git fetch --all
- id: set-dryrun
uses: ./.github/actions/enable-dryrun
with:
dry-run-flag: ${{ inputs.dry-run-flag }}
- uses: ./.github/actions/setup
with:
enable-cache: false
enable-node-modules-cache: false
- uses: ./.github/actions/download-node-modules-and-artifacts
- name: release Demoshell docker
run: |
npx nx build demoshell --configuration production
. ./scripts/github/release/docker-tag.sh
./scripts/github/release/release-demoshell-docker.sh ${{ steps.set-dryrun.outputs.dryrun }}
release-storybook:
needs: [setup]
timeout-minutes: 15
@ -194,7 +133,7 @@ jobs:
setMigrations();
- name: build libraries
run: |
npx nx affected:build $NX_CALCULATION_FLAGS --prod --exclude="demoshell" --skip-nx-cache
npx nx affected:build $NX_CALCULATION_FLAGS --prod --skip-nx-cache
npx nx affected $NX_CALCULATION_FLAGS --target=pretheme
npx nx affected $NX_CALCULATION_FLAGS --target=build-schematics
- uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
@ -250,7 +189,7 @@ jobs:
if: always()
runs-on: ubuntu-latest
name: Final Results
needs: [release-demoshell, release-storybook, release-npm, npm-check-bundle]
needs: [release-storybook, release-npm, npm-check-bundle]
steps:
- uses: slackapi/slack-github-action@37ebaef184d7626c5f204ab8d3baff4262dd30f0 # v1.27.0

5
.gitignore vendored
View File

@ -13,22 +13,17 @@ e2e/.env.cloud
tmp
temp
/nxcache
e2e-output*/
/e2e/downloads/
*.npmrc
.history
/ng2-components/ng2-alfresco-core/prebuilt-themes/
.ng_pkg_build/
/demo-shell/dist-dev-temp/
/lib/export-new.json
/lib/config/exportCheck.js
/docs/sourceinfo
/docs/docs.json
/protractorFailuresReport
coverage/
/desktop.ini
out-tsc
!/.protractor-smartrunner/
/reports/
e2e-result-*
licenses.txt

View File

@ -3,7 +3,7 @@ module.exports = {
name: '@storybook/angular',
options: {}
},
staticDirs: [{ from: '../../../demo-shell/src/app.config.json', to: 'app.config.json' }],
staticDirs: [],
docs: {},
stories: [],
addons: []

View File

@ -15,7 +15,6 @@ USER 101
ARG PROJECT_NAME
COPY docker/default.conf.template /etc/nginx/templates/
COPY docker/docker-entrypoint.d/* /docker-entrypoint.d/
COPY dist/$PROJECT_NAME /usr/share/nginx/html/
COPY dist/$PROJECT_NAME/app.config.json /etc/nginx/templates/app.config.json.template

View File

@ -28,6 +28,7 @@ You can find the sources for all ADF components in the [`lib`](/lib) folder.
## Libraries
ADF Libraries list:
- [Content services](https://github.com/Alfresco/alfresco-ng2-components/tree/develop/lib/content-services)
- [Core](https://github.com/Alfresco/alfresco-ng2-components/tree/develop/lib/core)
- [Extensions](https://github.com/Alfresco/alfresco-ng2-components/tree/develop/lib/extensions)
@ -36,24 +37,13 @@ ADF Libraries list:
- [Process service](https://github.com/Alfresco/alfresco-ng2-components/tree/develop/lib/process-services)
- [Stories](https://github.com/Alfresco/alfresco-ng2-components/tree/develop/lib/stories)
## Demo Application
A separate application showcasing integration of components can be found
[here](https://github.com/Alfresco/alfresco-ng2-components/tree/master/demo-shell).
The app has examples of basic interaction for both APS and ACS components.
## Application generator for Yeoman
To speed up the development, you can use the
[Generator for Yeoman](https://github.com/Alfresco/generator-ng2-alfresco-app).
## Browser Support
All components are supported in the following browsers:
|**Browser** |**Version** |
|--- |--- |
| Chrome | Latest |
| Safari | Latest |
| Firefox | Latest |
| Edge | Latest |
| **Browser** | **Version** |
|-------------|-------------|
| Chrome | Latest |
| Safari | Latest |
| Firefox | Latest |
| Edge | Latest |

View File

@ -1,95 +0,0 @@
/**
* This file decorates the Angular CLI with the Nx CLI to enable features such as computation caching
* and faster execution of tasks.
*
* It does this by:
*
* - Patching the Angular CLI to warn you in case you accidentally use the undecorated ng command.
* - Symlinking the ng to nx command, so all commands run through the Nx CLI
* - Updating the package.json postinstall script to give you control over this script
*
* The Nx CLI decorates the Angular CLI, so the Nx CLI is fully compatible with it.
* Every command you run should work the same when using the Nx CLI, except faster.
*
* Because of symlinking you can still type `ng build/test/lint` in the terminal. The ng command, in this case,
* will point to nx, which will perform optimizations before invoking ng. So the Angular CLI is always invoked.
* The Nx CLI simply does some optimizations before invoking the Angular CLI.
*
* To opt out of this patch:
* - Replace occurrences of nx with ng in your package.json
* - Remove the script from your postinstall script in your package.json
* - Delete and reinstall your node_modules
*/
const fs = require("fs");
const os = require("os");
const cp = require("child_process");
const isWindows = os.platform() === "win32";
const output = require('nx/src/utils/output').output;
/**
* Paths to files being patched
*/
const angularCLIInitPath = "node_modules/@angular/cli/lib/cli/index.js";
/**
* Patch index.js to warn you if you invoke the undecorated Angular CLI.
*/
function patchAngularCLI(initPath) {
const angularCLIInit = fs.readFileSync(initPath, "utf-8").toString();
if (!angularCLIInit.includes("NX_CLI_SET")) {
fs.writeFileSync(
initPath,
`
if (!process.env['NX_CLI_SET']) {
const { output } = require('@nrwl/workspace');
output.warn({ title: 'The Angular CLI was invoked instead of the Nx CLI. Use "npx ng [command]" or "nx [command]" instead.' });
}
${angularCLIInit}
`
);
}
}
/**
* Symlink of ng to nx, so you can keep using `ng build/test/lint` and still
* invoke the Nx CLI and get the benefits of computation caching.
*/
function symlinkNgCLItoNxCLI() {
try {
const ngPath = "./node_modules/.bin/ng";
const nxPath = "./node_modules/.bin/nx";
if (isWindows) {
/**
* This is the most reliable way to create symlink-like behavior on Windows.
* Such that it works in all shells and works with npx.
*/
["", ".cmd", ".ps1"].forEach(ext => {
fs.writeFileSync(ngPath + ext, fs.readFileSync(nxPath + ext));
});
} else {
// If unix-based, symlink
cp.execSync(`ln -sf ./nx ${ngPath}`);
}
} catch (e) {
output.error({
title:
"Unable to create a symlink from the Angular CLI to the Nx CLI:" +
e.message
});
throw e;
}
}
try {
symlinkNgCLItoNxCLI();
patchAngularCLI(angularCLIInitPath);
output.log({
title: "Angular CLI has been decorated to enable computation caching."
});
} catch (e) {
output.error({
title: "Decoration of the Angular CLI did not complete successfully"
});
}

View File

@ -1,73 +0,0 @@
path = require('path');
module.exports = {
extends: '../.eslintrc.js',
ignorePatterns: ['!**/*'],
overrides: [
{
files: ['*.ts'],
parserOptions: {
project: [
path.join(__dirname, 'tsconfig.app.json'),
path.join(__dirname, 'src/tsconfig.spec.json'),
path.join(__dirname, 'e2e/tsconfig.e2e.json')
],
createDefaultProgram: true
},
plugins: ['eslint-plugin-unicorn', 'eslint-plugin-rxjs'],
rules: {
'@angular-eslint/component-selector': [
'error',
{
type: 'element',
prefix: ['adf', 'app'],
style: 'kebab-case'
}
],
'@angular-eslint/directive-selector': [
'error',
{
type: ['element', 'attribute'],
prefix: ['adf', 'app'],
style: 'kebab-case'
}
],
'@angular-eslint/no-host-metadata-property': 'off',
'@angular-eslint/no-input-prefix': 'error',
'@typescript-eslint/consistent-type-definitions': 'error',
'@typescript-eslint/dot-notation': 'off',
'@typescript-eslint/explicit-member-accessibility': [
'off',
{
accessibility: 'explicit'
}
],
'@typescript-eslint/no-inferrable-types': 'off',
'@typescript-eslint/no-require-imports': 'off',
'@typescript-eslint/no-var-requires': 'error',
'comma-dangle': 'error',
'default-case': 'error',
'import/order': 'off',
'max-len': [
'error',
{
code: 240
}
],
'no-bitwise': 'off',
'no-duplicate-imports': 'error',
'no-multiple-empty-lines': 'error',
'no-redeclare': 'error',
'no-return-await': 'error',
'rxjs/no-create': 'error',
'rxjs/no-subject-unsubscribe': 'error',
'rxjs/no-subject-value': 'error',
'rxjs/no-unsafe-takeuntil': 'error',
'unicorn/filename-case': 'error'
}
},
{
files: ['*.html'],
rules: {}
}
]
};

59
demo-shell/.gitignore vendored
View File

@ -1,59 +0,0 @@
# See http://help.github.com/ignore-files/ for more about ignoring files.
# compiled output
/dist
/tmp
/out-tsc
# dependencies
/node_modules
# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
# misc
/.sass-cache
/connect.lock
/coverage
/libpeerconnection.log
npm-debug.log
testem.log
/typings
# e2e
/e2e/*.js
/e2e/*.map
# System Files
.DS_Store
Thumbs.db
typings/
node_modules/
bower_components/
lib/
app/**/*.js
app/**/*.js.map
app/**/*.d.ts
!app/js/Polyline.js
.idea
dist/
coverage/
!/e2e/protractor.conf.js

View File

@ -1,75 +0,0 @@
# ADF Demo Application
Please note that this application is not an official product, but a testing and demo application to showcase complex interactions of ADF components.
## Installing
To correctly use this demo check that on your machine you have [Node](https://nodejs.org/en/) version 5.x.x or higher.
```sh
git clone https://github.com/Alfresco/alfresco-ng2-components.git
cd alfresco-ng2-components
npm install
npm start
```
## Proxy settings and CORS
To simplify development and reduce the time to get the application started, we have the following Proxy settings:
- **http://localhost:3000/ecm** is mapped to **http://localhost:8080**
- **http://localhost:3000/bpm** is mapped to **http://localhost:9999**
The settings above address most common scenarios for running ACS on port 8080 and APS on port 9999 and allow you to skip the CORS configuration.
If you would like to change default proxy settings, please edit the `proxy.conf.js` file.
## Application settings (server-side)
All server-side application settings are stored in the [src/app.config.json](src/app.config.json).
By default the configuration files have the content similar to the following one:
```json
{
"$schema": "../../lib/core/app-config/schema.json",
"ecmHost": "http://{hostname}:{port}",
"bpmHost": "http://{hostname}:{port}",
"application": {
"name": "Alfresco ADF Application"
}
}
```
## Development build
```sh
npm start
```
This command compiles and starts the project in watch mode.
Browser will automatically reload upon changes.
Upon start, you can navigate to `http://localhost:3000` with your preferred browser.
### Important notes
This script is recommended for development environment and not suited for headless servers and network access.
## Production build
```sh
npm run build
npm run start:prod
```
This command builds project in `production` mode.
All output is placed to `dist` folder and can be served to your preferred web server.
You should need no additional files outside the `dist` folder.
## Development branch build
If you want to run the demo shell with the latest changes from the development branch, use the following command :
```sh
npm run start
```

View File

@ -1,56 +0,0 @@
// Karma configuration file, see link for more information
// https://karma-runner.github.io/0.13/config/configuration-file.html
process.env.CHROME_BIN = require('puppeteer').executablePath();
module.exports = function (config) {
config.set({
basePath: './',
frameworks: ['jasmine', '@angular-devkit/build-angular'],
plugins: [
require('karma-jasmine'),
require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'),
require('karma-coverage-istanbul-reporter'),
require('@angular-devkit/build-angular/plugins/karma'),
require('karma-mocha-reporter')
],
client: {
clearContext: false // leave Jasmine Spec Runner output visible in browser
},
files: [],
preprocessors: {},
mime: {
'text/x-typescript': ['ts', 'tsx']
},
coverageIstanbulReporter: {
dir: require('path').join(__dirname, 'coverage'), reports: ['html', 'lcovonly'],
fixWebpackSourcePaths: true
},
customLaunchers: {
ChromeHeadless: {
base: 'Chrome',
flags: [
'--no-sandbox',
'--headless',
'--disable-gpu',
'--remote-debugging-port=9222'
]
}
},
captureTimeout: 180000,
browserDisconnectTimeout: 180000,
browserDisconnectTolerance: 3,
browserNoActivityTimeout: 300000,
reporters: ['mocha', 'kjhtml'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['ChromeHeadless'],
singleRun: false
});
};

View File

@ -1,21 +0,0 @@
{
"name": "Alfresco-ADF-Angular-Demo",
"description": "Demo shell for Alfresco Angular components",
"version": "7.0.0-alpha.2",
"author": "Hyland Software, Inc. and its affiliates",
"repository": {
"type": "git",
"url": "https://github.com/Alfresco/alfresco-ng2-components.git"
},
"bugs": {
"url": "https://github.com/Alfresco/alfresco-ng2-components/issues"
},
"license": "Apache-2.0",
"keywords": [
"ng2",
"angular",
"angular2",
"alfresco"
],
"private": true
}

View File

@ -1,201 +0,0 @@
{
"name": "demoshell",
"$schema": "../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "demo-shell/src",
"projectType": "application",
"targets": {
"build": {
"executor": "@angular-devkit/build-angular:browser",
"options": {
"allowedCommonJsDependencies": [
"minimatch",
"minimatch-browser",
"superagent",
"event-emitter",
"brace-expansion",
"zen-observable",
"subscriptions-transport-ws",
"d",
"chart.js",
"cropperjs"
],
"outputPath": "dist/demo-shell",
"index": "demo-shell/src/index.html",
"main": "demo-shell/src/main.ts",
"tsConfig": "tsconfig.dev.json",
"polyfills": "demo-shell/src/polyfills.ts",
"stylePreprocessorOptions": {
"includePaths": ["lib", "lib/core/src/lib"]
},
"assets": [
"demo-shell/src/assets",
"demo-shell/src/favicon-96x96.png",
"demo-shell/src/app.config.json",
{
"glob": "**/*",
"input": "demo-shell/src/assets",
"output": "/assets"
},
{
"glob": "app.config.json",
"input": "demo-shell/src",
"output": "/"
},
{
"glob": "**/*",
"input": "demo-shell/resources",
"output": "/resources"
},
{
"glob": "**/*",
"input": "lib/core/src/lib/assets",
"output": "/assets"
},
{
"glob": "**/*",
"input": "lib/process-services/src/lib/assets",
"output": "/assets"
},
{
"glob": "**/*",
"input": "lib/process-services-cloud/src/lib/assets",
"output": "/assets"
},
{
"glob": "**/*",
"input": "lib/content-services/src/lib/assets",
"output": "/assets"
},
{
"glob": "**/*",
"input": "lib/core/src/lib/i18n",
"output": "/assets/adf-core/i18n"
},
{
"glob": "**/*",
"input": "lib/content-services/src/lib/i18n",
"output": "/assets/adf-content-services/i18n"
},
{
"glob": "**/*",
"input": "lib/process-services/src/lib/i18n",
"output": "/assets/adf-process-services/i18n"
},
{
"glob": "**/*",
"input": "lib/process-services-cloud/src/lib/i18n",
"output": "/assets/adf-process-services-cloud/i18n"
},
{
"glob": "**/*",
"input": "lib/insights/src/lib/i18n",
"output": "/assets/adf-insights/i18n"
},
{
"glob": "pdf.worker.min.js",
"input": "node_modules/pdfjs-dist/build",
"output": "/"
},
{
"glob": "**/*",
"input": "node_modules/monaco-editor",
"output": "/assets/monaco/"
}
],
"styles": [
"demo-shell/src/styles.scss",
"demo-shell/src/custom-style-dev.scss",
"node_modules/cropperjs/dist/cropper.min.css",
"node_modules/pdfjs-dist/web/pdf_viewer.css"
],
"scripts": ["node_modules/pdfjs-dist/build/pdf.js", "node_modules/pdfjs-dist/web/pdf_viewer.js", "node_modules/raphael/raphael.min.js"],
"vendorChunk": true,
"extractLicenses": false,
"buildOptimizer": false,
"sourceMap": true,
"optimization": false,
"namedChunks": true
},
"configurations": {
"production": {
"budgets": [
{
"type": "anyComponentStyle",
"maximumWarning": "12kb"
}
],
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"namedChunks": false,
"extractLicenses": true,
"buildOptimizer": true,
"verbose": false,
"fileReplacements": [
{
"replace": "demo-shell/src/environments/environment.ts",
"with": "demo-shell/src/environments/environment.prod.ts"
}
]
},
"canary": {
"fileReplacements": [
{
"replace": "demo-shell/src/environments/environment.ts",
"with": "demo-shell/src/environments/environment.canary.ts"
}
]
},
"e2e": {
"budgets": [
{
"type": "anyComponentStyle",
"maximumWarning": "6kb"
}
],
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"namedChunks": false,
"extractLicenses": true,
"buildOptimizer": true,
"verbose": false,
"fileReplacements": [
{
"replace": "demo-shell/src/environments/environment.ts",
"with": "demo-shell/src/environments/environment.e2e.ts"
}
]
}
},
"defaultConfiguration": ""
},
"serve": {
"executor": "@angular-devkit/build-angular:dev-server",
"options": {
"browserTarget": "demoshell:build",
"host": "0.0.0.0",
"port": 3000,
"proxyConfig": "demo-shell/proxy.conf.js",
"disableHostCheck": true
},
"configurations": {
"production": {
"browserTarget": "demoshell:build:production"
},
"canary": {
"browserTarget": "demoshell:build:canary"
},
"e2e": {
"browserTarget": "demoshell:build:e2e"
}
}
},
"lint": {
"executor": "@nrwl/linter:eslint",
"options": {
"lintFilePatterns": ["demo-shell/**/*.ts", "demo-shell/**/*.html"]
}
}
}
}

View File

@ -1,76 +0,0 @@
module.exports = {
getDeployedAppsProxy: function(processHost, deployedApps) {
let deployedAppProxy = {};
if (deployedApps) {
try {
const deployedAppsArray = JSON.parse(deployedApps);
for (const app of deployedAppsArray) {
const appName = app.name;
const appPath = `/${appName}`;
const appPathRewrite = `^/${appName}`;
deployedAppProxy = {
...deployedAppProxy,
[appPath]: {
target: `${processHost}`,
secure: false,
pathRewrite: {
[appPathRewrite]: appName,
},
changeOrigin: true,
},
};
}
} catch (e) {
console.log(e);
}
}
return deployedAppProxy;
},
getShareProxy: function(host) {
console.log('Target for /alfresco', host);
return {
'/alfresco': {
target: host,
secure: false,
logLevel: 'debug',
changeOrigin: true,
onProxyReq: function(request) {
if(request["method"] !== "GET")
request.setHeader("origin", host);
},
// workaround for REPO-2260
onProxyRes: function (proxyRes, req, res) {
const header = proxyRes.headers['www-authenticate'];
if (header && header.startsWith('Basic')) {
proxyRes.headers['www-authenticate'] = 'x' + header;
}
},
},
}
},
getApsProxy: function(host) {
console.log('Target for /activiti-app', host);
return {
'/activiti-app': {
target: host,
secure: false,
logLevel: 'debug',
changeOrigin: true,
},
}
},
getIdentityAdapterServiceProxy: function(host) {
console.log('Target for /identity-adapter-service', host);
return {
'/identity-adapter-service': {
target: host,
secure: false,
logLevel: 'debug',
changeOrigin: true,
},
}
}
};

View File

@ -1,15 +0,0 @@
require('dotenv').config();
const { getDeployedAppsProxy, getShareProxy, getApsProxy, getIdentityAdapterServiceProxy } = require('./proxy-helpers');
const legacyHost = process.env.PROXY_HOST_ADF;
const cloudHost = process.env.CLOUD_PROXY_HOST_ADF || process.env.PROXY_HOST_ADF;
const cloudApps = process.env.APP_CONFIG_APPS_DEPLOYED;
const apsHost = process.env.PROXY_HOST_ADF;
module.exports = {
...getShareProxy(legacyHost),
...getApsProxy(apsHost),
...getDeployedAppsProxy(cloudHost, cloudApps),
...getIdentityAdapterServiceProxy(cloudHost)
};

View File

@ -1,39 +0,0 @@
{
"title": "Welcome",
"NOTIFICATIONS": {
"TASK_ASSIGNED": "{{taskName}} task has been assigned to {{assignee}}",
"PROCESS_STARTED": "{{processName}} process has been started",
"TASK_UPDATED": "{{taskName}} task details have been updated",
"TASK_CREATED": "{{taskName}} task was created"
},
"SEARCH": {
"RESULTS": "Search results",
"NO_RESULT": "No results found",
"FACET_FIELDS": {
"TYPE": "1:Type",
"SIZE": "2:Size",
"CREATOR": "3:Creator",
"MODIFIER": "4:Modifier",
"CREATED": "5:Created"
},
"FACET_QUERIES": {
"MY_FACET_QUERIES": "My facet queries",
"CREATED_THIS_YEAR": "1.Created This Year",
"MIMETYPE": "2.Type: HTML",
"XTRASMALL": "3.Size: xtra small",
"SMALL": "4.Size: small",
"MEDIUM": "5.Size: medium",
"LARGE": "6.Size: large",
"XTRALARGE": "7.Size: xtra large",
"XXTRALARGE": "8.Size: XX large"
}
},
"GROUP-TITLE1-TRANSLATION-KEY": "CUSTOM TITLE TRANSLATION ONE",
"GROUP-TITLE2-TRANSLATION-KEY": "CUSTOM TITLE TRANSLATION TWO",
"ERROR_CONTENT": {
"507": {
"TITLE": "ACS Disk full",
"DESCRIPTION": "Content exceeds overall storage quota limit configured for the network or system"
}
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +0,0 @@
<div class="app-demo-app-container">
<router-outlet></router-outlet>
<router-outlet name="overlay"></router-outlet>
</div>

View File

@ -1,13 +0,0 @@
router-outlet[name='overlay'] + * {
width: 100%;
height: 100%;
z-index: 999;
position: absolute;
top: 0;
right: 0;
}
.app-demo-app-container {
height: 100%;
width: 100%;
}

View File

@ -1,62 +0,0 @@
/*!
* @license
* Copyright © 2005-2024 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Component, ViewEncapsulation, OnInit } from '@angular/core';
import {
AuthenticationService,
PageTitleService
} from '@alfresco/adf-core';
import { Router } from '@angular/router';
import { MatDialog } from '@angular/material/dialog';
import { AdfHttpClient } from '@alfresco/adf-core/api';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss'],
encapsulation: ViewEncapsulation.None
})
export class AppComponent implements OnInit {
constructor(private pageTitleService: PageTitleService,
private adfHttpClient: AdfHttpClient,
private authenticationService: AuthenticationService,
private router: Router,
private dialogRef: MatDialog) {
}
ngOnInit() {
this.pageTitleService.setTitle('title');
this.adfHttpClient.on('error', (error) => {
if (error.status === 401) {
if (!this.authenticationService.isLoggedIn()) {
this.dialogRef.closeAll();
this.router.navigate(['/login']);
}
}
if (error.status === 507) {
if (!this.authenticationService.isLoggedIn()) {
this.dialogRef.closeAll();
this.router.navigate(['error/507']);
}
}
});
}
}

View File

@ -1,64 +0,0 @@
/*!
* @license
* Copyright © 2005-2024 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { NgChartsModule } from 'ng2-charts';
import { HttpClientModule } from '@angular/common/http';
import { BrowserAnimationsModule, NoopAnimationsModule } from '@angular/platform-browser/animations';
import { TranslateModule } from '@ngx-translate/core';
import { AppConfigService, DebugAppConfigService, CoreModule, AuthModule, provideTranslations } from '@alfresco/adf-core';
import { ExtensionsModule } from '@alfresco/adf-extensions';
import { AppComponent } from './app.component';
import { appRoutes } from './app.routes';
import { MonacoEditorModule } from 'ngx-monaco-editor-v2';
import { ContentModule } from '@alfresco/adf-content-services';
import { ProcessModule } from '@alfresco/adf-process-services';
import { environment } from '../environments/environment';
import { ProcessServicesCloudModule } from '@alfresco/adf-process-services-cloud';
import { RouterModule } from '@angular/router';
import { CoreAutomationService } from '../testing/automation.service';
@NgModule({
imports: [
BrowserModule,
environment.e2e ? NoopAnimationsModule : BrowserAnimationsModule,
RouterModule.forRoot(appRoutes, { useHash: true }),
AuthModule.forRoot({ useHash: true }),
HttpClientModule,
TranslateModule.forRoot(),
CoreModule.forRoot(),
ContentModule.forRoot(),
ProcessModule.forRoot(),
ProcessServicesCloudModule.forRoot(),
ExtensionsModule.forRoot(),
NgChartsModule,
MonacoEditorModule.forRoot()
],
declarations: [AppComponent],
providers: [
{ provide: AppConfigService, useClass: DebugAppConfigService }, // not use this service in production
provideTranslations('app', 'resources'),
provideTranslations('adf-insights', 'assets/adf-insights')
],
bootstrap: [AppComponent]
})
export class AppModule {
constructor(automationService: CoreAutomationService) {
automationService.setup();
}
}

View File

@ -1,327 +0,0 @@
/*!
* @license
* Copyright © 2005-2024 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Routes } from '@angular/router';
import { AuthGuard, AuthGuardEcm, ErrorContentComponent, AuthGuardBpm, AuthGuardSsoRoleService } from '@alfresco/adf-core';
import { AppLayoutComponent } from './components/app-layout/app-layout.component';
import { HomeComponent } from './components/home/home.component';
import { LogoutComponent } from './components/logout/logout.component';
import { ProcessServiceComponent } from './components/process-service/process-service.component';
import { ShowDiagramComponent } from './components/process-service/show-diagram.component';
import { FormViewerComponent } from './components/process-service/form-viewer.component';
import { FormNodeViewerComponent } from './components/process-service/form-node-viewer.component';
import { AppsViewComponent } from './components/process-service/apps-view.component';
import { SearchResultComponent } from './components/search/search-result.component';
import { FilesComponent } from './components/files/files.component';
import { AppFormComponent } from './components/form/app-form.component';
import { DemoPermissionComponent } from './components/permissions/demo-permissions.component';
import { AppComponent } from './app.component';
import { AppsCloudDemoComponent } from './components/cloud/apps-cloud-demo.component';
import { CloudLayoutComponent } from './components/cloud/cloud-layout.component';
import { TasksCloudDemoComponent } from './components/cloud/tasks-cloud-demo.component';
import { ProcessesCloudDemoComponent } from './components/cloud/processes-cloud-demo.component';
import { StartTaskCloudDemoComponent } from './components/cloud/start-task-cloud-demo.component';
import { StartProcessCloudDemoComponent } from './components/cloud/start-process-cloud-demo.component';
import { TaskDetailsCloudDemoComponent } from './components/cloud/task-details-cloud-demo.component';
import { CloudViewerComponent } from './components/cloud/cloud-viewer.component';
import { ProcessDetailsCloudDemoComponent } from './components/cloud/process-details-cloud-demo.component';
import { FormCloudDemoComponent } from './components/app-layout/cloud/form-demo/cloud-form-demo.component';
import { DemoErrorComponent } from './components/error/demo-error.component';
import { ProcessCloudLayoutComponent } from './components/cloud/process-cloud-layout.component';
import { AppSearchFilterChipsComponent } from './components/search/search-filter-chips.component';
import { FileViewComponent } from './components/file-view/file-view.component';
import { SettingsComponent } from './components/settings/settings.component';
import { AppLoginComponent } from './components/login/login.component';
import { TaskListDemoComponent } from './components/task-list-demo/task-list-demo.component';
import { ProcessListDemoComponent } from './components/process-list-demo/process-list-demo.component';
import { AppCardViewComponent } from './components/card-view/card-view.component';
export const appRoutes: Routes = [
{ path: 'login', component: AppLoginComponent },
{ path: 'logout', component: LogoutComponent },
{
path: 'settings',
component: SettingsComponent
},
{
path: 'files/:nodeId/view',
component: AppComponent,
canActivate: [AuthGuardEcm],
canActivateChild: [AuthGuardEcm],
outlet: 'overlay',
children: [
{
path: '',
component: FileViewComponent
}
]
},
{
path: 'files/:nodeId/:versionId/view',
component: AppComponent,
canActivate: [AuthGuardEcm],
canActivateChild: [AuthGuardEcm],
outlet: 'overlay',
children: [
{
path: '',
component: FileViewComponent
}
]
},
{
path: 'preview/blob',
component: AppComponent,
outlet: 'overlay',
pathMatch: 'full',
children: [
{
path: '',
component: FileViewComponent
}
]
},
{
path: '',
component: AppLayoutComponent,
canActivate: [AuthGuard],
children: [
{
path: '',
redirectTo: `/home`,
pathMatch: 'full'
},
{
path: 'card-view',
children: [
{
path: '',
component: AppCardViewComponent
}
]
},
{
path: '',
component: HomeComponent
},
{
path: 'home',
component: HomeComponent
},
{
path: 'cloud',
canActivate: [AuthGuardSsoRoleService],
data: { roles: ['ACTIVITI_ADMIN', 'ACTIVITI_USER'], redirectUrl: '/error/403' },
children: [
{
path: '',
data: { roles: ['ACTIVITI_USER'], redirectUrl: '/error/403' },
component: AppsCloudDemoComponent
},
{
path: ':appName',
canActivate: [AuthGuardSsoRoleService],
data: { clientRoles: ['appName'], roles: ['ACTIVITI_USER'], redirectUrl: '/error/403' },
component: ProcessCloudLayoutComponent,
children: [
{
path: '',
component: CloudLayoutComponent,
children: [
{
path: 'tasks',
component: TasksCloudDemoComponent
},
{
path: 'processes',
component: ProcessesCloudDemoComponent
}
]
},
{
path: 'start-task',
component: StartTaskCloudDemoComponent
},
{
path: 'start-process',
component: StartProcessCloudDemoComponent
},
{
path: 'task-details/:taskId',
component: TaskDetailsCloudDemoComponent
},
{
path: 'task-details/:taskId/files/:nodeId/view',
component: CloudViewerComponent
},
{
path: 'process-details/:processInstanceId',
component: ProcessDetailsCloudDemoComponent
}
]
}
]
},
{
path: 'settings-layout',
component: SettingsComponent
},
{
path: 'files',
component: FilesComponent,
canActivate: [AuthGuardEcm]
},
{
path: 'files/:id',
component: FilesComponent,
canActivate: [AuthGuardEcm]
},
{
path: 'files/:id/display/:mode',
component: FilesComponent,
canActivate: [AuthGuardEcm]
},
{
path: 'search',
component: SearchResultComponent,
canActivate: [AuthGuardEcm]
},
{
path: 'search-filter-chips',
component: AppSearchFilterChipsComponent,
canActivate: [AuthGuardEcm]
},
{
path: 'activiti',
component: AppsViewComponent,
canActivate: [AuthGuardBpm]
},
{
path: 'activiti/apps',
component: AppsViewComponent,
canActivate: [AuthGuardBpm]
},
{
path: 'activiti/apps/:appId/tasks',
component: ProcessServiceComponent,
canActivate: [AuthGuardBpm]
},
{
path: 'activiti/apps/:appId/tasks/:filterId',
component: ProcessServiceComponent,
canActivate: [AuthGuardBpm]
},
{
path: 'activiti/apps/:appId/processes',
component: ProcessServiceComponent,
canActivate: [AuthGuardBpm]
},
{
path: 'activiti/apps/:appId/processes/:filterId',
component: ProcessServiceComponent,
canActivate: [AuthGuardBpm]
},
{
path: 'activiti/apps/:appId/diagram/:processDefinitionId',
component: ShowDiagramComponent,
canActivate: [AuthGuardBpm]
},
{
path: 'activiti/apps/:appId/report',
component: ProcessServiceComponent,
canActivate: [AuthGuardBpm]
},
// TODO: check if needed
{
path: 'activiti/appId/:appId',
component: ProcessServiceComponent,
canActivate: [AuthGuardBpm]
},
// TODO: check if needed
{
path: 'activiti/tasks/:id',
component: FormViewerComponent,
canActivate: [AuthGuardBpm]
},
// TODO: check if needed
{
/* cspell:disable-next-line */
path: 'activiti/tasksnode/:id',
component: FormNodeViewerComponent,
canActivate: [AuthGuardBpm]
},
{
path: 'permissions/:id',
component: DemoPermissionComponent,
canActivate: [AuthGuardEcm]
},
{ path: 'form-cloud', component: FormCloudDemoComponent },
{ path: 'form', component: AppFormComponent },
{
path: 'task-list',
canActivate: [AuthGuardBpm],
children: [
{
path: '',
component: TaskListDemoComponent
},
{
path: ':id',
component: TaskListDemoComponent
}
]
},
{
path: 'process-list',
canActivate: [AuthGuardBpm],
children: [
{
path: '',
component: ProcessListDemoComponent
},
{
path: ':id',
component: ProcessListDemoComponent
}
]
},
{
path: 'error/no-authorization',
component: ErrorContentComponent
}
]
},
{
path: 'error',
component: AppLayoutComponent,
children: [
{
path: '',
redirectTo: '/error/404',
pathMatch: 'full'
},
{
path: ':id',
component: DemoErrorComponent
}
]
},
{
path: '**',
redirectTo: 'error/404'
}
];

View File

@ -1,93 +0,0 @@
<adf-sidenav-layout
[sidenavMin]="70"
[sidenavMax]="220"
[stepOver]="780"
data-automation-id="sidenav-layout">
<adf-sidenav-layout-header>
<ng-template>
<adf-layout-header
id="adf-header"
title="ADF Demo Application"
redirectUrl="/home"
tooltip="ADF Demo Application"
[showSidenavToggle]="false">
<div class="app-layout-menu-spacer"></div>
<app-search-bar></app-search-bar>
<app-shell-user-info [menuPositionX]="'before'" [menuPositionY]="'above'"></app-shell-user-info>
<adf-avatar
size="32px"
src="https://avatars.githubusercontent.com/u/503991?v=4&size=64"
initials="DV"
tooltip="Denys Vuika"
cursor="pointer"
[matMenuTriggerFor]="userMenu">
</adf-avatar>
<mat-menu #userMenu="matMenu">
<button mat-menu-item>Item 1</button>
<button mat-menu-item>Item 2</button>
</mat-menu>
</adf-layout-header>
</ng-template>
</adf-sidenav-layout-header>
<adf-sidenav-layout-navigation>
<ng-template>
<mat-nav-list class="app-sidenav-linklist">
<ng-container *ngFor="let link of links">
<ng-container *ngIf="link.children">
<mat-list-item (click)="trigger.openMenu()" [attr.data-automation-id]="link.title | translate" class="app-sidenav-link">
<mat-icon matListItemIcon>{{link.icon}}</mat-icon>
<span matLine>{{ link.title | translate }}</span>
<mat-icon class="app-sidenav-link__expand-button" [matMenuTriggerData]="{links: link.children}"
rippleTrigger mat-icon-button #trigger="matMenuTrigger"
[matMenuTriggerFor]="nestedMenu">arrow_right</mat-icon>
</mat-list-item>
</ng-container>
<ng-container *ngIf="!link.children">
<mat-list-item [routerLink]="link.href"
routerLinkActive="app-sidenav-link--active" [routerLinkActiveOptions]="{ exact: true }"
[attr.data-automation-id]="link.title | translate" class="app-sidenav-link">
<mat-icon matListItemIcon >{{link.icon}}</mat-icon>
<span matLine>{{link.title | translate }}</span>
</mat-list-item>
</ng-container>
</ng-container>
<mat-list-item adf-logout [enableRedirect]="enableRedirect" redirectUri="/logout" class="app-sidenav-link" data-automation-id="Logout" >
<mat-icon matListItemIcon>exit_to_app</mat-icon>
<span matLine>Logout</span>
</mat-list-item>
</mat-nav-list>
</ng-template>
</adf-sidenav-layout-navigation>
<adf-sidenav-layout-content>
<ng-template>
<router-outlet></router-outlet>
</ng-template>
</adf-sidenav-layout-content>
</adf-sidenav-layout>
<mat-menu #nestedMenu="matMenu" xPosition="after" class="nestedMenu">
<ng-template matMenuContent let-links="links">
<button mat-menu-item *ngFor="let link of links"
class="app-sidenav-link"
[attr.data-automation-id]="link.title | translate"
routerLinkActive="app-sidenav-link--active"
[routerLink]="link.href"
[routerLinkActiveOptions]="{ exact: true }">
<mat-icon matListItemIcon>{{link.icon}}</mat-icon>
{{ link.title | translate }}
</button>
</ng-template>
</mat-menu>
<adf-file-uploading-dialog #fileDialog position="left"></adf-file-uploading-dialog>

View File

@ -1,14 +0,0 @@
.app-layout {
display: flex;
flex: 1;
min-width: 320px;
height: 100%;
.app-sidenav-link--active {
color: var(--theme-primary-color);
}
&-menu-spacer {
flex: 1 1 auto;
}
}

View File

@ -1,103 +0,0 @@
/*!
* @license
* Copyright © 2005-2024 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Component, ViewEncapsulation } from '@angular/core';
import {
AvatarComponent,
HeaderLayoutComponent,
LogoutDirective,
SidenavLayoutComponent,
SidenavLayoutContentDirective,
SidenavLayoutHeaderDirective,
SidenavLayoutNavigationDirective
} from '@alfresco/adf-core';
import { SearchBarComponent } from '../search/search-bar.component';
import { UserInfoComponent } from './user-info/user-info.component';
import { MatMenuModule } from '@angular/material/menu';
import { MatListModule } from '@angular/material/list';
import { TranslateModule } from '@ngx-translate/core';
import { MatIconModule } from '@angular/material/icon';
import { MatLineModule } from '@angular/material/core';
import { CommonModule } from '@angular/common';
import { RouterLink, RouterLinkActive, RouterOutlet } from '@angular/router';
import { AlfrescoApiService, FileUploadingDialogComponent } from '@alfresco/adf-content-services';
@Component({
standalone: true,
imports: [
CommonModule,
SidenavLayoutComponent,
SidenavLayoutHeaderDirective,
HeaderLayoutComponent,
SearchBarComponent,
UserInfoComponent,
AvatarComponent,
MatMenuModule,
SidenavLayoutNavigationDirective,
MatListModule,
TranslateModule,
MatIconModule,
MatLineModule,
RouterLink,
RouterLinkActive,
LogoutDirective,
SidenavLayoutContentDirective,
RouterOutlet,
FileUploadingDialogComponent
],
templateUrl: './app-layout.component.html',
styleUrls: ['./app-layout.component.scss'],
host: { class: 'app-layout' },
encapsulation: ViewEncapsulation.None
})
export class AppLayoutComponent {
links: Array<any> = [
{ href: '/home', icon: 'home', title: 'Home' },
{ href: '/files', icon: 'folder_open', title: 'Content Services' },
{ href: '/card-view', icon: 'view_headline', title: 'CardView' },
{ href: '/task-list', icon: 'assignment', title: 'Task List' },
{
href: '/cloud',
icon: 'cloud',
title: 'Process Cloud',
children: [
{ href: '/cloud/', icon: 'cloud', title: 'Home' },
{ href: '/form-cloud', icon: 'poll', title: 'Form' }
]
},
{
href: '/activiti',
icon: 'device_hub',
title: 'Process Services',
children: [
{ href: '/activiti', icon: 'vpn_key', title: 'App' },
{ href: '/process-list', icon: 'assignment', title: 'Process List' },
{ href: '/form', icon: 'poll', title: 'Form' }
]
},
{ href: '/login', icon: 'vpn_key', title: 'Login' },
{ href: '/settings-layout', icon: 'settings', title: 'Settings' }
];
enableRedirect = true;
constructor(private alfrescoApiService: AlfrescoApiService) {
if (this.alfrescoApiService.getInstance().isOauthConfiguration()) {
this.enableRedirect = false;
}
}
}

View File

@ -1,47 +0,0 @@
<div class="main-content">
<mat-tab-group [animationDuration]="0">
<mat-tab label="Form" class="form-cloud-render">
<div class="app-form-container">
<adf-cloud-form
[showRefreshButton]="false"
[form]="form"
(formSaved)="onFormSaved()"
(formError)="logErrors($event)">
</adf-cloud-form>
</div>
<div class="app-console">
<h3>Error log:</h3>
<p *ngFor="let error of errorFields">Error {{ error.name }} {{error.validationSummary.message | translate}}</p>
</div>
</mat-tab>
<mat-tab label="Editor" class="form-cloud-editor">
<ngx-monaco-editor
id="adf-form-config-editor"
class="app-form-config-editor"
[options]="editorOptions"
[(ngModel)]="formConfig"
(onInit)="onInitFormEditor($event)">
</ngx-monaco-editor>
<div class="app-form-editor-buttons">
<button mat-raised-button id="app-form-config-save" (click)="onSaveFormConfig()">Save form config</button>
<button mat-raised-button id="app-form-config-clear" (click)="onClearFormConfig()">Clear form config</button>
<a mat-raised-button class="app-upload-config-button">
<mat-icon>file_upload</mat-icon>
<label for="upload-config-file">Upload JSON File</label>
<input
id="upload-config-file"
data-automation-id="upload-single-file"
type="file"
name="uploadConfig"
accept=".json"
(change)="onConfigAdded($event)">
</a>
</div>
</mat-tab>
</mat-tab-group>
</div>

View File

@ -1,50 +0,0 @@
@import 'styles/mat-selectors';
.app-form-container {
padding: 10px;
}
.app-main-content {
padding: 0 15px;
}
.app-console {
width: 60%;
display: inline-block;
vertical-align: top;
margin-left: 10px;
height: 500px;
overflow: scroll;
padding-bottom: 30px;
h3 {
margin-top: 0;
}
p {
display: block;
font-family: monospace;
margin: 0;
}
}
.app-form-config-editor {
height: 500px;
}
.app-form-editor-buttons {
display: flex;
flex-direction: row;
column-gap: 4px;
}
.app-upload-config-button {
input {
cursor: pointer;
right: 0;
opacity: 0;
position: absolute;
top: 0;
z-index: 4;
}
}

View File

@ -1,129 +0,0 @@
/*!
* @license
* Copyright © 2005-2024 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Component, OnDestroy, OnInit } from '@angular/core';
import { FormFieldModel, FormModel, FormRenderingService, NotificationService } from '@alfresco/adf-core';
import { CloudFormRenderingService, FormCloudModule, FormCloudService } from '@alfresco/adf-process-services-cloud';
import { Subscription } from 'rxjs';
import { CustomEditorComponent, CustomWidgetComponent } from '../../../cloud/custom-form-components/custom-editor.component';
import { CoreAutomationService } from '../../../../../testing/automation.service';
import { CommonModule } from '@angular/common';
import { MatTabsModule } from '@angular/material/tabs';
import { MonacoEditorModule } from 'ngx-monaco-editor-v2';
import { TranslateModule } from '@ngx-translate/core';
import { FormsModule } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon';
@Component({
templateUrl: './cloud-form-demo.component.html',
standalone: true,
imports: [CommonModule, MatTabsModule, FormCloudModule, MonacoEditorModule, TranslateModule, FormsModule, MatButtonModule, MatIconModule],
styleUrls: ['./cloud-form-demo.component.scss'],
providers: [{ provide: FormRenderingService, useClass: CloudFormRenderingService }]
})
export class FormCloudDemoComponent implements OnInit, OnDestroy {
form: FormModel;
errorFields: FormFieldModel[] = [];
formConfig: string;
editor: any;
private subscriptions: Subscription[] = [];
editorOptions = {
theme: 'vs-dark',
language: 'json',
autoIndent: true,
formatOnPaste: true,
formatOnType: true,
automaticLayout: true
};
constructor(
private notificationService: NotificationService,
private formService: FormCloudService,
private automationService: CoreAutomationService,
private formRenderingService: FormRenderingService
) {
this.formRenderingService.register({
'demo-widget': () => CustomEditorComponent,
'custom-editor': () => CustomEditorComponent,
'custom-string': () => CustomWidgetComponent,
'custom-datetime': () => CustomWidgetComponent,
'custom-file': () => CustomWidgetComponent,
'custom-number': () => CustomWidgetComponent,
'custom-something': () => CustomWidgetComponent,
'custom-boolean': () => CustomWidgetComponent,
'custom-date': () => CustomWidgetComponent,
custom: () => CustomWidgetComponent
});
}
logErrors(errorFields: FormFieldModel[]) {
this.errorFields = errorFields;
}
ngOnInit() {
this.formConfig = JSON.stringify(this.automationService.forms.getFormCloudDefinition());
this.parseForm();
}
onFormSaved() {
this.notificationService.openSnackMessage('Task has been saved successfully');
}
ngOnDestroy() {
this.subscriptions.forEach((subscription) => subscription.unsubscribe());
this.subscriptions = [];
}
onInitFormEditor(editor) {
this.editor = editor;
setTimeout(() => {
this.editor.getAction('editor.action.formatDocument').run();
}, 1000);
}
parseForm() {
this.form = this.formService.parseForm(JSON.parse(this.formConfig));
}
onSaveFormConfig() {
try {
this.parseForm();
} catch (error) {
this.notificationService.openSnackMessage('Wrong form configuration');
}
}
onClearFormConfig() {
this.formConfig = '';
}
onConfigAdded($event: any): void {
const file = $event.currentTarget.files[0];
const fileReader = new FileReader();
fileReader.onload = () => {
this.formConfig = fileReader.result as string;
};
fileReader.readAsText(file);
this.onInitFormEditor(this.editor);
$event.target.value = '';
}
}

View File

@ -1,101 +0,0 @@
<div
id="userinfo_container"
[class.adf-userinfo-name-right]="showOnRight"
(keyup)="onKeyPress($event)"
tabindex="0"
role="button"
class="adf-userinfo-container"
*ngIf="canShow"
>
<span *ngIf="showName" id="adf-userinfo-ecm-name-display" class="adf-userinfo-name">
{{ecmUser | fullName}}
</span>
<button mat-button [matMenuTriggerFor]="menu" class="adf-userinfo-menu_button adf-content-userinfo-button"
data-automation-id="adf-user-profile">
<div class="adf-userinfo-button-profile" id="user-profile">
<div *ngIf="identityUser; else showEcmUserImage" id="identity-user-image">
<div *ngIf="ecmUser?.avatarId; else initialTemplate">
<div class="adf-userinfo-profile-container">
<img id="logged-user-img" [src]="getEcmAvatar(ecmUser.avatarId)" alt="user-info-profile-button"
class="adf-userinfo-profile-image"/>
</div>
</div>
<ng-template #initialTemplate>
<div [innerHTML]="identityUser | usernameInitials:'adf-userinfo-pic'"></div>
</ng-template>
</div>
<ng-template #showEcmUserImage>
<div id="ecm-user-image">
<div *ngIf="ecmUser.avatarId; else initialTemplate" class="adf-userinfo-profile-container">
<img id="logged-user-img" [src]="getEcmAvatar(ecmUser.avatarId)" alt="user-info-profile-button"
class="adf-userinfo-profile-image"/>
</div>
<ng-template #initialTemplate>
<div [outerHTML]="ecmUser | usernameInitials:'adf-userinfo-pic'"></div>
</ng-template>
</div>
</ng-template>
</div>
</button>
<mat-menu #menu="matMenu" id="user-profile-lists" [xPosition]="menuPositionX" [yPosition]="menuPositionY"
[overlapTrigger]="false" class="adf-userinfo-menu">
<mat-card appearance="outlined" *ngIf="mode === userInfoMode.CONTENT" class="adf-userinfo-card adf-content-userinfo-card">
<mat-card-header class="adf-userinfo-card-header" [style.background-image]="'url(' + ecmBackgroundImage + ')'">
<div *ngIf="ecmUser.avatarId; else initialTemplate"
class="adf-userinfo-profile-container adf-hide-small">
<img class="adf-userinfo-profile-picture" id="ecm-user-detail-image"
alt="ecm-profile-image" [src]="getEcmAvatar(ecmUser.avatarId)"/>
</div>
<ng-template #initialTemplate>
<div
[outerHTML]="ecmUser | usernameInitials:'adf-userinfo-profile-initials adf-hide-small'"></div>
</ng-template>
<div class="mat-headline-6" id="ecm-username">{{ecmUser | fullName}}</div>
</mat-card-header>
<mat-card-content>
<div class="adf-userinfo-supporting-text">
<div class="adf-userinfo-detail">
<h2 id="ecm-full-name"
class="adf-userinfo__detail-title">{{ecmUser | fullName}}</h2>
<span id="ecm-email"> {{ecmUser.email}} </span>
<a href="#/profile">{{ 'USER_PROFILE.LABELS.MY_PROFILE' | translate }}</a>
</div>
<div class="adf-userinfo-detail">
<span class="adf-userinfo__secondary-info" id="ecm-job-title-label">
{{ 'USER_PROFILE.LABELS.ECM.JOB_TITLE' | translate }}
<span id="ecm-job-title"> {{ ecmUser.jobTitle ? ecmUser.jobTitle : 'N/A' }} </span>
</span>
</div>
</div>
</mat-card-content>
</mat-card>
<mat-card appearance="outlined" *ngIf="mode === userInfoMode.CONTENT_SSO" class="adf-userinfo-card adf-content-userinfo-card">
<mat-card-header class="adf-userinfo-card-header"
[style.background-image]="'url(' + bpmBackgroundImage + ')'">
<div *ngIf="ecmUser.avatarId; else initialTemplate"
class="adf-userinfo-profile-container adf-hide-small">
<img class="adf-userinfo-profile-picture" id="ecm-user-detail-image"
alt="ecm-profile-image" [src]="getEcmAvatar(ecmUser.avatarId)"/>
</div>
<ng-template #initialTemplate>
<div
[outerHTML]="identityUser | usernameInitials:'adf-userinfo-profile-initials adf-hide-small'"></div>
</ng-template>
<div class="mat-headline-6" id="identity-username">{{identityUser | fullName}}</div>
</mat-card-header>
<mat-card-content>
<div class="adf-userinfo-supporting-text">
<div class="adf-userinfo-detail">
<h2 id="identity-full-name"
class="adf-userinfo__detail-title">{{identityUser | fullName}}</h2>
<span id="identity-email"> {{identityUser.email}} </span>
<a href="#/profile">
{{ 'USER_PROFILE.LABELS.MY_PROFILE' | translate }}</a>
</div>
</div>
</mat-card-content>
</mat-card>
</mat-menu>
</div>

View File

@ -1,154 +0,0 @@
@import 'styles/flex';
.adf {
&-userinfo-container {
display: flex;
align-items: center;
padding: 0 5px;
}
&-userinfo-name-right {
flex-direction: row-reverse;
}
&-userinfo-name {
padding: 0 5px;
@include layout-bp(lt-md) {
display: none;
}
}
&-userinfo-pic {
background: var(--adf-theme-primary-300);
display: inline-block;
width: 40px;
height: 40px;
border-radius: 100px;
text-align: center;
font-weight: bolder;
font-size: var(--theme-adf-picture-1-font-size);
text-transform: uppercase;
vertical-align: middle;
line-height: 40px;
}
&-userinfo-profile-image {
background: var(--adf-theme-primary-300);
text-align: center;
border-radius: 90%;
width: 40px;
height: 40px;
margin-right: 0;
cursor: pointer;
vertical-align: middle;
margin-left: 0;
}
&-userinfo-profile-container {
display: inline-block;
}
&-userinfo-menu_button.adf-content-userinfo-button {
margin-right: 0;
border-radius: 90%;
padding: 0;
min-width: 40px;
height: 40px;
}
&-userinfo-card-header {
align-items: center;
display: flex;
justify-content: stretch;
line-height: normal;
height: 100px;
box-sizing: border-box;
}
&-userinfo-card.adf-content-userinfo-card {
padding: 0;
}
&-userinfo-supporting-text {
overflow: hidden;
padding: 32px;
column-count: 2;
display: flex;
justify-content: space-between;
@include layout-bp(lt-sm) {
padding: 10px;
}
}
&-userinfo__detail-title {
text-overflow: ellipsis;
}
&-userinfo__secondary-info {
font-size: var(--theme-body-1-font-size);
font-weight: 400;
letter-spacing: 0;
line-height: 18px;
align-items: flex-end;
}
&-userinfo-profile-picture {
background: var(--adf-theme-primary-300);
background-size: cover;
border-radius: 50%;
height: 80px;
width: 80px;
margin-left: 0;
margin-right: 8px;
}
&-userinfo-profile-initials {
text-transform: uppercase;
background-size: cover;
background-color: var(--adf-theme-primary-300);
border-radius: 50%;
height: 80px;
width: 80px;
margin-left: 0;
margin-right: 8px;
font-size: 35px;
font-weight: 400;
letter-spacing: 0;
line-height: 78px;
overflow: hidden;
display: flex;
justify-content: space-around;
}
&-userinfo-button-profile {
display: inline-block;
border: 0;
vertical-align: middle;
}
&-userinfo-detail {
text-align: left;
}
&-hide-small.adf-userinfo-profile-initials,
&-hide-small.adf-userinfo-profile-container {
@include layout-bp(lt-md) {
display: none;
}
}
}
@media only screen and (device-width >= 480px) {
.adf-content-userinfo-menu.adf-userinfo-menu {
max-height: 450px;
min-width: 450px;
overflow: auto;
padding: 0;
}
}
.adf-content-userinfo-menu.adf-userinfo-menu > div {
padding: 0;
}

View File

@ -1,118 +0,0 @@
/*!
* @license
* Copyright © 2005-2024 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { FullNamePipe, IdentityUserModel, InitialUsernamePipe, UserInfoMode } from '@alfresco/adf-core';
import { Component, Input, OnDestroy, ViewChild, ViewEncapsulation } from '@angular/core';
import { MatMenuModule, MatMenuTrigger, MenuPositionX, MenuPositionY } from '@angular/material/menu';
import { Subject } from 'rxjs';
import { EcmUserModel, PeopleContentService } from '@alfresco/adf-content-services';
import { CommonModule } from '@angular/common';
import { MatButtonModule } from '@angular/material/button';
import { MatCardModule } from '@angular/material/card';
import { TranslateModule } from '@ngx-translate/core';
@Component({
selector: 'adf-content-user-info',
standalone: true,
imports: [CommonModule, FullNamePipe, MatMenuModule, MatButtonModule, InitialUsernamePipe, MatCardModule, TranslateModule],
templateUrl: './content-user-info.component.html',
styleUrls: ['./content-user-info.component.scss'],
encapsulation: ViewEncapsulation.None
})
export class ContentUserInfoComponent implements OnDestroy {
@ViewChild(MatMenuTrigger) trigger: MatMenuTrigger;
/** Determines if user is logged in. */
@Input()
isLoggedIn: boolean;
/** ECM user info. */
@Input()
ecmUser: EcmUserModel;
/** Identity user info. */
@Input()
identityUser: IdentityUserModel;
/** current mode. */
@Input()
mode: UserInfoMode = UserInfoMode.CONTENT;
/** Custom path for the background banner image for ACS users. */
@Input()
ecmBackgroundImage: string = './resources/images/ecm-background.png';
/** Custom path for the background banner image for APS users. */
@Input()
bpmBackgroundImage: string = './resources/images/bpm-background.png';
/** Custom choice for opening the menu at the bottom. Can be `before` or `after`. */
@Input()
menuPositionX: MenuPositionX = 'after';
/** Custom choice for opening the menu at the bottom. Can be `above` or `below`. */
@Input()
menuPositionY: MenuPositionY = 'below';
/** Shows/hides the username next to the user info button. */
@Input()
showName: boolean = true;
/**
* When the username is shown, this defines its position relative to the user info button.
* Can be `right` or `left`.
*/
@Input()
namePosition: string = 'right';
userInfoMode = UserInfoMode;
private destroy$ = new Subject();
constructor(private peopleContentService: PeopleContentService) {}
ngOnDestroy(): void {
this.destroy$.next(true);
this.destroy$.complete();
}
onKeyPress(event: KeyboardEvent) {
this.closeUserModal(event);
}
private closeUserModal($event: KeyboardEvent) {
if ($event.keyCode === 27) {
this.trigger.closeMenu();
}
}
stopClosing(event: Event) {
event.stopPropagation();
}
getEcmAvatar(avatarId: string): string {
return this.peopleContentService.getUserProfileImage(avatarId);
}
get showOnRight(): boolean {
return this.namePosition === 'right';
}
get canShow(): boolean {
return this.isLoggedIn && !!this.ecmUser && !!this.mode;
}
}

View File

@ -1,18 +0,0 @@
/*!
* @license
* Copyright © 2005-2024 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export * from './public-api';

View File

@ -1,18 +0,0 @@
/*!
* @license
* Copyright © 2005-2024 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export * from './content-user-info.component';

View File

@ -1,18 +0,0 @@
/*!
* @license
* Copyright © 2005-2024 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export * from './public-api';

View File

@ -1,109 +0,0 @@
<div
id="userinfo_container"
tabindex="0"
role="button"
[class.adf-userinfo-name-right]="showOnRight"
(keyup)="onKeyPress($event)"
class="adf-userinfo-container"
*ngIf="canShow"
>
<span *ngIf="showName" id="adf-userinfo-bpm-name-display" class="adf-userinfo-name">
{{bpmUser | fullName}}
</span>
<button mat-button [matMenuTriggerFor]="menu" class="adf-userinfo-menu_button"
data-automation-id="adf-user-profile">
<div class="adf-userinfo-button-profile" id="user-profile">
<div *ngIf="ecmUser; else showBpmUserImage" id="ecm-user-image">
<div *ngIf="ecmUser.avatarId; else initialTemplate" class="adf-userinfo-profile-container">
<img id="logged-user-img" [src]="getEcmAvatar(ecmUser.avatarId)" alt="user-info-profile-button"
class="adf-userinfo-profile-image"/>
</div>
<ng-template #initialTemplate>
<div [outerHTML]="ecmUser | usernameInitials:'adf-userinfo-pic'"></div>
</ng-template>
</div>
<ng-template #showBpmUserImage>
<div *ngIf="bpmUser" id="bpm-user-image">
<div *ngIf="bpmUser.pictureId; else initialTemplate" class="adf-userinfo-profile-container">
<img id="logged-user-img" [src]="getBpmUserImage()" alt="user-info-profile-button"
class="adf-userinfo-profile-image"/>
</div>
<ng-template #initialTemplate>
<div [outerHTML]="bpmUser | usernameInitials:'adf-userinfo-pic'"></div>
</ng-template>
</div>
</ng-template>
</div>
</button>
<mat-menu #menu="matMenu" id="user-profile-lists" [xPosition]="menuPositionX" [yPosition]="menuPositionY"
[overlapTrigger]="false" class="adf-userinfo-menu">
<mat-tab-group id="tab-group-env" (click)="stopClosing($event)" selectedIndex="0" role="menuitem"
class="adf-userinfo-tab" [class.adf-hide-tab]="!ecmUser">
<mat-tab label="{{ 'USER_PROFILE.TAB.CS' | translate }}" role="dialog"
*ngIf="mode===userInfoMode.ALL">
<mat-card appearance="outlined" class="adf-userinfo-card" *ngIf="ecmUser">
<mat-card-header class="adf-userinfo-card-header"
[style.background-image]="'url(' + ecmBackgroundImage + ')'">
<div *ngIf="ecmUser.avatarId; else initialTemplate"
class="adf-userinfo-profile-container adf-hide-small">
<img class="adf-userinfo-profile-picture" id="ecm-user-detail-image"
alt="ecm-profile-image" [src]="getEcmAvatar(ecmUser.avatarId)"/>
</div>
<ng-template #initialTemplate>
<div
[outerHTML]="ecmUser | usernameInitials:'adf-userinfo-profile-initials adf-hide-small'"></div>
</ng-template>
<div class="mat-headline-6" id="ecm-username">{{ecmUser | fullName}}</div>
</mat-card-header>
<mat-card-content>
<div class="adf-userinfo-supporting-text">
<div class="adf-userinfo-detail">
<h2 id="ecm-full-name"
class="adf-userinfo__detail-title">{{ecmUser | fullName}}</h2>
<span id="ecm-email"> {{ecmUser.email}} </span>
</div>
<div class="adf-userinfo-detail">
<span class="adf-userinfo__secondary-info" id="ecm-job-title-label">
{{ 'USER_PROFILE.LABELS.ECM.JOB_TITLE' | translate }}
<span id="ecm-job-title"> {{ ecmUser.jobTitle ? ecmUser.jobTitle : 'N/A' }} </span>
</span>
</div>
</div>
</mat-card-content>
</mat-card>
</mat-tab>
<mat-tab id="bpm-panel" label="{{ 'USER_PROFILE.TAB.PS' | translate }}" role="dialog"
*ngIf="mode===userInfoMode.PROCESS || mode===userInfoMode.ALL">
<mat-card appearance="outlined" class="adf-userinfo-card">
<mat-card-header class="adf-userinfo-card-header"
[style.background-image]="'url(' + bpmBackgroundImage + ')'">
<img *ngIf="bpmUser.pictureId; else initialTemplate"
class="adf-userinfo-profile-picture adf-hide-small" id="bpm-user-detail-image"
alt="bpm-profile-image" [src]="getBpmUserImage()"/>
<ng-template #initialTemplate>
<div
[outerHTML]="bpmUser | usernameInitials:'adf-userinfo-profile-initials adf-hide-small'"></div>
</ng-template>
<div class="mat-headline-6" id="bpm-username">{{bpmUser | fullName}}</div>
</mat-card-header>
<mat-card-content>
<div class="adf-userinfo-supporting-text">
<div class="adf-userinfo-detail">
<h2 id="bpm-full-name"
class="adf-userinfo__detail-title">{{ bpmUser | fullName }}</h2>
<span id="bpm-email"> {{bpmUser.email}} </span>
</div>
<div class="adf-userinfo-detail">
<span id="bpm-tenant" class="adf-userinfo__secondary-info">
{{ 'USER_PROFILE.LABELS.BPM.TENANT' | translate }}
<span>{{ bpmUser.tenantName ? bpmUser.tenantName : '' }}</span>
</span>
</div>
</div>
</mat-card-content>
</mat-card>
</mat-tab>
</mat-tab-group>
</mat-menu>
</div>

View File

@ -1,174 +0,0 @@
@import 'styles/flex';
@import 'styles/mat-selectors';
.adf {
&-userinfo-container {
display: flex;
align-items: center;
padding: 0 5px;
}
&-userinfo-name-right {
flex-direction: row-reverse;
}
&-userinfo-name {
padding: 0 5px;
@include layout-bp(lt-md) {
display: none;
}
}
&-userinfo-pic {
background: var(--adf-theme-primary-300);
display: inline-block;
width: 40px;
height: 40px;
border-radius: 100px;
text-align: center;
font-weight: bolder;
font-size: var(--theme-adf-picture-1-font-size);
text-transform: uppercase;
vertical-align: middle;
line-height: 40px;
}
&-userinfo-profile-image {
background: var(--adf-theme-primary-300);
text-align: center;
border-radius: 90%;
width: 40px;
height: 40px;
margin-right: 0;
cursor: pointer;
vertical-align: middle;
margin-left: 0;
}
&-userinfo-profile-container {
display: inline-block;
}
&-userinfo-menu_button#{$mat-button} {
margin-right: 0;
border-radius: 90%;
padding: 0;
min-width: 40px;
height: 40px;
}
&-userinfo-tab #{$mat-tab-header} {
align-self: center;
width: 100%;
min-width: 250px;
}
&-userinfo-tab #{$mat-tab-label-text} {
flex: auto;
font-weight: 500;
font-size: var(--theme-body-1-font-size);
text-transform: uppercase;
line-height: 48px;
text-align: center;
}
&-userinfo-card-header {
align-items: center;
display: flex;
justify-content: stretch;
line-height: normal;
height: 100px;
box-sizing: border-box;
}
&-userinfo-card#{$mat-card} {
padding: 0;
}
&-userinfo-supporting-text {
overflow: hidden;
padding: 32px;
column-count: 2;
display: flex;
justify-content: space-between;
@include layout-bp(lt-sm) {
padding: 10px;
}
}
&-userinfo__detail-title {
text-overflow: ellipsis;
}
&-userinfo__secondary-info {
font-size: var(--theme-body-1-font-size);
font-weight: 400;
letter-spacing: 0;
line-height: 18px;
align-items: flex-end;
}
&-userinfo-profile-picture {
background: var(--adf-theme-primary-300);
background-size: cover;
border-radius: 50%;
height: 80px;
width: 80px;
margin-left: 0;
margin-right: 8px;
}
&-userinfo-profile-initials {
text-transform: uppercase;
background-size: cover;
background-color: var(--adf-theme-primary-300);
border-radius: 50%;
height: 80px;
width: 80px;
margin-left: 0;
margin-right: 8px;
font-size: 35px;
font-weight: 400;
letter-spacing: 0;
line-height: 78px;
overflow: hidden;
display: flex;
justify-content: space-around;
}
&-userinfo-button-profile {
display: inline-block;
border: 0;
vertical-align: middle;
}
&-userinfo-detail {
text-align: left;
}
&-hide-tab.adf-userinfo-tab#tab-group-env {
display: none;
}
&-hide-small.adf-userinfo-profile-initials,
&-hide-small.adf-userinfo-profile-container {
@include layout-bp(lt-md) {
display: none;
}
}
}
@media only screen and (min-device-width: 480px) {
#{$mat-menu-panel}.adf-userinfo-menu {
max-height: 450px;
min-width: 450px;
overflow: auto;
padding: 0;
}
}
#{$mat-menu-panel}.adf-userinfo-menu #{$mat-menu-content} {
padding: 0;
}

View File

@ -1,230 +0,0 @@
/*!
* @license
* Copyright © 2005-2024 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { CoreTestingModule, UserInfoMode } from '@alfresco/adf-core';
import { fakeEcmUser, fakeEcmUserNoImage } from '@alfresco/adf-content-services';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { ProcessUserInfoComponent } from './process-user-info.component';
import { HarnessLoader } from '@angular/cdk/testing';
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
import { MatTabGroupHarness, MatTabHarness } from '@angular/material/tabs/testing';
const fakeBpmUser: any = {
apps: [],
capabilities: null,
company: 'fake-company',
created: 'fake-create-date',
email: 'fakeBpm@fake.com',
externalId: 'fake-external-id',
firstName: 'fake-bpm-first-name',
lastName: 'fake-bpm-last-name',
groups: [],
id: 'fake-id',
lastUpdate: 'fake-update-date',
latestSyncTimeStamp: 'fake-timestamp',
password: 'fake-password',
pictureId: 12,
status: 'fake-status',
tenantId: 'fake-tenant-id',
tenantName: 'fake-tenant-name',
tenantPictureId: 'fake-tenant-picture-id',
type: 'fake-type'
};
describe('ProcessUserInfoComponent', () => {
const profilePictureUrl = 'alfresco-logo.svg';
let component: ProcessUserInfoComponent;
let fixture: ComponentFixture<ProcessUserInfoComponent>;
let loader: HarnessLoader;
let element: HTMLElement;
const openUserInfo = () => {
fixture.detectChanges();
const imageButton = element.querySelector<HTMLButtonElement>('#logged-user-img');
imageButton.click();
fixture.detectChanges();
};
const whenFixtureReady = async () => {
fixture.detectChanges();
await fixture.whenStable();
fixture.detectChanges();
};
beforeEach(() => {
TestBed.configureTestingModule({
imports: [CoreTestingModule, ProcessUserInfoComponent]
});
fixture = TestBed.createComponent(ProcessUserInfoComponent);
component = fixture.componentInstance;
loader = TestbedHarnessEnvironment.documentRootLoader(fixture);
element = fixture.nativeElement;
spyOn(window, 'requestAnimationFrame').and.returnValue(1);
});
afterEach(() => {
fixture.destroy();
});
it('should not show any image if the user is not logged in', () => {
expect(element.querySelector('#userinfo_container')).toBeDefined();
expect(element.querySelector('#logged-user-img')).toBeNull();
});
it('should NOT have users immediately after ngOnInit', () => {
expect(element.querySelector('#userinfo_container')).toBeDefined();
expect(element.querySelector('#ecm_username')).toBeNull();
expect(element.querySelector('#bpm_username')).toBeNull();
expect(element.querySelector('#user-profile-lists')).toBeNull();
});
describe('when user is logged on bpm', () => {
beforeEach(async () => {
component.bpmUser = fakeBpmUser;
component.isLoggedIn = true;
});
it('should show full name next the user image', async () => {
await whenFixtureReady();
openUserInfo();
const bpmUserName = fixture.debugElement.query(By.css('#bpm-username'));
expect(element.querySelector('#userinfo_container')).not.toBeNull();
expect(bpmUserName).toBeDefined();
expect(bpmUserName).not.toBeNull();
expect(bpmUserName.nativeElement.innerHTML).toContain('fake-bpm-first-name fake-bpm-last-name');
});
it('should get the bpm current user image from the service', async () => {
spyOn(component, 'getBpmUserImage').and.returnValue(profilePictureUrl);
await whenFixtureReady();
expect(element.querySelector('#userinfo_container')).not.toBeNull();
expect(element.querySelector('#logged-user-img')).not.toBeNull();
expect(element.querySelector('#logged-user-img').getAttribute('src')).toContain(profilePictureUrl);
});
it('should show last name if first name is null', async () => {
component.bpmUser = {
firstName: null,
lastName: 'fake-last-name'
} as any;
await whenFixtureReady();
const fullNameElement = element.querySelector('#adf-userinfo-bpm-name-display');
fixture.detectChanges();
expect(element.querySelector('#userinfo_container')).toBeDefined();
expect(element.querySelector('#adf-userinfo-bpm-name-display')).not.toBeNull();
expect(fullNameElement.textContent).toContain('fake-last-name');
expect(fullNameElement.textContent).not.toContain('fake-first-name');
});
it('should not show the tabs', async () => {
await whenFixtureReady();
openUserInfo();
const tabGroupHost = await (await loader.getHarness(MatTabGroupHarness.with({ selector: '#tab-group-env' }))).host();
expect(await tabGroupHost.hasClass('adf-hide-tab')).toBeTruthy();
});
});
describe('when user is logged on bpm and ecm', () => {
beforeEach(async () => {
component.bpmUser = fakeBpmUser;
component.ecmUser = fakeEcmUser as any;
component.isLoggedIn = true;
component.mode = UserInfoMode.ALL;
});
it('should show the tabs', async () => {
await whenFixtureReady();
openUserInfo();
const tabGroupHost = await (await loader.getHarness(MatTabGroupHarness.with({ selector: '#tab-group-env' }))).host();
expect(await tabGroupHost.hasClass('adf-hide-tab')).toBeFalsy();
});
it('should get the bpm user information', async () => {
spyOn(component, 'getBpmUserImage').and.returnValue(profilePictureUrl);
await whenFixtureReady();
openUserInfo();
const bpmTab = await loader.getHarness(MatTabHarness.with({ label: 'USER_PROFILE.TAB.PS' }));
await bpmTab.select();
const bpmUsername = fixture.debugElement.query(By.css('#bpm-username'));
const bpmImage = fixture.debugElement.query(By.css('#bpm-user-detail-image'));
expect(element.querySelector('#userinfo_container')).not.toBeNull();
expect(bpmUsername).not.toBeNull();
expect(bpmImage).not.toBeNull();
expect(bpmImage.properties.src).toContain(profilePictureUrl);
expect(bpmUsername.nativeElement.textContent).toContain('fake-bpm-first-name fake-bpm-last-name');
expect(fixture.debugElement.query(By.css('#bpm-tenant')).nativeElement.textContent).toContain('fake-tenant-name');
});
it('should get the ecm user information', async () => {
spyOn(component, 'getEcmAvatar').and.returnValue(profilePictureUrl);
await whenFixtureReady();
openUserInfo();
const ecmUsername = fixture.debugElement.query(By.css('#ecm-username'));
const ecmImage = fixture.debugElement.query(By.css('#ecm-user-detail-image'));
fixture.detectChanges();
await fixture.whenStable();
expect(element.querySelector('#userinfo_container')).toBeDefined();
expect(ecmUsername).not.toBeNull();
expect(ecmImage).not.toBeNull();
expect(ecmImage.properties.src).toContain(profilePictureUrl);
expect(fixture.debugElement.query(By.css('#ecm-full-name')).nativeElement.textContent).toContain(
'fake-ecm-first-name fake-ecm-last-name'
);
expect(fixture.debugElement.query(By.css('#ecm-job-title')).nativeElement.textContent).toContain('job-ecm-test');
});
it('should show the ecm image if exists', async () => {
spyOn(component, 'getEcmAvatar').and.returnValue(profilePictureUrl);
await whenFixtureReady();
openUserInfo();
expect(element.querySelector('#userinfo_container')).toBeDefined();
expect(element.querySelector('#logged-user-img')).toBeDefined();
expect(element.querySelector('#logged-user-img').getAttribute('src')).toEqual(profilePictureUrl);
});
it('should show the ecm initials if the ecm user has no image', async () => {
component.ecmUser = fakeEcmUserNoImage as any;
await whenFixtureReady();
expect(element.querySelector('#userinfo_container')).toBeDefined();
expect(element.querySelector('[data-automation-id="user-initials-image"]').textContent).toContain('ff');
});
it('should show the tabs for the env', async () => {
await whenFixtureReady();
openUserInfo();
const tabGroup = await loader.getHarness(MatTabGroupHarness.with({ selector: '#tab-group-env' }));
const tabs = await tabGroup.getTabs();
expect(await (await tabGroup.host()).hasClass('adf-hide-tab')).toBeFalsy();
expect(tabs.length).toBe(2);
});
it('should not close the menu when a tab is clicked', async () => {
await whenFixtureReady();
openUserInfo();
const bpmTab = await loader.getHarness(MatTabHarness.with({ label: 'USER_PROFILE.TAB.PS' }));
bpmTab.select();
expect(fixture.debugElement.query(By.css('#user-profile-lists'))).not.toBeNull();
});
});
});

View File

@ -1,125 +0,0 @@
/*!
* @license
* Copyright © 2005-2024 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { FullNamePipe, InitialUsernamePipe, UserInfoMode } from '@alfresco/adf-core';
import { EcmUserModel, PeopleContentService } from '@alfresco/adf-content-services';
import { Component, Input, OnDestroy, ViewChild, ViewEncapsulation } from '@angular/core';
import { MatMenuModule, MatMenuTrigger, MenuPositionX, MenuPositionY } from '@angular/material/menu';
import { Subject } from 'rxjs';
import { PeopleProcessService } from '@alfresco/adf-process-services';
import { UserRepresentation } from '@alfresco/js-api';
import { CommonModule } from '@angular/common';
import { MatButtonModule } from '@angular/material/button';
import { MatTabsModule } from '@angular/material/tabs';
import { TranslateModule } from '@ngx-translate/core';
import { MatCardModule } from '@angular/material/card';
@Component({
selector: 'adf-process-user-info',
standalone: true,
imports: [CommonModule, FullNamePipe, MatButtonModule, MatMenuModule, InitialUsernamePipe, MatTabsModule, TranslateModule, MatCardModule],
templateUrl: './process-user-info.component.html',
styleUrls: ['./process-user-info.component.scss'],
encapsulation: ViewEncapsulation.None
})
export class ProcessUserInfoComponent implements OnDestroy {
@ViewChild(MatMenuTrigger) trigger: MatMenuTrigger;
/** Determines if user is logged in. */
@Input()
isLoggedIn: boolean;
/** BPM user info. */
@Input()
bpmUser: UserRepresentation;
/** ECM user info. */
@Input()
ecmUser: EcmUserModel;
/** current mode. */
@Input()
mode: UserInfoMode = UserInfoMode.PROCESS;
/** Custom path for the background banner image for APS users. */
@Input()
bpmBackgroundImage: string = './resources/images/bpm-background.png';
/** Custom path for the background banner image for ACS users. */
@Input()
ecmBackgroundImage: string = './resources/images/ecm-background.png';
/** Custom choice for opening the menu at the bottom. Can be `before` or `after`. */
@Input()
menuPositionX: MenuPositionX = 'after';
/** Custom choice for opening the menu at the bottom. Can be `above` or `below`. */
@Input()
menuPositionY: MenuPositionY = 'below';
/** Shows/hides the username next to the user info button. */
@Input()
showName: boolean = true;
/**
* When the username is shown, this defines its position relative to the user info button.
* Can be `right` or `left`.
*/
@Input()
namePosition: string = 'right';
userInfoMode = UserInfoMode;
private destroy$ = new Subject();
constructor(private peopleProcessService: PeopleProcessService, private peopleContentService: PeopleContentService) {}
ngOnDestroy(): void {
this.destroy$.next(true);
this.destroy$.complete();
}
onKeyPress(event: KeyboardEvent) {
this.closeUserModal(event);
}
private closeUserModal($event: KeyboardEvent) {
if ($event.keyCode === 27) {
this.trigger.closeMenu();
}
}
stopClosing(event: Event) {
event.stopPropagation();
}
getBpmUserImage(): string {
return this.peopleProcessService.getCurrentUserProfileImage();
}
getEcmAvatar(avatarId: string): string {
return this.peopleContentService.getUserProfileImage(avatarId);
}
get showOnRight(): boolean {
return this.namePosition === 'right';
}
get canShow(): boolean {
return this.isLoggedIn && !!this.bpmUser && !!this.mode;
}
}

View File

@ -1,18 +0,0 @@
/*!
* @license
* Copyright © 2005-2024 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export * from './process-user-info.component';

View File

@ -1,21 +0,0 @@
<ng-container>
<adf-content-user-info
*ngIf="mode === userInfoMode.CONTENT || mode === userInfoMode.CONTENT_SSO"
[ecmUser]="ecmUser$ | async"
[identityUser]="identityUser$ | async"
[isLoggedIn]="isLoggedIn"
[mode]="mode"
></adf-content-user-info>
<adf-identity-user-info
*ngIf="mode === userInfoMode.SSO"
[identityUser]="identityUser$ | async"
[isLoggedIn]="isLoggedIn"
></adf-identity-user-info>
<adf-process-user-info
*ngIf="mode === userInfoMode.PROCESS || mode === userInfoMode.ALL"
[bpmUser]="bpmUser$ | async"
[ecmUser]="ecmUser$ | async"
[isLoggedIn]="isLoggedIn"
[mode]="mode"
></adf-process-user-info>
</ng-container>

View File

@ -1,124 +0,0 @@
/*!
* @license
* Copyright © 2005-2024 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { EcmUserModel, PeopleContentService } from '@alfresco/adf-content-services';
import { PeopleProcessService } from '@alfresco/adf-process-services';
import {
AuthenticationService,
BasicAlfrescoAuthService,
IdentityUserInfoComponent,
IdentityUserModel,
IdentityUserService,
UserInfoMode
} from '@alfresco/adf-core';
import { Component, OnInit, Input } from '@angular/core';
import { MenuPositionX, MenuPositionY } from '@angular/material/menu';
import { Observable, of } from 'rxjs';
import { CommonModule } from '@angular/common';
import { UserRepresentation } from '@alfresco/js-api';
import { ContentUserInfoComponent } from './content-user-info';
import { ProcessUserInfoComponent } from './process-user-info';
@Component({
selector: 'app-shell-user-info',
standalone: true,
imports: [CommonModule, ContentUserInfoComponent, IdentityUserInfoComponent, ProcessUserInfoComponent],
templateUrl: './user-info.component.html'
})
export class UserInfoComponent implements OnInit {
/** Custom choice for opening the menu at the bottom. Can be `before` or `after`. */
@Input()
menuPositionX: MenuPositionX = 'after';
/** Custom choice for opening the menu at the bottom. Can be `above` or `below`. */
@Input()
menuPositionY: MenuPositionY = 'below';
mode: UserInfoMode;
ecmUser$: Observable<EcmUserModel>;
bpmUser$: Observable<UserRepresentation>;
identityUser$: Observable<IdentityUserModel>;
userInfoMode = UserInfoMode;
constructor(
private peopleContentService: PeopleContentService,
private peopleProcessService: PeopleProcessService,
private identityUserService: IdentityUserService,
private basicAlfrescoAuthService: BasicAlfrescoAuthService,
private authService: AuthenticationService
) {}
ngOnInit() {
this.getUserInfo();
}
getUserInfo() {
if (this.authService.isOauth()) {
this.loadIdentityUserInfo();
this.mode = UserInfoMode.SSO;
if (this.authService.isECMProvider() && this.authService.isEcmLoggedIn()) {
this.mode = UserInfoMode.CONTENT_SSO;
this.loadEcmUserInfo();
}
} else if (this.isAllLoggedIn()) {
this.loadEcmUserInfo();
this.loadBpmUserInfo();
this.mode = UserInfoMode.ALL;
} else if (this.isEcmLoggedIn()) {
this.loadEcmUserInfo();
this.mode = UserInfoMode.CONTENT;
} else if (this.isBpmLoggedIn()) {
this.loadBpmUserInfo();
this.mode = UserInfoMode.PROCESS;
}
}
get isLoggedIn(): boolean {
if (this.basicAlfrescoAuthService.isKerberosEnabled()) {
return true;
}
return this.authService.isLoggedIn();
}
private loadEcmUserInfo(): void {
this.ecmUser$ = this.peopleContentService.getCurrentUserInfo();
}
private loadBpmUserInfo() {
this.bpmUser$ = this.peopleProcessService.getCurrentUserInfo();
}
private loadIdentityUserInfo() {
this.identityUser$ = of(this.identityUserService.getCurrentUserInfo());
}
private isAllLoggedIn() {
return (
(this.authService.isEcmLoggedIn() && this.authService.isBpmLoggedIn()) ||
(this.authService.isALLProvider() && this.basicAlfrescoAuthService.isKerberosEnabled())
);
}
private isBpmLoggedIn() {
return this.authService.isBpmLoggedIn() || (this.authService.isECMProvider() && this.basicAlfrescoAuthService.isKerberosEnabled());
}
private isEcmLoggedIn() {
return this.authService.isEcmLoggedIn() || (this.authService.isECMProvider() && this.basicAlfrescoAuthService.isKerberosEnabled());
}
}

View File

@ -1,50 +0,0 @@
<div class="app-main-content">
<h1>CardView Component</h1>
<adf-card-view
[properties]="properties"
[editable]="isEditable"
[displayClearAction]="showClearDateAction"
[displayNoneOption]="showNoneOption"
[displayLabelForChips]="showLabelForChips">
</adf-card-view>
<div class="app-console">
<h3>Changes log:</h3>
<p *ngFor="let log of logs">{{ log }}</p>
</div>
</div>
<p class="app-toggle">
<mat-slide-toggle
id="app-toggle-editable"
[color]="'primary'"
(change)="toggleEditable()"
[checked]="isEditable">
Editable
</mat-slide-toggle><br>
<mat-slide-toggle
id="app-toggle-clear-date"
[color]="'primary'"
(change)="toggleClearDate()"
[checked]="showClearDateAction">
Show clear date icon
</mat-slide-toggle><br>
<mat-slide-toggle
id="app-toggle-none-option"
[color]="'primary'"
(change)="toggleNoneOption()"
[checked]="showNoneOption">
Show none option
</mat-slide-toggle><br>
<mat-slide-toggle
id="app-toggle-label-multivalued-chip"
[color]="'primary'"
(change)="toggleLabelForChips()"
[checked]="showLabelForChips">
Show label for chips property
</mat-slide-toggle>
</p>
<button mat-raised-button id="adf-reset-card-log" (click)="reset()">Reset Log</button>

View File

@ -1,28 +0,0 @@
.app-main-content {
padding: 0 15px;
}
adf-card-view {
width: 30%;
display: inline-block;
}
.app-console {
width: 60%;
display: inline-block;
vertical-align: top;
margin-left: 10px;
height: 500px;
overflow: scroll;
padding-bottom: 30px;
h3 {
margin-top: 0;
}
p {
display: block;
font-family: monospace;
margin: 0;
}
}

View File

@ -1,289 +0,0 @@
/*!
* @license
* Copyright © 2005-2024 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Component, OnInit, OnDestroy } from '@angular/core';
import {
CardViewTextItemModel,
CardViewDateItemModel,
CardViewDatetimeItemModel,
CardViewBoolItemModel,
CardViewIntItemModel,
CardViewFloatItemModel,
CardViewKeyValuePairsItemModel,
CardViewSelectItemModel,
CardViewUpdateService,
CardViewMapItemModel,
UpdateNotification,
DecimalNumberPipe,
CardViewArrayItemModel,
CardViewComponent
} from '@alfresco/adf-core';
import { of, Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { CommonModule } from '@angular/common';
import { MatSlideToggleModule } from '@angular/material/slide-toggle';
import { MatButtonModule } from '@angular/material/button';
@Component({
standalone: true,
imports: [CommonModule, MatSlideToggleModule, MatButtonModule, CardViewComponent],
templateUrl: './card-view.component.html',
styleUrls: ['./card-view.component.scss']
})
export class AppCardViewComponent implements OnInit, OnDestroy {
isEditable = true;
properties: any;
logs: string[];
showClearDateAction = false;
showNoneOption = false;
showLabelForChips = false;
private onDestroy$ = new Subject<boolean>();
constructor(private cardViewUpdateService: CardViewUpdateService, private decimalNumberPipe: DecimalNumberPipe) {
this.logs = [];
this.createCard();
}
respondToCardClick() {
this.logs.push(`clickable field`);
}
ngOnInit() {
this.cardViewUpdateService.itemUpdated$.pipe(takeUntil(this.onDestroy$)).subscribe(this.onItemChange.bind(this));
}
ngOnDestroy() {
this.onDestroy$.next(true);
this.onDestroy$.complete();
}
createCard() {
this.properties = [
new CardViewTextItemModel({
label: 'CardView Text Item',
value: 'Spock',
key: 'name',
default: 'default bar',
editable: this.isEditable
}),
new CardViewTextItemModel({
label: 'CardView Text Item - Protected value',
value: 'Spock',
key: 'name',
default: 'default bar',
multiline: false,
icon: 'icon',
editable: false
}),
new CardViewTextItemModel({
label: 'CardView Text Item - Multiline',
value: 'Spock',
key: 'name',
default: 'default bar',
multiline: true,
icon: 'icon',
editable: this.isEditable
}),
new CardViewTextItemModel({
label: 'CardView Text Item - Default Value',
value: '',
key: 'name',
default: 'default bar',
multiline: false,
icon: 'icon',
editable: this.isEditable
}),
new CardViewTextItemModel({
label: 'CardView Text Item - Multivalue (chips)',
value: [1, 2, 3],
key: 'name',
default: 'default bar',
multiline: true,
multivalued: true,
icon: 'icon',
editable: this.isEditable
}),
new CardViewDateItemModel({
label: 'CardView Date Item',
value: new Date('1983-11-24T00:00:00Z'),
key: 'date',
default: new Date('1983-11-24T00:00:00Z'),
format: 'shortDate',
editable: this.isEditable
}),
new CardViewDateItemModel({
label: 'CardView Date Item - Multivalue (chips)',
value: [new Date('1983-11-24T00:00:00Z')],
key: 'date',
default: new Date('1983-11-24T00:00:00Z'),
format: 'shortDate',
editable: this.isEditable,
multivalued: true
}),
new CardViewDatetimeItemModel({
label: 'CardView Datetime Item',
value: new Date(1983, 11, 24, 10, 0, 0),
key: 'datetime',
default: new Date(1983, 11, 24, 10, 0, 0),
format: 'short',
editable: this.isEditable
}),
new CardViewDatetimeItemModel({
label: 'CardView Datetime Item - Multivalue (chips)',
value: [new Date(1983, 11, 24, 10, 0, 0)],
key: 'datetime',
default: new Date(1983, 11, 24, 10, 0, 0),
format: 'short',
editable: this.isEditable,
multivalued: true
}),
new CardViewBoolItemModel({
label: 'CardView Boolean Item',
value: true,
key: 'boolean',
default: false,
editable: this.isEditable
}),
new CardViewBoolItemModel({
label: 'Agree to all terms and conditions',
value: true,
key: 'boolean',
default: false,
editable: this.isEditable
}),
new CardViewIntItemModel({
label: 'CardView Int Item',
value: 213,
key: 'int',
default: 1,
editable: this.isEditable
}),
new CardViewFloatItemModel({
label: 'CardView Float Item',
value: 9.9,
key: 'float',
default: 0.0,
editable: this.isEditable,
pipes: [{ pipe: this.decimalNumberPipe }]
}),
new CardViewFloatItemModel({
label: 'CardView Float Item - Multivalue (chips)',
value: [9.9],
key: 'float',
default: 0.0,
editable: this.isEditable,
multivalued: true,
pipes: [{ pipe: this.decimalNumberPipe }]
}),
new CardViewKeyValuePairsItemModel({
label: 'CardView Key-Value Pairs Item',
value: [
{ name: 'hey', value: 'you' },
{ name: 'hey', value: 'you' }
],
key: 'key-value-pairs',
editable: this.isEditable
}),
new CardViewKeyValuePairsItemModel({
label: 'CardView Key-Value Pairs Item',
value: [
{ name: 'hey', value: 'you' },
{ name: 'hey', value: 'you' }
],
key: 'key-value-pairs',
editable: false
}),
new CardViewSelectItemModel({
label: 'CardView Select Item',
value: 'one',
options$: of([
{ key: 'one', label: 'One' },
{ key: 'two', label: 'Two' }
]),
key: 'select',
editable: this.isEditable
}),
new CardViewMapItemModel({
label: 'My map',
value: new Map([['999', 'My Value']]),
key: 'map',
default: 'default map value'
}),
new CardViewTextItemModel({
label: 'This is clickable ',
value: 'click here',
key: 'click',
default: 'click here',
editable: this.isEditable,
clickable: true,
icon: 'close',
clickCallBack: () => {
this.respondToCardClick();
}
}),
new CardViewArrayItemModel({
label: 'CardView Array of items',
value: of([
{ icon: 'directions_bike', value: 'Zlatan' },
{ icon: 'directions_bike', value: 'Lionel Messi' },
// eslint-disable-next-line @typescript-eslint/naming-convention
{ value: 'Mohamed', directions_bike: 'save' },
{ value: 'Ronaldo' }
]),
key: 'array',
icon: 'edit',
default: 'Empty',
noOfItemsToDisplay: 2,
editable: this.isEditable
})
];
}
onItemChange(notification: UpdateNotification) {
let value = notification.changed[notification.target.key];
if (notification.target.type === 'keyvaluepairs') {
value = JSON.stringify(value);
}
this.logs.push(`[${notification.target.label}] - ${value}`);
}
toggleEditable() {
this.isEditable = !this.isEditable;
this.createCard();
}
toggleClearDate() {
this.showClearDateAction = !this.showClearDateAction;
}
toggleNoneOption() {
this.showNoneOption = !this.showNoneOption;
}
toggleLabelForChips() {
this.showLabelForChips = !this.showLabelForChips;
}
reset() {
this.isEditable = true;
this.createCard();
this.logs = [];
}
}

View File

@ -1 +0,0 @@
<adf-cloud-app-list (appClick)="onAppClick($event)"></adf-cloud-app-list>

View File

@ -1,35 +0,0 @@
/*!
* @license
* Copyright © 2005-2024 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Component } from '@angular/core';
import { Router } from '@angular/router';
import { CloudLayoutService } from './services/cloud-layout.service';
import { AppListCloudModule } from '@alfresco/adf-process-services-cloud';
@Component({
standalone: true,
imports: [AppListCloudModule],
templateUrl: './apps-cloud-demo.component.html'
})
export class AppsCloudDemoComponent {
constructor(private router: Router, private cloudLayoutService: CloudLayoutService) {}
onAppClick(app) {
this.cloudLayoutService.setCurrentTaskFilterParam({ key: 'my-tasks' });
this.router.navigate([`/cloud/${app.name}`]);
}
}

View File

@ -1,33 +0,0 @@
<mat-accordion>
<mat-expansion-panel [expanded]="expandTaskFilter" (opened)="onTaskFilterOpen()" (closed)="onTaskFilterClose()" data-automation-id='Task Filters'>
<mat-expansion-panel-header>
<mat-panel-title>
Task Filters
</mat-panel-title>
</mat-expansion-panel-header>
<adf-cloud-task-filters
*ngIf="expandTaskFilter"
[appName]="appName"
[showIcons]="true"
[filterParam]="currentTaskFilter$ | async"
(filterClicked)="onTaskFilterSelected($event)"
(filterSelected)="onTaskFilterSelected($event)">
</adf-cloud-task-filters>
</mat-expansion-panel>
<mat-expansion-panel [expanded]="expandProcessFilter" (opened)="onProcessFilterOpen()" (closed)="onProcessFilterClose()" data-automation-id='Process Filters'>
<mat-expansion-panel-header>
<mat-panel-title>
Process Filters
</mat-panel-title>
</mat-expansion-panel-header>
<adf-cloud-process-filters
*ngIf="expandProcessFilter"
[appName]="appName"
[showIcons]="true"
[filterParam]="currentProcessFilter$ | async"
(filterClicked)="onProcessFilterSelected($event)"
(filterSelected)="onProcessFilterSelected($event)">
</adf-cloud-process-filters>
</mat-expansion-panel>
</mat-accordion>

View File

@ -1,4 +0,0 @@
app-cloud-task-filters .app-filters__entry,
app-cloud-process-filters .app-filters__entry {
padding-left: 0;
}

View File

@ -1,106 +0,0 @@
/*!
* @license
* Copyright © 2005-2024 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Component, ViewEncapsulation, Input, OnInit } from '@angular/core';
import { Observable } from 'rxjs';
import { CloudLayoutService } from './services/cloud-layout.service';
import { Router, ActivatedRoute } from '@angular/router';
import { CloudProcessFiltersService } from './services/cloud-process-filters.service';
import { ProcessFilterCloudModel, ProcessFiltersCloudModule, TaskFiltersCloudModule } from '@alfresco/adf-process-services-cloud';
import { CommonModule } from '@angular/common';
import { MatExpansionModule } from '@angular/material/expansion';
@Component({
selector: 'app-cloud-filters-demo',
standalone: true,
imports: [CommonModule, MatExpansionModule, TaskFiltersCloudModule, ProcessFiltersCloudModule],
templateUrl: './cloud-filters-demo.component.html',
styleUrls: ['./cloud-filters-demo.component.scss'],
encapsulation: ViewEncapsulation.None
})
export class CloudFiltersDemoComponent implements OnInit {
@Input()
appName: string;
currentTaskFilter$: Observable<any>;
currentProcessFilter$: Observable<any>;
toggleTaskFilter = true;
toggleProcessFilter = true;
expandTaskFilter = true;
expandProcessFilter = false;
constructor(
private cloudLayoutService: CloudLayoutService,
private router: Router,
private route: ActivatedRoute,
private cloudProcessFiltersService: CloudProcessFiltersService
) {}
ngOnInit() {
this.currentTaskFilter$ = this.cloudLayoutService.taskFilter$;
this.currentProcessFilter$ = this.cloudLayoutService.processFilter$;
let root = '';
if (this.route.snapshot?.firstChild) {
root = this.route.snapshot.firstChild.url[0].path;
if (root === 'tasks') {
this.expandTaskFilter = true;
this.expandProcessFilter = false;
} else if (root === 'processes') {
this.expandProcessFilter = true;
this.expandTaskFilter = false;
}
}
}
onTaskFilterSelected(filter) {
if (filter) {
this.router.navigate([`/cloud/${this.appName}/tasks/`], { queryParams: filter });
}
}
onProcessFilterSelected(filter: ProcessFilterCloudModel) {
if (filter) {
const { appName } = this;
const { id } = filter;
const queryParams = this.cloudProcessFiltersService.writeQueryParams(filter, appName, id);
this.router.navigate([`/cloud/${appName}/processes/`], { queryParams });
}
}
onTaskFilterOpen(): boolean {
this.expandTaskFilter = true;
this.expandProcessFilter = false;
return this.toggleTaskFilter;
}
onTaskFilterClose(): boolean {
return !this.toggleTaskFilter;
}
onProcessFilterOpen(): boolean {
this.expandProcessFilter = true;
this.expandTaskFilter = false;
return this.toggleProcessFilter;
}
onProcessFilterClose(): boolean {
return !this.toggleProcessFilter;
}
}

View File

@ -1,42 +0,0 @@
<mat-tab-group class="app-cloud-layout-tab-body">
<mat-tab label="App">
<adf-sidenav-layout [sidenavMin]="70" [sidenavMax]="270" [stepOver]="780">
<adf-sidenav-layout-header>
<ng-template>
<div class="app-cloud-layout-toolbar">
<span>{{appName}}</span>
<span *ngIf="filterName">{{ ' > ' + (filterName | translate)}}</span>
</div>
</ng-template>
</adf-sidenav-layout-header>
<adf-sidenav-layout-navigation>
<ng-template>
<adf-sidebar-action-menu [expanded]="true" [width]="205" title="CREATE">
<mat-icon adf-sidebar-menu-title-icon>arrow_drop_down</mat-icon>
<div adf-sidebar-menu-options>
<button mat-menu-item data-automation-id="btn-start-task" (click)="onStartTask()">
<mat-icon>assessment</mat-icon>
<span>New Task</span>
</button>
</div>
<div adf-sidebar-menu-options>
<button mat-menu-item data-automation-id="btn-start-process" (click)="onStartProcess()">
<mat-icon>assessment</mat-icon>
<span>New Process</span>
</button>
</div>
</adf-sidebar-action-menu>
<app-cloud-filters-demo [appName]="appName"></app-cloud-filters-demo>
</ng-template>
</adf-sidenav-layout-navigation>
<adf-sidenav-layout-content>
<ng-template>
<router-outlet></router-outlet>
</ng-template>
</adf-sidenav-layout-content>
</adf-sidenav-layout>
</mat-tab>
<mat-tab label="Settings">
<app-cloud-settings></app-cloud-settings>
</mat-tab>
</mat-tab-group>

View File

@ -1,28 +0,0 @@
@import 'styles/mat-selectors';
.app-cloud-layout-overflow {
overflow: auto;
}
app-cloud-layout {
.app-cloud-layout-tab-body {
height: 100%;
min-height: 100%;
min-width: 100%;
width: 100%;
adf-sidenav-layout {
flex-direction: row;
box-sizing: border-box;
display: flex;
}
& > div {
height: 100%;
}
}
.app-cloud-layout-toolbar {
display: flex;
}
}

View File

@ -1,96 +0,0 @@
/*!
* @license
* Copyright © 2005-2024 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Component, OnInit, ViewEncapsulation } from '@angular/core';
import { Router, ActivatedRoute, RouterOutlet } from '@angular/router';
import { CloudLayoutService } from './services/cloud-layout.service';
import { CommonModule } from '@angular/common';
import { MatTabsModule } from '@angular/material/tabs';
import {
SidebarActionMenuComponent,
SidenavLayoutComponent,
SidenavLayoutContentDirective,
SidenavLayoutHeaderDirective,
SidenavLayoutNavigationDirective
} from '@alfresco/adf-core';
import { TranslateModule } from '@ngx-translate/core';
import { MatIconModule } from '@angular/material/icon';
import { MatMenuModule } from '@angular/material/menu';
import { CloudFiltersDemoComponent } from './cloud-filters-demo.component';
import { CloudSettingsComponent } from './shared/cloud-settings.component';
@Component({
selector: 'app-cloud-layout',
standalone: true,
imports: [
CommonModule,
MatTabsModule,
SidenavLayoutComponent,
TranslateModule,
SidenavLayoutHeaderDirective,
SidenavLayoutNavigationDirective,
SidebarActionMenuComponent,
MatIconModule,
MatMenuModule,
SidenavLayoutContentDirective,
RouterOutlet,
CloudFiltersDemoComponent,
CloudSettingsComponent
],
templateUrl: './cloud-layout.component.html',
styleUrls: ['./cloud-layout.component.scss'],
encapsulation: ViewEncapsulation.None
})
export class CloudLayoutComponent implements OnInit {
appName: string;
filterName: string;
constructor(private router: Router, private route: ActivatedRoute, private cloudLayoutService: CloudLayoutService) {}
ngOnInit() {
let root: string = '';
this.route.params.subscribe((params) => {
this.appName = params.appName;
});
if (this.route.snapshot?.firstChild) {
root = this.route.snapshot.firstChild.url[0].path;
}
this.route.queryParams.subscribe((params) => {
if (root === 'tasks' && params.id) {
this.cloudLayoutService.setCurrentTaskFilterParam({ id: params.id });
}
if (root === 'processes' && params.id) {
this.cloudLayoutService.setCurrentProcessFilterParam({ id: params.id });
}
if (params.filterName) {
this.filterName = params.filterName;
}
});
}
onStartTask() {
this.router.navigate([`/cloud/${this.appName}/start-task/`]);
}
onStartProcess() {
this.router.navigate([`/cloud/${this.appName}/start-process/`]);
}
}

View File

@ -1,3 +0,0 @@
.activiti-form-viewer {
margin: 10px;
}

View File

@ -1,2 +0,0 @@
<adf-alfresco-viewer [nodeId]="nodeId">
</adf-alfresco-viewer>

View File

@ -1,38 +0,0 @@
/*!
* @license
* Copyright © 2005-2024 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, Params } from '@angular/router';
import { AlfrescoViewerComponent } from '@alfresco/adf-content-services';
@Component({
selector: 'app-cloud-viewer',
standalone: true,
imports: [AlfrescoViewerComponent],
templateUrl: './cloud-viewer.component.html'
})
export class CloudViewerComponent implements OnInit {
nodeId: string;
constructor(private route: ActivatedRoute) {}
ngOnInit() {
this.route.params.subscribe((params: Params) => {
this.nodeId = params['nodeId'];
});
}
}

View File

@ -1,76 +0,0 @@
/*!
* @license
* Copyright © 2005-2024 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Component, OnInit } from '@angular/core';
import { ErrorWidgetComponent, FormService, WidgetComponent } from '@alfresco/adf-core';
import { CommonModule } from '@angular/common';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input';
import { TranslateModule } from '@ngx-translate/core';
import { FormsModule } from '@angular/forms';
/* eslint-disable @angular-eslint/component-selector */
@Component({
selector: 'custom-editor-widget',
standalone: true,
template: ` <div style="color: green">ADF version of custom form widget</div> `
})
export class CustomEditorComponent extends WidgetComponent {
constructor() {
super();
}
}
@Component({
selector: 'app-sample-widget',
standalone: true,
imports: [CommonModule, MatFormFieldModule, MatInputModule, TranslateModule, FormsModule, ErrorWidgetComponent],
template: `
<div style="color: red">
<p *ngIf="field.readOnly || readOnly">
<label class="adf-label" [attr.for]="field.id">{{ field.name | translate }}<span *ngIf="isRequired()">*</span></label>
<span>{{ field.value }}</span>
</p>
<mat-form-field *ngIf="!(field.readOnly || readOnly)">
<label class="adf-label" [attr.for]="field.id">{{ field.name | translate }}<span *ngIf="isRequired()">*</span></label>
<input
matInput
class="adf-input"
type="text"
[id]="field.id"
[required]="isRequired()"
[value]="field.value"
[(ngModel)]="field.value"
(ngModelChange)="onFieldChanged(field)"
/>
<mat-hint>{{ field.placeholder }}</mat-hint>
</mat-form-field>
<error-widget [error]="field.validationSummary"></error-widget>
<error-widget *ngIf="isInvalidFieldRequired()" required="{{ 'FORM.FIELD.REQUIRED' | translate }}"></error-widget>
</div>
`
})
export class CustomWidgetComponent extends WidgetComponent implements OnInit {
constructor(public formService: FormService) {
super(formService);
}
ngOnInit() {
this.field.value = typeof this.field.value === 'object' ? JSON.stringify(this.field.value) : this.field.value;
}
}

View File

@ -1,45 +0,0 @@
/*!
* @license
* Copyright © 2005-2024 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Component } from '@angular/core';
import { FormRenderingService } from '@alfresco/adf-core';
import { CloudFormRenderingService } from '@alfresco/adf-process-services-cloud';
import { CustomEditorComponent, CustomWidgetComponent } from './custom-form-components/custom-editor.component';
import { RouterOutlet } from '@angular/router';
@Component({
template: `<router-outlet></router-outlet>`,
standalone: true,
imports: [RouterOutlet],
providers: [{ provide: FormRenderingService, useClass: CloudFormRenderingService }]
})
export class ProcessCloudLayoutComponent {
constructor(private formRenderingService: FormRenderingService) {
this.formRenderingService.register({
'custom-editor': () => CustomEditorComponent,
'demo-widget': () => CustomEditorComponent,
'custom-string': () => CustomWidgetComponent,
'custom-datetime': () => CustomWidgetComponent,
'custom-file': () => CustomWidgetComponent,
'custom-number': () => CustomWidgetComponent,
'custom-something': () => CustomWidgetComponent,
'custom-boolean': () => CustomWidgetComponent,
'custom-date': () => CustomWidgetComponent,
custom: () => CustomWidgetComponent
});
}
}

View File

@ -1,21 +0,0 @@
<button data-automation-id="go-back" mat-icon-button (click)="onGoBack()">
<mat-icon>arrow_back</mat-icon> Go Back
</button>
<h4 data-automation-id="process-details-header">Simple page to show the process instance: {{ processInstanceId }} of the app: {{ appName }}</h4>
<div class="app-process-cloud-container">
<adf-cloud-task-list
class="app-cloud-layout-overflow"
[appName]="appName"
[processInstanceId]="processInstanceId"
(rowClick)="onRowClick($event)">
</adf-cloud-task-list>
<adf-cloud-process-header
class="app-process-cloud-header"
[appName]="appName"
[processInstanceId]="processInstanceId">
</adf-cloud-process-header>
</div>

View File

@ -1,17 +0,0 @@
app-process-details-cloud-demo {
.app {
&-process-cloud-container {
display: flex;
}
&-cloud-layout-overflow {
width: 67%;
flex: 1;
}
&-process-cloud-header {
margin-left: 10px;
width: 25%;
}
}
}

View File

@ -1,55 +0,0 @@
/*!
* @license
* Copyright © 2005-2024 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Component, ViewEncapsulation } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { MatIconModule } from '@angular/material/icon';
import { MatButtonModule } from '@angular/material/button';
import { ProcessHeaderCloudModule, TaskListCloudModule } from '@alfresco/adf-process-services-cloud';
@Component({
selector: 'app-process-details-cloud-demo',
standalone: true,
imports: [MatIconModule, MatButtonModule, TaskListCloudModule, ProcessHeaderCloudModule],
templateUrl: './process-details-cloud-demo.component.html',
styleUrls: ['./process-details-cloud-demo.component.scss'],
encapsulation: ViewEncapsulation.None
})
export class ProcessDetailsCloudDemoComponent {
processInstanceId: string;
appName: string;
constructor(private route: ActivatedRoute, private router: Router) {
this.route.params.subscribe((params) => {
this.processInstanceId = params.processInstanceId;
});
this.route.parent.params.subscribe((params) => {
this.appName = params.appName;
});
}
onGoBack() {
this.router.navigate([`/cloud/${this.appName}/`]);
}
onRowClick(taskId: string) {
if (taskId) {
this.router.navigate([`/cloud/${this.appName}/task-details/${taskId}`]);
}
}
}

View File

@ -1,71 +0,0 @@
<adf-cloud-edit-process-filter
[appName]="appName"
[id]="filterId"
[filterProperties]="filterProperties"
[sortProperties]="filterSortProperties"
[actions]="filterActions"
[processFilter]="editedFilter"
(filterChange)="onFilterChange($event)"
(action)="onProcessFilterAction($event)">
</adf-cloud-edit-process-filter>
<div
*ngIf="editedFilter"
class="app-processes-list-container">
<adf-cloud-process-list
#processCloud
class="app-cloud-layout-overflow"
[appName]="editedFilter.appName"
[appVersion]="editedFilter.appVersion"
[initiator]="editedFilter.initiator"
[processDefinitionId]="editedFilter.processDefinitionId"
[processDefinitionName]="editedFilter.processDefinitionName"
[processDefinitionKey]="editedFilter.processDefinitionKey"
[id]="editedFilter.processInstanceId"
[status]="editedFilter.status"
[name]="editedFilter.processName"
[businessKey]="editedFilter['businessKey']"
[lastModifiedFrom]="editedFilter.lastModifiedFrom"
[lastModifiedTo]="editedFilter.lastModifiedTo"
[sorting]="[{orderBy: editedFilter.sort, direction: editedFilter.order}]"
[selectionMode]="selectionMode"
[stickyHeader]="true"
[showActions]="actionMenu"
[showContextMenu]="contextMenu"
[multiselect]="multiselect"
(showRowActionsMenu)="onShowRowActionsMenu($event)"
(showRowContextMenu)="onShowRowContextMenu($event)"
(executeRowAction)="onExecuteRowAction($event)"
(rowClick)="onRowClick($event)"
(rowsSelected)="onRowsSelected($event)">
</adf-cloud-process-list>
<adf-pagination
[target]="processCloud"
(changePageSize)="onChangePageSize($event)"
(nextPage)="resetSelectedRows()"
(prevPage)="resetSelectedRows()">
</adf-pagination>
<div *ngIf="testingMode">
<div *ngIf="multiselect">
Selected Rows:
<ul>
<li *ngFor="let row of selectedRows">{{ row.id }}</li>
</ul>
</div>
<div *ngIf="actionMenu">
<span>Action Menu:</span>
<br>
<div *ngIf="selectedAction">
<span>Process Instance ID: {{ selectedAction.id }}</span><br>
<span>Action Type: {{ selectedAction.actionType }}</span>
</div>
</div>
<div *ngIf="contextMenu">
<span>Context Menu:</span>
<br>
<div *ngIf="selectedContextAction">
<span>Process Instance ID: {{ selectedContextAction.id }}</span><br>
<span>Action Type}: {{ selectedContextAction.actionType }}</span>
</div>
</div>
</div>
</div>

View File

@ -1,22 +0,0 @@
app-processes-cloud-demo {
height: 100%;
min-height: 100%;
min-width: 100%;
width: 100%;
.app-processes-list-container {
box-sizing: border-box;
place-content: stretch space-between;
align-items: stretch;
margin-top: 2px;
}
&, .app-processes-list-container {
flex-direction: column;
display: flex;
}
.app-processes-list-container, .app-cloud-layout-overflow {
flex: 1;
}
}

View File

@ -1,192 +0,0 @@
/*!
* @license
* Copyright © 2005-2024 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Component, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core';
import {
PROCESS_FILTER_ACTION_DELETE,
PROCESS_FILTER_ACTION_SAVE,
PROCESS_FILTER_ACTION_SAVE_AS,
ProcessFilterAction,
ProcessFilterCloudModel,
ProcessFiltersCloudModule,
ProcessListCloudModule
} from '@alfresco/adf-process-services-cloud';
import { ActivatedRoute, Router } from '@angular/router';
import { DataCellEvent, PaginationComponent, UserPreferencesService } from '@alfresco/adf-core';
import { CloudLayoutService, CloudServiceSettings } from './services/cloud-layout.service';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { Pagination } from '@alfresco/js-api';
import { CloudProcessFiltersService } from './services/cloud-process-filters.service';
import { CommonModule } from '@angular/common';
@Component({
selector: 'app-processes-cloud-demo',
standalone: true,
imports: [CommonModule, ProcessFiltersCloudModule, ProcessListCloudModule, PaginationComponent],
templateUrl: './processes-cloud-demo.component.html',
styleUrls: ['./processes-cloud-demo.component.scss'],
encapsulation: ViewEncapsulation.None
})
export class ProcessesCloudDemoComponent implements OnInit, OnDestroy {
appName: string = '';
isFilterLoaded = false;
filterId: string = '';
selectedRow: any;
multiselect: boolean;
selectionMode: string;
selectedRows: any[] = [];
testingMode: boolean;
actionMenu: boolean;
contextMenu: boolean;
actions: any[] = [];
selectedAction: { id: number; name: string; actionType: string };
selectedContextAction: { id: number; name: string; actionType: string };
filterProperties: string[];
filterSortProperties: string[];
filterActions: string[];
processDetailsRedirection: boolean;
editedFilter: ProcessFilterCloudModel;
private performAction$ = new Subject<any>();
private onDestroy$ = new Subject<boolean>();
constructor(
private route: ActivatedRoute,
private router: Router,
private cloudLayoutService: CloudLayoutService,
private cloudProcessFiltersService: CloudProcessFiltersService,
private userPreference: UserPreferencesService
) {}
ngOnInit() {
this.filterProperties = this.cloudProcessFiltersService.filterProperties;
this.filterSortProperties = this.cloudProcessFiltersService.sortProperties;
this.filterActions = this.cloudProcessFiltersService.actions;
this.route.queryParams.subscribe((params) => {
this.isFilterLoaded = true;
this.appName = params.appName;
this.filterId = params.id;
const model = this.cloudProcessFiltersService.readQueryParams(params);
this.loadFilter(model);
});
this.cloudLayoutService.settings$.pipe(takeUntil(this.onDestroy$)).subscribe((settings) => this.setCurrentSettings(settings));
this.performContextActions();
}
ngOnDestroy() {
this.onDestroy$.next(true);
this.onDestroy$.complete();
}
setCurrentSettings(settings: CloudServiceSettings) {
if (settings) {
this.multiselect = settings.multiselect;
this.testingMode = settings.testingMode;
this.selectionMode = settings.selectionMode;
this.processDetailsRedirection = settings.processDetailsRedirection;
this.actionMenu = settings.actionMenu;
this.contextMenu = settings.contextMenu;
this.actions = settings.actions;
}
}
onChangePageSize(event: Pagination) {
this.userPreference.paginationSize = event.maxItems;
}
resetSelectedRows() {
this.selectedRows = [];
}
onRowClick(processInstanceId: string) {
if (!this.multiselect && this.selectionMode !== 'multiple' && this.processDetailsRedirection) {
this.router.navigate([`/cloud/${this.appName}/process-details/${processInstanceId}`]);
}
}
onFilterChange(filter: ProcessFilterCloudModel) {
const queryParams = {
...this.cloudProcessFiltersService.writeQueryParams(filter, this.appName, filter.id),
filterId: filter.id
};
this.router.navigate([`/cloud/${this.appName}/processes/`], { queryParams });
}
onProcessFilterAction(filterAction: ProcessFilterAction) {
if (filterAction.actionType === PROCESS_FILTER_ACTION_DELETE) {
this.cloudLayoutService.setCurrentProcessFilterParam({ index: 0 });
} else {
this.cloudLayoutService.setCurrentProcessFilterParam({ id: filterAction.filter.id });
}
if ([PROCESS_FILTER_ACTION_SAVE, PROCESS_FILTER_ACTION_SAVE_AS].includes(filterAction.actionType)) {
this.onFilterChange(filterAction.filter);
}
}
onRowsSelected(nodes) {
this.resetSelectedRows();
this.selectedRows = nodes.map((node) => node.obj);
}
onShowRowActionsMenu(event: DataCellEvent) {
event.value.actions = this.actions;
}
onShowRowContextMenu(event: DataCellEvent) {
event.value.actions = this.actions.map((action) => ({
data: event.value.row['obj'],
model: action,
subject: this.performAction$
}));
}
onExecuteRowAction(row: any) {
const value = row.value.row['obj'].entry;
const action = row.value.action;
this.selectedAction = { id: value.id, name: value.name, actionType: action.title };
}
performContextActions() {
this.performAction$.pipe(takeUntil(this.onDestroy$)).subscribe((action: any) => {
if (action) {
this.onExecuteContextAction(action);
}
});
}
onExecuteContextAction(contextAction: any) {
const value = contextAction.data.entry;
const action = contextAction.model;
this.selectedContextAction = { id: value.id, name: value.name, actionType: action.title };
}
private loadFilter(model: ProcessFilterCloudModel) {
if (model?.appName && model.id) {
this.editedFilter = model;
}
}
}

View File

@ -1,80 +0,0 @@
/*!
* @license
* Copyright © 2005-2024 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs';
export interface CloudServiceSettings {
multiselect: boolean;
actionMenu: boolean;
contextMenu: boolean;
testingMode: boolean;
taskDetailsRedirection: boolean;
processDetailsRedirection: boolean;
selectionMode: string;
actions: any[];
}
export interface FilterSettings {
id?: string;
index?: number;
key?: string;
}
export class ActionMenuModel {
constructor(
public key: string,
public icon: string,
public title: string,
public visible?: boolean,
public disabled?: boolean
) { }
}
@Injectable({
providedIn: 'root'
})
export class CloudLayoutService {
private settings: CloudServiceSettings = {
multiselect: false,
actionMenu: false,
contextMenu: false,
testingMode: false,
taskDetailsRedirection: true,
processDetailsRedirection: true,
selectionMode: 'single',
actions: []
};
taskFilter$ = new BehaviorSubject<FilterSettings>({index: 0});
processFilter$ = new BehaviorSubject<FilterSettings>({index: 0});
settings$ = new BehaviorSubject<CloudServiceSettings>(this.settings);
setCurrentTaskFilterParam(param: FilterSettings) {
this.taskFilter$.next(param);
}
setCurrentProcessFilterParam(param: FilterSettings) {
this.processFilter$.next(param);
}
setCurrentSettings(param: CloudServiceSettings) {
this.settings$.next(param);
}
}

View File

@ -1,55 +0,0 @@
/*!
* @license
* Copyright © 2005-2024 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Injectable } from '@angular/core';
import { ProcessFilterCloudModel, ProcessFilterCloudService } from '@alfresco/adf-process-services-cloud';
import { AppConfigService } from '@alfresco/adf-core';
@Injectable({ providedIn: 'root' })
export class CloudProcessFiltersService {
constructor(private appConfigService: AppConfigService, private processFilterCloudService: ProcessFilterCloudService) {
}
get filterProperties(): string[] {
return this.appConfigService.get(
'adf-edit-process-filter.filterProperties',
['status', 'sort', 'order', 'processName']
);
}
get sortProperties(): string[] {
return this.appConfigService.get(
'adf-edit-process-filter.sortProperties',
['id', 'name', 'status', 'startDate']
);
}
get actions(): string[] {
return this.appConfigService.get(
'adf-edit-process-filter.actions',
['save', 'saveAs', 'delete']
);
}
readQueryParams(obj: any): ProcessFilterCloudModel {
return this.processFilterCloudService.readQueryParams(obj);
}
writeQueryParams(value: any, appName?: string, id?: string): any {
return this.processFilterCloudService.writeQueryParams(value, this.filterProperties, appName, id);
}
}

View File

@ -1,53 +0,0 @@
<mat-slide-toggle [checked]="multiselect" (change)="toggleMultiselect()" data-automation-id="multiSelection">
Multiselection
</mat-slide-toggle>
<mat-slide-toggle [checked]="actionMenu" (change)="toggleActionMenu()" data-automation-id="actionmenu">
Action Menu
</mat-slide-toggle>
<mat-slide-toggle [checked]="contextMenu" (change)="toggleContextMenu()" data-automation-id="contextmenu">
Context Menu
</mat-slide-toggle>
<mat-slide-toggle [checked]="testingMode" (change)="toggleTestingMode()" data-automation-id="testingMode">
Testing Mode
</mat-slide-toggle>
<mat-slide-toggle [checked]="taskDetailsRedirection" (change)="toggleTaskDetailsRedirection()" data-automation-id="taskDetailsRedirection">
Display task details on task click
</mat-slide-toggle>
<mat-slide-toggle [checked]="processDetailsRedirection" (change)="toggleProcessDetailsRedirection()" data-automation-id="processDetailsRedirection">
Display process details on process click
</mat-slide-toggle>
<mat-form-field data-automation-id="selectionMode" class="adf-cloud-settings-selection-mode">
<mat-label>Selection Mode</mat-label>
<mat-select [(ngModel)]="selectionMode" (selectionChange)="onSelectionModeChange()">
<mat-option *ngFor="let option of selectionModeOptions" [value]="option.value">
{{ option.title }}
</mat-option>
</mat-select>
</mat-form-field>
<div class="app-cloud-actions" *ngIf="actionMenu || contextMenu">
<h2>Add Action</h2>
<form class="app-cloud-settings-form" [formGroup]="actionMenuForm">
<mat-form-field class="app-cloud-settings-form-input">
<input matInput formControlName="key" placeholder="Key">
</mat-form-field>
<mat-form-field class="app-cloud-settings-form-input">
<input matInput formControlName="title" placeholder="Title">
</mat-form-field>
<mat-form-field class="app-cloud-settings-form-input">
<input matInput formControlName="icon" placeholder="Icon">
</mat-form-field>
<mat-checkbox formControlName="visible">Visible</mat-checkbox>
<mat-checkbox formControlName="disabled">Disable</mat-checkbox>
<button mat-raised-button (click)="addAction()">Add</button>
</form>
<div *ngIf="actions.length > 0">
<mat-chip-listbox>
<mat-chip-option *ngFor="let action of actions" [removable]="true">
{{action.title}}
<mat-icon matChipRemove (click)="removeAction(action)">cancel</mat-icon>
</mat-chip-option>
</mat-chip-listbox>
</div>
</div>

View File

@ -1,25 +0,0 @@
app-cloud-settings {
padding: 20px 30px;
flex-direction: column;
display: flex;
flex: 1;
.app-cloud-actions {
background-color: white;
}
.adf-cloud-settings-selection-mode {
max-width: 200px;
}
.app-cloud-settings-form {
display: flex;
place-content: center space-around;
align-items: center;
.app-cloud-settings-form-input {
flex: 1 1 100%;
max-width: 23%;
}
}
}

View File

@ -1,162 +0,0 @@
/*!
* @license
* Copyright © 2005-2024 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Component, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core';
import { ActionMenuModel, CloudLayoutService } from '../services/cloud-layout.service';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { FormsModule, ReactiveFormsModule, UntypedFormControl, UntypedFormGroup } from '@angular/forms';
import { CommonModule } from '@angular/common';
import { MatSlideToggleModule } from '@angular/material/slide-toggle';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatSelectModule } from '@angular/material/select';
import { MatInputModule } from '@angular/material/input';
import { MatCheckboxModule } from '@angular/material/checkbox';
import { MatButtonModule } from '@angular/material/button';
import { MatChipsModule } from '@angular/material/chips';
import { MatIconModule } from '@angular/material/icon';
@Component({
selector: 'app-cloud-settings',
standalone: true,
imports: [
CommonModule,
MatSlideToggleModule,
MatFormFieldModule,
MatSelectModule,
FormsModule,
MatInputModule,
ReactiveFormsModule,
MatCheckboxModule,
MatButtonModule,
MatChipsModule,
MatIconModule
],
templateUrl: './cloud-settings.component.html',
styleUrls: ['./cloud-settings.component.scss'],
encapsulation: ViewEncapsulation.None
})
export class CloudSettingsComponent implements OnInit, OnDestroy {
private onDestroy$ = new Subject<boolean>();
multiselect: boolean;
actionMenu: boolean;
contextMenu: boolean;
actions: ActionMenuModel[] = [];
selectionMode: string;
testingMode: boolean;
taskDetailsRedirection: boolean;
processDetailsRedirection: boolean;
selectionModeOptions = [
{ value: '', title: 'None' },
{ value: 'single', title: 'Single' },
{ value: 'multiple', title: 'Multiple' }
];
actionMenuForm = new UntypedFormGroup({
key: new UntypedFormControl(''),
title: new UntypedFormControl(''),
icon: new UntypedFormControl(''),
visible: new UntypedFormControl(true),
disabled: new UntypedFormControl(false)
});
constructor(private cloudLayoutService: CloudLayoutService) {}
ngOnInit() {
this.cloudLayoutService.settings$.pipe(takeUntil(this.onDestroy$)).subscribe((settings) => this.setCurrentSettings(settings));
}
ngOnDestroy() {
this.onDestroy$.next(true);
this.onDestroy$.complete();
}
setCurrentSettings(settings) {
if (settings) {
this.multiselect = settings.multiselect;
this.testingMode = settings.testingMode;
this.selectionMode = settings.selectionMode;
this.taskDetailsRedirection = settings.taskDetailsRedirection;
this.processDetailsRedirection = settings.processDetailsRedirection;
this.actionMenu = settings.actionMenu;
this.contextMenu = settings.contextMenu;
this.actions = settings.actions;
}
}
toggleMultiselect() {
this.multiselect = !this.multiselect;
this.setSetting();
}
toggleTestingMode() {
this.testingMode = !this.testingMode;
this.setSetting();
}
toggleTaskDetailsRedirection() {
this.taskDetailsRedirection = !this.taskDetailsRedirection;
this.setSetting();
}
toggleProcessDetailsRedirection() {
this.processDetailsRedirection = !this.processDetailsRedirection;
this.setSetting();
}
onSelectionModeChange() {
this.setSetting();
}
toggleActionMenu() {
this.actionMenu = !this.actionMenu;
this.setSetting();
}
toggleContextMenu() {
this.contextMenu = !this.contextMenu;
this.setSetting();
}
addAction() {
this.actions.push(this.actionMenuForm.value);
this.actionMenuForm.get('key').reset();
this.actionMenuForm.get('title').reset();
this.actionMenuForm.get('icon').reset();
this.setSetting();
}
removeAction(removedAction: ActionMenuModel) {
this.actions = this.actions.filter((action: ActionMenuModel) => action.key !== removedAction.key);
this.setSetting();
}
setSetting() {
this.cloudLayoutService.setCurrentSettings({
multiselect: this.multiselect,
actionMenu: this.actionMenu,
contextMenu: this.contextMenu,
actions: this.actions,
testingMode: this.testingMode,
selectionMode: this.selectionMode,
taskDetailsRedirection: this.taskDetailsRedirection,
processDetailsRedirection: this.processDetailsRedirection
});
}
}

View File

@ -1,10 +0,0 @@
<adf-cloud-start-process
[appName]="appName"
[name]="processName"
[values]="formValues"
[variables]="variables"
(error)="openSnackMessage($event)"
(success)="onStartProcessSuccess()"
(formContentClicked)="onFormContentClicked($event)"
(cancel)="onCancelStartProcess()">
</adf-cloud-start-process>

View File

@ -1,73 +0,0 @@
/*!
* @license
* Copyright © 2005-2024 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { NotificationService, AppConfigService, FormRenderingService } from '@alfresco/adf-core';
import { CloudLayoutService } from './services/cloud-layout.service';
import { PreviewService } from '../../services/preview.service';
import { CloudFormRenderingService, StartProcessCloudModule } from '@alfresco/adf-process-services-cloud';
@Component({
standalone: true,
imports: [StartProcessCloudModule],
templateUrl: './start-process-cloud-demo.component.html',
providers: [{ provide: FormRenderingService, useClass: CloudFormRenderingService }]
})
export class StartProcessCloudDemoComponent implements OnInit {
appName;
processName: string;
formValues: any;
variables: any;
constructor(
private appConfig: AppConfigService,
private cloudLayoutService: CloudLayoutService,
private route: ActivatedRoute,
private previewService: PreviewService,
private notificationService: NotificationService,
private router: Router
) {}
ngOnInit() {
this.route.parent.params.subscribe((params) => {
this.appName = params.appName;
});
this.processName = this.appConfig.get<string>('adf-cloud-start-process.name');
this.formValues = this.appConfig.get<string>('adf-cloud-start-process.values');
this.variables = this.appConfig.get<string>('adf-cloud-start-process.variables');
}
onStartProcessSuccess() {
this.cloudLayoutService.setCurrentProcessFilterParam({ key: 'running-processes' });
this.router.navigate([`/cloud/${this.appName}/processes`]);
}
onCancelStartProcess() {
this.cloudLayoutService.setCurrentProcessFilterParam({ key: 'all-processes' });
this.router.navigate([`/cloud/${this.appName}/processes`]);
}
openSnackMessage(event: any) {
this.notificationService.openSnackMessage(event.response.body.entry.message);
}
onFormContentClicked(resourceClicked: any) {
this.previewService.showResource(resourceClicked.nodeId);
}
}

View File

@ -1,6 +0,0 @@
<adf-cloud-start-task
[appName]="appName"
(error)="openSnackMessage($event)"
(success)="onStartTaskSuccess()"
(cancel)="onCancelStartTask()">
</adf-cloud-start-task>

View File

@ -1,58 +0,0 @@
/*!
* @license
* Copyright © 2005-2024 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { NotificationService } from '@alfresco/adf-core';
import { CloudLayoutService } from './services/cloud-layout.service';
import { StartTaskCloudModule } from '@alfresco/adf-process-services-cloud';
@Component({
standalone: true,
imports: [StartTaskCloudModule],
templateUrl: './start-task-cloud-demo.component.html'
})
export class StartTaskCloudDemoComponent implements OnInit {
appName;
constructor(
private cloudLayoutService: CloudLayoutService,
private route: ActivatedRoute,
private notificationService: NotificationService,
private router: Router
) {}
ngOnInit() {
this.route.parent.params.subscribe((params) => {
this.appName = params.appName;
});
}
onStartTaskSuccess() {
this.cloudLayoutService.setCurrentTaskFilterParam({ key: 'my-tasks' });
this.router.navigate([`/cloud/${this.appName}/tasks`]);
}
onCancelStartTask() {
this.cloudLayoutService.setCurrentTaskFilterParam({ key: 'my-tasks' });
this.router.navigate([`/cloud/${this.appName}/tasks`]);
}
openSnackMessage(event: any) {
this.notificationService.openSnackMessage(event.response.body.message);
}
}

View File

@ -1,20 +0,0 @@
<h4 data-automation-id="task-details-header">Simple page to show the taskId: {{ taskId }} of the app: {{ appName }}</h4>
<div class="app-task-details-cloud-column">
<div class="app-task-details-cloud-row">
<adf-cloud-task-form
[appName]="appName"
[taskId]="taskId"
(cancelClick)="goBack()"
(taskCompleted)="onTaskCompleted()"
(formContentClicked)="onFormContentClicked($event)"
(formSaved)="onFormSaved()"
(error)="onError($event)">
</adf-cloud-task-form>
<adf-cloud-task-header
#taskHeader
[appName]="appName"
[taskId]="taskId">
</adf-cloud-task-header>
</div>
</div>

View File

@ -1,48 +0,0 @@
app-task-details-cloud-demo {
.app {
&-task-detail-container {
display: flex;
}
&-task-title {
margin-left: 15px;
}
&-task-control {
width: 70%;
}
&-demop-card-container {
width: 30%;
font-family: inherit;
}
&-task-details-cloud-column {
margin: 0;
flex-direction: column;
.app-task-details-cloud-row {
flex-direction: row;
adf-cloud-task-form {
flex: 1 1 100%;
flex-direction: column;
display: flex;
max-width: 80%;
}
adf-cloud-task-header {
flex: 1;
}
}
}
&-task-details-cloud-column, &-task-details-cloud-row {
height: 100%;
min-height: 100%;
min-width: 100%;
width: 100%;
display: flex;
}
}
}

View File

@ -1,81 +0,0 @@
/*!
* @license
* Copyright © 2005-2024 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Component, ViewChild, ViewEncapsulation } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { NotificationService } from '@alfresco/adf-core';
import { TaskFormModule, TaskHeaderCloudComponent, TaskHeaderCloudModule } from '@alfresco/adf-process-services-cloud';
import { PreviewService } from '../../services/preview.service';
@Component({
selector: 'app-task-details-cloud-demo',
standalone: true,
imports: [TaskFormModule, TaskHeaderCloudModule],
templateUrl: './task-details-cloud-demo.component.html',
styleUrls: ['./task-details-cloud-demo.component.scss'],
encapsulation: ViewEncapsulation.None
})
export class TaskDetailsCloudDemoComponent {
@ViewChild('taskHeader', { static: true })
taskHeader: TaskHeaderCloudComponent;
taskId: string;
appName: string;
constructor(
private route: ActivatedRoute,
private router: Router,
private notificationService: NotificationService,
private previewService: PreviewService
) {
this.route.params.subscribe((params) => {
this.taskId = params.taskId;
});
this.route.parent.params.subscribe((params) => {
this.appName = params.appName;
});
}
isTaskValid(): boolean {
return this.appName !== undefined && this.taskId !== undefined;
}
goBack() {
this.router.navigate([`/cloud/${this.appName}/`]);
}
onTaskCompleted() {
this.goBack();
}
onFormContentClicked(resourceClicked: any) {
this.previewService.showResource(resourceClicked.nodeId);
}
onFormSaved() {
this.notificationService.openSnackMessage('Task has been saved successfully');
}
onError({ message: error }: Error) {
let errorMessage;
try {
errorMessage = JSON.parse(error).message || JSON.parse(error).entry?.message;
errorMessage = JSON.parse(errorMessage).message;
} catch {}
this.notificationService.showError(errorMessage || error);
}
}

View File

@ -1,71 +0,0 @@
<adf-cloud-edit-task-filter
[appName]="appName"
[id]="filterId"
[filterProperties]="taskFilterProperties.filterProperties"
[sortProperties]="taskFilterProperties.sortProperties"
[actions]="taskFilterProperties.actions"
(action)="onTaskFilterAction($event)"
(filterChange)="onFilterChange($event)">
</adf-cloud-edit-task-filter>
<div class="adf-cloud-task-list-container" *ngIf="editedFilter">
<adf-cloud-task-list
#taskCloud
class="app-cloud-layout-overflow"
[appName]="editedFilter.appName"
[processDefinitionId]="editedFilter.processDefinitionId"
[processInstanceId]="editedFilter.processInstanceId"
[name]="editedFilter.taskName"
[id]="editedFilter.taskId"
[parentTaskId]="editedFilter.parentTaskId"
[priority]="editedFilter.priority"
[owner]="editedFilter.owner"
[lastModifiedFrom]="editedFilter.lastModifiedFrom"
[lastModifiedTo]="editedFilter.lastModifiedTo"
[status]="editedFilter.status"
[assignee]="editedFilter.assignee"
[createdDate]="editedFilter.createdDate"
[dueDate]="editedFilter.dueDate"
[sorting]="sortArray"
[standalone]="editedFilter.standalone"
[multiselect]="multiselect"
[selectionMode]="selectionMode"
[stickyHeader]="true"
[showActions]="actionMenu"
[showContextMenu]="contextMenu"
(showRowActionsMenu)="onShowRowActionsMenu($event)"
(showRowContextMenu)="onShowRowContextMenu($event)"
(executeRowAction)="onExecuteRowAction($event)"
(rowClick)="onRowClick($event)"
(rowsSelected)="onRowsSelected($event)">
</adf-cloud-task-list>
<adf-pagination
[target]="taskCloud"
(changePageSize)="onChangePageSize($event)"
(nextPage)="resetSelectedRows()"
(prevPage)="resetSelectedRows()">
</adf-pagination>
<div *ngIf="testingMode">
<div *ngIf="multiselect">
Selected Rows:
<ul>
<li *ngFor="let row of selectedRows" [attr.data-automation-id]="row.id">{{ row.name }}</li>
</ul>
</div>
<div *ngIf="actionMenu">
<span>Action Menu:</span>
<br>
<div *ngIf="selectedAction">
<span [attr.data-automation-id]="selectedAction.id">Task ID: {{ selectedAction.id }}</span><br>
<span [attr.data-automation-id]="selectedAction.actionType">Action Type: {{ selectedAction.actionType }}</span>
</div>
</div>
<div *ngIf="contextMenu">
<span>Context Menu:</span>
<br>
<div *ngIf="selectedContextAction">
<span [attr.data-automation-id]="selectedContextAction.id">Task ID: {{ selectedContextAction.id }}</span><br>
<span [attr.data-automation-id]="selectedContextAction.actionType">Action Type: {{ selectedContextAction.actionType }}</span>
</div>
</div>
</div>
</div>

View File

@ -1,22 +0,0 @@
app-tasks-cloud-demo {
height: 100%;
min-height: 100%;
min-width: 100%;
width: 100%;
.adf-cloud-task-list-container {
box-sizing: border-box;
place-content: stretch space-between;
align-items: stretch;
margin-top: 2px;
}
&, .adf-cloud-task-list-container {
flex-direction: column;
display: flex;
}
.adf-cloud-task-list-container, .app-cloud-layout-overflow {
flex: 1;
}
}

View File

@ -1,187 +0,0 @@
/*!
* @license
* Copyright © 2005-2024 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Component, OnDestroy, OnInit, ViewChild, ViewEncapsulation } from '@angular/core';
import {
TaskFilterCloudModel,
TaskFiltersCloudModule,
TaskListCloudComponent,
TaskListCloudModule,
TaskListCloudSortingModel
} from '@alfresco/adf-process-services-cloud';
import { AppConfigService, DataCellEvent, PaginationComponent, UserPreferencesService } from '@alfresco/adf-core';
import { ActivatedRoute, Router } from '@angular/router';
import { CloudLayoutService } from './services/cloud-layout.service';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { CommonModule } from '@angular/common';
const ACTION_SAVE_AS = 'saveAs';
const ACTION_DELETE = 'delete';
const TASK_FILTER_PROPERTY_KEYS = 'adf-edit-task-filter';
@Component({
selector: 'app-tasks-cloud-demo',
standalone: true,
imports: [CommonModule, TaskFiltersCloudModule, TaskListCloudModule, PaginationComponent],
templateUrl: './tasks-cloud-demo.component.html',
styleUrls: ['./tasks-cloud-demo.component.scss'],
encapsulation: ViewEncapsulation.None
})
export class TasksCloudDemoComponent implements OnInit, OnDestroy {
@ViewChild('taskCloud')
taskCloud: TaskListCloudComponent;
appName: string = '';
isFilterLoaded = false;
selectedRow: any;
sortArray: TaskListCloudSortingModel[];
editedFilter: TaskFilterCloudModel;
taskFilterProperties: any = { filterProperties: [], sortProperties: [], actions: [] };
filterId;
multiselect: boolean;
selectedRows: any[] = [];
actionMenu: boolean;
contextMenu: boolean;
actions: any[] = [];
selectedAction: { id: number; name: string; actionType: string };
selectedContextAction: { id: number; name: string; actionType: string };
testingMode: boolean;
selectionMode: string;
taskDetailsRedirection: boolean;
private performAction$ = new Subject<any>();
private onDestroy$ = new Subject<boolean>();
constructor(
private cloudLayoutService: CloudLayoutService,
private route: ActivatedRoute,
private router: Router,
private userPreference: UserPreferencesService,
private appConfig: AppConfigService
) {
const properties = this.appConfig.get<Array<any>>(TASK_FILTER_PROPERTY_KEYS);
if (properties) {
this.taskFilterProperties = properties;
}
}
ngOnInit() {
this.isFilterLoaded = false;
this.route.parent.params.subscribe((params) => {
this.appName = params.appName;
});
this.route.queryParams.subscribe((params) => {
this.isFilterLoaded = true;
this.onFilterChange(params);
this.filterId = params.id;
});
this.cloudLayoutService.settings$.pipe(takeUntil(this.onDestroy$)).subscribe((settings) => this.setCurrentSettings(settings));
this.performContextActions();
}
ngOnDestroy() {
this.onDestroy$.next(true);
this.onDestroy$.complete();
}
setCurrentSettings(settings) {
if (settings) {
this.multiselect = settings.multiselect;
this.testingMode = settings.testingMode;
this.selectionMode = settings.selectionMode;
this.taskDetailsRedirection = settings.taskDetailsRedirection;
this.actionMenu = settings.actionMenu;
this.contextMenu = settings.contextMenu;
this.actions = settings.actions;
}
}
onChangePageSize(event) {
this.userPreference.paginationSize = event.maxItems;
}
resetSelectedRows() {
this.selectedRows = [];
}
onRowClick(taskId) {
if (!this.multiselect && this.selectionMode !== 'multiple' && this.taskDetailsRedirection) {
this.router.navigate([`/cloud/${this.appName}/task-details/${taskId}`]);
}
}
onRowsSelected(nodes) {
this.resetSelectedRows();
this.selectedRows = nodes.map((node) => node.obj);
}
onFilterChange(filter: any) {
this.editedFilter = Object.assign({}, filter);
this.sortArray = [new TaskListCloudSortingModel({ orderBy: this.editedFilter.sort, direction: this.editedFilter.order })];
}
onTaskFilterAction(filterAction: any) {
if (filterAction.actionType === ACTION_DELETE) {
this.cloudLayoutService.setCurrentTaskFilterParam({ index: 0 });
} else {
this.cloudLayoutService.setCurrentTaskFilterParam({ id: filterAction.filter.id });
}
if (filterAction.actionType === ACTION_SAVE_AS) {
this.router.navigate([`/cloud/${this.appName}/tasks/`], { queryParams: filterAction.filter });
}
}
onShowRowActionsMenu(event: DataCellEvent) {
event.value.actions = this.actions;
}
onShowRowContextMenu(event: DataCellEvent) {
event.value.actions = this.actions.map((action) => ({
data: event.value.row['obj'],
model: action,
subject: this.performAction$
}));
}
onExecuteRowAction(row: any) {
const value = row.value.row['obj'].entry;
const action = row.value.action;
this.selectedAction = { id: value.id, name: value.name, actionType: action.title };
}
performContextActions() {
this.performAction$.pipe(takeUntil(this.onDestroy$)).subscribe((action: any) => {
if (action) {
this.onExecuteContextAction(action);
}
});
}
onExecuteContextAction(contextAction: any) {
const value = contextAction.data.entry;
const action = contextAction.model;
this.selectedContextAction = { id: value.id, name: value.name, actionType: action.title };
}
}

View File

@ -1,5 +0,0 @@
<adf-error-content [errorCode]="errorCode">
<div adf-error-content-actions>
<button mat-raised-button (click)="onReturnButton()">Back to home</button>
</div>
</adf-error-content>

View File

@ -1,47 +0,0 @@
/*!
* @license
* Copyright © 2005-2024 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, Params, Router } from '@angular/router';
import { ErrorContentComponent } from '@alfresco/adf-core';
import { MatButtonModule } from '@angular/material/button';
@Component({
selector: 'app-demo-error',
standalone: true,
imports: [ErrorContentComponent, MatButtonModule],
templateUrl: './demo-error.component.html'
})
export class DemoErrorComponent implements OnInit {
errorCode: string = '';
constructor(private route: ActivatedRoute, private router: Router) {}
ngOnInit() {
if (this.route) {
this.route.params.subscribe((params: Params) => {
if (params['id']) {
this.errorCode = params['id'];
}
});
}
}
onReturnButton() {
this.router.navigate(['/']);
}
}

View File

@ -1,116 +0,0 @@
<ng-container *ngIf="nodeId">
<adf-alfresco-viewer
(showViewerChange)="onViewerClosed()"
[nodeId]="nodeId"
[versionId]="versionId"
[showToolbar]="true"
[allowRightSidebar]="true"
[sidebarRightTemplate]="sidebarRightTemplate">
</adf-alfresco-viewer>
</ng-container>
<ng-container *ngIf="blobFile">
<adf-viewer
[blobFile]="blobFile"
[fileName]="fileName"
[showToolbar]="true"
[allowRightSidebar]="true"
(showViewerChange)="onViewerClosed()"
[sidebarRightTemplate]="sidebarRightTemplate">
</adf-viewer>
</ng-container>
<ng-template let-node="node" #sidebarRightTemplate>
<adf-info-drawer title="Details">
<adf-info-drawer-tab label="Comments">
<adf-node-comments [nodeId]="nodeId" [readOnly]="!isCommentEnabled">
</adf-node-comments>
</adf-info-drawer-tab>
<adf-info-drawer-tab label="Properties">
<adf-content-metadata
[node]="node"
[multi]="multi"
[preset]="isPreset ? customPreset : null"
[readOnly]="isReadOnly"
[displayAspect]="showAspect"
[displayDefaultProperties]="displayDefaultProperties"
[displayTags]="false"
[displayCategories]="false"
[displayEmpty]="displayEmptyMetadata">
</adf-content-metadata>
<p class="toggle">
<mat-slide-toggle
id="adf-metadata-default-properties"
(change)="toggleDisplayProperties()"
[checked]="displayDefaultProperties">
Display Default Properties
</mat-slide-toggle>
</p>
<p class="toggle">
<mat-slide-toggle
id="adf-metadata-empty"
(change)="toggleEmptyMetadata()"
[checked]="displayEmptyMetadata">
Display Empty Metadata
</mat-slide-toggle>
</p>
<p class="toggle">
<mat-slide-toggle
id="adf-metadata-multi"
(change)="toggleMulti()"
[checked]="multi">
Multi accordion
</mat-slide-toggle>
</p>
<p class="toggle">
<mat-slide-toggle
id="adf-metadata-readonly"
(change)="toggleReadOnly()"
[checked]="isReadOnly">
Read Only
</mat-slide-toggle>
</p>
<p class="toggle">
<mat-slide-toggle
id="adf-toggle-custom-preset"
(change)="togglePreset()"
[checked]="isPreset">
Custom preset
</mat-slide-toggle>
</p>
<p class="toggle">
<mat-form-field floatPlaceholder="float">
<input matInput placeholder="Display Aspect" [(ngModel)]="desiredAspect">
</mat-form-field>
<button mat-raised-button (click)="applyAspect()">Apply Aspect</button>
</p>
<p class="toggle">
<ng-container *ngIf="isPreset">
<mat-form-field floatPlaceholder="float">
<input matInput placeholder="Custom Preset" [(ngModel)]="customPreset" data-automation-id="adf-text-custom-preset">
</mat-form-field>
<button mat-raised-button id="adf-metadata-aplly" (click)="applyCustomPreset()">Apply</button>
</ng-container>
</p>
</adf-info-drawer-tab>
<adf-info-drawer-tab label="Versions">
<adf-version-manager [node]="node"
(uploadError)="onUploadError($event)"
(viewVersion)="onViewVersion($event)">
</adf-version-manager>
</adf-info-drawer-tab>
</adf-info-drawer>
</ng-template>

View File

@ -1,158 +0,0 @@
/*!
* @license
* Copyright © 2005-2024 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Component, OnInit, ViewEncapsulation } from '@angular/core';
import { ActivatedRoute, PRIMARY_OUTLET, Router } from '@angular/router';
import { InfoDrawerComponent, InfoDrawerTabComponent, NotificationService, ViewerComponent } from '@alfresco/adf-core';
import {
AlfrescoViewerComponent,
AllowableOperationsEnum,
ContentMetadataComponent,
ContentService,
FileUploadErrorEvent,
NodeCommentsComponent,
NodesApiService,
PermissionsEnum,
VersionManagerComponent
} from '@alfresco/adf-content-services';
import { PreviewService } from '../../services/preview.service';
import { CommonModule } from '@angular/common';
import { MatSlideToggleModule } from '@angular/material/slide-toggle';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatButtonModule } from '@angular/material/button';
import { MatInputModule } from '@angular/material/input';
import { FormsModule } from '@angular/forms';
@Component({
selector: 'app-file-view',
standalone: true,
imports: [
CommonModule,
AlfrescoViewerComponent,
ViewerComponent,
NodeCommentsComponent,
ContentMetadataComponent,
MatSlideToggleModule,
MatFormFieldModule,
MatButtonModule,
MatInputModule,
FormsModule,
VersionManagerComponent,
InfoDrawerTabComponent,
InfoDrawerComponent
],
templateUrl: './file-view.component.html',
encapsulation: ViewEncapsulation.None
})
export class FileViewComponent implements OnInit {
nodeId: string = null;
versionId: string = null;
displayEmptyMetadata = false;
expanded: boolean;
multi = false;
isReadOnly = false;
isPreset = false;
customPreset: string = null;
displayDefaultProperties = true;
isCommentEnabled = false;
desiredAspect: string = null;
showAspect: string = null;
name: string;
fileName: string;
blobFile: Blob;
constructor(
private router: Router,
private route: ActivatedRoute,
private nodeApiService: NodesApiService,
private contentServices: ContentService,
private preview: PreviewService,
private notificationService: NotificationService
) {}
ngOnInit() {
this.route.params.subscribe((params) => {
const id = params.nodeId;
this.versionId = params.versionId;
if (id) {
this.nodeApiService.getNode(id).subscribe(
(node) => {
if (node?.isFile) {
this.isCommentEnabled =
this.contentServices.hasPermissions(node, PermissionsEnum.NOT_CONSUMER) ||
this.contentServices.hasAllowableOperations(node, AllowableOperationsEnum.UPDATE);
this.nodeId = id;
return;
}
this.router.navigate(['/files', id]);
},
() => this.router.navigate(['/files', id])
);
} else if (this.preview?.content) {
this.blobFile = this.preview.content;
this.fileName = this.preview.name;
}
});
}
onViewVersion(versionId: string) {
this.preview.showResource(this.nodeId, versionId);
}
onViewerClosed() {
const primaryUrl = this.router.parseUrl(this.router.url).root.children[PRIMARY_OUTLET].toString();
this.router.navigateByUrl(primaryUrl);
}
onUploadError(event: FileUploadErrorEvent) {
this.notificationService.showError(event.error);
}
toggleEmptyMetadata() {
this.displayEmptyMetadata = !this.displayEmptyMetadata;
}
toggleMulti() {
this.multi = !this.multi;
}
toggleReadOnly() {
this.isReadOnly = !this.isReadOnly;
}
toggleDisplayProperties() {
this.displayDefaultProperties = !this.displayDefaultProperties;
}
togglePreset() {
this.isPreset = !this.isPreset;
if (!this.isPreset) {
this.customPreset = null;
}
}
applyCustomPreset() {
this.isPreset = false;
setTimeout(() => {
this.isPreset = true;
}, 100);
}
applyAspect() {
this.showAspect = this.desiredAspect;
}
}

View File

@ -1,393 +0,0 @@
<div class="app-container">
<div
id="document-list-container"
class="app-document-list-container">
<adf-upload-drag-area
[disabled]="disableDragArea"
[acceptedFilesType]="getFileFiltering()"
[rootFolderId]="currentFolderId"
[versioning]="versioning"
[adf-check-allowable-operation]="'create'"
[adf-nodes]="disableDragArea ? getCurrentDocumentListNode() : []"
(updateFileVersion)="onUploadNewVersion($event)">
<div *ngIf="errorMessage" class="app-error-message">
<button (click)="resetError()" mat-icon-button>
<mat-icon>highlight_off</mat-icon>
</button>
<span class="app-error-message--text">{{errorMessage}}</span>
</div>
<adf-toolbar *ngIf="!disableDragArea" class="app-files-toolbar">
<adf-toolbar-title>
<adf-breadcrumb root="Personal Files" [target]="documentList">
</adf-breadcrumb>
<adf-dropdown-breadcrumb [target]="documentList">
</adf-dropdown-breadcrumb>
</adf-toolbar-title>
<div class="app-document-action-buttons">
<button
data-automation-id="create-new-folder"
mat-icon-button
[disabled]="!canCreateContent(documentList.folderNode)"
title="New folder"
(error)="openSnackMessageError($event)"
(success)="documentList.reload()"
[adf-create-folder]="currentFolderId">
<mat-icon>create_new_folder</mat-icon>
</button>
<button mat-icon-button
[disabled]="!hasSelection(documentList.selection)"
title="Download"
[adfNodeDownload]="documentList.selection">
<mat-icon>get_app</mat-icon>
</button>
<button mat-icon-button
data-automation-id="delete-toolbar-button"
adf-check-allowable-operation="delete"
[permanent]="true"
[adf-nodes]="documentList.selection"
title="Delete"
(delete)="onDeleteActionSuccess($event)"
[adf-delete]="documentList.selection">
<mat-icon>delete</mat-icon>
</button>
</div>
<button mat-icon-button (click)="showVersions = !showVersions" class="app-show-versions-button"
title="Toggle metadata">
<mat-icon>{{ showVersions ? 'chevron_right' : 'chevron_left' }}</mat-icon>
</button>
<adf-toolbar-divider class="app-toolbar-divider-before-more-menu"></adf-toolbar-divider>
<button class="app-toolbar-more-menu-button" mat-icon-button [matMenuTriggerFor]="menu">
<mat-icon>more_vert</mat-icon>
</button>
<mat-menu #menu="matMenu">
<button mat-menu-item
adf-check-allowable-operation="delete"
[adf-nodes]="documentList.selection"
(delete)="onDeleteActionSuccess($event)"
[adf-delete]="documentList.selection">
<mat-icon>delete</mat-icon>
<span>Delete</span>
</button>
</mat-menu>
</adf-toolbar>
<div class="app-document-list-container-in-upload-drag-area app-document-list-container" [ngClass]="{'adf-sticky-document-list': stickyHeader }">
<adf-document-list
#documentList
class="app-file-list-container"
[permissionsStyle]="permissionsStyle"
[currentFolderId]="currentFolderId"
[contextMenuActions]="true"
[contentActions]="true"
[allowDropFiles]="allowDropFiles"
[selectionMode]="selectionMode"
[multiselect]="multiselect"
[node]="nodeResult"
[includeFields]="includeFields"
[sorting]="sorting"
[sortingMode]="sortingMode"
[showHeader]="showHeader"
[stickyHeader]="stickyHeader"
[headerFilters]="headerFilters"
[filterValue]="paramValues"
(error)="onNavigationError($event)"
(success)="resetError()"
(ready)="emitReadyEvent($event)"
(preview)="showFile($event)"
(folderChange)="onFolderChange($event)"
(permissionError)="handlePermissionError($event)"
(name-click)="documentList.onNodeDblClick($any($event).detail?.node)"
(filterSelection)="onFilterSelected($event)">
<adf-custom-empty-content-template *ngIf="disableDragArea">
<div class="app-empty_template">
<div class="app-no-result-message">{{ 'SEARCH.NO_RESULT' | translate }}</div>
</div>
</adf-custom-empty-content-template>
<data-columns>
<data-column
key="$thumbnail"
type="image"
[sortable]="false">
</data-column>
<data-column
key="name"
sortingKey="name"
title="Display name"
class="adf-expand-cell-5">
</data-column>
<data-column
key="content.sizeInBytes"
title="Size"
type="fileSize"
sortingKey="sizeInBytes"
class="adf-ellipsis-cell">
</data-column>
<data-column
*ngIf="searchTerm"
key="search"
title="Search"
class="adf-expand-cell-3">
<ng-template let-entry="$implicit">
<div [innerHTML]="searchResultsHighlight(entry.row.node.entry.search) | highlight:searchTerm">
</div>
</ng-template>
</data-column>
<data-column
class="app-full-width adf-ellipsis-cell adf-desktop-only"
title="Node id"
[sortable]="false"
key="id">
</data-column>
<data-column
title="Lock"
key="lock"
[focus]="false"
[sortable]="false"
class="adf-ellipsis-cell">
<ng-template let-entry="$implicit">
<button mat-icon-button [adf-node-lock]="entry.row.node.entry" class="app-lock-button">
<mat-icon *ngIf="entry.row.getValue('isLocked')">lock</mat-icon>
<mat-icon *ngIf="!entry.row.getValue('isLocked')">lock_open</mat-icon>
</button>
</ng-template>
</data-column>
<data-column
title="Created by"
key="createdByUser.displayName"
sortingKey="createdByUser"
class="adf-ellipsis-cell">
</data-column>
<data-column
title="Created"
key="createdAt"
sortingKey="createdAt"
type="date"
[format]="'timeAgo'"
class="adf-ellipsis-cell">
</data-column>
</data-columns>
<content-actions>
<content-action
icon="get_app"
title="Download"
handler="download">
</content-action>
<content-action
icon="delete"
permission="delete"
[disableWithNoPermission]="true"
title="Delete"
(permissionEvent)="handlePermissionError($event)"
(success)="onDeleteActionSuccess($event)"
handler="delete">
</content-action>
<content-action
icon="supervisor_account"
title="Permission"
permission="copy"
(error)="onContentActionError($event)"
(execute)="onPermissionRequested($event)">
</content-action>
<!-- document actions -->
<content-action
icon="storage"
target="document"
title="Manage versions"
(execute)="onManageVersions($event)">
</content-action>
<content-action
icon="beach_access"
target="document"
title="Update Aspects"
(execute)="onAspectUpdate($event)">
</content-action>
</content-actions>
</adf-document-list>
</div>
<adf-pagination
[target]="documentList"
(changePageSize)="onChangePageSize($event)"
(changePageNumber)="onChangePageNumber($event)"
(nextPage)="onNextPage($event)"
(prevPage)="onPrevPage($event)">
</adf-pagination>
</adf-upload-drag-area>
<adf-info-drawer-layout *ngIf="showVersions" class="app-manage-versions-sidebar">
<div info-drawer-content>
<adf-info-drawer [title]="'Details'" *ngIf="documentList.selection[0]">
<adf-info-drawer-tab label="Properties">
<adf-content-metadata
[node]="documentList.selection[0].entry"
[displayEmpty]="displayEmptyMetadata"
[displayTags]="true"
[displayCategories]="true">
</adf-content-metadata>
</adf-info-drawer-tab>
<adf-info-drawer-tab label="Versions">
<ng-container *ngIf="hasOneFileSelected();else choose_document_template">
<ng-container *ngIf="userHasPermissionToManageVersions(); else no_permission_to_versions">
<adf-version-manager
[node]="documentList.selection[0].entry"
[showComments]="true"
[allowDownload]="allowVersionDownload">
</adf-version-manager>
</ng-container>
</ng-container>
<ng-template #choose_document_template>
<div class="app-manage-versions-empty">
<mat-icon class="app-manage-versions-empty-icon">face</mat-icon>
Select a file to see its versions
</div>
</ng-template>
<ng-template #no_permission_to_versions>
<div class="app-manage-versions-no-permission">
<mat-icon class="app-manage-versions-no-permission-icon">warning</mat-icon>
You don't have permission to manage versions of this content
</div>
</ng-template>
</adf-info-drawer-tab>
</adf-info-drawer>
</div>
</adf-info-drawer-layout>
</div>
</div>
<div *ngIf="showSettingsPanel" class="app-content-service-settings">
<p>Current folder ID: {{ documentList.currentFolderId }}</p>
<div class="app-p-10">
Selected Nodes:
<ul>
<li *ngFor="let node of documentList.selection">
{{ node.entry.name }}
</li>
</ul>
</div>
<div class="app-container">
<section>
<mat-slide-toggle data-automation-id="multiSelectToggle" [(ngModel)]="multiselect">
Multiselect (with checkboxes)
</mat-slide-toggle>
</section>
<section>
<mat-slide-toggle id="adf-multiple-upload-switch" [(ngModel)]="multipleFileUpload" >
Multiple File Upload
</mat-slide-toggle>
</section>
<section>
<mat-slide-toggle id="adf-folder-upload-switch" [(ngModel)]="folderUpload">
Folder upload
</mat-slide-toggle>
</section>
<section>
<mat-slide-toggle id="adf-extension-filter-upload-switch" [(ngModel)]="acceptedFilesTypeShow">
Custom extensions filter
</mat-slide-toggle>
</section>
<section>
<mat-slide-toggle id="adf-max-size-filter-upload-switch" [(ngModel)]="maxSizeShow">
Max size filter
</mat-slide-toggle>
</section>
<section>
<mat-slide-toggle id="adf-version-upload-switch" [(ngModel)]="versioning">
Enable versioning
</mat-slide-toggle>
</section>
<section>
<mat-slide-toggle [(ngModel)]="allowVersionDownload">
Enable version download
</mat-slide-toggle>
</section>
<section>
<mat-slide-toggle [(ngModel)]="displayEmptyMetadata" id="displayEmptyMetadata">
Display Empty Metadata
</mat-slide-toggle>
</section>
<section>
<mat-slide-toggle [(ngModel)]="stickyHeader" id="stickyHeader">
Sticky Header
</mat-slide-toggle>
</section>
<h5>Upload</h5>
<section *ngIf="acceptedFilesTypeShow">
<mat-form-field floatPlaceholder="float">
<input matInput
placeholder="Extension accepted"
[(ngModel)]="acceptedFilesType"
data-automation-id="accepted-files-type">
</mat-form-field>
</section>
<section *ngIf="maxSizeShow">
<mat-form-field>
<input matInput type="number" placeholder="Max file size" [(ngModel)]="maxFilesSize"
data-automation-id="max-files-size">
</mat-form-field>
</section>
<div *ngIf="!acceptedFilesTypeShow">
<adf-upload-button
[disabled]="!enableUpload"
[rootFolderId]="documentList.currentFolderId"
[multipleFiles]="multipleFileUpload"
[uploadFolders]="folderUpload"
[maxFilesSize]="maxSizeShow ? maxFilesSize : null"
(error)="openSnackMessageError($event)"
[versioning]="versioning"
[adf-check-allowable-operation]="'create'"
[adf-nodes]="enableUpload ? getCurrentDocumentListNode() : []"
(permissionEvent)="handlePermissionError($event)">
</adf-upload-button>
</div>
<div *ngIf="acceptedFilesTypeShow">
<adf-upload-button
[disabled]="!enableUpload"
[rootFolderId]="documentList.currentFolderId"
[acceptedFilesType]="acceptedFilesType"
[multipleFiles]="multipleFileUpload"
[uploadFolders]="folderUpload"
[versioning]="versioning"
(error)="openSnackMessageError($event)"
[adf-check-allowable-operation]="'create'"
[adf-nodes]="enableUpload ? getCurrentDocumentListNode() : []"
(permissionEvent)="handlePermissionError($event)">
</adf-upload-button>
</div>
<section>
<mat-checkbox id="adf-disable-upload" [(ngModel)]="enableUpload">
Enable upload
</mat-checkbox>
</section>
</div>
<div class="app-p-10">
<p>Use Cmd (Mac) or Ctrl (Windows) to toggle selection of multiple items</p>
<mat-form-field>
<mat-select placeholder="Selection Mode" [(ngModel)]="selectionMode" name="food">
<mat-option *ngFor="let mode of selectionModes" [value]="mode.value">
{{mode.viewValue}}
</mat-option>
</mat-select>
</mat-form-field>
</div>
</div>

View File

@ -1,76 +0,0 @@
.app-container:has(.app-document-list-container) {
margin: 10px;
}
.app-error-message {
text-align: left;
}
.app-error-message--text {
color: var(--theme-warn-color);
}
.app-document-list-container {
min-height: 400px;
.app-datatable-list {
min-height: 400px;
}
adf-upload-drag-area {
margin-right: 16px;
adf-toolbar-title {
flex: 0 1 auto;
}
.app-document-action-buttons {
flex: 0 0 auto;
}
}
&:not(.app-document-list-container-in-upload-drag-area) {
display: flex;
place-content: stretch flex-start;
align-items: stretch;
max-height: 100%;
}
}
.adf-datatable-card .app-lock-button {
top: -10px;
position: absolute;
}
.app-content-service-settings {
padding: 16px;
}
.app-manage-versions-sidebar {
flex: 0 0 auto;
&.adf-info-drawer-layout {
width: 360px;
}
}
.app-no-result__empty_doc_lib {
width: 565px;
height: 161px;
object-fit: contain;
margin-top: 17px;
}
.app-empty_template {
text-align: center;
margin-top: 20px;
margin-bottom: 20px;
}
.app-no-result-message {
height: 32px;
opacity: 0.26;
font-size: var(--theme-headline-font-size);
line-height: 1.33;
letter-spacing: -1px;
}

View File

@ -1,571 +0,0 @@
/*!
* @license
* Copyright © 2005-2024 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {
Component,
Input,
OnInit,
OnChanges,
OnDestroy,
Optional,
EventEmitter,
ViewChild,
SimpleChanges,
Output,
ViewEncapsulation
} from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { ActivatedRoute, Params, Router } from '@angular/router';
import { NodeEntry, NodePaging, Pagination, Node, SearchEntry } from '@alfresco/js-api';
import {
NotificationService,
UserPreferencesService,
PaginationComponent,
ShowHeaderMode,
FormRenderingService,
ToolbarTitleComponent,
ToolbarComponent,
ToolbarDividerComponent,
DataColumnComponent,
HighlightPipe,
DataColumnListComponent,
CustomEmptyContentTemplateDirective,
InfoDrawerTabComponent,
InfoDrawerComponent,
InfoDrawerLayoutComponent
} from '@alfresco/adf-core';
import {
ContentService,
FolderCreatedEvent,
UploadService,
DocumentListComponent,
PermissionStyleModel,
ContentMetadataService,
FilterSearch,
DialogAspectListService,
FileUploadEvent,
NodesApiService,
UploadDragAreaComponent,
CheckAllowableOperationDirective,
BreadcrumbComponent,
DropdownBreadcrumbComponent,
NodeDownloadDirective,
NodeDeleteDirective,
NodeLockDirective,
ContentActionListComponent,
ContentActionComponent,
ContentMetadataComponent,
VersionManagerComponent,
UploadButtonComponent
} from '@alfresco/adf-content-services';
import { ProcessFormRenderingService } from '@alfresco/adf-process-services';
import { VersionManagerDialogAdapterComponent } from './version-manager-dialog-adapter.component';
import { Subject } from 'rxjs';
import { PreviewService } from '../../services/preview.service';
import { takeUntil, debounceTime, scan } from 'rxjs/operators';
import { CommonModule } from '@angular/common';
import { MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon';
import { FolderCreateDirective } from '../../folder-directive';
import { MatMenuModule } from '@angular/material/menu';
import { TranslateModule } from '@ngx-translate/core';
import { MatSlideToggleModule } from '@angular/material/slide-toggle';
import { FormsModule } from '@angular/forms';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input';
import { MatCheckboxModule } from '@angular/material/checkbox';
import { MatSelectModule } from '@angular/material/select';
const DEFAULT_FOLDER_TO_SHOW = '-my-';
@Component({
selector: 'app-files-component',
standalone: true,
imports: [
CommonModule,
UploadDragAreaComponent,
MatButtonModule,
MatIconModule,
CheckAllowableOperationDirective,
ToolbarTitleComponent,
ToolbarComponent,
BreadcrumbComponent,
DropdownBreadcrumbComponent,
DocumentListComponent,
FolderCreateDirective,
NodeDownloadDirective,
NodeDeleteDirective,
ToolbarDividerComponent,
MatMenuModule,
TranslateModule,
DataColumnComponent,
NodeLockDirective,
ContentActionListComponent,
ContentActionComponent,
PaginationComponent,
ContentMetadataComponent,
VersionManagerComponent,
MatSlideToggleModule,
FormsModule,
MatFormFieldModule,
MatInputModule,
UploadButtonComponent,
MatCheckboxModule,
MatSelectModule,
HighlightPipe,
DataColumnListComponent,
CustomEmptyContentTemplateDirective,
VersionManagerDialogAdapterComponent,
InfoDrawerTabComponent,
InfoDrawerComponent,
InfoDrawerLayoutComponent
],
templateUrl: './files.component.html',
styleUrls: ['./files.component.scss'],
encapsulation: ViewEncapsulation.None,
providers: [{ provide: FormRenderingService, useClass: ProcessFormRenderingService }]
})
export class FilesComponent implements OnInit, OnChanges, OnDestroy {
protected onDestroy$ = new Subject<boolean>();
errorMessage: string = null;
nodeId: any;
showViewer = false;
showVersions = false;
allowDropFiles = true;
includeFields = ['isFavorite', 'isLocked', 'aspectNames', 'definition'];
selectionModes = [
{ value: 'none', viewValue: 'None' },
{ value: 'single', viewValue: 'Single' },
{ value: 'multiple', viewValue: 'Multiple' }
];
// The identifier of a node. You can also use one of these well-known aliases: -my- | -shared- | -root-
@Input()
currentFolderId: string = DEFAULT_FOLDER_TO_SHOW;
@Input()
sorting = ['name', 'ASC'];
@Input()
sortingMode: 'server' | 'client' = 'server';
@Input()
showSettingsPanel = true;
@Input()
showHeader = ShowHeaderMode.Always;
@Input()
selectionMode = 'multiple';
@Input()
multiselect = false;
@Input()
multipleFileUpload = false;
@Input()
folderUpload = false;
@Input()
acceptedFilesTypeShow = false;
@Input()
maxSizeShow = false;
@Input()
versioning = false;
@Input()
allowVersionDownload = true;
@Input()
acceptedFilesType = '.jpg,.pdf,.js';
@Input()
maxFilesSize: number = null;
@Input()
enableUpload = true;
@Input()
nodeResult: NodePaging;
@Input()
pagination: Pagination;
@Input()
disableDragArea = false;
@Input()
searchTerm = '';
@Input()
navigationRoute = '/files';
@Input()
headerFilters = false;
@Input()
paramValues: Map<any, any> = null;
@Input()
filterSorting: string = null;
@Output()
documentListReady: EventEmitter<any> = new EventEmitter();
@Output()
changedPageSize: EventEmitter<Pagination> = new EventEmitter();
@Output()
changedPageNumber: EventEmitter<Pagination> = new EventEmitter();
@Output()
turnedNextPage: EventEmitter<Pagination> = new EventEmitter();
@Output()
turnedPreviousPage: EventEmitter<Pagination> = new EventEmitter();
@Output()
loadNext: EventEmitter<Pagination> = new EventEmitter();
@Output()
deleteElementSuccess: EventEmitter<any> = new EventEmitter();
@ViewChild('documentList', { static: true })
documentList: DocumentListComponent;
@ViewChild('standardPagination')
standardPagination: PaginationComponent;
permissionsStyle: PermissionStyleModel[] = [];
stickyHeader: boolean;
displayEmptyMetadata = false;
constructor(
private notificationService: NotificationService,
private uploadService: UploadService,
private contentService: ContentService,
private dialog: MatDialog,
private router: Router,
private preference: UserPreferencesService,
private preview: PreviewService,
@Optional() private route: ActivatedRoute,
private contentMetadataService: ContentMetadataService,
private dialogAspectListService: DialogAspectListService,
private nodeService: NodesApiService
) {}
showFile(event) {
const entry = event.value.entry;
if (entry?.isFile) {
this.preview.showResource(entry.id);
}
}
ngOnInit() {
if (!this.pagination) {
this.pagination = {
maxItems: this.preference.paginationSize,
skipCount: 0
} as Pagination;
}
if (this.route) {
this.route.params.forEach((params: Params) => {
if (params['id'] && this.currentFolderId !== params['id']) {
this.currentFolderId = params['id'];
}
});
}
this.uploadService.fileUploadComplete
.pipe(
debounceTime(300),
scan((files, currentFile) => [...files, currentFile], []),
takeUntil(this.onDestroy$)
)
.subscribe((value: any[]) => {
this.onFileUploadEvent(value[0]);
});
this.uploadService.fileUploadDeleted.pipe(takeUntil(this.onDestroy$)).subscribe((value) => this.onFileUploadEvent(value));
this.contentService.folderCreated.pipe(takeUntil(this.onDestroy$)).subscribe((value) => this.onFolderCreated(value));
this.contentService.folderCreate.pipe(takeUntil(this.onDestroy$)).subscribe((value) => this.onFolderAction(value));
this.contentService.folderEdit.pipe(takeUntil(this.onDestroy$)).subscribe((value) => this.onFolderAction(value));
this.contentMetadataService.error.pipe(takeUntil(this.onDestroy$)).subscribe((err: { message: string }) => {
this.notificationService.showError(err.message);
});
}
onFileUploadEvent(event: FileUploadEvent) {
if (event && event.file.options.parentId === this.documentList.currentFolderId) {
this.documentList.reload();
}
}
ngOnDestroy() {
this.onDestroy$.next(true);
this.onDestroy$.complete();
}
ngOnChanges(changes: SimpleChanges) {
if (changes.nodeResult?.currentValue) {
this.nodeResult = changes.nodeResult.currentValue;
this.pagination = this.nodeResult.list.pagination;
}
if (!this.pagination) {
this.giveDefaultPaginationWhenNotDefined();
}
}
giveDefaultPaginationWhenNotDefined() {
this.pagination = {
maxItems: this.preference.paginationSize,
skipCount: 0,
totalItems: 0,
hasMoreItems: false
} as Pagination;
}
getCurrentDocumentListNode(): NodeEntry[] {
if (this.documentList.folderNode) {
return [{ entry: this.documentList.folderNode }];
} else {
return [];
}
}
onNavigationError(error: any) {
if (error) {
this.router.navigate(['/error', error.status]);
}
}
resetError() {
this.errorMessage = null;
}
onFolderCreated(event: FolderCreatedEvent) {
if (event && event.parentId === this.documentList.currentFolderId) {
this.documentList.reload();
}
}
onFolderAction(node) {
if (node && node.parentId === this.documentList.currentFolderId) {
this.documentList.reload();
}
}
onFolderChange($event) {
this.router.navigate([this.navigationRoute, $event.value.id]);
}
handlePermissionError(event: any) {
this.notificationService.showError('PERMISSION.LACKOF', null, {
permission: event.permission,
action: event.action,
type: event.type
});
}
openSnackMessageError(error: any) {
this.notificationService.showError(error.value || error);
}
openSnackMessageInfo(message: string) {
this.notificationService.showInfo(message);
}
emitReadyEvent(event: NodePaging) {
this.documentListReady.emit(event);
}
onContentActionError(errors: any) {
const errorStatusCode = JSON.parse(errors.message).error.statusCode;
let message: string;
switch (errorStatusCode) {
case 403:
message = 'OPERATION.ERROR.PERMISSION';
break;
case 409:
message = 'OPERATION.ERROR.CONFLICT';
break;
default:
message = 'OPERATION.ERROR.UNKNOWN';
}
this.openSnackMessageError(message);
}
onDeleteActionSuccess(message: string) {
this.uploadService.fileDeleted.next(message);
this.deleteElementSuccess.emit();
this.documentList.reload();
this.openSnackMessageInfo(message);
}
onPermissionRequested(node: any) {
this.router.navigate(['/permissions', node.value.entry.id]);
}
onManageVersions(event: any) {
const contentEntry = event.value.entry;
const showComments = true;
const allowDownload = this.allowVersionDownload;
if (this.contentService.hasAllowableOperations(contentEntry, 'update')) {
this.dialog.open(VersionManagerDialogAdapterComponent, {
data: { contentEntry, showComments, allowDownload },
panelClass: 'adf-version-manager-dialog',
width: '630px'
});
} else {
this.openSnackMessageError('OPERATION.ERROR.PERMISSION');
}
}
onAspectUpdate(event: any) {
this.dialogAspectListService.openAspectListDialog(event.value.entry.id).subscribe((aspectList) => {
this.nodeService.updateNode(event.value.entry.id, { aspectNames: [...aspectList] }).subscribe(() => {
this.openSnackMessageInfo('Node Aspects Updated');
});
});
}
hasSelection(selection: Array<any>): boolean {
return selection && selection.length > 0;
}
hasOneFileSelected(): boolean {
const selection = this.documentList.selection;
return selection && selection.length === 1 && selection[0].entry.isFile;
}
userHasPermissionToManageVersions(): boolean {
const selection = this.documentList.selection;
return this.contentService.hasAllowableOperations(selection[0].entry, 'update');
}
canCreateContent(parentNode: Node): boolean {
if (parentNode) {
return this.contentService.hasAllowableOperations(parentNode, 'create');
}
return false;
}
onChangePageSize(event: Pagination): void {
this.preference.paginationSize = event.maxItems;
this.pagination.maxItems = event.maxItems;
this.pagination.skipCount = event.skipCount;
this.changedPageSize.emit(event);
}
onChangePageNumber(event: Pagination): void {
this.pagination.maxItems = event.maxItems;
this.pagination.skipCount = event.skipCount;
this.changedPageNumber.emit(event);
}
onNextPage(event: Pagination): void {
this.pagination.maxItems = event.maxItems;
this.pagination.skipCount = event.skipCount;
this.turnedNextPage.emit(event);
}
onPrevPage(event: Pagination): void {
this.pagination.maxItems = event.maxItems;
this.pagination.skipCount = event.skipCount;
this.turnedPreviousPage.emit(event);
}
onUploadNewVersion(ev) {
const contentEntry = ev.detail.data.node.entry;
const showComments = true;
const allowDownload = this.allowVersionDownload;
const newFileVersion = ev.detail.files[0].file;
if (this.contentService.hasAllowableOperations(contentEntry, 'update')) {
this.dialog.open(VersionManagerDialogAdapterComponent, {
data: {
contentEntry,
showComments,
allowDownload,
newFileVersion,
showComparison: true
},
panelClass: 'adf-version-manager-dialog',
width: '630px'
});
} else {
this.openSnackMessageError('OPERATION.ERROR.PERMISSION');
}
}
getFileFiltering(): string {
return this.acceptedFilesTypeShow ? this.acceptedFilesType : '*';
}
searchResultsHighlight(search: SearchEntry): string {
if (search?.highlight) {
return search.highlight.map((currentHighlight) => currentHighlight.snippets).join(', ');
}
return '';
}
onFilterSelected(activeFilters: FilterSearch[]) {
if (activeFilters.length) {
this.navigateToFilter(activeFilters);
} else {
this.clearFilterNavigation();
}
}
navigateToFilter(activeFilters: FilterSearch[]) {
const objectFromMap = {};
activeFilters.forEach((filter: FilterSearch) => {
let paramValue = null;
if (filter.value?.from && filter.value.to) {
paramValue = `${filter.value.from}||${filter.value.to}`;
} else {
paramValue = filter.value;
}
objectFromMap[filter.key] = paramValue;
});
this.router.navigate([], { relativeTo: this.route, queryParams: objectFromMap });
}
clearFilterNavigation() {
this.documentList.node = null;
if (this.currentFolderId === '-my-') {
this.router.navigate([this.navigationRoute, '']);
} else {
this.router.navigate([this.navigationRoute, this.currentFolderId]);
}
this.documentList.reload();
}
}

View File

@ -1,45 +0,0 @@
<header mat-dialog-title>Manage Versions</header>
<section>
<mat-slide-toggle id="adf-version-manager-switch-readonly" [(ngModel)]="readOnly">
Read-only
</mat-slide-toggle>
</section>
<section>
<mat-slide-toggle id="adf-version-manager-switch-download" [(ngModel)]="allowDownload">
Enable version download
</mat-slide-toggle>
</section>
<section>
<mat-slide-toggle id="adf-version-manager-switch-comments" [(ngModel)]="showComments">
Show comments
</mat-slide-toggle>
</section>
<section *ngIf="newFileVersion">
<mat-slide-toggle id="adf-version-manager-switch-comparison" [(ngModel)]="showVersionComparison">
Show version comparison
</mat-slide-toggle>
</section>
<section mat-dialog-content *ngIf="!readOnly">
<adf-version-manager [node]="contentEntry"
(uploadCancel)="hideVersionComparison($event)"
(uploadSuccess)="hideVersionComparison($event)"
[showVersionComparison]="showVersionComparison"
[newFileVersion]="newFileVersion"
[allowDownload]="allowDownload"
[showComments]="showComments"
(uploadError)="uploadError($event)"
(viewVersion)="onViewVersion($event)"
></adf-version-manager>
</section>
<section mat-dialog-content *ngIf="readOnly">
<adf-version-list [node]="contentEntry" [showActions]="false"></adf-version-list>
</section>
<footer mat-dialog-actions>
<button mat-button (click)="close()">Close</button>
</footer>

View File

@ -1,74 +0,0 @@
/*!
* @license
* Copyright © 2005-2024 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Component, Inject, ViewEncapsulation } from '@angular/core';
import { MAT_DIALOG_DATA, MatDialogModule, MatDialogRef } from '@angular/material/dialog';
import { Node } from '@alfresco/js-api';
import { PreviewService } from '../../services/preview.service';
import { NotificationService } from '@alfresco/adf-core';
import { FileUploadErrorEvent, VersionListComponent, VersionManagerComponent } from '@alfresco/adf-content-services';
import { CommonModule } from '@angular/common';
import { MatSlideToggleModule } from '@angular/material/slide-toggle';
import { FormsModule } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button';
@Component({
templateUrl: './version-manager-dialog-adapter.component.html',
standalone: true,
imports: [CommonModule, MatDialogModule, MatSlideToggleModule, FormsModule, VersionManagerComponent, VersionListComponent, MatButtonModule],
encapsulation: ViewEncapsulation.None
})
export class VersionManagerDialogAdapterComponent {
contentEntry: Node;
newFileVersion: File;
showComments = true;
allowDownload = true;
readOnly = false;
showVersionComparison = false;
constructor(
private previewService: PreviewService,
private notificationService: NotificationService,
@Inject(MAT_DIALOG_DATA) data: any,
private containingDialog?: MatDialogRef<VersionManagerDialogAdapterComponent>
) {
this.contentEntry = data.contentEntry;
this.newFileVersion = Object.prototype.hasOwnProperty.call(data, 'newFileVersion') ? data.newFileVersion : this.newFileVersion;
this.showComments = Object.prototype.hasOwnProperty.call(data, 'showComments') ? data.showComments : this.showComments;
this.allowDownload = Object.prototype.hasOwnProperty.call(data, 'allowDownload') ? data.allowDownload : this.allowDownload;
}
uploadError(event: FileUploadErrorEvent) {
this.notificationService.showError(event.error);
}
close() {
this.containingDialog.close();
}
onViewVersion(versionId: string) {
this.previewService.showResource(this.contentEntry.id, versionId);
this.close();
}
hideVersionComparison(isCancelled: any) {
if (isCancelled) {
this.showVersionComparison = false;
this.newFileVersion = null;
}
}
}

Some files were not shown because too many files have changed in this diff Show More