├── .gitignore ├── CloudFormationDeployment_CloudFrontDistributions.yaml ├── CloudFormationDeployment_CustomInstallation.yaml ├── CloudFormationDeployment_FullInstallation.yaml ├── DynamoDB.md ├── LICENSE ├── NOTICE ├── README.md ├── SAMTemplate.yaml ├── backend ├── aws-serverless-cms-backup │ ├── functions │ │ ├── backup-service.mjs │ │ ├── codebuild-service.mjs │ │ └── lambda-utils.mjs │ ├── index.mjs │ ├── package-lock.json │ └── package.json ├── aws-serverless-cms-cf │ ├── functions │ │ ├── cf-service.mjs │ │ └── lambda-utils.mjs │ ├── index.mjs │ └── package.json ├── aws-serverless-cms-cognito │ ├── functions │ │ ├── cognito-service.mjs │ │ └── lambda-utils.mjs │ ├── index.mjs │ └── package.json ├── aws-serverless-cms-fileadmin │ ├── functions │ │ ├── lambda-utils.mjs │ │ └── s3-service.mjs │ ├── index.mjs │ └── package.json ├── aws-serverless-cms-fileupload │ ├── functions │ │ └── lambda-utils.mjs │ ├── index.mjs │ ├── package-lock.json │ └── package.json ├── aws-serverless-cms-forms │ ├── functions │ │ ├── lambda-utils.mjs │ │ └── reCAPTCHAv3.mjs │ ├── index.mjs │ ├── package-lock.json │ └── package.json ├── aws-serverless-cms-img │ ├── functions │ │ ├── lambda-utils.mjs │ │ └── storage-service.mjs │ ├── index.mjs │ ├── package-lock.json │ └── package.json ├── aws-serverless-cms-listcontent │ ├── functions │ │ └── lambda-utils.mjs │ └── index.mjs ├── aws-serverless-cms-migration │ ├── functions │ │ ├── lambda-utils.mjs │ │ └── storage-service.mjs │ ├── index.mjs │ └── package.json ├── aws-serverless-cms-publish │ ├── functions │ │ ├── feed-service.mjs │ │ ├── lambda-utils.mjs │ │ └── publish-service.mjs │ ├── index.mjs │ ├── package-lock.json │ └── package.json ├── aws-serverless-cms │ ├── functions │ │ ├── lambda-utils.mjs │ │ └── storage-service.mjs │ ├── index.mjs │ └── package.json └── optional │ ├── aws-serverless-cms-algoliaindexer │ ├── functions │ │ └── lambda-utils.mjs │ ├── index.mjs │ ├── package-lock.json │ └── package.json │ ├── aws-serverless-cms-mailer │ ├── index.js │ ├── package.json │ └── readme.md │ ├── aws-serverless-cms-search │ ├── index.js │ ├── package-lock.json │ ├── package.json │ └── readme.md │ └── aws-serverless-cms-sitemap │ ├── index.js │ ├── package-lock.json │ ├── package.json │ └── readme.md ├── buildspec_1_backend.yml ├── buildspec_2_frontend.yml ├── frontend ├── .browserslistrc ├── .eslintrc.js ├── .prettierrc ├── angular.json ├── e2e │ ├── protractor.conf.js │ ├── src │ │ ├── app.e2e-spec.ts │ │ └── app.po.ts │ └── tsconfig.json ├── initial-config-script │ ├── create-initial-config.js │ ├── package-lock.json │ └── package.json ├── karma.conf.js ├── package-lock.json ├── package.json ├── set-env.js ├── src │ ├── app │ │ ├── app.component.css │ │ ├── app.component.html │ │ ├── app.component.spec.ts │ │ ├── app.component.ts │ │ ├── app.module.ts │ │ ├── auth │ │ │ ├── auth.guard.ts │ │ │ ├── aws-config.js │ │ │ ├── myhttpinterceptor.ts │ │ │ └── userlogin.service.ts │ │ ├── editor │ │ │ ├── fileexplorer │ │ │ │ ├── fileeditor │ │ │ │ │ ├── fileeditor.compo.html │ │ │ │ │ └── fileeditor.compo.ts │ │ │ │ ├── fileuploader │ │ │ │ │ ├── FileUploadDialog.html │ │ │ │ │ ├── FileUploadDialog.ts │ │ │ │ │ └── UploadDialog.css │ │ │ │ ├── imageuploader │ │ │ │ │ ├── ImgUploadDialog.html │ │ │ │ │ ├── ImgUploadDialog.ts │ │ │ │ │ └── UploadDialog.css │ │ │ │ ├── listfiles.compo.css │ │ │ │ ├── listfiles.compo.html │ │ │ │ └── listfiles.compo.ts │ │ │ ├── forms │ │ │ │ ├── form.ts │ │ │ │ ├── formedit.compo.html │ │ │ │ ├── formedit.compo.ts │ │ │ │ ├── forms.compo.html │ │ │ │ └── forms.compo.ts │ │ │ ├── layoutblocks │ │ │ │ ├── PugBlock.ts │ │ │ │ ├── pugblockedit.compo.html │ │ │ │ ├── pugblockedit.compo.ts │ │ │ │ ├── pugblocks.compo.html │ │ │ │ └── pugblocks.compo.ts │ │ │ ├── layouts │ │ │ │ ├── dialogs │ │ │ │ │ ├── layoutfield.dialog.html │ │ │ │ │ └── layoutfield.dialog.ts │ │ │ │ ├── layout.ts │ │ │ │ ├── layoutedit.compo.html │ │ │ │ ├── layoutedit.compo.ts │ │ │ │ ├── layouts.compo.html │ │ │ │ └── layouts.compo.ts │ │ │ ├── microtemplates │ │ │ │ ├── MicroTemplate.ts │ │ │ │ ├── dialogs │ │ │ │ │ ├── IconSelectDialog.css │ │ │ │ │ ├── IconSelectDialog.html │ │ │ │ │ └── IconSelectDialog.ts │ │ │ │ ├── mtedit.compo.html │ │ │ │ ├── mtedit.compo.ts │ │ │ │ ├── mtlist.compo.html │ │ │ │ └── mtlist.compo.ts │ │ │ ├── pages │ │ │ │ ├── dialogs │ │ │ │ │ ├── FileSelectionDialog.html │ │ │ │ │ ├── FileSelectionDialog.ts │ │ │ │ │ ├── MTContentDialog.html │ │ │ │ │ ├── MTContentDialog.ts │ │ │ │ │ ├── MTSelectDialog.html │ │ │ │ │ ├── MTSelectDialog.ts │ │ │ │ │ ├── PublishDialog.css │ │ │ │ │ ├── PublishDialog.html │ │ │ │ │ └── PublishDialog.ts │ │ │ │ ├── mttable.component.css │ │ │ │ ├── mttable.component.html │ │ │ │ ├── mttable.component.ts │ │ │ │ ├── page.ts │ │ │ │ ├── pageedit.compo.html │ │ │ │ ├── pageedit.compo.ts │ │ │ │ ├── pages.compo.html │ │ │ │ ├── pages.compo.ts │ │ │ │ ├── treeview │ │ │ │ │ ├── treeview.compo.html │ │ │ │ │ ├── treeview.compo.ts │ │ │ │ │ └── treeview.component.css │ │ │ │ └── trumbowyg.table.css │ │ │ └── publication │ │ │ │ ├── dialogs │ │ │ │ ├── BulkPublishDialog.html │ │ │ │ ├── BulkPublishDialog.ts │ │ │ │ ├── CFInvalidationDialog.html │ │ │ │ ├── CFInvalidationDialog.ts │ │ │ │ ├── FeedPublishDialog.css │ │ │ │ ├── FeedPublishDialog.html │ │ │ │ ├── FeedPublishDialog.ts │ │ │ │ ├── PublishLogDialog.html │ │ │ │ └── PublishLogDialog.ts │ │ │ │ ├── pubqueue.compo.html │ │ │ │ └── pubqueue.compo.ts │ │ ├── form-inbox │ │ │ ├── formsinbox.compo.html │ │ │ ├── formsinbox.compo.ts │ │ │ ├── submittedform.compo.html │ │ │ └── submittedform.compo.ts │ │ ├── home │ │ │ ├── home.compo.css │ │ │ ├── home.compo.html │ │ │ └── home.compo.ts │ │ ├── login-dialog │ │ │ ├── logindialog.component.css │ │ │ ├── logindialog.component.html │ │ │ └── logindialog.component.ts │ │ ├── services │ │ │ ├── backend.service.ts │ │ │ ├── fileadmin.service.ts │ │ │ ├── filebrowser.service.ts │ │ │ └── tabs.service.ts │ │ ├── settings │ │ │ ├── AboutDialog.css │ │ │ ├── AboutDialog.html │ │ │ ├── AboutDialog.ts │ │ │ ├── dialogs │ │ │ │ ├── ImageConversion.ts │ │ │ │ ├── ImageProfile.ts │ │ │ │ ├── bookmarkedit-dialog.html │ │ │ │ ├── bookmarkedit-dialog.ts │ │ │ │ ├── bucketedit-dialog.html │ │ │ │ ├── bucketedit-dialog.ts │ │ │ │ ├── buildproject-dialog.html │ │ │ │ ├── buildproject-dialog.ts │ │ │ │ ├── cfdistedit-dialog.html │ │ │ │ ├── cfdistedit-dialog.ts │ │ │ │ ├── feededit-dialog.html │ │ │ │ ├── feededit-dialog.ts │ │ │ │ ├── fnedit-dialog.html │ │ │ │ ├── fnedit-dialog.ts │ │ │ │ ├── imgprofileedit-dialog.html │ │ │ │ ├── imgprofileedit-dialog.ts │ │ │ │ ├── pkgupload-dialog.css │ │ │ │ ├── pkgupload-dialog.html │ │ │ │ ├── pkgupload-dialog.ts │ │ │ │ ├── restore-dialog.html │ │ │ │ ├── restore-dialog.ts │ │ │ │ ├── updater-dialog.css │ │ │ │ ├── updater-dialog.html │ │ │ │ ├── updater-dialog.ts │ │ │ │ ├── variableedit-dialog.html │ │ │ │ └── variableedit-dialog.ts │ │ │ ├── migration-dialog │ │ │ │ ├── migration-dialog.html │ │ │ │ └── migration-dialog.ts │ │ │ ├── settings.compo.css │ │ │ ├── settings.compo.html │ │ │ └── settings.compo.ts │ │ ├── useradmin │ │ │ ├── dialogs │ │ │ │ ├── addgroup-dialog.html │ │ │ │ ├── addgroup-dialog.ts │ │ │ │ ├── newuserprofile.dialog.html │ │ │ │ ├── newuserprofile.dialog.ts │ │ │ │ ├── pwdchange-dialog.html │ │ │ │ ├── pwdchange-dialog.ts │ │ │ │ ├── userprofile.dialog.css │ │ │ │ ├── userprofile.dialog.html │ │ │ │ └── userprofile.dialog.ts │ │ │ ├── myprofile.compo.html │ │ │ ├── myprofile.compo.ts │ │ │ ├── userlist.compo.html │ │ │ └── userlist.compo.ts │ │ ├── utils │ │ │ ├── OrderBy.Pipe.ts │ │ │ └── searchfilterpipe.ts │ │ └── version.ts │ ├── assets │ │ ├── .gitkeep │ │ ├── bootstrap-grid.min.css │ │ ├── bootstrap-grid.min.css.map │ │ ├── img │ │ │ ├── CloudeeCMS-v0bb.svg │ │ │ ├── CloudeeCMS.svg │ │ │ ├── WebGateLogo.svg │ │ │ ├── apple-touch-icon.png │ │ │ ├── clouds1.svg │ │ │ ├── clouds2.svg │ │ │ ├── clouds3.svg │ │ │ ├── favicon-w.png │ │ │ ├── favicon.ico │ │ │ ├── favicon.png │ │ │ └── trumbowyg-icons.svg │ │ ├── js │ │ │ └── trumbowyg.plugins.js │ │ └── mat-webui.scss │ ├── environments │ │ └── README.md │ ├── index.html │ ├── main.ts │ ├── polyfills.ts │ ├── styles.scss │ └── test.ts ├── tsconfig.app.json ├── tsconfig.json └── tsconfig.spec.json └── packaging ├── cf-firstrun ├── index.mjs └── package.json └── create_package.sh /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | /frontend/src/environments/environment.prod.ts 4 | /frontend/src/environments/environment.ts 5 | /frontend/stackinfo.json 6 | /frontend/output.yaml 7 | /frontend/.angular 8 | 9 | # compiled output 10 | /frontend/dist 11 | /dist 12 | /tmp 13 | /out-tsc 14 | /frontend/.angular/cache 15 | 16 | # Only exists if Bazel was run 17 | /bazel-out 18 | 19 | # dependencies 20 | node_modules 21 | 22 | 23 | # profiling files 24 | chrome-profiler-events.json 25 | speed-measure-plugin.json 26 | 27 | # IDEs and editors 28 | /.idea 29 | .project 30 | .classpath 31 | .c9/ 32 | *.launch 33 | .settings/ 34 | *.sublime-workspace 35 | .editorconfig 36 | 37 | # IDE - VSCode 38 | .vscode/* 39 | !.vscode/settings.json 40 | !.vscode/tasks.json 41 | !.vscode/launch.json 42 | !.vscode/extensions.json 43 | .history/* 44 | 45 | # misc 46 | /.sass-cache 47 | /connect.lock 48 | /coverage 49 | /libpeerconnection.log 50 | npm-debug.log 51 | yarn-error.log 52 | testem.log 53 | /typings 54 | 55 | # System Files 56 | .DS_Store 57 | Thumbs.db 58 | -------------------------------------------------------------------------------- /DynamoDB.md: -------------------------------------------------------------------------------- 1 | ## Database GSI schema 2 | 3 | Entry type otype (GSIPK1) GSISK1 4 | ---------------------------------------------------------------- 5 | Page Page / 6 | Layout Layout / 7 | Layout block Block / 8 | Micro template MT / 9 | Form definition Form / 10 | Submitted form SubmittedForm YYYY-MM-DD/ 11 | 12 | 13 | 14 | Entry type ID 15 | ---------------------------------------------------------------- 16 | Configuration config 17 | Image profiles imageprofiles -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | ## License remarks 2 | 3 | CloudeeCMS 4 | 5 | Copyright 2020 WebGate Consulting AG 6 | Licensed under the Apache License, Version 2.0 7 | 8 | https://www.cloudee-cms.com 9 | https://www.webgate.biz 10 | 11 | This project uses additional software packages by: 12 | 13 | - trumbowyg editor and plugins: http://alex-d.github.com/Trumbowyg 14 | - ngx-trumbowyg: https://github.com/wermerb/ngx-trumbowyg 15 | - Angular: https://angular.io 16 | - Angular/material: https://github.com/angular/components 17 | - aws-amplify: https://github.com/aws-amplify/amplify-js 18 | 19 | This project includes resources by: 20 | 21 | - Bootstrap Grid v4.0.0 22 | Source: https://getbootstrap.com 23 | Copyright 2011-2018 The Bootstrap Authors 24 | Copyright 2011-2018 Twitter, Inc. 25 | Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 26 | 27 | - Upload plugin for Trumbowyg 28 | Source: https://github.com/Alex-D/Trumbowyg/blob/develop/plugins/upload/trumbowyg.upload.js 29 | License: MIT 30 | Original by: Alexandre Demode 31 | Modified by: Aleksandr-ru 32 | 33 | - Modified pasteimage plugin for Trumbowyg: 34 | Source: https://github.com/Alex-D/Trumbowyg/blob/develop/plugins/pasteimage/trumbowyg.pasteimage.js 35 | License: MIT 36 | Original by: Alexandre Demode 37 | Modified by: flexion (upload pasted images to cloud instead of inline base64) 38 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Serverless CMS for AWS 4 | by [WebGate Consulting AG](https://www.webgate.biz) - Switzerland 5 | 6 | CloudeeCMS is a serverless web content management system 7 | to manage and publish serverless webpages in the Amazon Web Services cloud! 8 | No EC2 servers required! 9 | 10 | No matter if you are a publisher, agency or corporate, CloudeeCMS provides a modern web content management system using next generation serverless technology, that gives your team full ownership over your content. 11 | 12 | [www.cloudee-cms.com](https://www.cloudee-cms.com) 13 | 14 | 15 | ## Installation 16 | 17 | You don't have to clone and download this repository! 18 | Simply use the provided CloudFormation Template to configure and create a CloudeeCMS instance in your AWS account. Read the [Installation Guide](https://www.cloudee-cms.com/documentation#!/doc/installation) in the [documentation](https://www.cloudee-cms.com/documentation). 19 | 20 | -------------------------------------------------------------------------------- /backend/aws-serverless-cms-backup/index.mjs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright WebGate Consulting AG, 2023 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at: 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 13 | * implied. See the License for the specific language governing 14 | * permissions and limitations under the License. 15 | * 16 | * File Version: 2023-06-06 12:02 - RSC 17 | */ 18 | 19 | // ES6 | nodejs18+ | AWS SDK v3 20 | 21 | import { codebuildSVC } from './functions/codebuild-service.mjs'; 22 | import { backupSVC } from './functions/backup-service.mjs'; 23 | import { lambdaCB } from './functions/lambda-utils.mjs'; 24 | 25 | export const handler = async (event, context, callback) => { 26 | const cbHandler = lambdaCB(callback); 27 | const method = event.httpMethod; 28 | const cognitoGroups = event.requestContext.authorizer.claims['cognito:groups'] || ''; 29 | const userGroups = cognitoGroups.split(','); 30 | const isAdmin = userGroups.indexOf('CloudeeCMS-Admin') >= 0; 31 | 32 | if (method === 'POST') { 33 | const payload = JSON.parse(event.body); 34 | const action = payload.action || ''; 35 | if (isAdmin) { 36 | if (action === 'importPackage') { 37 | return cbHandler.success(await backupSVC.importPackage(payload.params.targetenv, payload.params.s3key)); 38 | } else if (action === 'createBackup') { 39 | return cbHandler.success(await backupSVC.createBackup(payload.params.targetenv)); 40 | } else if (action === 'startUpdate') { 41 | return cbHandler.success(await codebuildSVC.startUpdate(payload)); 42 | } 43 | } 44 | 45 | if (action === 'getpipelinestatus') { 46 | return cbHandler.success(await codebuildSVC.getPipelineStatus()); 47 | } else if (action === 'getbuildprojectinfo') { 48 | return cbHandler.success(await codebuildSVC.getBuildProjectInfo()); 49 | } 50 | 51 | return cbHandler.success({ success: false, message: "Not allowed: " + action }); 52 | } else { 53 | return cbHandler.success({ success: false, message: "Method not supported" }); 54 | } 55 | }; 56 | -------------------------------------------------------------------------------- /backend/aws-serverless-cms-backup/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aws-serverless-cms-backup", 3 | "version": "1.0.0", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "aws-serverless-cms-backup", 9 | "version": "1.0.0", 10 | "license": "Apache-2.0", 11 | "dependencies": { 12 | "adm-zip": "^0.5.10", 13 | "mime": "^3.0.0" 14 | } 15 | }, 16 | "node_modules/adm-zip": { 17 | "version": "0.5.10", 18 | "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.10.tgz", 19 | "integrity": "sha512-x0HvcHqVJNTPk/Bw8JbLWlWoo6Wwnsug0fnYYro1HBrjxZ3G7/AZk7Ahv8JwDe1uIcz8eBqvu86FuF1POiG7vQ==", 20 | "engines": { 21 | "node": ">=6.0" 22 | } 23 | }, 24 | "node_modules/mime": { 25 | "version": "3.0.0", 26 | "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", 27 | "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", 28 | "bin": { 29 | "mime": "cli.js" 30 | }, 31 | "engines": { 32 | "node": ">=10.0.0" 33 | } 34 | } 35 | }, 36 | "dependencies": { 37 | "adm-zip": { 38 | "version": "0.5.10", 39 | "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.10.tgz", 40 | "integrity": "sha512-x0HvcHqVJNTPk/Bw8JbLWlWoo6Wwnsug0fnYYro1HBrjxZ3G7/AZk7Ahv8JwDe1uIcz8eBqvu86FuF1POiG7vQ==" 41 | }, 42 | "mime": { 43 | "version": "3.0.0", 44 | "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", 45 | "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==" 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /backend/aws-serverless-cms-backup/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aws-serverless-cms-backup", 3 | "version": "1.0.0", 4 | "description": "AWS Lambda backend backup and restore DB table", 5 | "main": "index.mjs", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "WebGate RSC", 10 | "license": "Apache-2.0", 11 | "dependencies": { 12 | "adm-zip": "^0.5.10", 13 | "mime": "^3.0.0" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /backend/aws-serverless-cms-cf/functions/cf-service.mjs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright WebGate Consulting AG, 2020 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at: 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 13 | * implied. See the License for the specific language governing 14 | * permissions and limitations under the License. 15 | * 16 | * File Version: 2023-05-31 14:57 - RSC 17 | */ 18 | 19 | import { CloudFrontClient, CreateInvalidationCommand } from "@aws-sdk/client-cloudfront"; 20 | 21 | const client = new CloudFrontClient({}); 22 | const cfService = {}; 23 | 24 | cfService.invalidateCF = async function (distribID, lstPaths) { 25 | try { 26 | if (!distribID || distribID === '') return { success: false, message: "CloudFront Distribution ID not specified" }; 27 | 28 | const input = { // CreateInvalidationRequest 29 | DistributionId: distribID, 30 | InvalidationBatch: { 31 | Paths: { 32 | Quantity: lstPaths.length, 33 | Items: lstPaths, 34 | }, 35 | CallerReference: Date.now().toString() 36 | }, 37 | }; 38 | const command = new CreateInvalidationCommand(input); 39 | const cfresponse = await client.send(command); 40 | return { success: true, cfresponse }; 41 | } catch (e) { 42 | console.log(e); 43 | return { success: false, message: e.message || 'Error' }; 44 | } 45 | }; 46 | 47 | export { cfService }; 48 | -------------------------------------------------------------------------------- /backend/aws-serverless-cms-cf/functions/lambda-utils.mjs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright WebGate Consulting AG, 2023 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at: 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 13 | * implied. See the License for the specific language governing 14 | * permissions and limitations under the License. 15 | * 16 | */ 17 | 18 | const lambdaCB = (callback, headers) => { 19 | const awsCB = {}; 20 | awsCB.callback = callback; 21 | awsCB.headers = headers ? headers : { 22 | 'Content-Type': 'application/json', 23 | 'Access-Control-Allow-Origin': '*', 24 | 'Access-Control-Allow-Methods': 'POST, GET, OPTIONS', 25 | 'Access-Control-Allow-Headers': 'X-Requested-With, Content-Type, Origin, Authorization, Accept, Client-Security-Token, Accept-Encoding' 26 | }; 27 | awsCB.success = function (res) { 28 | awsCB.callback(null, { statusCode: 200, body: JSON.stringify(res), headers: awsCB.headers }); 29 | }; 30 | awsCB.error = function (err, httpCode) { // staus 400 -> not returned to caller 31 | console.log(err); 32 | awsCB.callback(null, { 33 | statusCode: httpCode ? httpCode : 400, 34 | body: JSON.stringify({ message: err.message || err }), 35 | headers: awsCB.headers 36 | }); 37 | }; 38 | return awsCB; 39 | }; 40 | 41 | export { lambdaCB }; 42 | -------------------------------------------------------------------------------- /backend/aws-serverless-cms-cf/index.mjs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright WebGate Consulting AG, 2020 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at: 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 13 | * implied. See the License for the specific language governing 14 | * permissions and limitations under the License. 15 | * 16 | * File Version: 2023-05-31 14:50 - RSC 17 | */ 18 | 19 | // ES6 | nodejs18+ | AWS SDK v3 20 | 21 | import { cfService } from './functions/cf-service.mjs'; 22 | import { lambdaCB } from './functions/lambda-utils.mjs'; 23 | 24 | export const handler = async (event, context, callback) => { 25 | const cbHandler = lambdaCB(callback); 26 | const method = event.httpMethod; 27 | 28 | if (method === 'POST') { 29 | const payload = JSON.parse(event.body); 30 | const action = payload.action || ''; 31 | 32 | if (action === 'invalidateCF') { 33 | return cbHandler.success(await cfService.invalidateCF(payload.params.targetCF, payload.params.lstPaths)); 34 | } 35 | 36 | return cbHandler.success({ success: false, message: "Action not supported: " + action }); 37 | } else { 38 | return cbHandler.success({ success: false, message: "Method not supported" }); 39 | } 40 | }; 41 | -------------------------------------------------------------------------------- /backend/aws-serverless-cms-cf/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aws-serverless-cms-cf", 3 | "version": "1.0.0", 4 | "description": "CloudeeCMS CloudFront functions", 5 | "main": "index.mjs", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "WebGate Consulting AG", 10 | "license": "Apache-2.0" 11 | } 12 | -------------------------------------------------------------------------------- /backend/aws-serverless-cms-cognito/functions/lambda-utils.mjs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright WebGate Consulting AG, 2023 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at: 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 13 | * implied. See the License for the specific language governing 14 | * permissions and limitations under the License. 15 | * 16 | */ 17 | 18 | const lambdaCB = (callback, headers) => { 19 | const awsCB = {}; 20 | awsCB.callback = callback; 21 | awsCB.headers = headers ? headers : { 22 | 'Content-Type': 'application/json', 23 | 'Access-Control-Allow-Origin': '*', 24 | 'Access-Control-Allow-Methods': 'POST, GET, OPTIONS', 25 | 'Access-Control-Allow-Headers': 'X-Requested-With, Content-Type, Origin, Authorization, Accept, Client-Security-Token, Accept-Encoding' 26 | }; 27 | awsCB.success = function (res) { 28 | awsCB.callback(null, { statusCode: 200, body: JSON.stringify(res), headers: awsCB.headers }); 29 | }; 30 | awsCB.error = function (err, httpCode) { // staus 400 -> not returned to caller 31 | console.log(err); 32 | awsCB.callback(null, { 33 | statusCode: httpCode ? httpCode : 400, 34 | body: JSON.stringify({ message: err.message || err }), 35 | headers: awsCB.headers 36 | }); 37 | }; 38 | return awsCB; 39 | }; 40 | 41 | export { lambdaCB }; 42 | -------------------------------------------------------------------------------- /backend/aws-serverless-cms-cognito/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aws-serverless-cms-cognito", 3 | "version": "1.0.0", 4 | "description": "Cognito User Admin Functions", 5 | "main": "index.mjs", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "WebGate Consulting AG", 10 | "license": "Apache-2.0" 11 | } 12 | -------------------------------------------------------------------------------- /backend/aws-serverless-cms-fileadmin/functions/lambda-utils.mjs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright WebGate Consulting AG, 2023 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at: 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 13 | * implied. See the License for the specific language governing 14 | * permissions and limitations under the License. 15 | * 16 | */ 17 | 18 | const lambdaCB = (callback, headers) => { 19 | const awsCB = {}; 20 | awsCB.callback = callback; 21 | awsCB.headers = headers ? headers : { 22 | 'Content-Type': 'application/json', 23 | 'Access-Control-Allow-Origin': '*', 24 | 'Access-Control-Allow-Methods': 'POST, GET, OPTIONS', 25 | 'Access-Control-Allow-Headers': 'X-Requested-With, Content-Type, Origin, Authorization, Accept, Client-Security-Token, Accept-Encoding' 26 | }; 27 | awsCB.success = function (res) { 28 | awsCB.callback(null, { statusCode: 200, body: JSON.stringify(res), headers: awsCB.headers }); 29 | }; 30 | awsCB.error = function (err, httpCode) { // staus 400 -> not returned to caller 31 | console.log(err); 32 | awsCB.callback(null, { 33 | statusCode: httpCode ? httpCode : 400, 34 | body: JSON.stringify({ message: err.message || err }), 35 | headers: awsCB.headers 36 | }); 37 | }; 38 | return awsCB; 39 | }; 40 | 41 | export { lambdaCB }; 42 | -------------------------------------------------------------------------------- /backend/aws-serverless-cms-fileadmin/index.mjs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright WebGate Consulting AG, 2020 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at: 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 13 | * implied. See the License for the specific language governing 14 | * permissions and limitations under the License. 15 | * 16 | * File Version: 2023-05-25 15:00 - RSC 17 | */ 18 | 19 | // ES6 | nodejs18+ | AWS SDK v3 20 | 21 | import { s3service } from './functions/s3-service.mjs'; 22 | import { lambdaCB } from './functions/lambda-utils.mjs'; 23 | 24 | export const handler = async (event, context, callback) => { 25 | const cbHandler = lambdaCB(callback); 26 | let method = event.httpMethod; 27 | 28 | if (method === 'POST') { 29 | const payload = JSON.parse(event.body); 30 | const action = payload.action || ''; 31 | const bucketName = payload.params?.bucketName || ''; 32 | 33 | if (!bucketName || bucketName === "") cbHandler.success({ success: false, message: 'Bucket name not supplied' }); 34 | 35 | if (action === 'listFiles') { 36 | return cbHandler.success(await s3service.listFiles(bucketName, payload.params.bucketURL, payload.params.path)); 37 | } else if (action === 'deleteFile') { 38 | return cbHandler.success(await s3service.deleteFile(bucketName, payload.params.key)); 39 | } else if (action === 'batchDeleteFiles') { 40 | return cbHandler.success(await s3service.batchDelete(bucketName, payload.params.lstKeys)); 41 | } else if (action === 'getsigneduploadpolicy') { 42 | return cbHandler.success(await s3service.getSignedUploadPolicy(bucketName, payload.params.keyPrefix, 'public-read')); 43 | } else if (action === 'createFolder') { 44 | return cbHandler.success(await s3service.createFolder(bucketName, payload.params.key)); 45 | } else if (action === 'saveFile') { 46 | return cbHandler.success(await s3service.saveFile(bucketName, payload.params.fileInfo, payload.params.fileBody)); 47 | } else if (action === 'getFile') { 48 | return cbHandler.success(await s3service.getFile(bucketName, payload.params.key)); 49 | } 50 | 51 | return cbHandler.success({ success: false, message: "No Handle for action: " + action }); 52 | } else { 53 | return cbHandler.success({ success: false, message: "Method not supported" }); 54 | } 55 | }; 56 | -------------------------------------------------------------------------------- /backend/aws-serverless-cms-fileadmin/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aws-serverless-cms-fileadmin", 3 | "version": "1.0.0", 4 | "description": "CloudeeCMS S3 file admin functions", 5 | "main": "index.mjs", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "WebGate Consulting AG", 10 | "license": "Apache-2.0" 11 | } 12 | -------------------------------------------------------------------------------- /backend/aws-serverless-cms-fileupload/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wgc-serverless-cms-fileservice", 3 | "version": "1.0.0", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "wgc-serverless-cms-fileservice", 9 | "version": "1.0.0", 10 | "license": "Apache-2.0", 11 | "dependencies": { 12 | "aws-lambda-multipart-parser": "0.1.2" 13 | } 14 | }, 15 | "node_modules/aws-lambda-multipart-parser": { 16 | "version": "0.1.2", 17 | "resolved": "https://registry.npmjs.org/aws-lambda-multipart-parser/-/aws-lambda-multipart-parser-0.1.2.tgz", 18 | "integrity": "sha512-+ieuD/3KR5Fu2lzpW65wgDlObuAT9YXdXXLXhXoG2wRpGFmfCHkRmhavun4zKFkrVfov2nQRziMPk/DsW/g+dQ==" 19 | } 20 | }, 21 | "dependencies": { 22 | "aws-lambda-multipart-parser": { 23 | "version": "0.1.2", 24 | "resolved": "https://registry.npmjs.org/aws-lambda-multipart-parser/-/aws-lambda-multipart-parser-0.1.2.tgz", 25 | "integrity": "sha512-+ieuD/3KR5Fu2lzpW65wgDlObuAT9YXdXXLXhXoG2wRpGFmfCHkRmhavun4zKFkrVfov2nQRziMPk/DsW/g+dQ==" 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /backend/aws-serverless-cms-fileupload/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wgc-serverless-cms-fileservice", 3 | "version": "1.0.0", 4 | "description": "Lambda function to store binary file uploads from API-GW to S3", 5 | "main": "index.mjs", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "WebGate RSC", 10 | "license": "Apache-2.0", 11 | "dependencies": { 12 | "aws-lambda-multipart-parser": "0.1.2" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /backend/aws-serverless-cms-forms/functions/reCAPTCHAv3.mjs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright WebGate Consulting AG, 2023 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at: 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 13 | * implied. See the License for the specific language governing 14 | * permissions and limitations under the License. 15 | * 16 | * File Version: 2023-03-27 14:00 RSC 17 | */ 18 | 19 | import https from 'https'; 20 | import querystring from 'querystring'; 21 | 22 | const reCAPTCHAv3 = {}; 23 | 24 | reCAPTCHAv3.verifyResponse = async function (secret, reCAPTCHAResponse, clientIP) { 25 | try { 26 | let rc = await queryReCAPTCHA(secret, reCAPTCHAResponse, clientIP); 27 | return rc || { success: false }; 28 | } catch (e) { 29 | console.log(e); 30 | return { success: false, error: e, message: e.message || 'Unknown error' }; 31 | } 32 | }; 33 | function queryReCAPTCHA(secret, reCAPTCHAResponse, clientIP) { 34 | const body = querystring.stringify({ "response": reCAPTCHAResponse, "secret": secret, "remoteip": clientIP || '' }); 35 | const options = { 36 | hostname: 'www.google.com', path: '/recaptcha/api/siteverify', method: 'POST', port: 443, 37 | headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8', 'Content-Length': Buffer.byteLength(body) } 38 | }; 39 | return new Promise((resolve, reject) => { 40 | const req = https.request(options, res => { 41 | let rawData = ''; 42 | res.on('data', chunk => { 43 | rawData += chunk; 44 | }); 45 | res.on('end', () => { 46 | try { 47 | resolve(JSON.parse(rawData)); 48 | } catch (err) { 49 | reject(new Error(err)); 50 | } 51 | }); 52 | }); 53 | req.on('error', err => { 54 | reject(new Error(err)); 55 | }); 56 | req.write(body); 57 | req.end(); 58 | }); 59 | } 60 | export { reCAPTCHAv3 }; 61 | -------------------------------------------------------------------------------- /backend/aws-serverless-cms-forms/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aws-serverless-cms-forms", 3 | "version": "1.0.1", 4 | "description": "Forms POST API handler", 5 | "main": "index.mjs", 6 | "dependencies": { 7 | "ejs": "^3.1.9" 8 | }, 9 | "scripts": { 10 | "test": "echo \"Error: no test specified\" && exit 1" 11 | }, 12 | "author": "WebGate RSC", 13 | "license": "Apache-2.0" 14 | } 15 | -------------------------------------------------------------------------------- /backend/aws-serverless-cms-img/functions/lambda-utils.mjs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright WebGate Consulting AG, 2023 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at: 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 13 | * implied. See the License for the specific language governing 14 | * permissions and limitations under the License. 15 | * 16 | */ 17 | 18 | const lambdaCB = (callback, headers) => { 19 | const awsCB = {}; 20 | awsCB.callback = callback; 21 | awsCB.headers = headers ? headers : { 22 | 'Content-Type': 'application/json', 23 | 'Access-Control-Allow-Origin': '*', 24 | 'Access-Control-Allow-Methods': 'POST, GET, OPTIONS', 25 | 'Access-Control-Allow-Headers': 'X-Requested-With, Content-Type, Origin, Authorization, Accept, Client-Security-Token, Accept-Encoding' 26 | }; 27 | awsCB.success = function (res) { 28 | awsCB.callback(null, { statusCode: 200, body: JSON.stringify(res), headers: awsCB.headers }); 29 | }; 30 | awsCB.error = function (err, httpCode) { // staus 400 -> not returned to caller 31 | console.log(err); 32 | awsCB.callback(null, { 33 | statusCode: httpCode ? httpCode : 400, 34 | body: JSON.stringify({ message: err.message || err }), 35 | headers: awsCB.headers 36 | }); 37 | }; 38 | return awsCB; 39 | }; 40 | 41 | export { lambdaCB }; 42 | -------------------------------------------------------------------------------- /backend/aws-serverless-cms-img/index.mjs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright WebGate Consulting AG, 2020 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at: 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 13 | * implied. See the License for the specific language governing 14 | * permissions and limitations under the License. 15 | * 16 | * File Version: 2023-06-06 14:37 - RSC 17 | */ 18 | 19 | // ES6 | nodejs18+ | AWS SDK v3 20 | 21 | import { storage } from './functions/storage-service.mjs'; 22 | import { lambdaCB } from './functions/lambda-utils.mjs'; 23 | 24 | export const handler = async (event, context, callback) => { 25 | const cbHandler = lambdaCB(callback); 26 | const method = event.httpMethod; 27 | 28 | if (method === 'POST') { 29 | const payload = JSON.parse(event.body); 30 | const action = payload.action || ''; 31 | 32 | if (action === 'convertimages') { 33 | return cbHandler.success(await storage.convertImages(payload.bucketName, payload.targetpath, payload.lstFiles, payload.imageprofile)); 34 | } 35 | 36 | return cbHandler.success({ success: false, message: "Action not supported: " + action }); 37 | } else { 38 | return cbHandler.success({ success: false, message: "Method not supported" }); 39 | } 40 | }; 41 | -------------------------------------------------------------------------------- /backend/aws-serverless-cms-img/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aws-serverless-cms-img", 3 | "version": "1.0.0", 4 | "description": "Resize images", 5 | "main": "index.mjs", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "WebGate Consulting AG", 10 | "license": "Apache-2.0", 11 | "dependencies": { 12 | "sharp": "^0.32.1" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /backend/aws-serverless-cms-listcontent/index.mjs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright WebGate Consulting AG, 2020 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at: 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 13 | * implied. See the License for the specific language governing 14 | * permissions and limitations under the License. 15 | * 16 | * File Version: 2023-06-05 15:36 17 | */ 18 | 19 | // ES6 | nodejs18+ | AWS SDK v3 20 | 21 | import { lambdaCB, DDBScan } from './functions/lambda-utils.mjs'; 22 | const tableName = process.env.DB_TABLE || ''; 23 | 24 | export const handler = async (event, context, callback) => { 25 | const cbHandler = lambdaCB(callback); 26 | const method = event.httpMethod; 27 | 28 | if (method === 'GET') { 29 | const thisCategory = event.queryStringParameters['category'] || ''; 30 | const params = { 31 | TableName: tableName, 32 | FilterExpression: "otype = :ot AND contains(#fldC, :v1)", 33 | ExpressionAttributeNames: { "#fldC": "categories" }, 34 | ExpressionAttributeValues: { ":v1": thisCategory, ":ot": "Page" }, 35 | ProjectionExpression: 'categories, opath, title, descr, dt, pubdate, img' 36 | }; 37 | 38 | const lstPages = await DDBScan(params); // TODO: migrate to GSI 39 | lstPages.sort((a, b) => { 40 | return new Date(b.dt || b.pubdate) - new Date(a.dt || a.pubdate); 41 | }); 42 | 43 | return cbHandler.success({ success: true, data: lstPages }); 44 | 45 | } else { 46 | return cbHandler.success({ success: false, message: "Method not supported" }); 47 | } 48 | }; 49 | -------------------------------------------------------------------------------- /backend/aws-serverless-cms-migration/index.mjs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright WebGate Consulting AG, 2023 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at: 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 13 | * implied. See the License for the specific language governing 14 | * permissions and limitations under the License. 15 | * 16 | * File Version: 2023-06-06 12:02 - RSC 17 | */ 18 | 19 | // ES6 | nodejs18+ | AWS SDK v3 20 | 21 | import { storage } from './functions/storage-service.mjs'; 22 | import { lambdaCB } from './functions/lambda-utils.mjs'; 23 | 24 | export const handler = async (event, context, callback) => { 25 | const cbHandler = lambdaCB(callback); 26 | const method = event.httpMethod; 27 | const cognitoGroups = event.requestContext.authorizer.claims['cognito:groups'] || ''; 28 | const userGroups = cognitoGroups.split(','); 29 | const isAdmin = userGroups.indexOf('CloudeeCMS-Admin') >= 0; 30 | 31 | if (method === 'POST') { 32 | const payload = JSON.parse(event.body); 33 | const action = payload.action || ''; 34 | 35 | if (action === 'migrateGSI1') { 36 | if (isAdmin) { 37 | return cbHandler.success(await storage.migrateGSI1()); 38 | } else { 39 | return cbHandler.success({ success: false, message: "Only users with admin role can start this function." }); 40 | } 41 | } else if (action === 'getGSI1Status') { 42 | return cbHandler.success(await storage.getGSI1Status()); 43 | } 44 | 45 | return cbHandler.success({ success: false, message: "Not allowed: " + action }); 46 | } else { 47 | return cbHandler.success({ success: false, message: "Method not supported" }); 48 | } 49 | }; 50 | -------------------------------------------------------------------------------- /backend/aws-serverless-cms-migration/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aws-serverless-cms-migration", 3 | "version": "1.0.0", 4 | "description": "AWS Lambda update migration tools", 5 | "main": "index.mjs", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "WebGate RSC", 10 | "license": "Apache-2.0", 11 | "dependencies": { 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /backend/aws-serverless-cms-publish/index.mjs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright WebGate Consulting AG, 2023 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at: 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 13 | * implied. See the License for the specific language governing 14 | * permissions and limitations under the License. 15 | * 16 | * File Version: 2023-06-05 09:58 - RSC 17 | */ 18 | 19 | import { pubSVC } from './functions/publish-service.mjs'; 20 | import { feedSVC } from './functions/feed-service.mjs'; 21 | import { lambdaCB, DDBGet } from './functions/lambda-utils.mjs'; 22 | const tableName = process.env.DB_TABLE || ''; 23 | 24 | export const handler = async (event, context, callback) => { 25 | const cbHandler = lambdaCB(callback); 26 | const method = event.httpMethod; 27 | 28 | if (method === 'POST') { 29 | 30 | const cfg = await DDBGet({ TableName: tableName, Key: { id: "config" } }); 31 | if (!cfg) return cbHandler.success({ success: false, published: false, errorMessage: "Configuration document not found", log: [] }); 32 | 33 | const payload = JSON.parse(event.body); 34 | const action = payload.action ? payload.action : ''; 35 | 36 | if (action === 'publishPage') { 37 | return cbHandler.success(await pubSVC.publishPage(payload.params.targetenv, payload.params.id, payload.params.page, cfg)); 38 | } else if (action === 'bulkPublishPage') { 39 | return cbHandler.success(await pubSVC.bulkPublishPage(payload.params.targetenv, payload.params.lstPageIDs, payload.params.pubtype, payload.params.removeFromQueue, cfg)); 40 | } else if (action === 'unpublishPage') { 41 | return cbHandler.success(await pubSVC.unpublishPage(payload.params.targetenv, payload.params.id, payload.params.opath, cfg)); 42 | } else if (action === 'publishFeeds') { 43 | return cbHandler.success(await feedSVC.publishFeeds(payload.params.targetenv, payload.params.lstFeeds, cfg)); 44 | } 45 | 46 | return cbHandler.success({ success: false, message: "No handle for: " + action }); 47 | } else { 48 | return cbHandler.success({ success: false, message: "Method not supported" }); 49 | } 50 | }; 51 | -------------------------------------------------------------------------------- /backend/aws-serverless-cms-publish/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aws-serverless-cms-publish", 3 | "version": "1.0.0", 4 | "description": "AWS Lambda backend to publish pages to S3", 5 | "main": "index.mjs", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "WebGate RSC", 10 | "license": "Apache-2.0", 11 | "dependencies": { 12 | "feed": "^4.2.2", 13 | "pug": "^3.0.2", 14 | "sitemap": "^7.1.1" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /backend/aws-serverless-cms/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aws-serverless-cms", 3 | "version": "1.0.0", 4 | "description": "CloudeeCMS backend functions", 5 | "main": "index.mjs", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "WebGate Consulting AG", 10 | "license": "Apache-2.0" 11 | } 12 | -------------------------------------------------------------------------------- /backend/optional/aws-serverless-cms-algoliaindexer/functions/lambda-utils.mjs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright WebGate Consulting AG, 2023 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at: 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 13 | * implied. See the License for the specific language governing 14 | * permissions and limitations under the License. 15 | * 16 | */ 17 | 18 | import { DynamoDB } from "@aws-sdk/client-dynamodb"; // ES6 import 19 | import { DynamoDBDocument } from "@aws-sdk/lib-dynamodb"; 20 | 21 | const marshallOptions = { 22 | convertEmptyValues: true, 23 | removeUndefinedValues: true, 24 | convertClassInstanceToMap: false, 25 | }; 26 | const unmarshallOptions = { wrapNumbers: false }; 27 | const translateConfig = { marshallOptions, unmarshallOptions }; 28 | const client = new DynamoDB({}); 29 | const documentClient = DynamoDBDocument.from(client, translateConfig); // fullclient 30 | 31 | const DDBGet = async (params) => { 32 | let itm = await documentClient.get(params); 33 | if (!itm || !itm.Item) return null; 34 | return itm.Item; 35 | }; 36 | 37 | export { documentClient, DDBGet }; 38 | -------------------------------------------------------------------------------- /backend/optional/aws-serverless-cms-algoliaindexer/index.mjs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright WebGate Consulting AG, 2020 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at: 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 13 | * implied. See the License for the specific language governing 14 | * permissions and limitations under the License. 15 | * 16 | * File Version: 2023-06-05 1315 - RSC 17 | */ 18 | 19 | // Algolia search indexer 20 | // This function listens on SQS queue for indexing requests by the publish function 21 | // Sample message: { "action": "add", "tableName": "", "indexName":"", "bucketName": "", ids": [] }; 22 | 23 | // ES6 | nodejs18+ | AWS SDK v3 24 | 25 | import { SQSClient, DeleteMessageCommand } from "@aws-sdk/client-sqs"; 26 | import { DDBGet } from './functions/lambda-utils.mjs'; 27 | import algoliasearch from 'algoliasearch'; 28 | 29 | const FTclient = algoliasearch(process.env.ALGOLIA_APPID, process.env.ALGOLIA_APIKEY); 30 | const TASK_QUEUE_URL = process.env.TASK_QUEUE_URL; 31 | const sqsclient = new SQSClient({}); 32 | 33 | export const handler = async (event, context, callback) => { 34 | let indexName = ""; 35 | let FTindex = null; 36 | 37 | for (let m = 0; m < event.Records.length; m++) { 38 | let msg = event.Records[m]; 39 | let msgObj = JSON.parse(msg.body); 40 | try { 41 | if (indexName !== msgObj.indexName) { 42 | indexName = msgObj.indexName; 43 | FTindex = FTclient.initIndex(indexName); 44 | } 45 | if (msgObj.action === "add") { 46 | for (let i = 0; i < msgObj.ids.length; i++) { 47 | let putItem = await DDBGet({ TableName: msgObj.tableName, Key: { id: msgObj.ids[i] } }); 48 | if (putItem) { 49 | putItem.objectID = putItem.id; 50 | await FTindex.saveObject(putItem); 51 | } 52 | } 53 | } else if (msgObj.action === "remove") { 54 | for (let i = 0; i < msgObj.ids.length; i++) { 55 | console.log("Remove", msgObj.ids[i], indexName); 56 | await FTindex.deleteObject(msgObj.ids[i]); 57 | } 58 | } 59 | 60 | let command = new DeleteMessageCommand({ ReceiptHandle: msg.receiptHandle, QueueUrl: TASK_QUEUE_URL }); 61 | await sqsclient.send(command); 62 | 63 | } catch (e) { 64 | console.log(e); 65 | } 66 | } 67 | }; -------------------------------------------------------------------------------- /backend/optional/aws-serverless-cms-algoliaindexer/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aws-serverless-cms-algoliaindexer", 3 | "version": "1.0.0", 4 | "description": "Listens for indexing requests on SQS queue and forwards to algolia API", 5 | "main": "index.mjs", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "WebGate RSC", 10 | "license": "Apache-2.0", 11 | "dependencies": { 12 | "algoliasearch": "^4.17.1" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /backend/optional/aws-serverless-cms-mailer/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aws-serverless-cms-mailer", 3 | "version": "1.0.0", 4 | "description": "Poll SQS queue and send outgoing smtp mails", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "RSC", 10 | "license": "Apache-2.0", 11 | "dependencies": { 12 | "nodemailer": "^6.2.1" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /backend/optional/aws-serverless-cms-mailer/readme.md: -------------------------------------------------------------------------------- 1 | # SMTP mailer function 2 | 3 | You can use this function as an example to send SMTP emails. 4 | 5 | To receive notifications (as a site owner) about new submitted forms, we encourage you to use SNS instead! 6 | 7 | If you want to notify users about a successfully submitted form, you should configure this mailer function to send emails using your custom domain. 8 | 9 | The mailer function must be configured to listen to an SQS queue. 10 | Instructions can be found in the documentation: 11 | 12 | https://www.cloudee-cms.com/documentation#!/doc/forms-notifications 13 | -------------------------------------------------------------------------------- /backend/optional/aws-serverless-cms-search/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright WebGate Consulting AG, 2020 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at: 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 13 | * implied. See the License for the specific language governing 14 | * permissions and limitations under the License. 15 | * 16 | * File Version: 2020-04-15 0607 - RSC 17 | */ 18 | 19 | const algoliasearch = require('algoliasearch'); 20 | 21 | const FTclient = algoliasearch( process.env.ALGOLIA_APPID, process.env.ALGOLIA_APIKEY); 22 | const FTindex = FTclient.initIndex( process.env.ALGOLIA_INDEXNAME ); 23 | 24 | exports.handler = async (event, context, callback) => { 25 | const done = _done(callback); 26 | switch (event.httpMethod) { 27 | case 'POST': 28 | var b = JSON.parse(event.body); 29 | if (b.action === "search") { 30 | if (!b.query || b.query==="") return done.done({ "Items": [] }); 31 | try { 32 | const content = await FTindex.search(b.query, { attributesToRetrieve: ['objectID', 'title', 'descr', 'opath'], hitsPerPage: 50 }); 33 | done.done({ "Items": content.hits }); 34 | } catch (err) { 35 | console.log(err); 36 | done.done({ "Items": [], "error": err}); 37 | } 38 | } else { 39 | console.log("Unsupported action: " + b.action); 40 | } 41 | break; 42 | default: 43 | done.error(new Error(`Unsupported method "${event.httpMethod}"`)); 44 | } 45 | }; 46 | 47 | function _done(callback, headers) { 48 | let awsCB = {}; 49 | awsCB.callback = callback; 50 | awsCB.headers = headers ? headers : { 51 | 'Content-Type': 'application/json', 52 | 'Access-Control-Allow-Origin': '*', 53 | 'Access-Control-Allow-Methods': 'POST, GET, OPTIONS', 54 | 'Access-Control-Allow-Headers': 'X-Requested-With, Content-Type, Origin, Authorization, Accept, Client-Security-Token, Accept-Encoding' 55 | }; 56 | awsCB.done = function (res) { 57 | awsCB.callback(null, { 58 | statusCode: 200, 59 | body: JSON.stringify({ success: true, data: res }), 60 | headers: awsCB.headers 61 | }); 62 | }; 63 | awsCB.error = function (err, httpCode) { 64 | awsCB.callback(null, { 65 | statusCode: httpCode ? httpCode : 400, 66 | body: JSON.stringify({ success: false, message: err.message || err }), 67 | headers: awsCB.headers 68 | }); 69 | }; 70 | return awsCB; 71 | } -------------------------------------------------------------------------------- /backend/optional/aws-serverless-cms-search/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aws-serverless-cms-search", 3 | "version": "1.0.0", 4 | "description": "Algolia Search Index", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "WebGate RSC", 10 | "license": "Apache-2.0", 11 | "dependencies": { 12 | "algoliasearch": "^4.1.0" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /backend/optional/aws-serverless-cms-search/readme.md: -------------------------------------------------------------------------------- 1 | # algolia search function 2 | 3 | You can use this function as an example REST API to query algolia search index. -------------------------------------------------------------------------------- /backend/optional/aws-serverless-cms-sitemap/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aws-serverless-cms-sitemap", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@types/node": { 8 | "version": "12.12.29", 9 | "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.29.tgz", 10 | "integrity": "sha512-yo8Qz0ygADGFptISDj3pOC9wXfln/5pQaN/ysDIzOaAWXt73cNHmtEC8zSO2Y+kse/txmwIAJzkYZ5fooaS5DQ==" 11 | }, 12 | "@types/sax": { 13 | "version": "1.2.1", 14 | "resolved": "https://registry.npmjs.org/@types/sax/-/sax-1.2.1.tgz", 15 | "integrity": "sha512-dqYdvN7Sbw8QT/0Ci5rhjE4/iCMJEM0Y9rHpCu+gGXD9Lwbz28t6HI2yegsB6BoV1sShRMU6lAmAcgRjmFy7LA==", 16 | "requires": { 17 | "@types/node": "*" 18 | } 19 | }, 20 | "arg": { 21 | "version": "4.1.3", 22 | "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", 23 | "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==" 24 | }, 25 | "sax": { 26 | "version": "1.2.4", 27 | "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", 28 | "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" 29 | }, 30 | "sitemap": { 31 | "version": "5.1.0", 32 | "resolved": "https://registry.npmjs.org/sitemap/-/sitemap-5.1.0.tgz", 33 | "integrity": "sha512-RAOCHYPbALEaIlqlGs46LZ9RFfh+61YYjexMqri4h+gsW6y0MLZ+WB4eJCopVP9WCWsng6z9JSPHqcKtjoydLw==", 34 | "requires": { 35 | "@types/node": "^12.12.3", 36 | "@types/sax": "^1.2.0", 37 | "arg": "^4.1.1", 38 | "sax": "^1.2.4", 39 | "xmlbuilder": "^13.0.2" 40 | } 41 | }, 42 | "xmlbuilder": { 43 | "version": "13.0.2", 44 | "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-13.0.2.tgz", 45 | "integrity": "sha512-Eux0i2QdDYKbdbA6AM6xE4m6ZTZr4G4xF9kahI2ukSEMCzwce2eX9WlTI5J3s+NU7hpasFsr8hWIONae7LluAQ==" 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /backend/optional/aws-serverless-cms-sitemap/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aws-serverless-cms-sitemap", 3 | "version": "1.0.0", 4 | "description": "sitemap.xml generator", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "WebGate RSC", 10 | "license": "Apache-2.0", 11 | "dependencies": { 12 | "sitemap": "^5.1.0" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /backend/optional/aws-serverless-cms-sitemap/readme.md: -------------------------------------------------------------------------------- 1 | # sitemap.xml generator lamdba 2 | 3 | This lambda function is optional, a sitemap.xml generator is already present in the CloudeeCMS UI. 4 | To use the built-in sitemap generator, add a new feed in the "Settings" page and select "Sitemap" as type. You can then use "Publish feeds.." in the Publication Queue page to update the sitemap file. 5 | https://www.cloudee-cms.com/documentation#!/doc/settings-feeds 6 | 7 | 8 | You can also add this lambda function as cron job via CloudWatch to run daily, if you want to have your sitemap.xml updated automatically. 9 | (05 0 * * ? *) 10 | 11 | Please note that sitemap.xml is no longer considered necessary for SEO. 12 | -------------------------------------------------------------------------------- /buildspec_1_backend.yml: -------------------------------------------------------------------------------- 1 | version: 0.2 2 | 3 | phases: 4 | build: 5 | commands: 6 | - node --version 7 | - cd ./backend/aws-serverless-cms-publish 8 | - npm install 9 | - cd ../.. 10 | - cd ./backend/aws-serverless-cms-fileupload 11 | - npm install 12 | - cd ../.. 13 | - cd ./backend/aws-serverless-cms-img 14 | - npm install 15 | - cd ../.. 16 | - cd ./backend/aws-serverless-cms-forms 17 | - npm install 18 | - cd ../.. 19 | - cd ./backend/aws-serverless-cms-backup 20 | - npm install 21 | - cd ../.. 22 | post_build: 23 | commands: 24 | - aws cloudformation package --template-file SAMTemplate.yaml --s3-bucket ${PIPELINE_BUCKET} --output-template-file outputSAMTemplate_backend.yaml 25 | artifacts: 26 | files: 27 | - outputSAMTemplate_backend.yaml -------------------------------------------------------------------------------- /buildspec_2_frontend.yml: -------------------------------------------------------------------------------- 1 | version: 0.2 2 | 3 | phases: 4 | build: 5 | commands: 6 | - node --version 7 | - echo "Bucket name is ${S3BUCKET_ADMIN} and Stack name is ${STACKNAME}" 8 | - aws cloudformation describe-stacks --region ${REGION} --stack-name ${STACKNAME} > ./frontend/stackinfo.json 9 | - cd ./frontend/initial-config-script 10 | - npm install 11 | - node create-initial-config.js 12 | - cd .. 13 | - npm run update-env 14 | - npm install --legacy-peer-deps 15 | - npm install -g @angular/cli 16 | - npm run build 17 | - cd .. 18 | post_build: 19 | commands: 20 | - aws s3 sync --delete frontend/dist/aws-serverless-cms s3://${S3BUCKET_ADMIN}/scms --cache-control max-age=3600 --acl public-read 21 | -------------------------------------------------------------------------------- /frontend/.browserslistrc: -------------------------------------------------------------------------------- 1 | # This file is used by the build system to adjust CSS and JS output to support the specified browsers below. 2 | # For additional information regarding the format and rule options, please see: 3 | # https://github.com/browserslist/browserslist#queries 4 | 5 | # You can see what browsers were selected by your queries by running: 6 | # npx browserslist 7 | 8 | > 0.5% 9 | last 2 versions 10 | Firefox ESR 11 | not dead 12 | not IE 9-11 # For IE 9-11 support, remove 'not'. -------------------------------------------------------------------------------- /frontend/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 240, 3 | "singleQuote": true, 4 | "useTabs": false, 5 | "tabWidth": 2, 6 | "semi": true, 7 | "bracketSpacing": true 8 | } -------------------------------------------------------------------------------- /frontend/e2e/protractor.conf.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | // Protractor configuration file, see link for more information 3 | // https://github.com/angular/protractor/blob/master/lib/config.ts 4 | 5 | const { SpecReporter } = require('jasmine-spec-reporter'); 6 | 7 | /** 8 | * @type { import("protractor").Config } 9 | */ 10 | exports.config = { 11 | allScriptsTimeout: 11000, 12 | specs: [ 13 | './src/**/*.e2e-spec.ts' 14 | ], 15 | capabilities: { 16 | 'browserName': 'chrome' 17 | }, 18 | directConnect: true, 19 | baseUrl: 'http://localhost:4200/', 20 | framework: 'jasmine', 21 | jasmineNodeOpts: { 22 | showColors: true, 23 | defaultTimeoutInterval: 30000, 24 | print: function() {} 25 | }, 26 | onPrepare() { 27 | require('ts-node').register({ 28 | project: require('path').join(__dirname, './tsconfig.json') 29 | }); 30 | jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } })); 31 | } 32 | }; -------------------------------------------------------------------------------- /frontend/e2e/src/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { AppPage } from './app.po'; 2 | import { browser, logging } from 'protractor'; 3 | 4 | describe('workspace-project App', () => { 5 | let page: AppPage; 6 | 7 | beforeEach(() => { 8 | page = new AppPage(); 9 | }); 10 | 11 | it('should display welcome message', () => { 12 | page.navigateTo(); 13 | expect(page.getTitleText()).toEqual('Welcome to aws-serverless-cms!'); 14 | }); 15 | 16 | afterEach(async () => { 17 | // Assert that there are no errors emitted from the browser 18 | const logs = await browser.manage().logs().get(logging.Type.BROWSER); 19 | expect(logs).not.toContain(jasmine.objectContaining({ 20 | level: logging.Level.SEVERE, 21 | } as logging.Entry)); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /frontend/e2e/src/app.po.ts: -------------------------------------------------------------------------------- 1 | import { browser, by, element } from 'protractor'; 2 | 3 | export class AppPage { 4 | navigateTo() { 5 | return browser.get(browser.baseUrl) as Promise; 6 | } 7 | 8 | getTitleText() { 9 | return element(by.css('app-root h1')).getText() as Promise; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /frontend/e2e/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/e2e", 5 | "module": "commonjs", 6 | "target": "es2018", 7 | "types": [ 8 | "jasmine", 9 | "jasminewd2", 10 | "node" 11 | ] 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /frontend/initial-config-script/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "aws-sdk": "^2.1373.0" 4 | }, 5 | "name": "initial-config-script", 6 | "version": "1.0.0", 7 | "description": "Create configuration during deployment", 8 | "main": "create-initial-config.js", 9 | "scripts": { 10 | "test": "echo \"Error: no test specified\" && exit 1" 11 | }, 12 | "author": "WebGate", 13 | "license": "ISC" 14 | } 15 | -------------------------------------------------------------------------------- /frontend/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular-devkit/build-angular'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-jasmine-html-reporter'), 12 | require('karma-coverage-istanbul-reporter'), 13 | require('@angular-devkit/build-angular/plugins/karma') 14 | ], 15 | client: { 16 | clearContext: false // leave Jasmine Spec Runner output visible in browser 17 | }, 18 | coverageIstanbulReporter: { 19 | dir: require('path').join(__dirname, './coverage/aws-serverless-cms'), 20 | reports: ['html', 'lcovonly', 'text-summary'], 21 | fixWebpackSourcePaths: true 22 | }, 23 | reporters: ['progress', 'kjhtml'], 24 | port: 9876, 25 | colors: true, 26 | logLevel: config.LOG_INFO, 27 | autoWatch: true, 28 | browsers: ['Chrome'], 29 | singleRun: false, 30 | restartOnFileChange: true 31 | }); 32 | }; 33 | -------------------------------------------------------------------------------- /frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cloudeecms-aws", 3 | "license": "Apache-2.0", 4 | "author": "Richie Schmid", 5 | "version": "1.0.0", 6 | "scripts": { 7 | "ng": "ng", 8 | "start": "ng serve", 9 | "build": "ng build --configuration production --base-href /scms/index.html", 10 | "build:dev": "ng build --aot --output-hashing all --base-href /scms-dev/index.html", 11 | "build:test": "ng build --aot --output-hashing all --base-href /scms-test/index.html --configuration=test", 12 | "update-env": "node set-env.js", 13 | "initial-config": "node create-initial-config.js" 14 | }, 15 | "private": true, 16 | "dependencies": { 17 | "@angular/animations": "~13.3.11", 18 | "@angular/cdk": "~13.3.9", 19 | "@angular/common": "~13.3.11", 20 | "@angular/compiler": "~13.3.11", 21 | "@angular/core": "~13.3.11", 22 | "@angular/forms": "~13.3.11", 23 | "@angular/material": "^13.3.9", 24 | "@angular/platform-browser": "~13.3.11", 25 | "@angular/platform-browser-dynamic": "~13.3.11", 26 | "@angular/router": "~13.3.11", 27 | "aws-amplify": "^4.2.9", 28 | "jquery": "^3.4.1", 29 | "ngx-trumbowyg": "^6.0.7", 30 | "rxjs": "^6.5.4", 31 | "rxjs-compat": "^6.5.4", 32 | "trumbowyg": "^2.21.0", 33 | "tslib": "^2.0.0", 34 | "util": "^0.12.4", 35 | "zone.js": "~0.11.4" 36 | }, 37 | "devDependencies": { 38 | "@angular-devkit/build-angular": "^13.3.9", 39 | "@angular/cli": "~13.3.9", 40 | "@angular/compiler-cli": "~13.3.11", 41 | "@angular/language-service": "~13.3.11", 42 | "@types/jasmine": "~3.6.0", 43 | "@types/jasminewd2": "~2.0.3", 44 | "@types/node": "^16.9.1", 45 | "@typescript-eslint/eslint-plugin": "^5.59.5", 46 | "@typescript-eslint/parser": "^5.59.5", 47 | "codelyzer": "^6.0.0", 48 | "eslint": "^8.40.0", 49 | "jasmine-core": "~3.8.0", 50 | "jasmine-spec-reporter": "~5.0.0", 51 | "karma": "~6.3.16", 52 | "karma-chrome-launcher": "~3.1.0", 53 | "karma-coverage-istanbul-reporter": "~3.0.2", 54 | "karma-jasmine": "~4.0.0", 55 | "karma-jasmine-html-reporter": "^1.7.0", 56 | "prettier": "^2.0.4", 57 | "protractor": "~7.0.0", 58 | "ts-node": "~7.0.0", 59 | "typescript": "~4.6.3" 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /frontend/src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright WebGate Consulting AG, 2020 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at: 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 13 | * implied. See the License for the specific language governing 14 | * permissions and limitations under the License. 15 | * 16 | * File Version: 2020-04-15 0607 - RSC 17 | */ 18 | 19 | import { TestBed, waitForAsync } from '@angular/core/testing'; 20 | import { RouterTestingModule } from '@angular/router/testing'; 21 | import { AppComponent } from './app.component'; 22 | 23 | describe('AppComponent', () => { 24 | beforeEach(waitForAsync(() => { 25 | TestBed.configureTestingModule({ 26 | imports: [ 27 | RouterTestingModule 28 | ], 29 | declarations: [ 30 | AppComponent 31 | ], 32 | }).compileComponents(); 33 | })); 34 | 35 | it('should create the app', () => { 36 | const fixture = TestBed.createComponent(AppComponent); 37 | const app = fixture.debugElement.componentInstance; 38 | expect(app).toBeTruthy(); 39 | }); 40 | 41 | it(`should have as title 'aws-serverless-cms'`, () => { 42 | const fixture = TestBed.createComponent(AppComponent); 43 | const app = fixture.debugElement.componentInstance; 44 | expect(app.title).toEqual('aws-serverless-cms'); 45 | }); 46 | 47 | it('should render title in a h1 tag', () => { 48 | const fixture = TestBed.createComponent(AppComponent); 49 | fixture.detectChanges(); 50 | const compiled = fixture.debugElement.nativeElement; 51 | expect(compiled.querySelector('h1').textContent).toContain('Welcome to aws-serverless-cms!'); 52 | }); 53 | }); 54 | -------------------------------------------------------------------------------- /frontend/src/app/auth/auth.guard.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { CanActivate } from '@angular/router'; 3 | import { Observable } from 'rxjs'; 4 | import { TabsNavService } from '../services/tabs.service'; 5 | import { UserLoginService } from './userlogin.service'; 6 | 7 | @Injectable() 8 | export class AuthGuard implements CanActivate { 9 | 10 | constructor( 11 | private userLoginSVC: UserLoginService, 12 | private tabSVC: TabsNavService 13 | ) { } 14 | 15 | public canActivate(): boolean | Observable { 16 | return new Observable((observer) => { 17 | this.userLoginSVC.isSessionValidAuthGuard((isAuth: boolean, groups: any) => { 18 | observer.next(isAuth); 19 | observer.complete(); 20 | 21 | if (!isAuth) this.tabSVC.showLoginForm({ onSuccessReload: true }); 22 | }); 23 | }); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /frontend/src/app/auth/aws-config.js: -------------------------------------------------------------------------------- 1 | import { environment } from '../../environments/environment'; 2 | const cfg = { 3 | region: environment.region, 4 | userPoolId: environment.userPoolId, 5 | userPoolWebClientId: environment.userPoolWebClientId, 6 | identityPoolId: '', 7 | oauth: { 8 | domain: environment.domain, 9 | scope: ['email', 'openid', 'profile', 'aws.cognito.signin.user.admin'], 10 | redirectSignIn: environment.redirectURL, 11 | redirectSignOut: environment.redirectURL, 12 | responseType: 'code' 13 | } 14 | }; 15 | 16 | export default cfg; 17 | -------------------------------------------------------------------------------- /frontend/src/app/auth/myhttpinterceptor.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright WebGate Consulting AG, 2020 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at: 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 13 | * implied. See the License for the specific language governing 14 | * permissions and limitations under the License. 15 | * 16 | */ 17 | 18 | import { Injectable } from '@angular/core'; 19 | import { HttpInterceptor, HttpRequest, HttpHandler } from '@angular/common/http'; 20 | import { from } from 'rxjs'; 21 | import { switchMap } from 'rxjs/operators'; 22 | import { environment } from '../../environments/environment'; 23 | import { Auth } from '@aws-amplify/auth'; 24 | 25 | @Injectable() 26 | export class MyHttpInterceptor implements HttpInterceptor { 27 | constructor() { } 28 | 29 | intercept(req: HttpRequest, next: HttpHandler) { 30 | // only intercept requests towards API-GW 31 | if (req.url.search(environment.API_Gateway_Endpoint) === -1) { 32 | return next.handle(req); 33 | } else { 34 | 35 | return from(Auth.currentSession()).pipe( 36 | switchMap(data => { 37 | const headers = req.headers.set('Authorization', data.getIdToken().getJwtToken()); 38 | const requestClone = req.clone({ headers }); 39 | return next.handle(requestClone); 40 | }) 41 | ); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /frontend/src/app/editor/fileexplorer/fileeditor/fileeditor.compo.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | 6 | help_outline 7 | 8 | 12 | 13 |
14 |

