From 45ffd3bdf6c468090534cefaa1e3299cc5579544 Mon Sep 17 00:00:00 2001 From: Matthias Stein Date: Fri, 13 Sep 2024 13:31:59 +0200 Subject: [PATCH] Add bundle code --- .editorconfig | 11 + .eslintrc | 6 + .github/workflows/devnet-bundle-release.yml | 33 + .github/workflows/devnet-bundle-snapshot.yml | 21 + .gitignore | 17 + .stylelintignore | 3 + .stylelintrc | 3 + .vscode/extensions.json | 8 + .vscode/launch.json | 15 + .vscode/tasks.json | 56 ++ CHANGELOG.md | 471 ++++++++++ LICENSE | 202 +++++ README.md | 25 + RELEASE.md | 4 + apache2-license-header.txt | 13 + gulpfile.js | 179 ++++ package.json | 36 + pom.xml | 828 ++++++++++++++++++ src/main/config/assembly.xml | 37 + src/main/js/apps/sample/app.json | 157 ++++ src/main/js/apps/sample/images/hybrid.png | Bin 0 -> 7657 bytes .../js/apps/sample/images/logo_conterra.png | Bin 0 -> 12694 bytes src/main/js/apps/sample/images/streets.png | Bin 0 -> 3010 bytes src/main/js/apps/sample/images/topo.png | Bin 0 -> 5898 bytes .../PortalContentController.ts | 174 ++++ .../dn_portalcontent/PortalContentModel.ts | 62 ++ .../PortalContentWidgetFactory.ts | 92 ++ .../bundles/dn_portalcontent/css/styles.css | 109 +++ .../js/bundles/dn_portalcontent/manifest.json | 224 +++++ .../js/bundles/dn_portalcontent/module.ts | 20 + .../js/bundles/dn_portalcontent/nls/bundle.js | 56 ++ .../bundles/dn_portalcontent/nls/de/bundle.js | 53 ++ .../templates/FilterWidget.vue | 178 ++++ .../templates/PortalContentWidget.vue | 191 ++++ .../dn_portalcontent/templates/PortalItem.vue | 175 ++++ src/main/types/mocha-global.d.ts | 1 + src/main/types/thirdparty.d.ts | 7 + src/main/types/vue-shim.d.ts | 4 + src/support/js/check-licenses.ts | 59 ++ src/test/resources/application.properties | 89 ++ src/test/webapp/WEB-INF/web.xml | 34 + src/test/webapp/favicon.png | Bin 0 -> 45042 bytes src/test/webapp/index.html | 134 +++ src/test/webapp/init.css | 326 +++++++ src/test/webapp/js/tests/init-packs.js | 21 + src/test/webapp/js/tests/runTests.html | 27 + src/test/webapp/js/tests/test-init.js | 42 + src/test/webapp/login.css | 132 +++ tsconfig.json | 19 + 49 files changed, 4354 insertions(+) create mode 100644 .editorconfig create mode 100644 .eslintrc create mode 100644 .github/workflows/devnet-bundle-release.yml create mode 100644 .github/workflows/devnet-bundle-snapshot.yml create mode 100644 .gitignore create mode 100644 .stylelintignore create mode 100644 .stylelintrc create mode 100644 .vscode/extensions.json create mode 100644 .vscode/launch.json create mode 100644 .vscode/tasks.json create mode 100644 CHANGELOG.md create mode 100644 LICENSE create mode 100644 README.md create mode 100644 RELEASE.md create mode 100644 apache2-license-header.txt create mode 100644 gulpfile.js create mode 100644 package.json create mode 100644 pom.xml create mode 100644 src/main/config/assembly.xml create mode 100644 src/main/js/apps/sample/app.json create mode 100644 src/main/js/apps/sample/images/hybrid.png create mode 100644 src/main/js/apps/sample/images/logo_conterra.png create mode 100644 src/main/js/apps/sample/images/streets.png create mode 100644 src/main/js/apps/sample/images/topo.png create mode 100644 src/main/js/bundles/dn_portalcontent/PortalContentController.ts create mode 100644 src/main/js/bundles/dn_portalcontent/PortalContentModel.ts create mode 100644 src/main/js/bundles/dn_portalcontent/PortalContentWidgetFactory.ts create mode 100644 src/main/js/bundles/dn_portalcontent/css/styles.css create mode 100644 src/main/js/bundles/dn_portalcontent/manifest.json create mode 100644 src/main/js/bundles/dn_portalcontent/module.ts create mode 100644 src/main/js/bundles/dn_portalcontent/nls/bundle.js create mode 100644 src/main/js/bundles/dn_portalcontent/nls/de/bundle.js create mode 100644 src/main/js/bundles/dn_portalcontent/templates/FilterWidget.vue create mode 100644 src/main/js/bundles/dn_portalcontent/templates/PortalContentWidget.vue create mode 100644 src/main/js/bundles/dn_portalcontent/templates/PortalItem.vue create mode 100644 src/main/types/mocha-global.d.ts create mode 100644 src/main/types/thirdparty.d.ts create mode 100644 src/main/types/vue-shim.d.ts create mode 100644 src/support/js/check-licenses.ts create mode 100644 src/test/resources/application.properties create mode 100644 src/test/webapp/WEB-INF/web.xml create mode 100644 src/test/webapp/favicon.png create mode 100644 src/test/webapp/index.html create mode 100644 src/test/webapp/init.css create mode 100644 src/test/webapp/js/tests/init-packs.js create mode 100644 src/test/webapp/js/tests/runTests.html create mode 100644 src/test/webapp/js/tests/test-init.js create mode 100644 src/test/webapp/login.css create mode 100644 tsconfig.json diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..db3dfb0 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,11 @@ +# http://editorconfig.org +# top-most EditorConfig file +root = true + +[*] +indent_style = space +indent_size = 4 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000..ae8ae61 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,6 @@ +{ + "extends": "eslint-config-ct-prodeng", + "plugins": [ + "vue" + ] +} diff --git a/.github/workflows/devnet-bundle-release.yml b/.github/workflows/devnet-bundle-release.yml new file mode 100644 index 0000000..273d217 --- /dev/null +++ b/.github/workflows/devnet-bundle-release.yml @@ -0,0 +1,33 @@ +name: devnet-bundle-release + +on: + workflow_dispatch: + inputs: + releaseVersion: + description: "Release version" + required: true + nextDevVersion: + description: "Next SNAPSHOT version" + required: true + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: conterra/devnet-bundle-release-action@main + with: + nexus_id: ${{secrets.NEXUS_ID}} + nexus_user: ${{secrets.NEXUS_USER}} + nexus_user_pw: ${{secrets.NEXUS_USER_PW}} + nexus_url: ${{secrets.NEXUS_URL}} + nexus_url_release: ${{secrets.NEXUS_URL_RELEASE}} + nexus_url_snapshots: ${{secrets.NEXUS_URL_SNAPSHOTS}} + release_token: ${{secrets.RELEASE_TOKEN}} + git_mail: ${{secrets.GIT_MAIL}} + git_user: ${{secrets.GIT_USER}} + release_version: ${{ github.event.inputs.releaseVersion }} + next_dev_version: ${{ github.event.inputs.nextDevVersion }} + ms_teams_webhook_uri: ${{secrets.MS_TEAMS_WEBHOOK_URI}} + demo_user: ${{secrets.DEMO_USER}} + demo_user_pw: ${{secrets.DEMO_USER_PW}} + demo_url: ${{secrets.DEMO_URL}} diff --git a/.github/workflows/devnet-bundle-snapshot.yml b/.github/workflows/devnet-bundle-snapshot.yml new file mode 100644 index 0000000..f045a08 --- /dev/null +++ b/.github/workflows/devnet-bundle-snapshot.yml @@ -0,0 +1,21 @@ +name: devnet-bundle-snapshot + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: conterra/devnet-bundle-snapshot-action@main + with: + nexus_id: ${{secrets.NEXUS_ID}} + nexus_user: ${{secrets.NEXUS_USER}} + nexus_user_pw: ${{secrets.NEXUS_USER_PW}} + nexus_url: ${{secrets.NEXUS_URL}} + nexus_url_release: ${{secrets.NEXUS_URL_RELEASE}} + nexus_url_snapshots: ${{secrets.NEXUS_URL_SNAPSHOTS}} + release_token: ${{secrets.RELEASE_TOKEN}} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e41ab5f --- /dev/null +++ b/.gitignore @@ -0,0 +1,17 @@ +/.idea +/.vs +**/nbactions.xml +**/*.iml +**/*.lnk +**/target +/build.properties +/node_modules +/node +/nb-configuration.xml +/.classpath +/.project +/.settings/ +/.vscode/settings.json +/.externalToolBuilders/ +/package-lock.json +/gulpfile.overrides.js diff --git a/.stylelintignore b/.stylelintignore new file mode 100644 index 0000000..b07fa47 --- /dev/null +++ b/.stylelintignore @@ -0,0 +1,3 @@ +# add path patterns to ignore style files +**/external-libs/** +**/styles/dijit/** \ No newline at end of file diff --git a/.stylelintrc b/.stylelintrc new file mode 100644 index 0000000..57c90c1 --- /dev/null +++ b/.stylelintrc @@ -0,0 +1,3 @@ +{ + "extends": "stylelint-config-ct-prodeng" +} diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..67efcac --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,8 @@ +{ + "recommendations": [ + "dbaeumer.vscode-eslint", + "Vue.volar", + "Esri.arcgis-jsapi-snippets", + "ctjdr.vscode-apprt-bundles" + ] +} \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..e8ab909 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,15 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "chrome", + "request": "launch", + "name": "Launch Chrome against localhost", + "url": "http://localhost:9090", + "webRoot": "${workspaceFolder}/src/main" + } + ] +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..d1643d5 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,56 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Initialize", + "detail": "Initialize map.apps for Developers project", + "type": "shell", + "command": "mvn initialize", + "group": "build" + }, + { + "label": "Run", + "detail": "Run map.apps for Developers development server", + "type": "shell", + "command": "mvn", + "args": ["clean", "compile", "-Denv=dev", "'-Dlocal.configfile=./build.properties'", "-Pinclude-mapapps-deps"], + "group": "build", + "isBackground": true + }, + { + "label": "Run (Remote project mode)", + "detail": "Run map.apps for Developers development server in 'remote project' mode", + "type": "shell", + "command": "mvn", + "args": ["clean", "compile", "-Denv=dev", "'-Dlocal.configfile=./build.properties'"], + "group": "build", + "isBackground": true + }, + { + "label": "Compress", + "detail": "Prepare bundles and apps for deployment on a map.apps instance", + "type": "shell", + "command": "mvn", + "args": ["clean", "install", "-Pcompress"], + "group": "build", + "presentation": { + "reveal": "always", + "panel": "dedicated" + } + }, + { + "label": "Clean", + "type": "shell", + "command": "mvn clean", + "group": "build" + }, + { + "label": "Watch types", + "detail": "Start TypeScript compiler in watch mode", + "type": "npm", + "script": "watch-types", + "group": "build", + "problemMatcher": [], + } + ] +} diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..a964a6b --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,471 @@ +# Changelog + +All notable changes to this project will be documented in this file. +## [4.17.0] - 16.02.2024 +- Support for map.apps 4.17.0 + +### Changes in pom.xml +- Update `mapapps.version` property to `4.17.0` +- Update `ct.jsregistry.version` property to `2.0.1` + +### Changes in package.json +- Update `@conterra/ct-mapapps-typings` to `4.17.0` + +## [4.16.0] - 11.01.2024 + +- Support for map.apps 4.16.0 +- The bundle `sample_camera` is now implemented in Typescript +- The bundle `sample_camera_js` with the same functionality is still implemented in Javascript + +### Changes in pom.xml + +- Update `mapapps.version` property to `4.16.0` +- Add missing `vuetify.version` with value `1.5.28` +- Update `ct.jsregistry.version` property to `1.5.14` +- Update `frontend-maven-plugin` plugin version to `1.14.2` +- Update `frontend-maven-plugin` configuration to: + ```xml + + v20.9.0 + + ``` +- Update `maven-compiler-plugin` plugin version to `3.11.0` +- Update `maven-javadoc-plugin` plugin version to `3.5.0` +- Add version `3.5.0` to `maven-dependency-plugin` plugin + +### Changes in package.json + +- Update `@conterra/ct-mapapps-typings` to `4.16.0` +- Update `@types/arcgis-js-api` to `4.28.0` +- Update `ct-mapapps-gulp-js` property to `0.10.2` +- Update `ct-mapapps-browser-sync` property to `0.0.35` +- Update `typescript` to `5.2.2`, +- Update `vue-template-compiler` to `2.7.15`, +- Add `vue` with version `2.7.15`, +- Update `@types/chai` to `4.3.10` +- Update `chai` to `4.3.10` +- Update `@types/mocha` to `10.0.4` +- Update `puppeteer` to `21.5.2` +- Update `stylelint` to `15.11.0` +- Update `stylelint-config-ct-prodeng` to `2.0.0` +- Update `stylelint-config-recommended` to `13.0.0` +- Update `stylelint-config-recommended-less` to `2.0.0` + + +### Changes due to Typescript migration of the `sample_camera` bundle +- changes in `package.json` file + - Add script `"check-types": "tsc --noEmit"` + - Add script `"watch-types": "tsc -w --noEmit` + - Add `"@types/chai": "^4.3.10",` to `devDependencies` + - Add `"@types/mocha": "^10.0.3"` to `devDependencies` + - Add `"ts-node": "^10.9.1"` to `devDependencies` + - Add `"typescript": "^5.1.6"` to `devDependencies` +- New files + - `types\mocha-global.d.ts` + - `types\thirdparty.d.ts` + - `types\vue-shim.d.ts` +- Renamed folder `sample_camera` to `sample_camera_js` +- Implemented sample camera widget in Typescript, see folder `sample_camera` +- changes in `tsconfig.json` + - add `"strict": true,` + - add `"noImplicitAny": true,` + - add `"strictNullChecks": true,` + - add `"include": ["src"]` +- Migrated `sample_tests\all.js` to Typescript: `sample_tests\all.ts` +- Migrated `sample_tests\test-init.js` to Typescript: `sample_tests\test-init.ts` +- Deleted `sample_tests\intern-all.js` + +## [4.15.1] - 06.09.2023 + +- Support for map.apps 4.15.1 +- Update `mapapps.version` property in `./pom.xml` to `4.15.1` +- Update `@conterra/ct-mapapps-typings` in `package.json` to `4.15.1` +- Update `ct.jsregistry.version` property in `./pom.xml` to `1.5.10` +- Remove following entry from ``, it is automatically provided by the ´ct-mapapps` parent pom import: + + ```xml + + de.conterra.mapapps + ct-mapapps-js + ${mapapps.version} + + ``` + +## [4.15.0] - 26.05.2023 + +- Support for map.apps 4.15.0 +- Use `ct-mapapps-browser-sync` drop use of jetty +- Update `mapapps.version` property in `./pom.xml` to `4.15.0` +- Update `@conterra/ct-mapapps-typings` in `package.json` to `4.15.0` +- Add properties and samples to use Identity Service in dev project in `test/resources/application.properties` +- Rename property `proxy.cors.trustedServers` to `cors.request.trustedServers` in `./pom.xml`, `test/resources/application.properties` and `test/webapp/index.html` +- Update `ct.jsregistry.version` property in `./pom.xml` to `1.5.9` +- Update `mocha` to `^10.2.0`, +- Update `puppeteer` to `^19.11.1` +- Update `chai` to `^4.3.7` +- Update `@conterra/mapapps-mocha-runner` to `^1.1.1` +- Update `"@types/arcgis-js-api` to `4.26.0` +- Integrate the `rollup-build` task into the gulpfile.js +- Add `build.config.js` to `sample_camera` sample, to demonstrate the rollup build and change `sample_camera/module.js` to make `module.js` the only entrypoint of the bundle. +- Integrate the optional `gulpfile.overrides.js` file. +- Properties `jsregistry.replacement.paths`, `jsregistry.directoryscanner.npmfolder`, `jsregistry.directoryscanner.npmincludes` are moved into gulpfile.js in the `registerBrowserSync` config. +- Property `jsregistry.sourcemaps.enabled` is obsolete, the dev registry will always support sourcemaps. +- Property `jsregistry.root.url` is provided automatically by the browsersync dev server. +- `sample_camera` is using 1.0.0-SNAPSHOT version, like in the pom.xml + +## [4.14.3] - 20.03.2023 + +- Support for map.apps 4.14.3 +- Update `mapapps.version` property in `./pom.xml` to `4.14.3` +- Update `@conterra/ct-mapapps-typings` in `package.json` to `4.14.3` +- Update Node.js and npm version in `./pom.xml` to latest LTS Version (18.x) +- Add `transpileTargets` property to `gulpfile.js` to streamline transpilation with ArcGIS Maps SDK for JS requirements. +- Ignore gulp task on VS Code file change by adding `` to `frontend-maven-plugin` in `./pom.xml` + +## [4.14.2] - 24.01.2023 + +- Support for map.apps 4.14.2 +- Update `mapapps.version` property in `./pom.xml` to `4.14.2` + +## [4.14.1] - 01.12.2022 + +- Support for map.apps 4.14.1 +- Update `mapapps.version` property in `./pom.xml` to `4.14.1` +- Update `ct.jsregistry.version` property in `./pom.xml` to `1.5.7` +- Update `@conterra/ct-mapapps-typings` in `package.json` to `4.14.1` + +## [4.14.0] - 28.10.2022 + +- Support for map.apps 4.14.0 +- Update `mapapps.version` property in `./pom.xml` to `4.14.0` +- Update `ct.jsregistry.version` property in `./pom.xml` to `1.5.6` +- Update `@conterra/ct-mapapps-typings` in `package.json` to `4.14.0` +- Update `@types/arcgis-js-api` in `package.json` to `4.24.0` +- Update `chai` in `package.json` to `^4.3.6` +- Update `vue-template-compiler` in `package.json` to `2.7.8` +- Update `ct-mapapps-gulp-js` in `package.json` to `^0.7.4` +- Add `jsregistry.sourcemaps.enabled=true` in `test/resources/application.properties` to ship source maps during development +- Add `sourceMaps: "file"` in `gulpfile.js` to generate `.js.map` files instead of inlined source maps +- Replace `module.exports` in nls files with `export default`. + Support for `module.exports` will be removed soon and should be replaced + with an appropriate `export` or `export default` directive. +- replaced the old `favicon.ico` with a modern png base `favicon.png`. The file link inside the `ìndex.html` + was set accordingly +- `ct-mapapps-js-api` for `ct-mapapps-js` in `./pom.xml` + + +## 28.04.2022 + +- Make `proxy.cors.trustedServers` configurable, via application.properties. + +## 22.04.2022 + +- The `compress` profile was modified to remove usage of the google closure compiler. + [Terser](https://github.com/terser/terser) is now used to optimize JavaScript files. + This solves an issue with the old configuration where too modern JavaScript syntax could + be introduced by accident. +- Update `ct-mapapps-gulp-js` in `package.json` to `0.6.20` +- Update the `optimize js` plugin execution in `pom.xml` +- Introduce internal `gulp.node.env` property that configures the environment variable `NODE_ENV` +- Update `gulpfile.js` to enable JavaScript compression if `NODE_ENV` is `production` + +## [4.13.1] - 06.04.2022 + +- Support for map.apps 4.13.1 +- Update `mapapps.version` property in `./pom.xml` to `4.13.1` +- Update `@conterra/ct-mapapps-typings` in `package.json` to `4.13.1` + +## [4.13.0] - 29.03.2022 + +- Support for map.apps 4.13.0 +- Update `mapapps.version` property in `./pom.xml` to `4.13.0` +- Update `nodeVersion` in `./pom.xml` to `v16.14.0` +- Update `npmVersion` in `./pom.xml` to `8.3.1` +- Update `@types/arcgis-js-api` in `package.json` to `4.22.0` +- Update `@conterra/ct-mapapps-typings` in `package.json` to `4.13.0` +- Update `ct-mapapps-gulp-js` in `package.json` to `^0.6.18` +- Update `eslint-config-ct-prodeng` in `package.json` to `^1.2.5` +- Update `puppeteer` in `package.json` to `^13.3.2` +- Update babel defaults to target modern browsers by default +- Add support for oauth tokens in automatic deployments by updating to latest `ct-jsregistry-maven-plugin` +- Use basemap `streets-vector` by default +- Update jetty to version `10.0.8` + + :warning: There are breaking configuration changes for the jetty maven plugin in the `pom.xml`. Please compare the plugin configuration in the `pom.xml` with your existing configuration to find all changes. + For example, the `scanIntervalSeconds` property is now called `scan` and the `webAppConfig` is now called `webApp`. + See the documentation of the [jetty maven plugin](https://www.eclipse.org/jetty/documentation/jetty-10/programming-guide/index.html#jetty-maven-plugin). +- Add new tasks to `.vscode/tasks.json` and prefer `-Denv=dev` over `-Pwatch-all` + +## [4.12.3] - 15.12.2021 + +- Support for map.apps 4.12.3 that includes a critical security fix +- Update `mapapps.version` property in `./pom.xml` to `4.12.3` + +## [4.12.2] - 13.12.2021 + +- Support for map.apps 4.12.2 that includes a critical security fix +- Update `mapapps.version` property in `./pom.xml` to `4.12.2` + +## [4.12.1] - 10.11.2021 + +- Support for map.apps 4.12.1 +- Update `mapapps.version` property in `./pom.xml` to `4.12.1` +- Update `ct.jsregistry.version` property in `./pom.xml` to `1.4.4` +- Update `@types/arcgis-js-api` to `4.20.1` (`package.json`) +- Update `eslint-config-ct-prodeng` to `1.2.3` (`package.json`)` + +NOTE: since `eslint-config-ct-prodeng@1.2.0` linting for basic a11y rules is activated for custom vue components. +The occurring warnings should be easily be fixable. More infos can be found at the [eslint-plugin-vuejs-accessibility](https://github.com/vue-a11y/eslint-plugin-vuejs-accessibility) page. + +## [4.12.0] - 2021-08-31 + +- Support for map.apps 4.12.0 +- Update `mapapps.version` property in `./pom.xml` to `4.12.0` +- Update `ct.jsregistry.version` property in `./pom.xml` to `1.4.3` +- Update `ct.jsrt-test.version` property in `./pom.xml` to `2.0.2` +- Update `@conterra/ct-mapapps-typings` to `~4.12.0` (`package.json`) +- Update `@types/arcgis-js-api` to `4.20.0` (`package.json`) +- Update `ct-mapapps-gulp-js` to `^0.5.27` (`package.json`) +- Update `vue-template-compiler` to `2.6.14` (`package.json`) +- Update `puppeteer` to `^10.0.0` (`package.json`) +- Add `"@conterra/mapapps-mocha-runner": "^1.0.0"` (`package.json`) +- Add `"chai": "^4.3.4"` (`package.json`) +- Add `"mocha": "^9.0.0"` (`package.json`) +- Changed default test-runner from intern-js to mocha. (see [MIGRATION.md](./MIGRATION.md)) for details. + +## [4.11.1] - 2021-02-22 + +- Support for map.apps 4.11.1 +- Change `mapapps.version` property in `./pom.xml` to `4.11.1` +- Change `ct.jsregistry.version` property in `./pom.xml` to `1.4.1` +- Update `ct-mapapps-gulp-js` to `^0.5.14` (`package.json`) +- Update `@types/arcgis-js-api` to `4.18.0` (`package.json`) + +## [4.11.0] - 2021-02-17 + +- Support for map.apps 4.11.0 +- Change `mapapps.version` property in `./pom.xml` to `4.11.0` +- Change `ct.jsregistry.version` property in `./pom.xml` to `1.4.0` +- Change `ct.jsrt-test.version` property in `./pom.xml` to `2.0.0` +- Update `ct-mapapps-gulp-js` to `^0.5.13` (`package.json`) +- Add `@conterra/ct-mapapps-typings` with version `~4.11.0` (`package.json`) +- Update `@types/arcgis-js-api` to `4.18.0` (`package.json`) +- Use `tsconfig.json` file from mapapps-4-developers 4.11.0 release. +- Update splashscreen styles (see [MIGRATION.md](./MIGRATION.md)) for details. Relevant for non-customized splashscreens. + +## [4.10.1] - 2020-12-09 + +- Support for map.apps 4.10.1 + +## [4.10.0] - 2020-12-08 + +- Support for map.apps 4.10.0 +- Change `mapapps.version` property in `./pom.xml` to `4.10.0` +- Change `ct.jsregistry.version` property in `./pom.xml` to `1.3.10` +- Change `nodeVersion` property in `./pom.xml` to `v14.15.1` +- Change `npmVersion` property in `./pom.xml` to `6.14.9` +- Update `@types/arcgis-js-api` to `4.17.0` (`package.json`) +- Update `ct-mapapps-gulp-js` to `^0.5.5` (`package.json`) +- Update `puppeteer` to `^5.5.0` (`package.json`) +- Update `eslint-config-ct-prodeng` to `^1.1.16` (`package.json`) +- Update `stylelint-config-ct-prodeng` to `1.0.3` (`package.json`) + +- improved documentation, e.g. [MIGRATION.md](./MIGRATION.md) +- remove `optimizeCSS` goal from `./pom.xml` + +```xml + + optimize CSS + + optimizeCSS + + compile + + + + bundles/*/*/*.css + + + **/themeSettings.css + + + + +``` + +- add execution of `gulp compress` task to `compress` profile in `pom.xml` + +```xml + + compress + + compress + + ... + +``` + +- add `compress` task to `./gulpfile.js` + +```js +gulp.task( + "compress", + gulp.series( + "copy-resources", + "themes-copy", + gulp.parallel("js-transpile", gulp.series("themes-compile", "themes-compress")) + ) +); +``` + +## [4.9.2] - 2020-10-06 + +- Support for map.apps 4.9.2 +- Change `mapapps.version` property in `./pom.xml` to `4.9.2` + +## [4.9.1] - 2020-09-04 + +- Support for map.apps 4.9.1 +- Change `mapapps.version` property in `./pom.xml` to `4.9.1` + +## [4.9.0] - 2020-08-18 + +- Support for map.apps 4.9.0 +- Change `mapapps.version` property in `./pom.xml` to `4.9.0` +- Change `ct.jsregistry.version` property in `./pom.xml` to `1.3.8` +- Update `ct-mapapps-gulp-js` to `^0.4.5` (`package.json`) +- Update `@types/arcgis-js-api` to `4.16.0` (`package.json`) +- Update `puppeteer` to `^3.3.0` (`package.json`) +- Update `eslint-config-ct-prodeng` to `^1.1.11` (`package.json`) + +- Added several omnisearch properties in `src/main/js/bundles/theme-custom/styles/themeSettings.less`: + +```less +// Omni Search Colors +@ct-omnisearch-input-background-color: #fff; +@ct-omnisearch-input-text-color: #000; +@ct-omnisearch-result-list-hover-color: @minor-selected-color; +@ct-omnisearch-drawer-button-background-color: @ct-omnisearch-input-background-color; +@ct-omnisearch-drawer-button-icon-color: darken(@disabled-text-color, 30); +``` + +## [4.8.4] - 2020-05-20 + +- Upgrade some Maven-Plugin versions in `./pom.xml`. +- Change `mapapps.version` property in `./pom.xml` to `4.8.4` +- Change `ct.jsregistry.version` property in `./pom.xml` to `1.3.7` +- Update `ct-mapapps-gulp-js` to `^0.4.4` (`package.json`) this change requires to list additional `peerDependencies`: + +```js + // for .vue file support: + "vue-template-compiler": "2.6.6", + // for test execution (optional): + "puppeteer": "^3.1.0", + // for js linting (optional): + "eslint-config-ct-prodeng": "^1.1.10", + // for css/less linting (optional): + "stylelint-config-ct-prodeng": "1.0.2" +``` + +- Change dependency `ct-mapapps-proxy` to `ct-proxy-servlet` in `pom.xml`: + +```xml + + de.conterra.ct-proxy + ct-proxy-servlet + test + +``` + +- Change class name of Proxy Servlet in `src/test/webapp/WEB-INF/web.xml` (optional): + +```xml + + ProxyServlet + ProxyServlet + de.conterra.proxy.servlet.ProxyServlet + 0 + +``` + +## [4.8.3] - 2020-03-06 + +- Support for map.apps 4.8.3. +- Update ct-mapapps-gulp-js to 0.3.6 (package.json) + +## [4.8.2] - 2020-02-11 + +- Support for map.apps 4.8.2. +- Replace `babel-polyfill` by `apprt-polyfill`. +- Replace `$apprt.load` and `$apprt.lauchAppFromParam` by `$apprt.startApp` + note that the function signature also changed + instead of: + + ```js + $apprt.load(function(Launcher) { + new Launcher({ + configLocation: "builderapps" + }).launchApp("@@appId@@"); + ``` + + it will be: + + ```js + $apprt.startApp({ + configLocation: "builderapps", + param: "app", + defaultApp: "@@appId@@" + }); + ``` + +- Introduce property `skip.apps.upload` to decide if apps should be uploaded +- Apps located in `/src/main/js/apps/[app]` are by default zipped into the folder `/target/[app].zip`. + +## [4.8.1] - 2020-01-10 + +- Support for map.apps 4.8.1. + +## [4.8.0] - 2020-01-06 + +- Support for map.apps 4.8.0. +- Update ct.jsregistry.version version. +- Update ct-mapapps-gulp-js version. + +## [4.7.2] - 2019-09-18 + +- Support for map.apps 4.7.2. +- Update ct.jsregistry.version version. +- sample_camera sample for sync logging added +- sample_camera small enhancements +- Update ct-mapapps-gulp-js version (requires additional changes, see this [commit](https://github.com/conterra/mapapps-4-developers/commit/c974a74a08a70316204d5c09aee22f8d39c70446)) + +## [4.7.1] - 2019-08-16 + +- Support for map.apps 4.7.1. +- Update ct.jsregistry.version version. + +## [4.7.0] - 2019-06-28 + +- Support for map.apps 4.7.0. +- Update ct.jsregistry.version version. +- Update vue-template-compiler version. +- Update eslint-config-ct-prodeng version. + +## [4.6.1] - 2019-04-24 + +- Explain `-Denv=dev`. +- Add support for local configuration of `proxy.use.rules`. +- Support for map.apps 4.6.1. +- Update node/npm versions, add profile for dedicated npm install, use newer jetty version. + +## [4.6.0] - 2019-03-01 + +- Support for map.apps 4.6.0. +- Update ct-mapapps-gulp-js version. +- Use maven.home not M2_HOME. +- Update node, npm and dependencies. +- Property `trustedServers` has been removed with property `corsEnabledServers`. + +[unreleased]: https://github.com/conterra/mapapps-4-developers/compare/4.6.0...HEAD +[4.6.1]: https://github.com/conterra/mapapps-4-developers/compare/4.6.0...4.6.1 +[4.6.0]: https://github.com/conterra/mapapps-4-developers/compare/4.5.0...4.6.0 diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..8f71f43 --- /dev/null +++ b/LICENSE @@ -0,0 +1,202 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + 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. + diff --git a/README.md b/README.md new file mode 100644 index 0000000..16ab6a0 --- /dev/null +++ b/README.md @@ -0,0 +1,25 @@ +# mapapps-devnet-blueprint + +**This project is not intended for use by non-con terra users.** It is designed for the creation of bundles and their releases in GitHub and can access con terra internal infrastructures for this purpose. To develop your own map.apps bundles, use the [mapapps-4-developers project](https://github.com/conterra/mapapps-4-developers). + +This project is a starting point for programming custom map.apps bundles and themes. It contains examples for common tasks such as building widgets with Vue.js or creating your own custom themes. +You may use this project as a blueprint for starting your own map.apps project. + +For detailed documentation on how to use map.apps for Developers to extend map.apps, see the [map.apps Developer's Guide](https://docs.conterra.de/en/mapapps/latest/developersguide/getting-started/). + +## Quick start + +Clone this project and ensure that you have all required dependencies installed correctly (see [Documentation](https://docs.conterra.de/en/mapapps/latest/developersguide/getting-started/set-up-development-environment.html)). + +Then run the following commands from the project root directory to start a local development server: + +```bash +# install all required node modules +$ mvn initialize + +# start dev server +$ mvn compile -Denv=dev -Pinclude-mapapps-deps + +# run unit tests +$ mvn test -P run-js-tests,include-mapapps-deps +``` diff --git a/RELEASE.md b/RELEASE.md new file mode 100644 index 0000000..4820d0b --- /dev/null +++ b/RELEASE.md @@ -0,0 +1,4 @@ +✅ Tested for map.apps 4.17.0 / Linie 4 + +#### Release Notes +- autogenerated SNAPSHOT-Release diff --git a/apache2-license-header.txt b/apache2-license-header.txt new file mode 100644 index 0000000..08ebe58 --- /dev/null +++ b/apache2-license-header.txt @@ -0,0 +1,13 @@ +Copyright (C) ${project.inceptionYear} ${owner} (${email}) + +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. \ No newline at end of file diff --git a/gulpfile.js b/gulpfile.js new file mode 100644 index 0000000..9be30b8 --- /dev/null +++ b/gulpfile.js @@ -0,0 +1,179 @@ +/* + * Copyright (C) 2024 con terra GmbH (info@conterra.de) + * + * 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. + */ +const gulp = require("gulp"); +const mapapps = require('ct-mapapps-gulp-js'); +const mapappsBrowserSync = require("ct-mapapps-browser-sync"); + +const isProduction = process.env.NODE_ENV === "production"; +console.info(`Configuring gulp build for ${isProduction ? "production" : "development"}`); + +const localOverrides = (function () { + if (isProduction) { + // Never override defaults in production mode + return undefined; + } + + try { + return require("./gulpfile.overrides"); + } catch (e) { + // File may not exist + return undefined; + } +})(); + +// used to transport test urls in "run-browser-tests-local" task +const runBrowserTests = []; + +mapapps.registerTasks({ + /** Enable debug logging */ + debug: localOverrides?.debug ?? false, + /** enable linting */ + lintOnWatch: localOverrides?.lintOnWatch ?? true, + /** enable es6 by default */ + forceTranspile: true, + /* A detailed description of available setting is available at https://www.npmjs.com/package/ct-mapapps-gulp-js */ + compress: isProduction, + + /* build source maps as e.g. ".js.map" */ + sourceMaps: "file", + + /** Build Unit-Tests only in dev mode */ + rollupBuildTests: !isProduction, + /** Amount of Threads used to build the bundles, if there are only a few bundles 1 is ok. + * More as 3 is normally not required. + */ + rollupBuildMaxWorkers: localOverrides?.rollupBuildMaxWorkers ?? 1, + + /** List of build time flags, usage like: import { debug } from "build-config!". + */ + rollupConfig: { + debug: !isProduction + }, + + /* a list of themes inside this project */ + themes: [], + /* state that the custom theme will be dependant from map.apps everlasting theme that provides the base styles */ + hasBaseThemes: true, + /* state that we want to support vuetify components and therefore need the vuetify core styles*/ + hasVuetify: true, + themeChangeTargets: { + "vuetify": [] + }, + /* A list oft target browser versions. This should be streamlined with Esri JS API requirements. */ + transpileTargets: { + firefox: 102, + edge: 104, + chrome: 104, + safari: 15 + }, + runBrowserTests, + watchFinishedReceiver() { + if (localOverrides?.autoReload ?? true) { + mapappsBrowserSync.state.reload(); + } + } +}, gulp); + +mapappsBrowserSync.registerTask({ + // on which port to listen + port: localOverrides?.port ?? 9090, + // activate https protocol, generates a self signed certificate for "localhost" + // https://browsersync.io/docs/options#option-https + https: localOverrides?.https ?? false, + + // to prevent auto open of browser, set this to false + urlToOpen: localOverrides?.openBrowser ?? true, + properties: { + paths: [ + // Ensure @@key@@ expressions filtered in tests files + /^\/js\/tests\/(runTests.html|test-init.js|init-packs.js)$/ + ] + }, + jsreg: { + //npmDir : __dirname + "/node_modules/", + npmModules: [ + "mocha", + "chai", + "@conterra/mapapps-mocha-runner" + ] + }, + // prevent reload by browser sync (reload triggered on watch end) + externalReloadTrigger: true +}, gulp); + +gulp.task("build", + gulp.series( + "copy-resources", + "themes-copy", + gulp.parallel( + "js-transpile", + "rollup-build", + "themes-compile" + ) + ) +); + +gulp.task("lint", + gulp.parallel( + "js-lint" + /*, comment in to lint .css/.less files + "style-lint" + */ + )); + +gulp.task("preview", + gulp.series( + "build", + gulp.parallel( + "watch", + "browser-sync" + ) + )); + +gulp.task("run-tests", + gulp.series( + "browser-sync-start", + function transportTestUrls() { + // transport test url to run-browser-tests + // eslint-disable-next-line max-len + const testsAt = mapappsBrowserSync.state.url + "/resources/jsregistry/root/@conterra/mapapps-mocha-runner/latest/mocha.html?boot=/js/tests/test-init.js&timeout=5000&test=sample_helloworld/tests/all&reporter=tap"; + runBrowserTests.push(testsAt); + return Promise.resolve(); + }, + "run-browser-tests", + "browser-sync-stop" + )); + +gulp.task("test", + gulp.series( + "build", + "lint", + "run-tests" + )); + +gulp.task("compress", + gulp.series( + "build", + "themes-compress", + "lint" + ) +); + +gulp.task("default", + gulp.series( + "build", + "lint" + )); diff --git a/package.json b/package.json new file mode 100644 index 0000000..6c71dea --- /dev/null +++ b/package.json @@ -0,0 +1,36 @@ +{ + "name": "mapapps-4-developers", + "description": "test build", + "version": "0.0.1", + "license": "CC0-1.0", + "scripts": { + "check-licenses": "tsx ./src/support/js/check-licenses.ts", + "check-types": "tsc --noEmit", + "watch-types": "tsc -w --noEmit" + }, + "devDependencies": { + "@conterra/ct-mapapps-typings": "~4.18.1", + "@conterra/mapapps-mocha-runner": "1.1.1", + "@conterra/reactivity-core": "^0.4.0", + "@types/chai": "4.3.10", + "@types/license-checker": "^25.0.6", + "@types/mocha": "10.0.4", + "arcgis-js-api": "4.29.10", + "chai": "4.3.10", + "ct-mapapps-browser-sync": "0.0.39", + "ct-mapapps-gulp-js": "0.10.3", + "eslint-config-ct-prodeng": "1.4.0", + "license-checker": "25.0.1", + "mocha": "10.2.0", + "puppeteer": "21.5.2", + "stylelint": "15.11.0", + "stylelint-config-ct-prodeng": "2.0.0", + "stylelint-config-recommended": "13.0.0", + "stylelint-config-recommended-less": "2.0.0", + "ts-node": "^10.9.1", + "tsx": "^4.6.0", + "typescript": "5.4.5", + "vue": "2.7.15", + "vue-template-compiler": "2.7.15" + } +} diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..ff4983c --- /dev/null +++ b/pom.xml @@ -0,0 +1,828 @@ + + + + 4.0.0 + de.conterra.devnet + mapapps-portal-item-loader + 4.18.1 + jar + + + 52n-dependencies + 52n-dependencies + http://52north.org/maven/repo/releases + + + + + 52n-dependencies + 52n-dependencies + http://52north.org/maven/repo/releases + + + + + + de.conterra.mapapps + ct-mapapps + ${mapapps.version} + pom + import + + + + + + + org.vuejs + vue + dev + test + + + org.vuetifyjs + vuetify + dev + test + + + + + de.conterra.jsrt + ct-jsrt-test-intern + ${ct.jsrt-test.version} + test + + + de.conterra.jsrt + ct-jsrt-test-uitest + ${ct.jsrt-test.version} + test + + + + + + + de.conterra.mapapps + mapapps-maven-plugin + ${mapapps.version} + + + de.conterra.jsregistry + ct-jsregistry-maven-plugin + ${ct.jsregistry.version} + + + maven-deploy-plugin + 2.8.2 + + + org.apache.maven.plugins + maven-resources-plugin + 3.1.0 + + \ + + + + org.apache.maven.plugins + maven-source-plugin + 3.2.1 + + + org.apache.maven.plugins + maven-jar-plugin + 3.2.0 + + ${js.build.outputPath} + + **/* + + + apps/** + **/tests/** + **/sample_tests/** + **/theme-common/** + + + + + maven-war-plugin + 3.2.3 + + + org.codehaus.mojo + properties-maven-plugin + 1.0.0 + + + com.google.code.maven-replacer-plugin + replacer + 1.5.3 + + + com.github.eirslett + frontend-maven-plugin + 1.14.2 + + v20.9.0 + + + + org.codehaus.mojo + build-helper-maven-plugin + 3.1.0 + + + maven-release-plugin + 2.5.3 + + + maven-assembly-plugin + 3.0.0 + + + com.mycila + license-maven-plugin + 2.11 + + + de.conterra.maven + setproperties-maven-plugin + 1.0.2 + + + + src/main/js + + + src/test/resources + + application.properties + + true + + + src/test/resources + + application.properties + + + + src/test/webapp + + **/* + + + + + + org.apache.maven.plugins + maven-enforcer-plugin + 3.4.1 + + + enforce-versions + + enforce + + + + + [3.8.0,) + + + [17,) + + + + + + + + de.conterra.maven + setproperties-maven-plugin + 1.0.3 + + + + setProperties + + + + + + . + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.11.0 + + true + + + + org.apache.maven.plugins + maven-javadoc-plugin + 3.5.0 + + true + + + + org.apache.maven.plugins + maven-dependency-plugin + 3.5.0 + + + unpack-themes-src + generate-sources + + unpack + + + true + + + de.conterra.mapapps + ct-mapapps-js + src + ${project.build.directory}/unpacked + layout/theme-everlasting/**,layout/theme-common/** + layout/theme-everlasting/styles/vuetify/** + + + + org.vuetifyjs + vuetify + ${vuetify.version} + dev + ${project.build.directory}/unpacked/layout + vuetify/stylus/** + + + + + + copy js-libs into 'bundle-imports' + generate-resources + + copy-dependencies + + + ${project.build.directory}/bundle-imports + + + + + + org.apache.maven.plugins + maven-resources-plugin + + + + + testResources + + process-resources + + + + UTF-8 + + + + com.github.eirslett + frontend-maven-plugin + + + + run gulp ${gulp.task} + + gulp + + process-resources + + ${gulp.task} + + ${gulp.node.env} + + + + + + + com.mycila + license-maven-plugin + +
${project.baseUri}apache2-license-header.txt
+ + con terra GmbH + info@conterra.de + 2024 + + + src/main/js/**/*.js + src/main/js/**/*.ts + src/main/js/**/*.vue + src/main/js/**/*.css + src/test/**/*.js + src/test/**/*.html + src/test/**/*.css + src/test/**/application.properties + src/main/config/assembly.xml + **/build.properties + **/gulpfile.js + **/pom.xml + + + src/main/js/**/*.min.js + + + XML_STYLE + +
+ + + + format + + process-sources + + +
+ + com.google.code.maven-replacer-plugin + replacer + + + replace some config settings in app.json + prepare-package + + replace + + + ${js.build.outputPath} + + **/app.json + + true + + + + + \s*"bundleLocations"\s*:\s*\[\s*"localbundles"\s*,\s*"bundles"\s*\]\s*,\s* + + + + + + + + + de.conterra.jsregistry + ct-jsregistry-maven-plugin + + + build-jsregistry.properties + process-resources + + buildPacksProperties + + + + apps/**,bundles/sample_tests + + + + + ${js.build.outputPath} + ${js.build.outputPath} + + + + de.conterra.mapapps + mapapps-maven-plugin + + + true + false + ${js.build.outputPath}/apps + ${project.build.directory} + + + + + create-app-zips + + deployApps + + + + build-app-template + prepare-package + + deployAppTemplate + + + ${project.build.directory}/${project.artifactId}-sample-app.zip + + + ${js.build.outputPath}/apps/sample + + **/* + + + + + + + + + maven-assembly-plugin + + + src/main/config/assembly.xml + + ${project.artifactId}-bundle + false + + + + make-zip + package + + single + + + + + + maven-release-plugin + + @{project.version} + clean + compress + true + + +
+
+ + scm:git:https://github.com/conterra/mapapps-devnet-blueprint.git + scm:git:https://github.com/conterra/mapapps-devnet-blueprint.git + + https://github.com/conterra/mapapps-devnet-blueprint + HEAD + + + + ${dist.releases.id} + ${dist.releases.url} + + + ${dist.snapshots.id} + ${dist.snapshots.url} + + + + UTF-8 + ${project.build.directory}/webapp + ${root.build.outputPath}/js + + 4.18.1 + 1.5.30 + + ${mapapps.version} + + 2.1.1 + 2.0.2 + + + sample + + + + *://*:*/**; + + * + + $\{mapapps.remote.base\} + + mvn + + development + + + + + + + + + + true + + false + + false + + + + include-mapapps-deps + + + de.conterra.mapapps + ct-mapapps-js + test + + + org.dojotoolkit + dojo-release + test + + + org.dojotoolkit + dgrid + test + + + org.dojotoolkit + dstore + test + + + com.esri.js + ags-js-api4 + test + + + com.esri + terraformer-js + test + + + moment-js + moment-js + test + + + de.conterra.js + apprt-polyfill + test + + + de.conterra.js + reactivity + test + + + + + node-install + + + env + !dev + + + + + + com.github.eirslett + frontend-maven-plugin + + + install node and npm + initialize + + install-node-and-npm + + + + npm install + initialize + + npm + + + install + + + + + + + + + env-dev + + + + env + dev + + + + ${basedir}/build.properties + + preview + + + + + org.codehaus.mojo + properties-maven-plugin + + + initialize + + read-project-properties + + + true + + ${local.configfile} + + + + + + + + + + run-js-tests + + test + + + + compress + + compress + production + + + + + maven-deploy-plugin + + true + + + + de.conterra.jsregistry + ct-jsregistry-maven-plugin + + + calculate js dependencies + + calculateDependencies + + + + + + + + + + upload + + + + de.conterra.mapapps + mapapps-maven-plugin + + ${skip.apps.upload} + true + ${project.artifactId} + ${project.artifactId}-${project.version} + PUBLISHED + ${mapapps.remote.base}/resources + ${mapapps.user} + ${mapapps.pw} + ${mapapps.token} + ${mapapps.useChunkedRequestEncoding} + ${triggerPreOptimization} + + + + de.conterra.jsregistry + ct-jsregistry-maven-plugin + + + deploy-bundles + + deployJSPackage + + + ${mapapps.remote.base}/resources/jsregistry + ${mapapps.user} + ${mapapps.pw} + ${mapapps.token} + ${mapapps.useChunkedRequestEncoding} + + + + + + + + + write-release-versions + + ${project.version} + ${project.version} + + + + + + com.google.code.maven-replacer-plugin + replacer + + + + Set Versions from ${replace.source.version} to ${replace.target.version} + validate + + replace + + + ${basedir} + + src/main/js/**/manifest.json + + + src/main/js/**/tests/** + + false + + + "${replace.source.version}" + "${replace.target.version}" + + + "~${replace.source.version}" + "~${replace.target.version}" + + + ^${replace.source.version} + ^${replace.target.version} + + + + + + + + + + +
diff --git a/src/main/config/assembly.xml b/src/main/config/assembly.xml new file mode 100644 index 0000000..9a6a512 --- /dev/null +++ b/src/main/config/assembly.xml @@ -0,0 +1,37 @@ + + + makeZip + false + + zip + + + + ${js.build.outputPath} + + **/* + + + apps/** + **/tests/** + + . + + + \ No newline at end of file diff --git a/src/main/js/apps/sample/app.json b/src/main/js/apps/sample/app.json new file mode 100644 index 0000000..4c07972 --- /dev/null +++ b/src/main/js/apps/sample/app.json @@ -0,0 +1,157 @@ +{ + "load": { + "bundleLocations": [ + "localbundles", + "bundles" + ], + "allowedBundles": [ + "system", + "console", + "notifier", + "splashscreen", + "templatelayout", + "template-seasons", + "theme-autumn", + "theme-everlasting", + "map-init", + "map-preload-2D", + "toolset", + "banner", + "mapnavigation", + "popups-default", + "scalebar", + "locateme", + "parameter-url", + "search-ui", + "locator-store", + "toc", + "dn_portalitemloader" + ] + }, + "bundles": { + "dn_portalitemloader": { + "Config": { + "portals": [ + { + "id": "arcgis", + "title": "ArcGIS Online", + "url": "https://arcgis.com" + }, + { + "id": "portal", + "title": "ArcGIS Portal", + "url": "https://dev0311w.conterra.de/portal" + } + ] + } + }, + "banner": { + "BannerWidget": { + "label": "Developer Network", + "image": "resource('${app}:/images/logo_conterra.png')", + "imageWidth": 200, + "imagePosition": "above", + "link": "http://developernetwork.conterra.de" + } + }, + "map-init": { + "Config": { + "basemaps": [ + { + "id": "esri_street", + "title": "Straßenkarte (grau)", + "thumbnailUrl": "resource('${app}:/images/streets.png')", + "selected": true, + "basemap": "gray-vector" + }, + { + "id": "esri_hybrid", + "title": "Luftbild (hybrid)", + "thumbnailUrl": "resource('${app}:/images/hybrid.png')", + "basemap": "hybrid" + } + ], + "map": { + "layers": [] + }, + "view": { + "viewmode": "2D", + "center": { + "x": 774853, + "y": 6610677, + "spatialReference": 3857 + }, + "scale": 140000 + } + } + }, + "themes": { + "ThemeModel": { + "_selectedTheme": "autumn" + }, + "ThemeSelector": { + "componentEnabled": true + } + }, + "toc": { + "Config": { + "showBasemaps": true, + "showLayerLegend": true + } + }, + "toolset": { + "ToolsetManager": { + "toolsets": [ + { + "id": "mapview_tools", + "tools": [ + "locateMeTool", + "zoomInTool", + "zoomOutTool", + "compassTool", + "restoreInitialViewTool", + "viewmodeSwitcherTool" + ], + "registerWidget": { + "widgetRole": "mapview_tools" + }, + "container": "ignore", + "windowType": "container", + "cssClass": "muted", + "tooltipPositions": [ + "before", + "above", + "below", + "after" + ] + }, + { + "id": "drawer_left", + "title": "Werkzeuge", + "cssClass": "ct-main-app-menu", + "tools": [ + "tocToggleTool", + "printingToggleTool", + "sharelinkTool", + "helloTool", + "IMPRINT_Imprint_CustomInfoTool", + "IMPRINT_Imprint_CustomInfoTool_mobile", + "portalContentToggleTool" + ], + "registerWidget": { + "widgetRole": "drawer_button" + }, + "container": "ignore", + "windowType": "drawer_left", + "tooltipPositions": [ + "after", + "above", + "below", + "before" + ] + } + ] + } + } + } +} diff --git a/src/main/js/apps/sample/images/hybrid.png b/src/main/js/apps/sample/images/hybrid.png new file mode 100644 index 0000000000000000000000000000000000000000..423452eba6485cce1b5dc0cc344645822d04fb02 GIT binary patch literal 7657 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 gN { + model.portalItems = []; + this.queryPortalItems(model.pagination, value, model.searchText, model.spaceFilter, + model.sortAscending, model.sortByField); + }); + + portalContentModel.watch("searchText", ({ value }) => { + this.queryPortalItems(model.pagination, model.portalFilter, value, model.spaceFilter, + model.sortAscending, model.sortByField); + }); + + portalContentModel.watch("pagination", ({ value }) => { + this.queryPortalItems(value, model.portalFilter, model.searchText, model.spaceFilter, + model.sortAscending, model.sortByField); + }); + + portalContentModel.watch("spaceFilter", ({ value }) => { + this.queryPortalItems(model.pagination, model.portalFilter, model.searchText, value, + model.sortAscending, model.sortByField); + }); + + portalContentModel.watch("sortAscending", ({ value }) => { + this.queryPortalItems(model.pagination, model.portalFilter, model.searchText, model.spaceFilter, + value, model.sortByField); + }); + + portalContentModel.watch("sortByField", ({ value }) => { + this.queryPortalItems(model.pagination, model.portalFilter, model.searchText, model.spaceFilter, + model.sortAscending, value); + }); + } + + queryPortalItems(pagination: any, portalFilter: string, searchText: string, spaceFilter: "all" | "organisation" | "my-content", + sortAscending: boolean, + sortByField: Fields): void { + const model = this.portalContentModel; + clearTimeout(this.lastTimeout); + this.lastTimeout = setTimeout(() => { + model.loading = true; + if (this.abortController) { + this.abortController.abort(); + } + const selectedPortal = model.portals.find((portalConfig) => portalConfig.id === model.portalFilter); + const promise = + this.queryPortal(selectedPortal, pagination, searchText, spaceFilter, sortAscending, sortByField); + promise.then((result) => { + this.abortController = null; + model.loading = false; + if (!result) { + return; + } + this.addPortalItemsToModel(result); + model.totalItems = result.total; + }); + }, 500); + } + + private queryPortal(portalConfig: any, pagination: any, searchText: string, spaceFilter: "all" | "organisation" | "my-content", + sortAscending: boolean, + sortByField: Fields): Promise<__esri.PortalQueryResult> { + const page = pagination.page; + const rowsPerPage = pagination.rowsPerPage; + const portal = new Portal(portalConfig.url); + return new Promise(resolve => { + portal.load().then(() => { + let query; + switch (spaceFilter) { + case "all": + query = "1=1"; + break; + case "organisation": + query = "orgid:" + portal.user.orgId; + break; + case "my-content": + query = "owner:" + portal.user.username; + } + if (searchText !== "") { + query += " AND title:" + searchText; + } + const queryParams: __esri.PortalQueryParamsProperties = { + query: query, + sortField: sortByField, + sortOrder: sortAscending ? "asc" : "desc", + filter: 'typeKeywords:Service', + // filter: 'type:"Feature Service"', + num: rowsPerPage, + start: page * rowsPerPage - rowsPerPage + 1 + }; + + const abortController = this.abortController = new AbortController(); + portal.queryItems(queryParams, { signal: abortController.signal }).then((result) => { + resolve(result); + }); + }, (error) => { + resolve(null); + }); + }); + + } + + private addPortalItemsToModel(result: __esri.PortalQueryResult): void { + let portalItems = result.results.filter((result) => result.isLayer); + portalItems = portalItems.map((portalItem) => { + return { + id: portalItem.id, + title: portalItem.title, + snippet: portalItem.snippet, + description: portalItem.description, + thumbnailUrl: portalItem.thumbnailUrl, + tags: portalItem.tags, + owner: portalItem.owner, + numViews: portalItem.numViews, + created: portalItem.created, + modified: portalItem.modified, + type: portalItem.type, + url: portalItem.url, + itemPageUrl: portalItem.itemPageUrl, + portalUrl: portalItem.portal.url + }; + }); + this.portalContentModel.portalItems = portalItems; + } + + addPortalItemLayerToMap(item: any): void { + Layer.fromPortalItem({ + portalItem: { + // autocasts new PortalItem() + id: item.id, + // autocastable to Portal + portal: { + url: item.portalUrl + } + } + }).then((layer) => { + // add the layer to the map + this.mapWidgetModel.map.add(layer); + }); + } + +} diff --git a/src/main/js/bundles/dn_portalcontent/PortalContentModel.ts b/src/main/js/bundles/dn_portalcontent/PortalContentModel.ts new file mode 100644 index 0000000..e5ef204 --- /dev/null +++ b/src/main/js/bundles/dn_portalcontent/PortalContentModel.ts @@ -0,0 +1,62 @@ +/// +/// Copyright (C) 2024 con terra GmbH (info@conterra.de) +/// +/// 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 { Mutable, properties } from "apprt-core/Mutable"; + +function defineProperties(mutableDefinition: any, mutableProperties: P): Impl & Mutable

{ + properties(mutableDefinition, mutableProperties); + return mutableDefinition; +} + +class PortalContentModel extends Mutable {} + +interface PortalContentModelProps { + portals: any[], + portalItems: __esri.PortalItem[], + loading: boolean, + totalItems: number, + rowsPerPageItems: number[], + pagination: object, + portalFilter: string, + spaceFilters: any[], + spaceFilter: "all" | "organisation" | "my-content", + searchText: "", + sortAscending: boolean, + sortByField: "modified" | "title" | "uploaded" | "username" | "created" | "type" | "owner" | "avg-rating" | "num-ratings" | "num-comments" | "num-views", + sortByFields: any[] +} + +export default defineProperties(PortalContentModel, { + portals: [], + portalItems: [], + loading: false, + totalItems: 0, + rowsPerPageItems: [ + 10, + 50, + 100 + ], + pagination: { + rowsPerPage: 10 + }, + portalFilter: "", + spaceFilters: [], + spaceFilter: "all", + searchText: "", + sortAscending: false, + sortByField: "modified", + sortByFields: [] +}); diff --git a/src/main/js/bundles/dn_portalcontent/PortalContentWidgetFactory.ts b/src/main/js/bundles/dn_portalcontent/PortalContentWidgetFactory.ts new file mode 100644 index 0000000..a74f0fa --- /dev/null +++ b/src/main/js/bundles/dn_portalcontent/PortalContentWidgetFactory.ts @@ -0,0 +1,92 @@ +/// +/// Copyright (C) 2024 con terra GmbH (info@conterra.de) +/// +/// 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 type {InjectedReference} from "apprt-core/InjectedReference"; +import Vue from "apprt-vue/Vue"; +import VueDijit from "apprt-vue/VueDijit"; +import Binding from "apprt-binding/Binding"; +import MapWidgetModel from "map-widget/MapWidgetModel"; +import PortalContentController from "./PortalContentController"; +import PortalContentModel from "./PortalContentModel"; +import PortalContentWidget from "./templates/PortalContentWidget.vue"; + +export default class PortalContentWidgetFactory { + + private readonly _i18n!: InjectedReference; + private readonly _mapWidgetModel!: InjectedReference; + private readonly _portalContentModel!: InjectedReference; + private controller: PortalContentController; + private vm: Vue; + private binding: Binding; + + activate(): void { + this.initComponent(); + const i18n = this._i18n.get().ui; + this.controller = new PortalContentController(i18n, this._mapWidgetModel, this._portalContentModel); + } + + deactivate(): void { + this.binding.unbind(); + this.binding = undefined; + } + + createInstance(): any { + const controller = this.controller; + const widget = VueDijit(this.vm, {class: "portal-content-widget"}); + + widget.activateTool = async () => { + this.binding.enable().syncToLeftNow(); + const model = this._portalContentModel; + controller.queryPortalItems(model.pagination, model.portalFilter, model.searchText, + model.spaceFilter, model.sortAscending, model.sortByField); + + this.vm.$on("load-item", (item) => { + controller.addPortalItemLayerToMap(item); + }); + }; + widget.deactivateTool = () => { + this.binding.disable(); + this.vm.$off(); + }; + + widget.own({ + remove() { + this.binding.unbind(); + this.binding = undefined; + this.vm.$off(); + } + }); + return widget; + } + + private initComponent(): void { + const vm = this.vm = new Vue(PortalContentWidget); + const model = this._portalContentModel; + vm.i18n = this._i18n.get().ui; + vm.pagination = model.pagination; + vm.rowsPerPageItems = model.rowsPerPageItems; + vm.portals = model.portals; + vm.spaceFilters = model.spaceFilters; + vm.sortByField = model.sortByField; + vm.sortByFields = model.sortByFields; + + this.binding = Binding.for(vm, model) + .syncAll("portalFilter") + .syncAllToLeft("portalItems", "totalItems", "loading") + .syncAllToRight("pagination", "searchText", "sortByField", "sortAscending", "spaceFilter"); + } + +} diff --git a/src/main/js/bundles/dn_portalcontent/css/styles.css b/src/main/js/bundles/dn_portalcontent/css/styles.css new file mode 100644 index 0000000..334bf03 --- /dev/null +++ b/src/main/js/bundles/dn_portalcontent/css/styles.css @@ -0,0 +1,109 @@ +/** + * Copyright (C) 2024 con terra GmbH (info@conterra.de) + * + * 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. + */ +.ctAppRoot .portal-content-widget { + overflow: hidden; +} + +.ctAppRoot .portal-content-widget .portal-content--container { + height: 100%; +} + +.ctAppRoot .portal-content-widget .left { + width: 300px; +} + +.ctAppRoot .portal-content-widget .center { + overflow: auto;; +} + +.ctAppRoot .portal-content-widget .v-data-iterator { + display: flex; + flex-direction: column; +} + +.ctAppRoot .portal-content-widget .v-data-iterator .loading-indicator { + height: 8px; +} + +.ctAppRoot .portal-content-widget .v-data-iterator>div:first-child { + flex: 0 0 auto; +} + +.ctAppRoot .portal-content-widget .v-data-iterator>div:nth-child(2) { + flex: 1 1 auto; + overflow: auto; +} + +.ctAppRoot .portal-content-widget .v-data-iterator>div:nth-child(3) { + flex: 0 0 auto; +} + +.ctAppRoot .portal-content-widget .v-toolbar__content { + padding-left: 4px; + padding-right: 4px; +} + +/* .ctAppRoot .portal-content-widget .portal-flex-item { + height: 480px; +} */ + +.ctAppRoot .portal-content-widget .portal-flex-item .portal-item-node { + height: 100%; + padding: 4px; +} + +.ctAppRoot .portal-content-widget .portal-item-card { + display: flex; +} + +.ctAppRoot .portal-content-widget .portal-item-card .v-image { + height: 200px; + max-height: 200px; +} + +.ctAppRoot .portal-content-widget .portal-item-card .backup-icon { + height: 200px; + max-height: 200px; +} + +.ctAppRoot .portal-content-widget .portal-item-card .v-list { + padding: 0 0 4px; +} + +.ctAppRoot .portal-content-widget .portal-item-card .v-list--dense .v-list__tile--avatar { + height: 30px; + padding: 0; +} + +.ctAppRoot .portal-content-widget .portal-item-card .v-icon--left { + margin-right: 4px; +} + +.ctAppRoot .portal-content-widget .portal-item-card .item-text { + display: block; + display: -webkit-box; + -webkit-line-clamp: 4; + -webkit-box-orient: vertical; + overflow: hidden; + text-overflow: ellipsis; + + padding-left: 8px; + padding-right: 8px; +} + +.ctAppRoot .portal-content-widget .filter-widget .sort-ascending-button .v-icon { + margin-right: 0; +} diff --git a/src/main/js/bundles/dn_portalcontent/manifest.json b/src/main/js/bundles/dn_portalcontent/manifest.json new file mode 100644 index 0000000..6a6f402 --- /dev/null +++ b/src/main/js/bundles/dn_portalcontent/manifest.json @@ -0,0 +1,224 @@ +{ + "name": "dn_portalitemloader", + "version": "1.0.0-SNAPSHOT", + "title": "${bundleName}", + "description": "${bundleDescription}", + "vendor": "con terra GmbH", + "main": "", + "icon": {}, + "productName": "devnet-mapapps-portal-item-loader", + "keywords": [ + "portal" + ], + "dependencies": { + "apprt-vue": "^4.14.0", + "apprt-vuetify": "^4.14.0", + "apprt-binding": "^4.14.0", + "map-widget": "^4.14.0", + "esri": "^4.22.0" + }, + "cssThemesExtension": [ + { + "name": "*", + "files": [ + "./css/styles.css" + ] + } + ], + "layout-widgets": [ + { + "widgetRole": "portalContentWidget", + "window": { + "dockTool": "portalContentToggleTool", + "closable": true, + "minimizeOnClose": true, + "maximizable": true, + "resizable": true + } + }, + { + "widgetRole": "portalContentWidget", + "sublayout": [ + "desktop" + ], + "window": { + "marginBox": { + "h": "50%", + "w": "50%" + }, + "minSize": { + "h": 600, + "w": 1000 + } + } + }, + { + "widgetRole": "portalContentWidget", + "sublayout": [ + "tablet_landscape", + "tablet_portrait" + ], + "window": { + "resizable": false, + "marginBox": { + "h": "70%", + "w": "70%" + }, + "minSize": { + "h": 600, + "w": 1000 + } + } + }, + { + "widgetRole": "portalContentWidget", + "sublayout": [ + "mobile_landscape", + "mobile_portrait" + ], + "window": { + "resizable": false, + "marginBox": { + "w": "100%", + "h": "100%", + "b": 40, + "l": 0 + } + } + } + ], + "components": [ + { + "name": "Config", + "impl": "./PortalContentModel", + "provides": "dn_portalitemloader.PortalContentModel", + "propertiesConstructor": true, + "properties": { + "portals": [ + { + "id": "arcgis", + "title": "ArcGIS Online", + "url": "https://arcgis.com" + } + ], + "rowsPerPageItems": [ + 10, + 25, + 50, + 100 + ], + "pagination": { + "rowsPerPage": 10 + }, + "spaceFilters": [ + { + "id": "all", + "title": "${ui.spaceFilters.all}" + }, + { + "id": "organisation", + "title": "${ui.spaceFilters.organisation}" + }, + { + "id": "my-content", + "title": "${ui.spaceFilters.my-content}" + } + ], + "spaceFilter": "all", + "sortAscending": false, + "sortByField": "modified", + "sortByFields": [ + { + "id": "modified", + "title": "${ui.sortByFields.modified}" + }, + { + "id": "title", + "title": "${ui.sortByFields.title}" + }, + { + "id": "uploaded", + "title": "${ui.sortByFields.uploaded}" + }, + { + "id": "username", + "title": "${ui.sortByFields.username}" + }, + { + "id": "created", + "title": "${ui.sortByFields.created}" + }, + { + "id": "type", + "title": "${ui.sortByFields.type}" + }, + { + "id": "owner", + "title": "${ui.sortByFields.owner}" + }, + /*{ + "id": "avg-rating", + "title": "${ui.sortByFields.avg-rating}" + }, + { + "id": "num-ratings", + "title": "${ui.sortByFields.num-ratings}" + }, + { + "id": "num-comments", + "title": "${ui.sortByFields.num-comments}" + },*/ + { + "id": "num-view", + "title": "${ui.sortByFields.num-view}" + } + ] + } + }, + { + "name": "PortalContentWidgetFactory", + "provides": [ + "dijit.Widget", + "dn_portalitemloader.Widget" + ], + "instanceFactory": true, + "properties": { + "widgetRole": "portalContentWidget" + }, + "references": [ + { + "name": "_portalContentModel", + "providing": "dn_portalitemloader.PortalContentModel" + }, + { + "name": "_mapWidgetModel", + "providing": "map-widget.MapWidgetModel" + } + ] + }, + { + "name": "PortalContentToggleTool", + "impl": "ct/tools/Tool", + "provides": [ + "ct.tools.Tool" + ], + "propertiesConstructor": true, + "properties": { + "id": "portalContentToggleTool", + "title": "${tool.title}", + "tooltip": "${tool.tooltip}", + "iconClass": "icon-toolcase", + "toolRole": "toolset", + "togglable": true, + "activateHandler": "activateTool", + "deactivateHandler": "deactivateTool" + }, + "references": [ + { + "name": "handlerScope", + "providing": "dn_portalitemloader.Widget" + } + ] + } + ] +} diff --git a/src/main/js/bundles/dn_portalcontent/module.ts b/src/main/js/bundles/dn_portalcontent/module.ts new file mode 100644 index 0000000..dd6b5a8 --- /dev/null +++ b/src/main/js/bundles/dn_portalcontent/module.ts @@ -0,0 +1,20 @@ +/// +/// Copyright (C) 2024 con terra GmbH (info@conterra.de) +/// +/// 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 "."; +import "./PortalContentWidgetFactory"; +import "./PortalContentModel"; +import "ct/tools/Tool"; diff --git a/src/main/js/bundles/dn_portalcontent/nls/bundle.js b/src/main/js/bundles/dn_portalcontent/nls/bundle.js new file mode 100644 index 0000000..2a52adc --- /dev/null +++ b/src/main/js/bundles/dn_portalcontent/nls/bundle.js @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2024 con terra GmbH (info@conterra.de) + * + * 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. + */ +module.exports = { + root: { + bundleName: "Portal Content", + bundleDescription: "This bundle provides access to portal content.", + tool: { + title: "Add Portal content", + tooltip: "Add Portal content to map" + }, + ui: { + all: "All portals", + addToMap: "Add to map", + searchForItems: "Search for items", + searchForItemsPlaceholder: "Search for...", + filterForPortal: "Select portal", + tags: "Tags", + noDataText: "No portal items", + noResultsText: "No portal items found", + spaceFilter: "Where to search", + spaceFilters: { + all: "Everywhere", + organisation: "Only my organisation", + "my-content": "Only my content" + }, + sortBy: "Sort by", + sortByFields: { + title: "Title", + uploaded: "Uploaded", + modified: "Modified", + username: "Username", + created: "Created", + type: "Type", + owner: "Owner", + "avg-rating": "Average rating", + "num-ratings": "Number of ratings", + "num-comments": "Number of comments", + "num-view": "Number of views" + } + } + }, + de: true +}; diff --git a/src/main/js/bundles/dn_portalcontent/nls/de/bundle.js b/src/main/js/bundles/dn_portalcontent/nls/de/bundle.js new file mode 100644 index 0000000..207f225 --- /dev/null +++ b/src/main/js/bundles/dn_portalcontent/nls/de/bundle.js @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2024 con terra GmbH (info@conterra.de) + * + * 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. + */ +module.exports = { + bundleName: "Portalinhalte", + bundleDescription: "Dieses Modul ermöglicht es Portalinhalte zu laden.", + tool: { + title: "Portalinhalte hinzufügen", + tooltip: "Portalinhalte zur Karte hinzufügen" + }, + ui: { + all: "Alle Portale", + addToMap: "Hinzufügen", + searchForItems: "Nach Inhalten suchen", + searchForItemsPlaceholder: "Suchen nach...", + filterForPortal: "Portal auswählen", + tags: "Tags", + noDataText: "Keine Portalinhalte vorhanden", + noResultsText: "Es konnten keine Portalinhalte gefunden werden", + spaceFilter: "Wo suchen", + spaceFilters: { + all: "Überall", + organisation: "Nur meine Organisation", + "my-content": "Nur meine Inhalte" + }, + sortBy: "Sortieren nach", + sortByFields: { + title: "Titel", + uploaded: "Hochgeladen", + modified: "Zuletzt bearbeitet", + username: "Nutzername", + created: "Erstellt", + type: "Typ", + owner: "Besitzer", + "avg-rating": "Durchschnittliche Bewertung", + "num-ratings": "Anzahl der Bewertungen", + "num-comments": "Anzahl der Kommentare", + "num-view": "Anzahl der Zugriffe" + } + } +}; diff --git a/src/main/js/bundles/dn_portalcontent/templates/FilterWidget.vue b/src/main/js/bundles/dn_portalcontent/templates/FilterWidget.vue new file mode 100644 index 0000000..437ae80 --- /dev/null +++ b/src/main/js/bundles/dn_portalcontent/templates/FilterWidget.vue @@ -0,0 +1,178 @@ + + + diff --git a/src/main/js/bundles/dn_portalcontent/templates/PortalContentWidget.vue b/src/main/js/bundles/dn_portalcontent/templates/PortalContentWidget.vue new file mode 100644 index 0000000..be79be0 --- /dev/null +++ b/src/main/js/bundles/dn_portalcontent/templates/PortalContentWidget.vue @@ -0,0 +1,191 @@ + + + diff --git a/src/main/js/bundles/dn_portalcontent/templates/PortalItem.vue b/src/main/js/bundles/dn_portalcontent/templates/PortalItem.vue new file mode 100644 index 0000000..a91e59f --- /dev/null +++ b/src/main/js/bundles/dn_portalcontent/templates/PortalItem.vue @@ -0,0 +1,175 @@ + + + diff --git a/src/main/types/mocha-global.d.ts b/src/main/types/mocha-global.d.ts new file mode 100644 index 0000000..7ae74ef --- /dev/null +++ b/src/main/types/mocha-global.d.ts @@ -0,0 +1 @@ +declare const testConfig: (opts?: Record) => void; diff --git a/src/main/types/thirdparty.d.ts b/src/main/types/thirdparty.d.ts new file mode 100644 index 0000000..476971b --- /dev/null +++ b/src/main/types/thirdparty.d.ts @@ -0,0 +1,7 @@ +/* + * Copyright (C) con terra GmbH + */ + +declare module "dojo/*"; +declare module "dijit/*"; +declare module "dojox/*"; diff --git a/src/main/types/vue-shim.d.ts b/src/main/types/vue-shim.d.ts new file mode 100644 index 0000000..d6e6b86 --- /dev/null +++ b/src/main/types/vue-shim.d.ts @@ -0,0 +1,4 @@ +declare module "*.vue" { + import Vue from "vue"; + export default Vue; +} diff --git a/src/support/js/check-licenses.ts b/src/support/js/check-licenses.ts new file mode 100644 index 0000000..9eda7f4 --- /dev/null +++ b/src/support/js/check-licenses.ts @@ -0,0 +1,59 @@ +/* + Checks licenses of node dependencies against a list of allowed licenses. + Fails with exit code != 0 and an error message if a disallowed license is encountered. + The script should be executed from the project root directory after dependencies have been installed: + + $ tsx ./src/support/js/check-licenses.ts + + To run checks yourself (e.g. to update the allow list or to get details), install + license-checker yourself and run it from the project root directory: + + $ npm install -g license-checker + $ license-checker --summary # outputs list of used licenses + $ license-checker --json # outputs details + + See also https://www.npmjs.com/package/license-checker +*/ + +import { init as initChecker } from "license-checker"; +import { cwd, exit} from "node:process"; + +// Licenses known to be OK. +const ACCEPTED_LICENSES = [ + "MIT", + "ISC", + "Apache-2.0", + "Apache 2.0", + "BSD-2-Clause", + "BSD-3-Clause", + "BSD", + "CC0-1.0", + "CC-BY-3.0", + "CC-BY-4.0", + "Python-2.0", + "Unlicense" // Note: not unlicenseD (https://opensource.org/licenses/unlicense) +]; + +// Packages with licenses that are not recognized properly by license-checker. +// These must be checked manually. +const SKIP_PACKAGES = [ + "event-stream@3.0.20", // MIT License not recognized + "taffydb@2.6.2" // BSD-1-Clause License in source code +]; + +initChecker( + { + start: cwd(), + onlyAllow: ACCEPTED_LICENSES.join(";"), + excludePackages: SKIP_PACKAGES.join(";") + }, + (error, packages) => { + void packages; // currently unused + + if (error) { + console.error("Error: ", error); + exit(1); + } + exit(0); + } +); diff --git a/src/test/resources/application.properties b/src/test/resources/application.properties new file mode 100644 index 0000000..3236f13 --- /dev/null +++ b/src/test/resources/application.properties @@ -0,0 +1,89 @@ +# +# Copyright (C) 2024 con terra GmbH (info@conterra.de) +# +# 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. +# + +# Global configuration properties +# These properties are used inside the development server +# and can be used to document required global properties + +# url to the remote map.apps instance +mapapps.remote.base=${mapapps.remote.base} +# title in apps +client.config.productname=Developer Network +# defines runtime version for the sample project. Defaults to map.apps 3.x +# to develope mapapps 4.x apps, change this property +apprt.version=${apprt.version} + +# the init file to bootstrap map.apps +client.config.initjs=$\{mapapps.remote.base\}/resources/jsregistry/root/apprt-boot/$\{apprt.version\}/boot.js + +# defines the initialization layer file +# you can use a comma separated list of layer files +# or change the name of the main layer file +client.config.mainlayerfiles=apprt/launch/layer + +# This is used to filter a default app name in the *.html files +default.app.name=${default.app.name} + +# the following is used by the ProxyServlet to read the allowed url which can be forwared by this application +# For ex. (secured server): http://myserver.mycompany.com/arcgis/rest/services,ayn2C2iPvqjeqWoXwV6rjmr43kyo23mhIPnXz2CEiMA6rVu0xR0St8gKsd0olv8a; +# For ex. (non-secured server): http://sampleserver1.arcgisonline.com/arcgis/rest/services; +# [,];\ or +# [,,];\ (username/password currently not encrypted) +proxy.allowedServerUrls=${proxy.allowedServerUrls} + +# Defines rules for proxy access based on host names. +# It provides more fine grained control what requests use a proxy page. +# Sample: +# proxy.use.rules=sample1.mapapps.de,sample2.mapapps.de|/proxyPage +# +# sample1.mapapps.de goes over default proxy page "/proxy", +# sample2.mapapps.de goes over own proxy page "/proxyPage' +proxy.use.rules=${proxy.use.rules} + +# Starting with version 4.6.0 map.apps assumes accessed resources/webservers support CORS +# http://enable-cors.org/ +# These properties won't be considered anymore: +# proxy.cors.enabledServers, proxy.cors.detection, proxy.cors.enabled +# comma separated list of servers like: +# http://servicesbeta.esri.com,http://server.organization.com +cors.request.trustedServers=${cors.request.trustedServers} + +# Enable debug logging (in browser console). +client.config.logging=DEBUG + +############## Using the Identity Service ############## + +security.login.base=$\{mapapps.remote.base\} +security.login.service.url=$\{security.login.base\}/account/login +security.logout.service.url=$\{security.login.base\}/account/logout +security.self.service.url=$\{security.login.base\}/account/self +security.identity.tokens.service.url=$\{security.login.base\}/account/tokens +security.identity.self.service.url=$\{security.self.service.url\} + +# Configuration options for a remote identity service instance +#security.mode=IDENTITY +#security.login.base=https://identity.service.hostname/identity +#esri.api.arcgisPortalUrl=https://myhost.example.com/portal +# Optional: +#security.identity.cookieName=ctIDENTITY +#cors.request.trustedServers=https://identity.service.hostname + +# Configuration options for a local identity service instance e.g. based on ct-identity docker image +#security.mode=IDENTITY +# URL of the docker container providing the identity service +#security.login.base=http://localhost:8080 +#esri.api.arcgisPortalUrl=https://myhost.example.com/portal +#proxy.allowedServerUrls=http://localhost:8080/**,trusted:true; diff --git a/src/test/webapp/WEB-INF/web.xml b/src/test/webapp/WEB-INF/web.xml new file mode 100644 index 0000000..fc35e73 --- /dev/null +++ b/src/test/webapp/WEB-INF/web.xml @@ -0,0 +1,34 @@ + + + + map.apps sample + + + + + ProxyServlet + ProxyServlet + de.conterra.proxy.servlet.ProxyServlet + 0 + + + ProxyServlet + /proxy + + + + + + default + + useFileMappedBuffer + false + + + + diff --git a/src/test/webapp/favicon.png b/src/test/webapp/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..899ca557bfbbd79efe37f425a2bac85cf7fa23d4 GIT binary patch literal 45042 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

)>@;{k^pZ$=OS4^NB) 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 literal 0 HcmV?d00001 diff --git a/src/test/webapp/index.html b/src/test/webapp/index.html new file mode 100644 index 0000000..f6bd269 --- /dev/null +++ b/src/test/webapp/index.html @@ -0,0 +1,134 @@ + + + + + + + + + + + + + + + + + + + + map.apps sample + + + + +