Edit File

15 |
16 |
17 |
18 | 19 | 20 | 21 |
22 |
23 | 24 | 25 | 26 |
27 |
28 | 29 | 30 | 31 | {{ itm.label }} 32 | 33 | 34 | 35 |
36 |
37 | 38 | 39 | 40 | 41 | 48 |
49 |
50 | 51 |
52 |
53 | 54 | 55 | 56 | 58 | 59 | 60 |
61 |
62 |
63 |
-------------------------------------------------------------------------------- /frontend/src/app/editor/fileexplorer/fileuploader/FileUploadDialog.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

File upload

4 |

Destination folder: /{{uplPath}}

5 |
6 |
7 | 8 | 11 |
or drop files here
12 |
13 |
14 | 15 | 16 | 17 | {{ itm.label }} 18 | 19 | 20 | 21 |
22 |
23 | 24 |
25 |
26 | {{file.name}}
27 | 28 |
29 |
30 | 31 |

32 |
33 |
There was a problem
34 |
35 | 36 |
37 |
-------------------------------------------------------------------------------- /frontend/src/app/editor/fileexplorer/fileuploader/UploadDialog.css: -------------------------------------------------------------------------------- 1 | .uplProgress { border: 1px solid #d8d8d8; border-radius: 3px; padding: 2px; margin: 12px 0; } 2 | .uplProgress .uplFile { background-color: #f1f1f1; padding: 3px 6px; border-radius: 4px; margin-bottom: 4px; } 3 | label .mat-icon { vertical-align: middle; } 4 | .filedrop { padding: 12px; border: 1px dashed gray; margin-top: 10px; } -------------------------------------------------------------------------------- /frontend/src/app/editor/fileexplorer/imageuploader/ImgUploadDialog.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

Image upload and resize

4 |
5 |

Destination folder: /{{uplPath}}

6 | 7 | 8 |
9 | 10 | 11 | {{imgp.label}} 12 | 13 | 14 | 15 | 16 |
17 |
{{selectedProfile.descr}}

18 |
19 |
20 | 21 | 22 |
23 |
24 | 26 | 30 |
or drop files here
31 |
32 |
33 |
34 | {{file.name}}
35 | 37 |
38 |
39 |
40 | 41 |
42 | 43 |
44 | {{errormsg}}
45 |
46 |
{{row}}
47 |
48 |
49 | 50 |

51 |
52 | 53 |
54 | 55 |
56 |
-------------------------------------------------------------------------------- /frontend/src/app/editor/fileexplorer/imageuploader/UploadDialog.css: -------------------------------------------------------------------------------- 1 | .uplProgress { border: 1px solid #d8d8d8; border-radius: 3px; padding: 2px; margin: 12px 0; } 2 | .uplProgress .uplFile { background-color: #f1f1f1; padding: 3px 6px; border-radius: 4px; margin-bottom: 4px; } 3 | label .mat-icon { vertical-align: middle; } 4 | .logdisplay { font-size: 11px; overflow: scroll; max-height: 400px; border: 1px solid lightgray; } 5 | .filedrop { padding: 12px; border: 1px dashed gray; margin-top: 10px; } -------------------------------------------------------------------------------- /frontend/src/app/editor/fileexplorer/listfiles.compo.css: -------------------------------------------------------------------------------- 1 | .fileExplorer .clickable { cursor: pointer; } 2 | .fileExplorer a:hover { text-decoration: underline; } 3 | .folderActions button { margin-right: 6px; } 4 | .folderActions { margin-bottom: 20px; } 5 | .fileExplorer TR:hover .hoverActions .mat-icon { visibility: visible; cursor: pointer; cursor: pointer; margin-left: 6px; border-radius: 3px; } 6 | .fileExplorer TR:hover .hoverActions .mat-icon:hover { background-color: #f5a30f; } 7 | .fileExplorer .oneUp { font-weight: bold; font-size: 22px; } -------------------------------------------------------------------------------- /frontend/src/app/editor/forms/form.ts: -------------------------------------------------------------------------------- 1 | export class Form { 2 | id: string; 3 | title: string; 4 | otype: string; 5 | okey: string; 6 | custFields: Array = []; 7 | body: string; 8 | descr: string; 9 | staticcaptcha: string; 10 | redirectFailure: string; 11 | redirectSuccess: string; 12 | captchaMethod: string; 13 | notify: string; 14 | lstEmail: any; 15 | 16 | sqsQueueURL: string; 17 | mailFrom: string; 18 | mailSubject: string; 19 | mailBody: string; 20 | } 21 | -------------------------------------------------------------------------------- /frontend/src/app/editor/forms/forms.compo.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | 5 | 6 | highlight_off 7 | 8 | 9 | 10 | 11 | 12 | 13 | 16 | 19 |
20 |

Forms

21 |
22 | 23 | 24 | 25 | 30 | 31 | 32 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 47 | 48 | 49 |
26 | 29 | IDTitle 33 | 35 | 36 |
assignment{{item.id}}{{item.title}} 45 | 46 |
50 |
51 | -------------------------------------------------------------------------------- /frontend/src/app/editor/layoutblocks/PugBlock.ts: -------------------------------------------------------------------------------- 1 | export class PugBlock { 2 | id: string; 3 | title: string; 4 | otype: string; 5 | okey: string; 6 | body: string; 7 | descr: string; 8 | } 9 | -------------------------------------------------------------------------------- /frontend/src/app/editor/layoutblocks/pugblocks.compo.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | 5 | 6 | highlight_off 7 | 8 | 9 | 10 | 11 | 12 | 13 | 16 | 19 |
20 |

Layout Blocks

21 |
22 | 23 | 24 | 25 | 30 | 31 | 32 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 47 | 48 | 49 |
26 | 29 | KeyTitle 33 | 35 | 36 |
art_track{{item.okey}}{{item.title}} 45 | 46 |
50 | 51 |
-------------------------------------------------------------------------------- /frontend/src/app/editor/layouts/layout.ts: -------------------------------------------------------------------------------- 1 | export class Layout { 2 | id: string; 3 | title: string; 4 | otype: string; 5 | okey: string; 6 | custFields: Array = []; 7 | body: string; 8 | descr: string; 9 | } 10 | -------------------------------------------------------------------------------- /frontend/src/app/editor/layouts/layouts.compo.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | 5 | 6 | highlight_off 7 | 8 | 9 | 10 | 11 | 12 | 13 | 16 | 19 |
20 |

Layouts

21 |
22 | 23 | 24 | 25 | 30 | 31 | 32 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 47 | 48 | 49 |
26 | 29 | KeyTitle 33 | 35 | 36 |
table_chart{{item.okey}}{{item.title}} 45 | 46 |
50 |
51 | -------------------------------------------------------------------------------- /frontend/src/app/editor/microtemplates/MicroTemplate.ts: -------------------------------------------------------------------------------- 1 | export class MicroTemplate { 2 | id: string; 3 | title: string; 4 | otype: string; 5 | okey: string; 6 | icon: string; 7 | descr: string; 8 | custFields: Array = []; 9 | body: string; 10 | } 11 | -------------------------------------------------------------------------------- /frontend/src/app/editor/microtemplates/dialogs/IconSelectDialog.css: -------------------------------------------------------------------------------- 1 | .iconListContainer { overflow-y: auto; overflow-x: hidden; height: 260px; border: 1px solid lightgray; border-radius: 4px; } 2 | .iconList { list-style: none; padding: 0 0 20px 0; } 3 | .iconList li { float: left; border-radius: 4px; padding: 3px; margin: 3px; cursor: pointer; } 4 | .iconList li:hover { background-color: lightgray; } 5 | .mat-dialog-content { overflow-x: hidden; } -------------------------------------------------------------------------------- /frontend/src/app/editor/microtemplates/dialogs/IconSelectDialog.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | 5 | 6 | highlight_off 7 | 8 | 9 |
10 |

Select Icon

11 |
12 |

{{ selectionText }}

13 |
14 |
    15 |
  • 16 | {{ icn }} 17 |
  • 18 |
19 |
20 |
21 | 22 |   Use icon not in list.. 23 |
24 |
25 |

You can use any of the icons provided by material design. Enter the icon label of the icon in the field below. Uploading custom icons is not supported.

26 | 27 | 28 | E.g.: feedback, favorite, help etc. 29 | {{ customIcon }} 30 | 31 |
32 |
33 |
34 |
35 | 39 | 40 |
41 |
-------------------------------------------------------------------------------- /frontend/src/app/editor/microtemplates/mtlist.compo.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | 5 | 6 | highlight_off 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 18 | 21 |
22 |

Micro Templates

23 |
24 | 25 | 26 | 27 | 32 | 33 | 34 | 35 | 40 | 41 | 42 | 43 | 45 | 48 | 49 | 50 | 56 | 59 | 60 | 61 |
28 | 31 | TitleDescription 36 | 38 | 39 |
46 | {{ item.icon || 'dns' }} 47 | {{item.title}}{{item.descr}} 51 |
52 | check_circle 53 | clear 54 |
55 |
57 | 58 |
62 | 63 |
-------------------------------------------------------------------------------- /frontend/src/app/editor/pages/dialogs/FileSelectionDialog.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |

{{dlgTitle}}

5 |
6 | 7 | 9 | 10 | {{bucket.label}} 11 | 12 | 13 | 14 |
15 |
16 | 19 |
20 |
21 |
22 |
23 | 24 |
25 |
26 | 27 |
28 | 29 |
30 | {{errorMessage}} 31 |
32 |
33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 46 | 47 | 48 | 49 | 50 | 54 | 55 | 56 | 57 | 58 | 63 | 64 | 65 |
44 | folder_open 45 | ..
51 | folder_open 52 | description 53 | {{item.label}}{{item.label}} 59 | 60 | link 61 | 62 |
66 |
67 |
68 | 69 |
-------------------------------------------------------------------------------- /frontend/src/app/editor/pages/dialogs/MTSelectDialog.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

Add Micro Template to {{fld.fldName}}

4 |
5 | 6 |
7 |
8 | {{errorMessage}} 9 |
10 | 11 |
12 | 13 | 14 | 15 | {{ selectedMT.title }} 16 | 17 | 18 | {{ itm.icon || 'dns' }} {{ itm.title }} 19 | 20 | 21 | 22 | 23 |
24 |
25 |
26 | 27 |
28 |
29 | 33 | 34 |
35 |
-------------------------------------------------------------------------------- /frontend/src/app/editor/pages/dialogs/MTSelectDialog.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright WebGate Consulting AG, 2020 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at: 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 13 | * implied. See the License for the specific language governing 14 | * permissions and limitations under the License. 15 | * 16 | */ 17 | 18 | import { Component, Inject, OnInit } from '@angular/core'; 19 | import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; 20 | import { BackendService } from 'src/app/services/backend.service'; 21 | 22 | @Component({ 23 | selector: 'app-mtselect-dialog', 24 | templateUrl: 'MTSelectDialog.html' 25 | }) 26 | 27 | export class MTSelectDialogComponent implements OnInit { 28 | constructor( 29 | public dialogRef: MatDialogRef, 30 | private backendSVC: BackendService, 31 | @Inject(MAT_DIALOG_DATA) public data: any) { } 32 | 33 | selectedMT: any = ''; 34 | errorMessage = ''; 35 | loading: boolean; 36 | lstMT: any = []; 37 | fld: any; 38 | shortdesc = ''; 39 | 40 | ngOnInit(): void { 41 | const that = this; 42 | this.fld = this.data.fld; 43 | this.loading = true; 44 | this.backendSVC.getAllMicroTemplates(false).then( 45 | (data: any) => { 46 | if (that.fld.fldType === 'container' && that.fld.restrictChilds && that.fld.accepts.length > 0) { 47 | const mt = JSON.parse(JSON.stringify(data)); 48 | const lst = []; 49 | mt.forEach(element => { 50 | if (that.fld.accepts.indexOf(element.id) >= 0) { lst.push(element); } 51 | }); 52 | that.lstMT = lst; 53 | } else { 54 | that.lstMT = JSON.parse(JSON.stringify(data)); 55 | } 56 | that.loading = false; 57 | }, 58 | (err) => { 59 | that.loading = false; 60 | console.log('Error while loading microtemplates', err); 61 | } 62 | ); 63 | } 64 | btnCancel(): void { 65 | this.dialogRef.close(null); 66 | } 67 | btnAdd(): void { 68 | if (this.selectedMT === '') { return; } 69 | this.selectedMT.shortdesc = this.shortdesc; 70 | this.dialogRef.close({ mt: this.selectedMT, action: 'add', fld: this.fld }); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /frontend/src/app/editor/pages/dialogs/PublishDialog.css: -------------------------------------------------------------------------------- 1 | .isOKBox .mat-stroked-button { margin-left: 4px; float: right; } 2 | .radiogrp-targets { display: flex; flex-direction: column; } 3 | .radiogrp-target { margin: 5px; } -------------------------------------------------------------------------------- /frontend/src/app/editor/pages/dialogs/PublishDialog.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 5 | help_outline 6 | 7 |

Publish page

8 |
9 | 10 |
11 |
12 |
{{errorMessage}}
13 | 14 |
15 |
16 | cloud_done Published! 17 | View page 18 |
19 |
20 |
21 | cloud_off Page removed! 22 |
23 |
24 | Publish to:

25 | 26 | 27 | {{bucket.label}} 28 | 29 | 30 |
31 |
32 |
33 |
34 |
35 | 39 | 43 | 44 |
45 |
46 | 48 | 49 | 50 |
51 |
52 |
-------------------------------------------------------------------------------- /frontend/src/app/editor/pages/mttable.component.css: -------------------------------------------------------------------------------- 1 | .mat-card { margin-left: 3px; margin-right: 3px; margin-top: 5px; } 2 | .permaActions { text-align: right; } 3 | .hoverActions { text-align: right; float: right; } 4 | 5 | .toggler .mat-icon:hover { background-color: #5ad1ff; color: black; } 6 | 7 | .fieldsTable { width: 100%; border-spacing: 0; border-collapse: collapse; font-size: 14px; } 8 | .fieldsTable th { text-align: left; border-bottom: 2px solid lightgray; padding: 6px; } 9 | .fieldsTable tbody td { border-bottom: 1px solid lightgray; padding: 6px; vertical-align: top; } 10 | 11 | .fieldsTable.clickableRow TBODY TR > TD { cursor: pointer; transition: background-color 0.4s linear; } 12 | 13 | .fieldsTable .hoverActions { white-space: nowrap; } 14 | .fieldsTable .hoverActions .mat-icon { visibility: hidden; margin-left: 6px; } 15 | .fieldsTable tr:hover > TD .hoverActions .mat-icon, .fieldsTable .permaActions .mat-icon { visibility: visible; cursor: pointer; cursor: pointer; margin-left: 6px; border-radius: 3px; } 16 | .fieldsTable tr:hover > TS .hoverActions .mat-icon:hover, .fieldsTable .permaActions .mat-icon:hover { background-color: #f5a30f; } 17 | -------------------------------------------------------------------------------- /frontend/src/app/editor/pages/mttable.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | 7 | 13 | 16 | 21 | 22 | 23 | 24 | 25 | 26 | 29 | 42 | 43 | 44 |
8 | chevron_right 10 | expand_more 12 | 14 | {{parentField.fldTitle}} - ({{parentField.fldName}}) 15 | 17 | content_paste 19 | add 20 |
27 | drag_indicator 28 | {{fldMT.title}} - {{fldMT.shortdesc}} 30 |
31 | edit 32 | content_copy 33 | delete 34 | 35 |
36 |
37 |
38 | 39 | 40 |
41 |
45 |
46 | 47 |
-------------------------------------------------------------------------------- /frontend/src/app/editor/pages/mttable.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input } from '@angular/core'; 2 | import { MatDialog } from '@angular/material/dialog'; 3 | import { MTContentDialogComponent } from './dialogs/MTContentDialog'; 4 | import { MTSelectDialogComponent } from './dialogs/MTSelectDialog'; 5 | import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop'; 6 | import { TabsNavService } from 'src/app/services/tabs.service'; 7 | 8 | @Component({ 9 | selector: 'app-mttable-component', 10 | templateUrl: './mttable.component.html', 11 | styleUrls: ['./mttable.component.css'] 12 | }) 13 | 14 | export class MTTableComponent { 15 | @Input() parentField: any; 16 | 17 | isExpanded: boolean; 18 | 19 | constructor( 20 | public dialog: MatDialog, 21 | private tabsSVC: TabsNavService 22 | ) { } 23 | 24 | toggleExpand() { 25 | this.isExpanded = !this.isExpanded; 26 | } 27 | btnEditObj(fldMT: any) { 28 | this.dialog.open(MTContentDialogComponent, { width: '800px', disableClose: false, data: { fldMT } }); 29 | } 30 | btnCopyObj(fldMT: any) { 31 | this.tabsSVC.setInternalClipboard(JSON.parse(JSON.stringify(fldMT))); // store dereferenced copy 32 | this.tabsSVC.printNotification('Object copied to memory. You can now insert it in another position or page.'); 33 | } 34 | btnPasteObj(pos: any) { 35 | const cp = this.tabsSVC.getInternalClipboard(); 36 | if (!cp || cp === '') { 37 | this.tabsSVC.printNotification('No object in memory. You must copy an object to memory first.'); 38 | return; 39 | } 40 | if (pos.restrictChilds) { 41 | // check if parent object accepts pasted object 42 | if (pos.accepts) { 43 | if (pos.accepts.indexOf(cp.id) < 0) { 44 | this.tabsSVC.printNotification('Unable to insert at this position. Object type is not allowed here.'); 45 | return; 46 | } 47 | } 48 | } 49 | if (!pos.lstObj) { pos.lstObj = []; } 50 | let cpDeref = JSON.parse(JSON.stringify(cp)); // dereference again, in case same object gets pasted again 51 | pos.lstObj.push(cpDeref); 52 | this.tabsSVC.printNotification('Object inserted.'); 53 | } 54 | // actions for nested MicroTemplates 55 | btnAddNewObj(fld: any) { 56 | const dialogRef = this.dialog.open(MTSelectDialogComponent, { width: '450px', disableClose: false, data: { fld } }); 57 | dialogRef.afterClosed().subscribe(result => { 58 | if (result && result.action === 'add') { 59 | if (typeof fld.lstObj === 'undefined') { fld.lstObj = []; } 60 | fld.lstObj.push(result.mt); 61 | } 62 | }); 63 | } 64 | btnDeleteObj(lst: any, fldMT: any, idx: any) { 65 | if (!confirm('Do you really want to delete \'' + fldMT.title + '\'?')) { return; } 66 | lst.splice(idx, 1); 67 | } 68 | dropSortObj(lst: any, event: CdkDragDrop) { 69 | moveItemInArray(lst, event.previousIndex, event.currentIndex); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /frontend/src/app/editor/pages/page.ts: -------------------------------------------------------------------------------- 1 | export class Page { 2 | id: string; 3 | title: string; 4 | otype: string; 5 | descr: string; 6 | opath: string; 7 | layout: string; 8 | body: string; 9 | lstMTObj: any = {}; 10 | doc: any = {}; 11 | queue: string; 12 | categories: any = []; 13 | dt: Date; 14 | sitemap: boolean; 15 | ftindex: boolean; 16 | listnav: boolean; 17 | keywords: string; 18 | navlabel: string; 19 | img: string; 20 | } 21 | -------------------------------------------------------------------------------- /frontend/src/app/editor/pages/pages.compo.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | 5 | 6 | highlight_off 7 | 8 |   11 | {{flatMode?'Flat view':'Tree view'}}   12 | 15 |
16 |

Pages

17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 |
PathTitle
description{{item.opath}}{{item.title}}
35 |
36 |
37 | 38 | 39 |
40 | 41 |
-------------------------------------------------------------------------------- /frontend/src/app/editor/pages/pages.compo.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright WebGate Consulting AG, 2020 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at: 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 13 | * implied. See the License for the specific language governing 14 | * permissions and limitations under the License. 15 | * 16 | */ 17 | 18 | import { Component, OnInit } from '@angular/core'; 19 | import { BackendService } from 'src/app/services/backend.service'; 20 | import { TabsNavService } from 'src/app/services/tabs.service'; 21 | 22 | @Component({ 23 | selector: 'app-pages', 24 | templateUrl: './pages.compo.html' 25 | }) 26 | 27 | export class PagesComponent implements OnInit { 28 | 29 | constructor( 30 | private backendSVC: BackendService, 31 | private tabsSVC: TabsNavService 32 | ) { } 33 | 34 | viewList: any = []; 35 | catTree: any = []; 36 | flatMode = false; 37 | viewFilter = ''; 38 | loading: boolean; 39 | 40 | tabID = 'tab-pages'; 41 | 42 | ngOnInit() { 43 | this.tabsSVC.addTabEvent(this.tabID, 'onTabFocus', () => { 44 | if (this.tabsSVC.isTabDataExpired(this.tabID)) this.loadPages(true); 45 | }); 46 | this.loadPages(false); 47 | } 48 | 49 | loadPages(forceUpdate: boolean) { 50 | this.backendSVC.getAllPages(forceUpdate).then( 51 | (data: any) => { 52 | if (data.success) { 53 | this.viewList = data.lstPages; 54 | this.catTree = data.tree; 55 | this.tabsSVC.setTabDataExpired(this.tabID, false); // mark data of tab as up to date 56 | } else { 57 | this.tabsSVC.printNotification(data.message || 'Error while loading'); 58 | } 59 | this.setLoading(false); 60 | }, 61 | (err: any) => { 62 | this.tabsSVC.printNotification('Error while loading'); 63 | console.log(err); 64 | this.setLoading(false); 65 | } 66 | ); 67 | } 68 | treeClick(row: any) { 69 | row.value.expanded = true; 70 | } 71 | 72 | btnNavigateTo(npath: string): void { 73 | this.tabsSVC.navigateTo(npath); 74 | } 75 | 76 | setLoading(on: boolean) { 77 | this.loading = on; 78 | this.tabsSVC.setLoading(on); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /frontend/src/app/editor/pages/treeview/treeview.compo.html: -------------------------------------------------------------------------------- 1 |
    2 |
  • 3 |
    4 | 5 | 6 | chevron_right 7 | expand_more 8 | 9 | 10 | 11 | arrow_right 12 | 13 | 14 | {{row.label}} 15 |
    16 | 17 |
  • 18 |
-------------------------------------------------------------------------------- /frontend/src/app/editor/pages/treeview/treeview.compo.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright WebGate Consulting AG, 2020 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at: 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 13 | * implied. See the License for the specific language governing 14 | * permissions and limitations under the License. 15 | * 16 | */ 17 | 18 | import { Component, OnInit, Input } from '@angular/core'; 19 | import { TabsNavService } from 'src/app/services/tabs.service'; 20 | 21 | @Component({ 22 | selector: 'app-flx-treeview', 23 | templateUrl: './treeview.compo.html', 24 | styleUrls: ['./treeview.component.css'] 25 | }) 26 | 27 | export class TreeViewComponent implements OnInit { 28 | @Input() treeBranch: any; 29 | @Input() pagelinkprefix: string; 30 | constructor( 31 | private tabsSVC: TabsNavService 32 | ) { } 33 | 34 | ngOnInit() { } 35 | 36 | public branchClick(row: any) { 37 | if (row.id) { // this is a page without childs 38 | this.tabsSVC.navigateTo(this.pagelinkprefix + row.id); 39 | } else { // toggle category 40 | row.expanded = !row.expanded; 41 | } 42 | } 43 | public toggleExpand(row: any) { 44 | row.expanded = !row.expanded; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /frontend/src/app/editor/pages/treeview/treeview.component.css: -------------------------------------------------------------------------------- 1 | .flx-tree { list-style: none; padding-left: 12px; font-size: 18px; } 2 | .flx-tree .mat-icon { vertical-align: bottom; border-radius: 3px; font-size: 23px; width: 24px; height: 24px; margin-right: 6px; margin-top: 2px; margin-bottom: 2px; border: 1px solid lightgray; background-color: white; } 3 | .flx-tree .treeRow a { cursor: pointer; } 4 | .clickable { cursor: pointer; } -------------------------------------------------------------------------------- /frontend/src/app/editor/pages/trumbowyg.table.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Trumbowyg v2.21.0 - A lightweight WYSIWYG editor 3 | * Trumbowyg plugin stylesheet 4 | * ------------------------ 5 | * @link http://alex-d.github.io/Trumbowyg 6 | * @license MIT 7 | * @author Alexandre Demode (Alex-D) 8 | * Twitter : @AlexandreDemode 9 | * Website : alex-d.fr 10 | */ 11 | 12 | .trumbowyg-editor table { 13 | width: 100%; } 14 | .trumbowyg-editor table td { 15 | border: 1px dotted #e7eaec; 16 | padding: 8px; } 17 | 18 | .trumbowyg-dropdown-table table { 19 | margin: 10px; 20 | display: inline-block; } 21 | 22 | .trumbowyg-dropdown-table table td { 23 | display: inline-block; 24 | height: 20px; 25 | width: 20px; 26 | margin: 1px; 27 | padding: 0; 28 | background-color: #fff; 29 | box-shadow: 0 0 0 1px #cecece inset; } 30 | .trumbowyg-dropdown-table table td.active { 31 | background-color: #00b393; 32 | box-shadow: none; 33 | cursor: pointer; } 34 | 35 | .trumbowyg-dropdown-table .trumbowyg-table-size { 36 | text-align: center; } 37 | -------------------------------------------------------------------------------- /frontend/src/app/editor/publication/dialogs/BulkPublishDialog.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

Publish {{pubtype}} pages

4 |
5 | 6 |
7 |
8 |

9 | You are about to (re-)publish ALL pages of your website project!

10 | Note: In most cases, this is only required after a layout change affecting every page in your 11 | project. 12 |

13 |

You are about to publish selected {{lstPageIDs.length}} pages!

14 | 15 | Publish to:

16 | 17 | 18 | {{bucket.label}} 19 | 20 | 21 |
22 |
23 | Remove pages from queue when published 24 |
25 |
26 |
27 |
{{errorMessage}}
28 | 29 |
30 |
31 | cloud_done Pages published! 32 |
33 |
34 |
35 | 36 |
37 |
38 |
39 | 43 | 44 |
45 |
46 | 47 | 48 |
49 | 50 |
51 |
-------------------------------------------------------------------------------- /frontend/src/app/editor/publication/dialogs/BulkPublishDialog.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright WebGate Consulting AG, 2020 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at: 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 13 | * implied. See the License for the specific language governing 14 | * permissions and limitations under the License. 15 | * 16 | */ 17 | 18 | import { Component, Inject, OnInit } from '@angular/core'; 19 | import { MatDialogRef, MAT_DIALOG_DATA, MatDialog } from '@angular/material/dialog'; 20 | import { BackendService } from 'src/app/services/backend.service'; 21 | import { PublishLogDialogComponent } from './PublishLogDialog'; 22 | 23 | @Component({ 24 | selector: 'app-bulk-publish-dialog', 25 | templateUrl: 'BulkPublishDialog.html', 26 | styleUrls: ['../../pages/dialogs/PublishDialog.css'] 27 | }) 28 | 29 | export class BulkPublishDialogComponent implements OnInit { 30 | constructor( 31 | public dialogRef: MatDialogRef, 32 | public dialog: MatDialog, 33 | private backendSVC: BackendService, 34 | @Inject(MAT_DIALOG_DATA) public data: any) { } 35 | 36 | selectedTargetEnv = ''; 37 | errorMessage = ''; 38 | lstLog: any; 39 | published: boolean; 40 | loading: boolean; 41 | lstPageIDs: any; 42 | pubtype: string; 43 | removeFromQueue = true; 44 | buckets: any = []; 45 | 46 | ngOnInit(): void { 47 | this.pubtype = this.data.pubtype; 48 | this.lstPageIDs = this.data.lstPageIDs; 49 | if (this.data.config && this.data.config.buckets) { 50 | this.data.config.buckets.forEach(bucket => { 51 | if (!bucket.noPublish) { this.buckets.push(bucket); } 52 | }); 53 | } 54 | } 55 | btnCancel(): void { 56 | this.dialogRef.close(null); 57 | } 58 | btnPublish(): void { 59 | if (this.selectedTargetEnv === '') return; 60 | this.loading = true; 61 | this.published = false; 62 | this.errorMessage = ''; 63 | this.lstLog = null; 64 | this.backendSVC.actionPublish('bulkPublishPage', { targetenv: this.selectedTargetEnv, pubtype: this.pubtype, lstPageIDs: this.lstPageIDs, removeFromQueue: this.removeFromQueue }).then( 65 | (data: any) => { 66 | if (data.success) { 67 | this.published = true; 68 | } else { 69 | this.errorMessage = data.message || 'Error while publishing' 70 | } 71 | this.lstLog = data.log || []; 72 | this.loading = false; 73 | }, 74 | (err: any) => { 75 | console.log('Error while publishing page', err); 76 | this.errorMessage = (err.statusText === 'Unknown Error' ? 'Unable to track progress, this action takes longer to complete.' : err.statusText); 77 | this.loading = false; 78 | } 79 | ); 80 | } 81 | btnViewLog(): void { 82 | this.dialog.open(PublishLogDialogComponent, { width: '860px', disableClose: false, data: { log: this.lstLog } }); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /frontend/src/app/editor/publication/dialogs/CFInvalidationDialog.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

CloudFront Invalidation

4 | 5 |
6 | 7 |
8 |
9 | {{errorMessage}} 10 |
11 |
12 | cloud_done Invalidation request submitted!
13 | This will take some time to process. 14 |
15 | 16 |
17 | 18 | 19 | {{dist.label}} 20 | 21 | 22 | 23 | 24 | 26 | 27 |
28 | 29 |
30 |
31 | 32 | 33 | 34 |
35 |
-------------------------------------------------------------------------------- /frontend/src/app/editor/publication/dialogs/CFInvalidationDialog.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright WebGate Consulting AG, 2020 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at: 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 13 | * implied. See the License for the specific language governing 14 | * permissions and limitations under the License. 15 | * 16 | */ 17 | 18 | import { Component, Inject, OnInit } from '@angular/core'; 19 | import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; 20 | import { BackendService } from 'src/app/services/backend.service'; 21 | 22 | @Component({ 23 | templateUrl: 'CFInvalidationDialog.html' 24 | }) 25 | 26 | export class CFInvalidationDialogComponent implements OnInit { 27 | constructor( 28 | public dialogRef: MatDialogRef, 29 | private backendSVC: BackendService, 30 | @Inject(MAT_DIALOG_DATA) public data: any) { } 31 | 32 | paths: string; 33 | loading: boolean; 34 | success: boolean; 35 | selectedTargetCF: string; 36 | errorMessage: string; 37 | cfdists: any = []; 38 | 39 | ngOnInit(): void { 40 | if (this.data.opaths && this.data.opaths.length > 0) { 41 | this.paths = this.data.opaths.join('\n'); 42 | } else { 43 | this.paths = '/*'; 44 | } 45 | this.cfdists = this.data.cfdists; 46 | } 47 | 48 | btnSubmit(): void { 49 | if (!this.selectedTargetCF || this.selectedTargetCF === '') return; 50 | this.errorMessage = ''; 51 | this.success = false; 52 | this.loading = true; 53 | const lstPaths = this.paths.split('\n'); 54 | this.backendSVC.actionCF('invalidateCF', { targetCF: this.selectedTargetCF, lstPaths }).then( 55 | (data: any) => { 56 | this.success = data.success; 57 | if (data.message) { this.errorMessage = data.message; } 58 | this.loading = false; 59 | }, 60 | (err: any) => { 61 | console.error(err); 62 | this.errorMessage = err.message || 'Error while submitting request'; 63 | this.loading = false; 64 | } 65 | ); 66 | } 67 | 68 | btnCancel(): void { 69 | this.dialogRef.close(null); 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /frontend/src/app/editor/publication/dialogs/FeedPublishDialog.css: -------------------------------------------------------------------------------- 1 | .isOKBox .mat-stroked-button { margin-left: 4px; float: right; } 2 | .radiogrp-targets { display: flex; flex-direction: column; } 3 | .radiogrp-target { margin: 5px; } 4 | .logdisplay { font-size: 11px; overflow: scroll; max-height: 400px; border: 1px solid lightgray; } -------------------------------------------------------------------------------- /frontend/src/app/editor/publication/dialogs/PublishLogDialog.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

Publish Log

4 |
5 |
{{row}}
6 |
7 |
8 |
9 | 10 |
11 |
-------------------------------------------------------------------------------- /frontend/src/app/editor/publication/dialogs/PublishLogDialog.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright WebGate Consulting AG, 2020 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at: 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 13 | * implied. See the License for the specific language governing 14 | * permissions and limitations under the License. 15 | * 16 | */ 17 | 18 | import { Component, Inject, OnInit } from '@angular/core'; 19 | import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; 20 | 21 | @Component({ 22 | selector: 'app-publish-log-dialog', 23 | templateUrl: 'PublishLogDialog.html', 24 | styles: [`.logdisplay { font-size: 11px; overflow: scroll; max-height: 400px; border: 1px solid lightgray; }`], 25 | }) 26 | 27 | export class PublishLogDialogComponent implements OnInit { 28 | constructor( 29 | public dialogRef: MatDialogRef, 30 | @Inject(MAT_DIALOG_DATA) public data: any) { } 31 | 32 | lstLog: any; 33 | 34 | ngOnInit(): void { 35 | this.lstLog = this.data.log; 36 | } 37 | btnCancel(): void { 38 | this.dialogRef.close(null); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /frontend/src/app/form-inbox/formsinbox.compo.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | 5 | 6 | highlight_off 7 | 8 | 9 | 10 | 11 | 12 | 13 | 16 |
17 |

Submitted Forms

18 |
19 | 20 | 21 | 22 | 27 | 28 | 29 | 30 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 46 | 47 | 48 |
23 | 26 | DateForm TitleEmail 31 | 33 | 34 |
assignment{{item.dt | date: 'dd.MM.yyyy HH:mm'}}{{item.title}}{{item.email}} 44 | 45 |
49 |
50 | -------------------------------------------------------------------------------- /frontend/src/app/form-inbox/submittedform.compo.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | 5 |
6 |

Submitted Form (ID:{{frm.id}})

7 |
8 | 9 |
10 |
11 | 12 | 13 | 14 |
15 |
16 | 17 | 18 | 19 |
20 |
21 | 22 |
23 |
24 | 25 | 26 | 27 |
28 |
29 | 30 | 31 | 32 |
33 |
34 | 35 | 36 | 37 |
38 |
39 |
40 | 41 | 42 |
43 |
44 |

Form Data

45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 |
{{item.key}}{{item.value}}
53 |
54 |
55 |
56 | 57 |
58 |
-------------------------------------------------------------------------------- /frontend/src/app/home/home.compo.css: -------------------------------------------------------------------------------- 1 | .loaderMessage { text-align: center; } 2 | .loaderMessage .mat-spinner { width: 100px; height: 100px; margin-left: auto; margin-right: auto; margin-bottom: 20px; } 3 | .copy { font-size: 14px; } 4 | .copy .logoWGC { height: 14px; vertical-align: middle; margin-left: 4px; } -------------------------------------------------------------------------------- /frontend/src/app/home/home.compo.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |
6 | CloudeeCMS - by 7 |
8 |

9 |
10 |
11 | 12 |
13 | 14 | Loading.. 15 |
16 | 17 |
18 |
19 | 20 | 24 |
25 | 29 |
30 | 34 |
35 |
36 | 37 |
38 | 42 |
43 | 47 |
48 | 52 |
53 |
54 |
55 | 59 |
60 | 64 |
65 | 69 |

70 |
71 | 72 |
73 | 74 |
-------------------------------------------------------------------------------- /frontend/src/app/home/home.compo.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright WebGate Consulting AG, 2020 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at: 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 13 | * implied. See the License for the specific language governing 14 | * permissions and limitations under the License. 15 | * 16 | */ 17 | 18 | import { Component } from '@angular/core'; 19 | import { TabsNavService } from '../services/tabs.service'; 20 | import { BackendService } from '../services/backend.service'; 21 | 22 | @Component({ 23 | selector: 'app-home', 24 | templateUrl: './home.compo.html', 25 | styleUrls: ['./home.compo.css'] 26 | }) 27 | 28 | export class HomeComponent { 29 | 30 | constructor( 31 | public tabsSVC: TabsNavService, 32 | public backendSVC: BackendService 33 | ) { } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /frontend/src/app/login-dialog/logindialog.component.css: -------------------------------------------------------------------------------- 1 | .loginDialog { font-family: Roboto, "Helvetica Neue", sans-serif; } 2 | .full-width { width: 100%; } 3 | .warning { font-family: Roboto, "Helvetica Neue", sans-serif; padding: 7px 12px; border-radius: 24px; background-color: #ffa640; color: rgb(0, 0, 0); } 4 | .spinnerContainer { height: 120px; } 5 | .loginLogo { width: 100px; float: right; } -------------------------------------------------------------------------------- /frontend/src/app/settings/AboutDialog.css: -------------------------------------------------------------------------------- 1 | .logo { width: 225px; } 2 | .logoWGC { height: 14px; vertical-align: middle; margin-left: 6px; } 3 | .copy { font-size: 14px; padding-top: 4px; } 4 | a, a:visited { color: #3a77c0; text-decoration: none; } 5 | a:hover { color: #3a77c0; text-decoration: underline; } 6 | .btnClose { float: right; } 7 | 8 | .versionInfo { 9 | opacity: 0; 10 | } 11 | .reveal{ 12 | opacity: 1; 13 | transition: all 6s linear; 14 | } 15 | .thesky .cloudContainer { 16 | position: relative; 17 | background-repeat: repeat-x; 18 | background-image: url( "../../assets/img/clouds1.svg" ); 19 | background-size: 140% 100%; 20 | background-position: -320px 7px; 21 | width: 100%; 22 | height: 166px; 23 | } 24 | 25 | .thesky.move-it .cloudContainer { 26 | background-position: -430px 7px; 27 | transition: all 6s ease-in-out; 28 | } 29 | 30 | .thesky .cloudContainer > div { width: 100%; } 31 | 32 | .thesky .clouds2 { z-index: 20; background-image: url( '../../assets/img/clouds2.svg' ); 33 | position: absolute; 34 | height: 160px; 35 | background-size: cover; 36 | background-position: 56% 10%; 37 | bottom: 27px; 38 | } 39 | 40 | .thesky.move-it .clouds2 { 41 | background-position: 45% 10%; 42 | transition: all 9s ease-in-out; 43 | bottom: 32px; 44 | } 45 | .thesky .clouds3 { z-index: 30; background-image: url( '../../assets/img/clouds3.svg' ); 46 | position: absolute; 47 | bottom: 0px; 48 | background-repeat: no-repeat; 49 | background-size: cover; 50 | background-position: 20% 10%; 51 | height: 90px; 52 | } 53 | 54 | .thesky.move-it .clouds3 { 55 | background-position: 34% 10%; 56 | transition: all 9s ease-in-out; 57 | } 58 | -------------------------------------------------------------------------------- /frontend/src/app/settings/AboutDialog.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 6 |

7 | 8 |

9 | 10 |
11 | cloudee-cms.com 12 |
13 | Version {{APP_VERSION}} 14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | 22 |
23 | © 2019 - 2024 24 | | License | Notice 25 |
26 |
27 |
28 |
29 |
-------------------------------------------------------------------------------- /frontend/src/app/settings/dialogs/ImageConversion.ts: -------------------------------------------------------------------------------- 1 | export class ImageConversion { 2 | id: string; 3 | convertformat: string; 4 | convertheight: number; 5 | convertwidth: number; 6 | quality: number; 7 | compressionLevel: number; 8 | suffix: string; 9 | withoutEnlargement: boolean; 10 | resizeMode: string; 11 | } 12 | -------------------------------------------------------------------------------- /frontend/src/app/settings/dialogs/ImageProfile.ts: -------------------------------------------------------------------------------- 1 | import { ImageConversion } from './ImageConversion'; 2 | export class ImageProfile { 3 | id: string; 4 | label: string; 5 | descr: string; 6 | tpath: string; 7 | deleteOriginal: boolean; 8 | ccMaxAge: string; 9 | conversions: Array = []; 10 | } 11 | -------------------------------------------------------------------------------- /frontend/src/app/settings/dialogs/bookmarkedit-dialog.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

{{isNew?'Add':'Edit'}} Bookmark

4 |
5 | {{errorMessage}} 6 |
7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 | 18 |
19 |
20 | 24 | 25 |
26 |
-------------------------------------------------------------------------------- /frontend/src/app/settings/dialogs/bookmarkedit-dialog.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright WebGate Consulting AG, 2020 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at: 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 13 | * implied. See the License for the specific language governing 14 | * permissions and limitations under the License. 15 | * 16 | */ 17 | 18 | import { Component, Inject, OnInit } from '@angular/core'; 19 | import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; 20 | 21 | @Component({ 22 | selector: 'app-bmedit-dialog', 23 | templateUrl: 'bookmarkedit-dialog.html' 24 | }) 25 | 26 | export class BookmarkEditDialogComponent implements OnInit { 27 | constructor( 28 | public dialogRef: MatDialogRef, 29 | @Inject(MAT_DIALOG_DATA) public data: any) { } 30 | 31 | errorMessage = ''; 32 | loading: boolean; 33 | bm: any; 34 | isNew: boolean; 35 | 36 | ngOnInit(): void { 37 | if (!this.data.bm) { this.isNew = true; } 38 | this.bm = this.data.bm || {}; 39 | } 40 | btnCancel(): void { 41 | this.dialogRef.close(null); 42 | } 43 | btnDone(): void { 44 | this.dialogRef.close({ action: (this.isNew ? 'add' : ''), bm: this.bm }); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /frontend/src/app/settings/dialogs/bucketedit-dialog.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

{{isNew?'Add':'Edit'}} Bucket

4 |
5 | {{errorMessage}} 6 |
7 |
8 |
9 | 10 | 12 | 13 |
14 |
15 | 16 | 17 | 18 |
19 |
20 |
21 |
22 | 23 | 25 | E.g. https://{{bucket.bucketname?bucket.bucketname:'[BUCKET-NAME]'}}.s3.amazonaws.com/ 26 | 27 |

28 |
29 |
30 | 31 |
32 |
33 | 34 | 35 | E.g. https://{{bucket.label==='CDN'?'cdn':'www'}}.yourdomain.com 36 |

37 |
38 |
39 |
40 |
41 | 42 | 43 | 44 |
45 |
46 | 47 | 48 | 49 | Do not show in publish dialog 50 |
51 | Use for images pasted in richtext editor 52 | 53 |
54 |
55 | 56 |
57 |
58 | 62 | 63 |
64 |
-------------------------------------------------------------------------------- /frontend/src/app/settings/dialogs/bucketedit-dialog.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright WebGate Consulting AG, 2020 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at: 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 13 | * implied. See the License for the specific language governing 14 | * permissions and limitations under the License. 15 | * 16 | */ 17 | 18 | import { Component, Inject, OnInit } from '@angular/core'; 19 | import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; 20 | 21 | @Component({ 22 | selector: 'app-bucketedit-dialog', 23 | templateUrl: 'bucketedit-dialog.html' 24 | }) 25 | 26 | export class BucketEditDialogComponent implements OnInit { 27 | constructor( 28 | public dialogRef: MatDialogRef, 29 | @Inject(MAT_DIALOG_DATA) public data: any) { } 30 | 31 | errorMessage = ''; 32 | loading: boolean; 33 | bucket: any; 34 | isNew: boolean; 35 | lastBucketName: string; 36 | 37 | ngOnInit(): void { 38 | if (!this.data.bucket) { this.isNew = true; } 39 | this.bucket = this.data.bucket || {}; 40 | this.lastBucketName = this.bucket.bucketname || ''; 41 | } 42 | btnCancel(): void { 43 | this.dialogRef.close(null); 44 | } 45 | validateInput() { 46 | const webURL = this.bucket.webURL || ''; 47 | if (webURL !== '' && webURL.lastIndexOf('/') !== webURL.length - 1) { this.bucket.webURL += '/'; } 48 | } 49 | btnDone(): void { 50 | let warn = ''; 51 | if (!this.bucket.webURL.startsWith('https')) { 52 | warn = 'S3 Bucket Web URL should use https to avoid mixed-content warnings. \nContinue?'; 53 | } else { // uses https 54 | if (this.bucket.webURL.toLowerCase().indexOf('s3-website') > 0) { 55 | warn = 'S3 Bucket Web URL does not support "s3-website" over SSL. You should replace "s3-website" in the URL with just "s3".'; 56 | } 57 | } 58 | if (warn !== '') { 59 | if (!confirm(warn)) { 60 | return; 61 | } 62 | } 63 | if (this.bucket.bucketname !== this.lastBucketName) { 64 | alert('Remember to update IAM role to grant access for bucket ' + this.bucket.bucketname); 65 | } 66 | this.dialogRef.close({ action: (this.isNew ? 'add' : ''), bucket: this.bucket }); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /frontend/src/app/settings/dialogs/buildproject-dialog.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 |

CodePipeline Build Projects

5 |
6 | 7 |
8 |
9 | {{errorMessage}} 10 |
11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 29 | 30 | 31 | 37 | 38 | 39 |
Build ProjectBuild Container Image VersionRecommended Version
26 | {{bp.name}}
27 | {{bp.computeType}} 28 |
{{bp.image}}{{buildinfo.EXPECTED_BUILD_IMAGE}} 32 | check_circle 34 | warning 36 |
40 |
41 |
42 |
43 | Upgrade of BuildProject image recommended! Please read how to 45 | update the CodePipeline CloudFormation stack. 46 |
47 |
48 |
49 |
50 | 51 |
52 |
-------------------------------------------------------------------------------- /frontend/src/app/settings/dialogs/buildproject-dialog.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright WebGate Consulting AG, 2020 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at: 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 13 | * implied. See the License for the specific language governing 14 | * permissions and limitations under the License. 15 | * 16 | */ 17 | 18 | import { Component, OnInit } from '@angular/core'; 19 | import { MatDialogRef } from '@angular/material/dialog'; 20 | import { BackendService } from 'src/app/services/backend.service'; 21 | 22 | @Component({ 23 | templateUrl: 'buildproject-dialog.html', 24 | styles: ['.logo { width: 145px; float: right; } .warn { padding: 12px; border-radius: 4px; background-color: #ffc275; } a, a:visited { color: #3a77c0; text-decoration: underline; } '] 25 | }) 26 | 27 | export class BuildprojectDialogComponent implements OnInit { 28 | constructor( 29 | private backendSVC: BackendService, 30 | public dialogRef: MatDialogRef) { } 31 | 32 | errorMessage = ''; 33 | loading = true; 34 | buildinfo: any = {}; 35 | 36 | ngOnInit(): void { 37 | this.getBuildProjectInfo(); 38 | } 39 | getBuildProjectInfo(): void { 40 | this.loading = true; 41 | this.backendSVC.actionBkup('getbuildprojectinfo', {}).then( 42 | (rc: any) => { 43 | let data = rc.data; // for backwards compatibility 44 | if (data.success) { 45 | this.buildinfo = data.buildinfo || {}; 46 | this.loading = false; 47 | } else { 48 | console.log("Error while retrieving buildproject info:", data.message); 49 | this.errorMessage = "Error while retrieving buildproject info: " + (data.message || ''); 50 | this.loading = false; 51 | } 52 | }, 53 | (err: any) => { 54 | console.log('Error while fetching buildproject information', err); 55 | this.errorMessage = 'Error while fetching buildproject information'; 56 | this.loading = false; 57 | } 58 | ); 59 | } 60 | btnCancel(): void { 61 | this.dialogRef.close(null); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /frontend/src/app/settings/dialogs/cfdistedit-dialog.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

{{isNew?'Add':'Edit'}} CF Distribution

4 |
5 | {{errorMessage}} 6 |
7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | Illegal characters in variable name! 20 | 21 |
22 |
23 | 24 |
25 |
26 | 30 | 31 |
32 |
-------------------------------------------------------------------------------- /frontend/src/app/settings/dialogs/cfdistedit-dialog.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright WebGate Consulting AG, 2020 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at: 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 13 | * implied. See the License for the specific language governing 14 | * permissions and limitations under the License. 15 | * 16 | */ 17 | 18 | import { Component, Inject, OnInit } from '@angular/core'; 19 | import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; 20 | 21 | @Component({ 22 | selector: 'app-cfdistedit-dialog', 23 | templateUrl: 'cfdistedit-dialog.html' 24 | }) 25 | 26 | export class CFDistEditDialogComponent implements OnInit { 27 | constructor( 28 | public dialogRef: MatDialogRef, 29 | @Inject(MAT_DIALOG_DATA) public data: any) { } 30 | 31 | errorMessage = ''; 32 | loading: boolean; 33 | dist: any; 34 | isNew: boolean; 35 | chkVar = new RegExp(/^[a-z0-9]+$/i); 36 | webURLVarError = false; 37 | 38 | ngOnInit(): void { 39 | if (!this.data.dist) { this.isNew = true; } 40 | this.dist = this.data.dist || {}; 41 | } 42 | btnCancel(): void { 43 | this.dialogRef.close(null); 44 | } 45 | btnDone(): void { 46 | if (this.webURLVarError) { 47 | this.dist.webURLVAR = ''; 48 | alert('Illegal characters in web URL variable name!'); 49 | return; 50 | } 51 | this.dialogRef.close({ action: (this.isNew ? 'add' : ''), dist: this.dist }); 52 | } 53 | chkWebURLVarError(): void { 54 | this.webURLVarError = false; 55 | const webURLVAR = this.dist.webURLVAR || ''; 56 | if (webURLVAR === '') { return; } 57 | if (webURLVAR === 'navtree') { return; } // reserved name 58 | this.webURLVarError = !this.chkVar.test(webURLVAR); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /frontend/src/app/settings/dialogs/feededit-dialog.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright WebGate Consulting AG, 2020 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at: 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 13 | * implied. See the License for the specific language governing 14 | * permissions and limitations under the License. 15 | * 16 | */ 17 | 18 | import { Component, Inject, OnInit } from '@angular/core'; 19 | import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; 20 | 21 | @Component({ 22 | selector: 'app-feededit-dialog', 23 | templateUrl: 'feededit-dialog.html' 24 | }) 25 | 26 | export class FeedEditDialogComponent implements OnInit { 27 | constructor( 28 | public dialogRef: MatDialogRef, 29 | @Inject(MAT_DIALOG_DATA) public data: any) { } 30 | 31 | errorMessage = ''; 32 | loading: boolean; 33 | feed: any = {}; 34 | isNew: boolean; 35 | 36 | lstCategories = []; 37 | 38 | lstFTypes = [ 39 | { label: 'JSON Feed', val: 'JSON'}, 40 | { label: 'Sitemap for Search Engine', val: 'Sitemap'}, 41 | { label: 'Atom Feed', val: 'Atom' } 42 | ]; 43 | 44 | ngOnInit(): void { 45 | this.feed = this.data.feed || {}; 46 | if (!this.data.feed) { 47 | this.isNew = true; 48 | this.feed.id = this.getGUID(); 49 | } 50 | this.lstCategories = this.data.lstCategories || []; 51 | } 52 | onFeedTypeChange(): void { 53 | this.feed.category = null; 54 | this.feed.filename = ''; 55 | if (this.feed.ftype === 'Sitemap') { 56 | this.feed.filename = 'sitemap.xml'; 57 | // } else if (this.feed.ftype === 'Atom') { 58 | // } else if (this.feed.ftype === 'JSON') { 59 | } 60 | } 61 | validateDialog(): boolean { 62 | let rc = true; 63 | if (this.feed.ftype === 'Sitemap') { 64 | if (!this.feed.sitename || this.feed.sitename === '') { rc = false; } 65 | } else if (this.feed.ftype === 'Atom') { 66 | if (!this.feed.sitename || this.feed.sitename === '') { rc = false; } 67 | if (!this.feed.title || this.feed.title === '') { rc = false; } 68 | // } else if (this.feed.ftype === 'JSON') { 69 | } 70 | return rc; 71 | } 72 | getGUID() { 73 | function s4() { 74 | return Math.floor((1 + Math.random()) * 0x10000) 75 | .toString(16) 76 | .substring(1); 77 | } 78 | return s4() + s4() + '-' + s4() + '-' + s4(); 79 | } 80 | btnCancel(): void { 81 | this.dialogRef.close(null); 82 | } 83 | btnDone(): void { 84 | if (this.validateDialog()) { 85 | this.dialogRef.close({ action: (this.isNew ? 'add' : ''), feed: this.feed }); 86 | } else { 87 | alert('Please fill in all required fields.'); 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /frontend/src/app/settings/dialogs/fnedit-dialog.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

{{isNew?'Add':'Edit'}} Global Pug Script Function

4 |
5 | {{errorMessage}} 6 |
7 | 8 | 9 | 10 | 11 | 12 | 15 | {{fnHint}} 16 | 17 | 18 |
19 |
20 | 21 |
22 |
23 | 27 | 28 |
29 |
-------------------------------------------------------------------------------- /frontend/src/app/settings/dialogs/fnedit-dialog.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright WebGate Consulting AG, 2020 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at: 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 13 | * implied. See the License for the specific language governing 14 | * permissions and limitations under the License. 15 | * 16 | */ 17 | 18 | import { Component, Inject, OnInit } from '@angular/core'; 19 | import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; 20 | 21 | @Component({ 22 | selector: 'app-fnedit-dialog', 23 | templateUrl: 'fnedit-dialog.html' 24 | }) 25 | 26 | export class GlobalFunctionEditDialogComponent implements OnInit { 27 | constructor( 28 | public dialogRef: MatDialogRef, 29 | @Inject(MAT_DIALOG_DATA) public data: any) { } 30 | 31 | errorMessage = ''; 32 | loading: boolean; 33 | fn: any; 34 | isNew: boolean; 35 | fnHint = ''; 36 | 37 | ngOnInit(): void { 38 | if (!this.data.fn) { this.isNew = true; } 39 | this.fn = this.data.fn || {}; 40 | } 41 | checkFn(): void { 42 | this.fnHint = ''; 43 | if (!this.fn.body || this.fn.body === '') { 44 | return; 45 | } else if (!this.fn.body.startsWith('- function ')) { 46 | this.fnHint = 'Function code should start with: - function'; 47 | } 48 | } 49 | btnCancel(): void { 50 | this.dialogRef.close(null); 51 | } 52 | btnDone(): void { 53 | this.dialogRef.close({ action: (this.isNew ? 'add' : ''), fn: this.fn }); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /frontend/src/app/settings/dialogs/pkgupload-dialog.css: -------------------------------------------------------------------------------- 1 | .uplProgress { border: 1px solid #d8d8d8; border-radius: 3px; padding: 2px; margin: 12px 0; } 2 | .uplProgress .uplFile { background-color: #f1f1f1; padding: 3px 6px; border-radius: 4px; margin-bottom: 4px; } 3 | label .mat-icon { vertical-align: middle; } 4 | .logdisplay { font-size: 11px; overflow: scroll; max-height: 400px; border: 1px solid lightgray; } 5 | .loader { margin-bottom: 20px; } -------------------------------------------------------------------------------- /frontend/src/app/settings/dialogs/pkgupload-dialog.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

Package import

4 | 5 |
6 | 7 | 10 | 11 |
12 |
13 | {{file.name}}
14 | 15 |
16 |
17 |
18 | 19 |
20 | 21 |
22 |
23 | {{installMsg}} 24 |
25 |
{{row}}
26 |
27 |
28 | 29 |

30 |
31 |
{{errorMessage}}
32 |
33 | 34 |
35 |
-------------------------------------------------------------------------------- /frontend/src/app/settings/dialogs/restore-dialog.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

Import package

4 |
5 | 6 |
7 |
8 | {{errorMessage}} 9 |
10 |
No files found in backup folder of selected bucket
11 |
12 | Place your package file (.zip) in the backup folder of the selected bucket. 13 |

14 | 15 | 16 | {{file.label}} 17 | 18 | 19 | 20 |
21 |
22 |
{{row}}
23 |
24 |
25 | 26 |
27 |
28 | 32 | 33 |
34 |
-------------------------------------------------------------------------------- /frontend/src/app/settings/dialogs/updater-dialog.css: -------------------------------------------------------------------------------- 1 | .borderbox { 2 | font-size: 14px; 3 | border-radius: 3px; 4 | border: 1px solid lightgrey; 5 | padding: 6px; 6 | margin-bottom: 8px; 7 | } 8 | 9 | .warn { 10 | background-color: #fde852; 11 | border-radius: 3px; 12 | padding: 6px; 13 | margin-top: 14px; 14 | font-size: 12px; 15 | } 16 | .logo { width: 145px; float: right; } 17 | a, a:visited { color: #3a77c0; text-decoration: underline; } 18 | .mat-dialog-content { font-size: 14px; } 19 | ul.updStatus { list-style: none; margin: 0; padding: 0; color: black; } 20 | ul.updStatus li { color: black; padding: 6px; margin-bottom: 2px; background-color: #f3f3f3; border-radius: 3px;} 21 | ul.updStatus li.InProgress { background-color: #c5ff9f; } 22 | ul.updStatus li.Failed { background-color: #ff9f9f; } 23 | ul.updStatus li.Abandoned { background-color: #ffe59f; } 24 | ul.updStatus li mat-icon { vertical-align: middle; margin-right: 6px; } 25 | ul.updStatus li mat-spinner { display: inline-block; vertical-align: middle; margin-right: 10px;} -------------------------------------------------------------------------------- /frontend/src/app/settings/dialogs/variableedit-dialog.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

{{isNew?'Add':'Edit'}} Variable

4 |
5 | {{errorMessage}} 6 |
7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 17 | 18 | 19 |
20 |
21 | 22 |
23 |
24 | 28 | 29 |
30 |
-------------------------------------------------------------------------------- /frontend/src/app/settings/dialogs/variableedit-dialog.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright WebGate Consulting AG, 2020 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at: 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 13 | * implied. See the License for the specific language governing 14 | * permissions and limitations under the License. 15 | * 16 | */ 17 | 18 | import { Component, Inject, OnInit } from '@angular/core'; 19 | import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; 20 | 21 | @Component({ 22 | selector: 'app-varedit-dialog', 23 | templateUrl: 'variableedit-dialog.html' 24 | }) 25 | 26 | export class VariableEditDialogComponent implements OnInit { 27 | constructor( 28 | public dialogRef: MatDialogRef, 29 | @Inject(MAT_DIALOG_DATA) public data: any) { } 30 | 31 | errorMessage = ''; 32 | loading: boolean; 33 | variable: any; 34 | isNew: boolean; 35 | 36 | ngOnInit(): void { 37 | if (!this.data.variable) { this.isNew = true; } 38 | this.variable = this.data.variable || {}; 39 | } 40 | btnCancel(): void { 41 | this.dialogRef.close(null); 42 | } 43 | btnDone(): void { 44 | this.dialogRef.close({ action: (this.isNew ? 'add' : ''), variable: this.variable }); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /frontend/src/app/settings/migration-dialog/migration-dialog.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright WebGate Consulting AG, 2024 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at: 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 13 | * implied. See the License for the specific language governing 14 | * permissions and limitations under the License. 15 | * 16 | */ 17 | 18 | import { Component, OnInit } from '@angular/core'; 19 | import { MatDialogRef } from '@angular/material/dialog'; 20 | import { BackendService } from 'src/app/services/backend.service'; 21 | 22 | @Component({ 23 | templateUrl: 'migration-dialog.html', 24 | styles: ['.logo { width: 145px; float: right; } .warn { padding: 12px; border-radius: 4px; background-color: #ffc275; } a, a:visited { color: #3a77c0; text-decoration: underline; } '] 25 | }) 26 | 27 | export class MigrationDialogComponent implements OnInit { 28 | constructor( 29 | private backendSVC: BackendService, 30 | public dialogRef: MatDialogRef) { } 31 | 32 | errorMessage = ''; 33 | loading = true; 34 | migstatus: any = {}; 35 | showMigResult = false; 36 | lstMigResult: Array = []; 37 | 38 | ngOnInit(): void { 39 | this.getGSI1Status(); 40 | } 41 | getGSI1Status(): void { 42 | this.loading = true; 43 | this.backendSVC.migrationAction('getGSI1Status', {}).then( 44 | (rc: any) => { 45 | if (rc.success) this.migstatus = rc; 46 | this.loading = false; 47 | }, 48 | (err: any) => { 49 | this.errorMessage = 'Error while checking GSI1-Status'; 50 | console.error(err); 51 | } 52 | ); 53 | } 54 | btnStartUpgrade(): void { 55 | if (!confirm('Start content migration to support GSI queries?')) return; 56 | this.loading = true; 57 | this.backendSVC.migrationAction('migrateGSI1', {}).then( 58 | (rc: any) => { 59 | if (rc.success) { 60 | this.lstMigResult = rc.lst || []; 61 | this.showMigResult = true; 62 | this.getGSI1Status(); 63 | } else { 64 | this.errorMessage = rc.message || 'Upgrade failed'; 65 | } 66 | this.loading = false; 67 | }, 68 | (err: any) => { 69 | this.errorMessage = 'Error while performing update'; 70 | console.error(err); 71 | } 72 | ); 73 | } 74 | btnCancel(): void { 75 | if (this.showMigResult) { 76 | // if migration lambda was triggered, reload app to prevent user from saving the currently open settings document, or the GSI1MigDone flag might be lost 77 | window.location.reload(); 78 | } else { 79 | this.dialogRef.close(null); 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /frontend/src/app/settings/settings.compo.css: -------------------------------------------------------------------------------- 1 | .multiTiles .mat-card { height: 100%; } 2 | .multiTiles { padding-bottom: 20px; } 3 | .addBtn { float: right; cursor: pointer; } 4 | h4 { margin-top: 0; margin-bottom: 0; } .icnRestartReq { color: #ff8300; } 5 | .logdisplay { font-size: 11px; overflow: scroll; max-height: 400px; border: 1px solid lightgray; } 6 | .inputform { font-size: 14px; } 7 | .sectionHint { margin-bottom: 10px; margin-top: 4px; } 8 | .clrhdr { height: 0; } -------------------------------------------------------------------------------- /frontend/src/app/useradmin/dialogs/addgroup-dialog.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

Add user to group

4 | 5 | 6 | 7 | 8 | {{itm}} 9 | 10 | 11 | 12 |
13 | 14 |
15 |
16 | 17 | 18 |
19 |
-------------------------------------------------------------------------------- /frontend/src/app/useradmin/dialogs/addgroup-dialog.ts: -------------------------------------------------------------------------------- 1 | import { Component, Inject, OnInit } from '@angular/core'; 2 | import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; 3 | import { TabsNavService } from 'src/app/services/tabs.service'; 4 | import { BackendService } from 'src/app/services/backend.service'; 5 | 6 | @Component({ 7 | selector: 'app-addgroupdialog', 8 | templateUrl: 'addgroup-dialog.html' 9 | }) 10 | 11 | export class GroupAddDialogComponent implements OnInit { 12 | constructor( 13 | public dialogRef: MatDialogRef, 14 | private backendSVC: BackendService, 15 | private tabsSVC: TabsNavService, 16 | @Inject(MAT_DIALOG_DATA) public data: any) { } 17 | 18 | allgroups = []; 19 | loading = true; 20 | selectedGroup = ''; 21 | userID: string; 22 | 23 | ngOnInit(): void { 24 | this.userID = this.data.userID; 25 | this.backendSVC.cognitoAction('listGroups', {}).then( 26 | (data: any) => { 27 | this.allgroups = data.groups || []; 28 | this.loading = false; 29 | }, 30 | (err: any) => { 31 | console.log('Error', err); 32 | this.tabsSVC.printNotification('Failed to retrieve list of groups!'); 33 | } 34 | ); 35 | } 36 | 37 | btnDialogConfirm(): void { 38 | if (!this.userID || this.userID === '') { 39 | this.tabsSVC.printNotification('UserID is missing'); 40 | return; 41 | } 42 | this.backendSVC.cognitoAction('addUserToGroup', { id: this.userID, groupname: this.selectedGroup }).then( 43 | (data: any) => { 44 | if (data.success) { 45 | this.dialogRef.close(this.selectedGroup); 46 | } else { 47 | this.tabsSVC.printNotification(data.message || 'Failed to add group!'); 48 | } 49 | }, 50 | (err) => { 51 | this.tabsSVC.printNotification('Failed to add group!'); 52 | console.log('Error', err); 53 | } 54 | ); 55 | } 56 | btnCancel(): void { 57 | this.dialogRef.close(null); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /frontend/src/app/useradmin/dialogs/newuserprofile.dialog.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

{{regDone?'User registered':'Create new User'}}

4 |
5 | 6 |
7 |
8 | {{errorMessage}} 9 |
10 |
11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 |
26 | Username: {{usr.Username}}

27 | E-Mail: {{usr.email}}

28 | Temporary password: {{tempPwd}}

29 | User will be prompted to change password on first login.

30 |
31 | 32 |
33 |
34 | 35 | 36 | 37 |
38 |
-------------------------------------------------------------------------------- /frontend/src/app/useradmin/dialogs/newuserprofile.dialog.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright WebGate Consulting AG, 2020 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at: 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 13 | * implied. See the License for the specific language governing 14 | * permissions and limitations under the License. 15 | * 16 | */ 17 | 18 | import { Component, OnInit, Inject } from '@angular/core'; 19 | import { BackendService } from '../../services/backend.service'; 20 | import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; 21 | 22 | @Component({ 23 | selector: 'app-newuserprofile', 24 | templateUrl: './newuserprofile.dialog.html' 25 | }) 26 | 27 | export class NewUserProfileDialogComponent implements OnInit { 28 | 29 | constructor( 30 | private backendSVC: BackendService, 31 | public dialogRef: MatDialogRef, 32 | @Inject(MAT_DIALOG_DATA) public data: any 33 | ) { } 34 | 35 | 36 | loading: boolean; 37 | usr: any = {}; 38 | errorMessage: string; 39 | regDone: boolean; 40 | tempPwd: string; 41 | 42 | ngOnInit() { 43 | } 44 | 45 | btnCreateUser() { 46 | this.loading = true; 47 | this.errorMessage = ''; 48 | this.backendSVC.cognitoAction('createCognitoUser', { usr: this.usr }).then( 49 | (data: any) => { 50 | if (data.success) { 51 | this.regDone = true; 52 | this.tempPwd = data.tempPwd; 53 | } else { 54 | this.errorMessage = data.message || 'Failed to create user'; 55 | } 56 | this.loading = false; 57 | }, 58 | (err: any) => { 59 | this.loading = false; 60 | console.error('Error', err); 61 | this.errorMessage = 'Failed to create user'; 62 | } 63 | ); 64 | } 65 | 66 | btnClose(refreshView: boolean) { 67 | if (refreshView) { 68 | this.dialogRef.close({ action: 'refreshview' }); 69 | } else { 70 | this.dialogRef.close(null); 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /frontend/src/app/useradmin/dialogs/pwdchange-dialog.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 |
5 |

Change password

6 | 7 | 9 | {{hidepwd ? 'visibility' : 'visibility_off'}} 10 | 11 | 12 | 13 | 14 | 16 | {{hidepwd ? 'visibility' : 'visibility_off'}} 17 | 18 | 19 | 20 | 22 | {{hidepwd ? 'visibility' : 'visibility_off'}} 23 | 24 | 25 |
{{errorMessage}}
26 | 27 |
28 |
29 | 30 | 31 |
32 |
33 | 34 |
35 |
36 |

Change password

37 |

Password successfully changed!

38 |
39 |
40 |
41 | 42 |
43 |
44 |
45 | 46 |
-------------------------------------------------------------------------------- /frontend/src/app/useradmin/dialogs/pwdchange-dialog.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright WebGate Consulting AG, 2020 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at: 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 13 | * implied. See the License for the specific language governing 14 | * permissions and limitations under the License. 15 | * 16 | */ 17 | 18 | import { Component, OnInit } from '@angular/core'; 19 | import { MatDialogRef } from '@angular/material/dialog'; 20 | import { Auth } from 'aws-amplify'; 21 | 22 | @Component({ 23 | selector: 'app-newpassword', 24 | templateUrl: './pwdchange-dialog.html' 25 | }) 26 | export class PasswordChangeDialogComponent implements OnInit { 27 | 28 | errorMessage: string; 29 | pwdChangeDone: boolean; 30 | hidepwd = true; 31 | inProgress = false; 32 | 33 | existingPassword: string; 34 | password: string; 35 | pwdrepeat: string; 36 | 37 | constructor( 38 | public dialogRef: MatDialogRef 39 | ) { } 40 | 41 | ngOnInit() { 42 | this.pwdChangeDone = false; 43 | this.errorMessage = null; 44 | } 45 | 46 | btnSubmitPwdChange() { 47 | const that = this; 48 | this.errorMessage = null; 49 | 50 | if (!this.existingPassword || this.existingPassword === '' || !this.password || this.password === '') { 51 | this.errorMessage = 'Please fill in all the fields!'; 52 | return; 53 | } 54 | if (this.password !== this.pwdrepeat) { 55 | this.errorMessage = 'New passwords do not match!'; 56 | return; 57 | } 58 | 59 | that.inProgress = true; 60 | Auth.currentAuthenticatedUser() 61 | .then(user => { 62 | return Auth.changePassword(user, that.existingPassword, that.password); 63 | }) 64 | .then(data => { 65 | that.inProgress = false; 66 | console.log(data); 67 | that.pwdChangeDone = true; 68 | }) 69 | .catch(err => { 70 | that.inProgress = false; 71 | console.log(err); 72 | that.errorMessage = err.message || 'Unable to change password'; 73 | }); 74 | } 75 | 76 | btnClose() { 77 | this.dialogRef.close(null); 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /frontend/src/app/useradmin/dialogs/userprofile.dialog.css: -------------------------------------------------------------------------------- 1 | ul.grouplist li.add { background-color: #96c9fd; padding: 3px 2px 3px 6px; } 2 | ul.grouplist { font-size: 13px; list-style-type: none; margin: 0; padding: 4px; border: 1px solid lightgray; border-radius: 4px; } 3 | ul.grouplist li { background-color: #D0D0D0; padding: 3px 2px 3px 8px; margin: 3px; border-radius: 4px; float: left; } 4 | ul.grouplist li .mat-icon { font-size: 20px; margin-top: 3px; vertical-align: middle; cursor: pointer; } 5 | ul.grouplist li .mat-icon:hover { color: white; } 6 | .label { color: #737373; font-size: 13px; margin-bottom: 4px; } -------------------------------------------------------------------------------- /frontend/src/app/useradmin/dialogs/userprofile.dialog.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

Edit User {{id}}

4 |
5 | 6 |
7 |
8 | {{errorMessage}} 9 |
10 |
11 |
12 |
13 |
14 | 15 | 16 | 17 |
18 |
19 |
20 |
21 | 22 | 23 | 24 |
25 |
26 | 27 | 28 | 29 |
30 |
31 |
32 |
Groups:
33 |
    34 |
  • 35 | add_circle_outline 36 |
  • 37 |
  • {{g}} 38 | highlight_off 39 |
  • 40 |
    41 |
42 |
43 |
44 |
45 | 46 |
47 |
48 | 49 | 51 | 53 | 54 |
55 |
-------------------------------------------------------------------------------- /frontend/src/app/useradmin/myprofile.compo.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 |
5 |

My User Profile

6 |
7 |
8 |
9 |
Loading user profile..
10 | 11 | 12 |
13 | account_circle 14 |
15 | {{userData.username}} 16 | 17 |
18 | 19 |
20 | 21 |
22 |
23 | 24 | 25 | 26 |
27 |
28 | 29 | 30 | 31 |
32 |
33 |
34 |
35 | 36 | 37 | 38 |
39 |
40 | 41 | 42 | 43 |
44 |
45 | 46 |
{{errorMessage}}
47 | 48 |
49 |
50 |
51 |

52 |
53 |
54 |
-------------------------------------------------------------------------------- /frontend/src/app/useradmin/myprofile.compo.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright WebGate Consulting AG, 2020 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at: 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 13 | * implied. See the License for the specific language governing 14 | * permissions and limitations under the License. 15 | * 16 | */ 17 | 18 | import { Component, OnInit } from '@angular/core'; 19 | import { TabsNavService } from '../services/tabs.service'; 20 | import { MatDialog } from '@angular/material/dialog'; 21 | import { PasswordChangeDialogComponent } from './dialogs/pwdchange-dialog'; 22 | import { Auth } from 'aws-amplify'; 23 | 24 | @Component({ 25 | selector: 'app-myprofile', 26 | templateUrl: './myprofile.compo.html' 27 | }) 28 | 29 | export class MyProfileComponent implements OnInit { 30 | 31 | constructor( 32 | private tabsSVC: TabsNavService, 33 | public dialog: MatDialog 34 | ) { } 35 | 36 | loading: boolean; 37 | userLoaded: boolean; 38 | errorMessage: string; 39 | userData: any; 40 | 41 | ngOnInit() { 42 | Auth.currentAuthenticatedUser().then(data => { 43 | console.log('user', data); 44 | this.userData = data; 45 | this.setLoading(false); 46 | this.userLoaded = true; 47 | }).catch(err => { 48 | this.setLoading(false); 49 | this.errorMessage = err.message || 'Error while loading user data'; 50 | }); 51 | } 52 | 53 | btnChangePassword() { 54 | const dialogRef = this.dialog.open(PasswordChangeDialogComponent, { width: '450px', disableClose: true, data: {} }); 55 | 56 | dialogRef.afterClosed().subscribe(result => { 57 | if (!result || !result.action || result.action === 'close') { return; } 58 | if (result.action === 'success') { 59 | console.log('Reloading window after login'); 60 | window.location.reload(); 61 | } 62 | }); 63 | } 64 | 65 | setLoading(on: boolean) { 66 | this.loading = on; 67 | this.tabsSVC.setLoading(on); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /frontend/src/app/useradmin/userlist.compo.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | 5 | help_outline 6 | 7 | 10 |
11 |

User Profiles

12 |
13 | 14 | 15 | 16 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 |
17 | 20 | UsernameLastnameFirstnameE-MailStatus
person{{usr.Username}}{{usr.attribs.family_name}}{{usr.attribs.given_name}}{{usr.attribs.email}}{{usr.Enabled?usr.UserStatus:'Disabled'}}
39 |
40 | -------------------------------------------------------------------------------- /frontend/src/app/useradmin/userlist.compo.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright WebGate Consulting AG, 2020 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at: 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 13 | * implied. See the License for the specific language governing 14 | * permissions and limitations under the License. 15 | * 16 | */ 17 | 18 | import { Component, OnInit } from '@angular/core'; 19 | import { BackendService } from '../services/backend.service'; 20 | import { TabsNavService } from '../services/tabs.service'; 21 | import { MatDialog } from '@angular/material/dialog'; 22 | import { UserProfileDialogComponent } from './dialogs/userprofile.dialog'; 23 | import { NewUserProfileDialogComponent } from './dialogs/newuserprofile.dialog'; 24 | 25 | @Component({ 26 | selector: 'app-userlist', 27 | templateUrl: './userlist.compo.html' 28 | }) 29 | 30 | export class UserListComponent implements OnInit { 31 | 32 | constructor( 33 | private backendSVC: BackendService, 34 | public tabsSVC: TabsNavService, 35 | private dialog: MatDialog 36 | ) { } 37 | 38 | loading: boolean; 39 | viewList: any = []; 40 | maxEntries = 60; // hard limit by cognito! 41 | nextToken: string = null; 42 | 43 | ngOnInit() { 44 | this.loadView(false); 45 | } 46 | 47 | loadView(forceUpdate: boolean): void { 48 | this.setLoading(true); 49 | this.backendSVC.cognitoAction('listCognitoUsers', { maxEntries: this.maxEntries, nextToken: this.nextToken }).then( 50 | (data: any) => { 51 | if (data.success) { 52 | this.viewList = data.list || []; 53 | this.nextToken = data.PaginationToken || null; 54 | } else { 55 | this.tabsSVC.printNotification(data.message || 'Error while loading'); 56 | } 57 | this.setLoading(false); 58 | }, 59 | (err: any) => { 60 | console.error(err); 61 | this.tabsSVC.printNotification('Error while loading'); 62 | this.setLoading(false); 63 | } 64 | ); 65 | } 66 | btnEditUser(thisID: string) { 67 | const dialogRef = this.dialog.open(UserProfileDialogComponent, { width: '650px', disableClose: false, data: { id: thisID } }); 68 | dialogRef.afterClosed().subscribe(result => { 69 | if (result && result.action === 'refreshview') { this.loadView(true); } 70 | }); 71 | } 72 | btnNewUser() { 73 | const dialogRef = this.dialog.open(NewUserProfileDialogComponent, { width: '450px', disableClose: false, data: {} }); 74 | dialogRef.afterClosed().subscribe(result => { 75 | if (result && result.action === 'refreshview') { this.loadView(true); } 76 | }); 77 | } 78 | setLoading(on: boolean) { 79 | this.loading = on; 80 | this.tabsSVC.setLoading(on); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /frontend/src/app/utils/OrderBy.Pipe.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright WebGate Consulting AG, 2020 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at: 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 13 | * implied. See the License for the specific language governing 14 | * permissions and limitations under the License. 15 | * 16 | */ 17 | 18 | import { Pipe, PipeTransform } from '@angular/core'; 19 | @Pipe({ name: 'orderBy' }) 20 | export class OrderByPipe implements PipeTransform { 21 | transform(records: Array, args?: any): any { 22 | return records.sort((a, b) => { 23 | const iA = a[args.property] || ''; 24 | const iB = b[args.property] || ''; 25 | if (iA < iB) { 26 | return -1 * args.direction; 27 | } else if (iA > iB) { 28 | return 1 * args.direction; 29 | } else { 30 | return 0; 31 | } 32 | }); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /frontend/src/app/utils/searchfilterpipe.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright WebGate Consulting AG, 2020 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at: 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 13 | * implied. See the License for the specific language governing 14 | * permissions and limitations under the License. 15 | * 16 | */ 17 | 18 | import { Injectable, Pipe, PipeTransform } from '@angular/core'; // for view filter 19 | @Pipe({ 20 | name: 'searchfilter' 21 | }) 22 | 23 | @Injectable() 24 | export class SearchFilterPipe implements PipeTransform { 25 | transform(items: any[], fields: any[], searchstring: string): any[] { 26 | if (!items || !searchstring) { return items; } 27 | const LCvalue = searchstring.toLowerCase(); 28 | return items.filter((item) => { 29 | if (fields && fields.length > 0) { // search within properties of object array 30 | for (const fld of fields) { 31 | if (typeof item[fld] !== 'undefined') { 32 | const thisFld = item[fld] || ''; 33 | if (thisFld.toLowerCase().indexOf(LCvalue) !== -1) { return true; } 34 | } 35 | } 36 | } else { // search in string array 37 | if (item.toLowerCase().indexOf(LCvalue) !== -1) { return true; } 38 | } 39 | return false; 40 | }); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /frontend/src/app/version.ts: -------------------------------------------------------------------------------- 1 | export const versioninfo = { 2 | version: '2024-08-20-1912' 3 | }; 4 | -------------------------------------------------------------------------------- /frontend/src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WebGateConsultingAG/CloudeeCMS/3752d076a062f4d6fa54a42e159e3b4b0b518d1b/frontend/src/assets/.gitkeep -------------------------------------------------------------------------------- /frontend/src/assets/img/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WebGateConsultingAG/CloudeeCMS/3752d076a062f4d6fa54a42e159e3b4b0b518d1b/frontend/src/assets/img/apple-touch-icon.png -------------------------------------------------------------------------------- /frontend/src/assets/img/favicon-w.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WebGateConsultingAG/CloudeeCMS/3752d076a062f4d6fa54a42e159e3b4b0b518d1b/frontend/src/assets/img/favicon-w.png -------------------------------------------------------------------------------- /frontend/src/assets/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WebGateConsultingAG/CloudeeCMS/3752d076a062f4d6fa54a42e159e3b4b0b518d1b/frontend/src/assets/img/favicon.ico -------------------------------------------------------------------------------- /frontend/src/assets/img/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WebGateConsultingAG/CloudeeCMS/3752d076a062f4d6fa54a42e159e3b4b0b518d1b/frontend/src/assets/img/favicon.png -------------------------------------------------------------------------------- /frontend/src/environments/README.md: -------------------------------------------------------------------------------- 1 | # environment.ts 2 | will be generated by set-env.js during frontend build in CodePipeline. 3 | 4 | Note: This folder should not be empty during deployment to prevent set-env.js from failing. -------------------------------------------------------------------------------- /frontend/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 27 | 28 | 29 | CloudeeCMS 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /frontend/src/main.ts: -------------------------------------------------------------------------------- 1 | 2 | import { enableProdMode } from '@angular/core'; 3 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 4 | 5 | import { AppModule } from './app/app.module'; 6 | import { environment } from './environments/environment'; 7 | 8 | // AWS Amplify for Cognito 9 | import Amplify from '@aws-amplify/core'; 10 | import awsconfig from './app/auth/aws-config'; 11 | Amplify.configure({ Auth: awsconfig }); 12 | 13 | if (environment.production) { 14 | enableProdMode(); 15 | } 16 | 17 | platformBrowserDynamic().bootstrapModule(AppModule) 18 | .catch(err => console.error(err)); 19 | -------------------------------------------------------------------------------- /frontend/src/polyfills.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file includes polyfills needed by Angular and is loaded before the app. 3 | * You can add your own extra polyfills to this file. 4 | * 5 | * This file is divided into 2 sections: 6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. 7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main 8 | * file. 9 | * 10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that 11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), 12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. 13 | * 14 | * Learn more in https://angular.io/guide/browser-support 15 | */ 16 | 17 | /*************************************************************************************************** 18 | * BROWSER POLYFILLS 19 | */ 20 | 21 | /** 22 | * By default, zone.js will patch all possible macroTask and DomEvents 23 | * user can disable parts of macroTask/DomEvents patch by setting following flags 24 | * because those flags need to be set before `zone.js` being loaded, and webpack 25 | * will put import in the top of bundle, so user need to create a separate file 26 | * in this directory (for example: zone-flags.ts), and put the following flags 27 | * into that file, and then add the following code before importing zone.js. 28 | * import './zone-flags.ts'; 29 | * 30 | * The flags allowed in zone-flags.ts are listed here. 31 | * 32 | * The following flags will work for all browsers. 33 | * 34 | * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame 35 | * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick 36 | * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames 37 | * 38 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js 39 | * with the following flag, it will bypass `zone.js` patch for IE/Edge 40 | * 41 | * (window as any).__Zone_enable_cross_context_check = true; 42 | * 43 | */ 44 | 45 | /*************************************************************************************************** 46 | * Zone JS is required by default for Angular itself. 47 | */ 48 | import 'zone.js'; // Included with Angular CLI. 49 | 50 | 51 | /*************************************************************************************************** 52 | * APPLICATION IMPORTS 53 | */ 54 | 55 | // For AWS Amplify 56 | (window as any).global = window; 57 | (window as any).process = { env: { DEBUG: undefined } }; 58 | (window as any).global.util = (window as any).global.util || require("util").util; 59 | -------------------------------------------------------------------------------- /frontend/src/styles.scss: -------------------------------------------------------------------------------- 1 | // Custom Theming for Angular Material 2 | @use '@angular/material' as mat; 3 | // For more information: https://material.angular.io/guide/theming 4 | // Plus imports for other components in your app. 5 | 6 | // Include the common styles for Angular Material. We include this here so that you only 7 | // have to load a single css file for Angular Material in your app. 8 | // Be sure that you only ever include this mixin once! 9 | @include mat.core(); 10 | 11 | // Define the palettes for your theme using the Material Design palettes available in palette.scss 12 | // (imported above). For each palette, you can optionally specify a default, lighter, and darker 13 | // hue. Available color palettes: https://material.io/design/color/ 14 | $aws-serverless-cms-primary: mat.define-palette(mat.$indigo-palette); 15 | $aws-serverless-cms-accent: mat.define-palette(mat.$indigo-palette, A200, A100, A400); 16 | 17 | // The warn palette is optional (defaults to red). 18 | $aws-serverless-cms-warn: mat.define-palette(mat.$indigo-palette); 19 | 20 | // Create the theme object (a Sass map containing all of the palettes). 21 | $aws-serverless-cms-theme: mat.define-light-theme($aws-serverless-cms-primary, $aws-serverless-cms-accent, $aws-serverless-cms-warn); 22 | 23 | // Include theme styles for core and each component used in your app. 24 | // Alternatively, you can import and @include the theme mixins for each component 25 | // that you are using. 26 | @include mat.all-component-themes($aws-serverless-cms-theme); 27 | 28 | /* You can add global styles to this file, and also import other style files */ 29 | 30 | html { height: 100%; } 31 | body { height: 100%; margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; } 32 | 33 | @import "~trumbowyg/dist/ui/trumbowyg.min.css"; -------------------------------------------------------------------------------- /frontend/src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | 3 | import 'zone.js/testing'; 4 | import { getTestBed } from '@angular/core/testing'; 5 | import { 6 | BrowserDynamicTestingModule, 7 | platformBrowserDynamicTesting 8 | } from '@angular/platform-browser-dynamic/testing'; 9 | 10 | declare const require: any; 11 | 12 | // First, initialize the Angular testing environment. 13 | getTestBed().initTestEnvironment( 14 | BrowserDynamicTestingModule, 15 | platformBrowserDynamicTesting(), { 16 | teardown: { destroyAfterEach: false } 17 | } 18 | ); 19 | // Then we find all the tests. 20 | const context = require.context('./', true, /\.spec\.ts$/); 21 | // And load the modules. 22 | context.keys().map(context); 23 | -------------------------------------------------------------------------------- /frontend/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./out-tsc/app", 5 | "types": [] 6 | }, 7 | "include": [ 8 | "src/**/*.ts" 9 | ], 10 | "exclude": [ 11 | "src/test.ts", 12 | "src/**/*.spec.ts" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /frontend/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "baseUrl": "./", 5 | "outDir": "./dist/out-tsc", 6 | "sourceMap": true, 7 | "declaration": false, 8 | "downlevelIteration": true, 9 | "experimentalDecorators": true, 10 | "module": "es2020", 11 | "moduleResolution": "node", 12 | "importHelpers": true, 13 | "target": "es2015", 14 | "typeRoots": [ 15 | "node_modules/@types" 16 | ], 17 | "lib": [ 18 | "es2018", 19 | "dom" 20 | ] 21 | }, 22 | "angularCompilerOptions": { 23 | "fullTemplateTypeCheck": false, // set to false until amplify prod build bug is resolved: https://github.com/aws-amplify/amplify-js/issues/3620 24 | "strictInjectionParameters": true 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /frontend/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./out-tsc/spec", 5 | "types": [ 6 | "jasmine", 7 | "node" 8 | ] 9 | }, 10 | "files": [ 11 | "src/test.ts", 12 | "src/polyfills.ts" 13 | ], 14 | "include": [ 15 | "src/**/*.spec.ts", 16 | "src/**/*.d.ts" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /packaging/cf-firstrun/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aws-serverless-cms-firstrun", 3 | "version": "1.0.0", 4 | "description": "CloudeeCMS first run lambda while deploying from CloudFormation template", 5 | "main": "index.mjs", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "WebGate Consulting AG", 10 | "license": "Apache-2.0" 11 | } 12 | -------------------------------------------------------------------------------- /packaging/create_package.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Create a package for the online updater, ignoring all local node_modules 3 | cd .. 4 | PKGVERSION=`cat ./frontend/src/app/version.ts | grep version: | cut -d"'" -f2` 5 | git archive --format=zip HEAD -o $PKGVERSION.zip 6 | echo $PKGVERSION.zip created. Now uploading to S3. 7 | /usr/bin/aws --profile cloudeecms s3 cp $PKGVERSION.zip s3://cloudeecms-updates/cloudeecms/$PKGVERSION.zip --cache-control max-age=3600 --acl public-read 8 | rm $PKGVERSION.zip --------------------------------------------------------------------------------