+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+ +

+
+
+

 

+
+
+
+ +
+ + diff --git a/src/test/webapp/init.css b/src/test/webapp/init.css new file mode 100644 index 0000000..1f96acb --- /dev/null +++ b/src/test/webapp/init.css @@ -0,0 +1,326 @@ +/** + * Copyright (C) 2024 con terra GmbH (info@conterra.de) + * + * 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. + */ +/* Styles for progress on startup function */ +@import url("login.css"); + +html, +body { + margin: 0; + padding: 0; + height: 100%; +} + +.ct-splashscreen { + /* splashscreen colors*/ + --light-bg-color: #f3f5f8; + --light-secondary-bg-color: #f3f3f3; + --light-normal-text-color: #3c3c3c; + --dark-bg-color: #29292b; + --dark-secondary-bg-color: #48484b; + --dark-normal-text-color: #fff; + --header-expanded-height: 42px; + --footer-expanded-height: var(--header-expanded-height); + + position: absolute; + top: 0; left: 0; right:0; bottom: 0; + display: flex; + flex-direction: column; + background: var(--light-bg-color); + font-size: 15px; /*Set base font-size to prevent jiggling when theme comes in*/ + font-family: sans-serif; +} +.ct-splashscreen, +.ct-splashscreen-header, +.ct-splashscreen-footer { + transition: background-color .8s ease-in-out; +} + +/* in and out of loader */ +.ct-splashscreen { + z-index: 1; /*ensure Splash is above app and can fade in and out properly*/ + animation: splashscreen-appear ease .3s 1 forwards; +} + +.started .ct-splashscreen, +.ct-splashscreen--disappearing { + animation: splashscreen-disappear .8s linear 1 forwards; +} +/* ensure splashscreen is hidden when app is started even without splashscreen bundle*/ +.started .ct-splashscreen:not(.ct-splashscreen--disappearing){ + display: none; +} + +.ct-splashscreen-header, +.ct-splashscreen-footer { + flex: 0 0 0; + opacity: 0; + display: flex; + align-items: center; + background-color: var(--light-secondary-bg-color); + box-shadow: rgba(0, 0, 0, 0.12) 0 1px 6px, rgba(0, 0, 0, 0.12) 0 1px 4px; + animation: .5s ease-in-out 2s forwards; +} +.ct-splashscreen-header { + animation-name: header-appear; +} +.ct-splashscreen-footer { + animation-name: footer-appear; + border-bottom: 3px solid #2980b9; +} + +.ct-splashscreen-main { + flex: 1 1 auto; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + opacity: 0; + animation: splashscreen-appear .5s ease-in-out 1 forwards; +} + +.ct-splashscreen-stage { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + overflow: hidden; + padding-top: 15px; + color: var(--light-normal-text-color); + position: absolute; + top: 0; + width: 100%; + height: 100%; + +} + +.ct-splashscreen-stage .splashHeader { + font-weight: normal; + font-size: 1.07rem; + min-height: 1em; + line-height: 1.2857em; + margin: calc(1.85715rem) 0 1rem; +} + +/*.splashHeader,*/ +.ct-splashscreen-stage .launchLog{ + transition: opacity .5s ease-in-out; +} + +.ct-splashscreen-stage .launchLog--error{ + color: #e74c3c; +} + +.ct-splashscreen-stage .launch__bundles{ + font-size: .8rem; + padding-left: .8rem; + color: rgba(94,94,94,.6); + position: absolute; + bottom: 55px; + left: 3px; +} + +.ct-splashscreen-stage .launch__animation { + position: relative; + width: 600px; + height: 60px; + text-align: center; + animation-duration: 6s; +} + +.everlasting .ct-splashscreen, + /* --dark Fuzzy-Selector for dark themes other than everlasting*/ +.ctAppRoot[class*=" --dark"] .ct-splashscreen, +.ct-splashscreen--dark{ + background: var(--dark-bg-color); +} +.everlasting .ct-splashscreen-header, +.everlasting .ct-splashscreen-footer, +.ctAppRoot[class*=" --dark"] .ct-splashscreen-header, +.ctAppRoot[class*=" --dark"] .ct-splashscreen-footer, +.ct-splashscreen--dark .ct-splashscreen-header, +.ct-splashscreen--dark .ct-splashscreen-footer { + background: var(--dark-secondary-bg-color); +} + +.everlasting .ct-splashscreen-stage, +.ctAppRoot[class*=" --dark"] .ct-splashscreen-stage, +.ct-splashscreen--dark .ct-splashscreen-stage { + color: #fff +} + +.everlasting .ct-splashscreen-footer, +.ctAppRoot[class*=" --dark"] .ct-splashscreen-footer, +.ct-splashscreen--dark .ct-splashscreen-footer { + border-bottom: 3px solid #2a2a2a; +} +/* Animated Dots */ +.ct-splashscreen-stage .dot-outer { + display: inline-block; +} + +.ct-splashscreen-stage .dot { + display: inline-block; + width: 30px; + height: 30px; + border-radius: 100%; + background: #12a5f4; + position: absolute; + left: 0; + top: 0; + animation: bring-in-small-dots 3s infinite ease-in-out; +} + +.splashScreen--error .dot{ + animation-play-state: paused; +} +.ct-splashscreen-stage .dot-outer:first-child .dot { + animation-name: bring-in-and-scale-first-dot; +} +.ct-splashscreen-stage.dot-outer:last-child .dot { + animation-name: bring-in-and-scale-last-dot; +} +.ct-splashscreen-stage .dot-1 { + animation-delay: 0s; +} +.ct-splashscreen-stage .dot-2 { + animation-delay: 0.21s; +} +.ct-splashscreen-stage .dot-3 { + animation-delay: 0.42s; +} +.ct-splashscreen-stage .dot-4 { + animation-delay: 0.63s; +} +.ct-splashscreen-stage .dot-5 { + animation-delay: 0.84s; +} +.ct-splashscreen-stage .dot-6 { + animation-delay: 1.05s; +} +@keyframes bring-in-small-dots { + from { + opacity: 0; + transform: translateX(0); + } + 25% { + opacity: 1; + } + 35% { + transform: translateX(285px); + } + 65% { + transform: translateX(285px); + } + 75% { + opacity: 1; + } + to { + opacity: 0; + transform: translateX(585px); + } +} +@keyframes bring-in-and-scale-first-dot { + from { + opacity: 0; + transform: translateX(0); + } + 25% { + opacity: 1; + } + 35% { + transform: translateX(285px) scale(1); + } + 65% { + transform: translateX(285px) scale(1.7); + } + 75% { + opacity: 1; + transform: translateX(285px) scale(1); + } + to { + opacity: 0; + transform: translateX(585px) scale(1); + } +} +@keyframes bring-in-and-scale-last-dot { + from { + opacity: 0; + transform: translateX(0) scale(1); + } + 25% { + opacity: 1; + } + 30% { + transform: translateX(285px) scale(1); + } + 31% { + transform: translateX(285px) scale(1.7); + } + /*begin of shrink*/ + 40% { + transform: translateX(285px) scale(1.7); + } + /*end of shrink and start of move right*/ + 65% { + opacity: 1; + transform: translateX(285px) scale(1); + } + to { + opacity: 0; + transform: translateX(585px); + } +} + +@keyframes splashscreen-appear { + 0% { opacity: 0 } + 100% { opacity: 1 } +} +@keyframes splashscreen-disappear { + from { opacity: 1 } + to { opacity: 0 } +} + +@keyframes header-appear { + from { + opacity: 0; + flex-basis: 0; + } + to { + opacity: 1; + flex-basis: var(--header-expanded-height); + } +} +@keyframes footer-appear { + from { + opacity: 0; + flex-basis: 0; + } + to { + opacity: 1; + flex-basis: var(--footer-expanded-height); + } +} + +/* hide mocked header and footer in mobile layouts*/ +@media (max-width: 768px) { + .ct-splashscreen-header, + .ct-splashscreen-footer{ + display: none; + } + .ct-splashscreen-stage .launch__bundles{ + bottom: 5px; + } +} diff --git a/src/test/webapp/js/tests/init-packs.js b/src/test/webapp/js/tests/init-packs.js new file mode 100644 index 0000000..4d0d65f --- /dev/null +++ b/src/test/webapp/js/tests/init-packs.js @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2024 con terra GmbH (info@conterra.de) + * + * 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. + */ +if (require.packs["@vue/test-utils"]) { + require.packs["@vue/test-utils"].main = "dist/vue-test-utils.umd"; +} +if (require.packs["chai"]) { + require.packs["chai"].main = "chai"; +} diff --git a/src/test/webapp/js/tests/runTests.html b/src/test/webapp/js/tests/runTests.html new file mode 100644 index 0000000..befe8bb --- /dev/null +++ b/src/test/webapp/js/tests/runTests.html @@ -0,0 +1,27 @@ + + + + + Tests + + + + Redirecting to test runner. + + diff --git a/src/test/webapp/js/tests/test-init.js b/src/test/webapp/js/tests/test-init.js new file mode 100644 index 0000000..85d5f99 --- /dev/null +++ b/src/test/webapp/js/tests/test-init.js @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2024 con terra GmbH (info@conterra.de) + * + * 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. + */ +// eslint-disable-next-line no-undef +testConfig({ + jsregistry: [{ + //root: "url to registry..", + packages: [ + // register all self hosted packages + "*" + ]} + //uncomment, if project runs in remote mode + // ,{ + // root: "@@mapapps.remote.base@@/resources/jsregistry/root", + // packages: [ + // "apprt-polyfill", + // "apprt@4.x", + // "apprt-vue@4.x", + // "vuetify@~0.14.7", + // "esri@4.x" + // ] + // } + ], + // ensure babel polyfill is loaded during test execution + deps: [ + "apprt-polyfill", + // Needed for import { assert } from "chai" + "/js/tests/init-packs.js" + ] +}); diff --git a/src/test/webapp/login.css b/src/test/webapp/login.css new file mode 100644 index 0000000..6c614f5 --- /dev/null +++ b/src/test/webapp/login.css @@ -0,0 +1,132 @@ +/** + * Copyright (C) 2024 con terra GmbH (info@conterra.de) + * + * 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. + */ +/* +* +*styles for login window +* +*/ +.start.notheme .esriSignInDialog { + font-family: Helvetica, 'sans-serifs'; + background-color: #48484B; + border: none; + color: #fff; + -webkit-box-shadow: 1px 1px 5px rgba(0, 0, 0, 0.25) ; + -moz-box-shadow: 1px 1px 5px rgba(0, 0, 0, 0.25) ; + box-shadow: 1px 1px 5px rgba(0, 0, 0, 0.25) ; + font-size: .688em; + padding: 0; +} +.start.notheme .esriSignInDialog .dijitDialogTitleBar{ + background-color: #2C2D2E; + font-size: 1.5em; + padding: 5px 8px; + cursor: move; +} +.start.notheme .esriSignInDialog .dijitDialogCloseIcon{ + float: right; + cursor: pointer; + position: relative; + top: -2px; + right: 1px; +} +.start.notheme .esriSignInDialog .dijitDialogPaneContentArea{ + padding: 5px 7px 4px 7px; +} +.start.notheme .esriSignInDialog .dijitDialogPaneActionBar{ + padding: 5px 8px 14px; + text-align: right; +} +.start.notheme .esriSignInDialog .dijitButtonNode{ + padding: 4px 12px; + color: #fff; + font-weight: bold; + background-color: #12A5F4; +} +.start.notheme .dijitDialogPaneActionBar .dijitButton:last-child .dijitButtonNode{ + background: #7C7C7C; +} +.start.notheme .esriSignInDialog .dijitButtonHover .dijitButtonNode{ + background-color: #2AAEF5; +} +.start.notheme .esriSignInDialog .dijitTextBox { + background-color: #FFF; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + border: 1px solid #48484B; + color: #000; + margin-top: 3px; +} +.start.notheme .esriSignInDialog .dijitTextBox .dijitInputField { + padding-left: 5px; +} +.start.notheme .esriSignInDialog .dijitInputInner { + border: 0 !important; + vertical-align: middle !important; + background-color: transparent !important; + width: 100% !important; +} +.start.notheme .esriSignInDialog .dijitTextBox .dijitInputInner{ + padding: 1px 2px 1px 0; + outline: none; +} +.start.notheme .esriSignInDialog .dijitTextBox .dijitValidationContainer, +.start.notheme .esriSignInDialog .dijitToggleButtonIconChar { + display: none !important; +} +.start.notheme .dijitButton{ + margin: 0.2em; + vertical-align: middle; + cursor: pointer; +} +.start.notheme .dijitOffScreen { + position: absolute; + left: 50%; + top: -10000px; +} +.start.notheme .esriSignInDialog .esriErrorMsg{ + background: #C0392B; + padding: 3px; +} +.start.notheme .esriSignInDialog .dijitTextBoxHover, +.start.notheme .esriSignInDialog .dijitTextBoxFocused{ + border-color: #12A5F4; + -ms-transition-duration: .1s; + -moz-transition-duration: .1s; + -webkit-transition-duration: .1s; + -o-transition-duration: .1s; + transition-duration: .1s; +} + +.start.notheme .esriSignInDialog .dijitTextBoxError{ + border-color: #C0392B; + border-right-width: 10px; + position: relative; +} +.start.notheme .esriSignInDialog .dijitTextBoxError:after { + content: '!'; + position: absolute; + right: -7px; + top: 1px; + color: #FFF; + font-size: 17px; +} + +/*IE and FF calculates the textbox input smaller so that the surround error container doesn't fit'*/ +.dj_ie .start.notheme .dijitInputInner, +.dj_gecko .start.notheme .dijitInputInner{ + height: 22px; +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..ff7e3ae --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "target": "ESNext", + "module": "ESNext", + "moduleResolution": "bundler", + "baseUrl": ".", + "paths": { + "*": ["./src/main/js/bundles/*", "./node_modules/@conterra/ct-mapapps-typings/*"] + }, + "allowJs": true, + "noEmit": true, + "strict": true, + "noImplicitAny": true, + "strictNullChecks": true, + "skipLibCheck": true, + "esModuleInterop": true + }, + "include": ["src"] +}